toolchain.configure 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
  2. # vim: set filetype=python:
  3. # This Source Code Form is subject to the terms of the Mozilla Public
  4. # License, v. 2.0. If a copy of the MPL was not distributed with this
  5. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. # PGO
  7. # ==============================================================
  8. option(env='MOZ_PGO', help='Build with profile guided optimizations')
  9. set_config('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
  10. add_old_configure_assignment('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
  11. # yasm detection
  12. # ==============================================================
  13. yasm = check_prog('YASM', ['yasm'], allow_missing=True)
  14. @depends_if(yasm)
  15. @checking('yasm version')
  16. def yasm_version(yasm):
  17. version = check_cmd_output(
  18. yasm, '--version',
  19. onerror=lambda: die('Failed to get yasm version.')
  20. ).splitlines()[0].split()[1]
  21. return Version(version)
  22. # Until we move all the yasm consumers out of old-configure.
  23. # bug 1257904
  24. add_old_configure_assignment('_YASM_MAJOR_VERSION',
  25. yasm_version.major)
  26. add_old_configure_assignment('_YASM_MINOR_VERSION',
  27. yasm_version.minor)
  28. @depends(yasm, target)
  29. def yasm_asflags(yasm, target):
  30. if yasm:
  31. asflags = {
  32. ('OSX', 'x86'): '-f macho32',
  33. ('OSX', 'x86_64'): '-f macho64',
  34. ('WINNT', 'x86'): '-f win32',
  35. ('WINNT', 'x86_64'): '-f x64',
  36. }.get((target.os, target.cpu), None)
  37. if asflags is None:
  38. # We're assuming every x86 platform we support that's
  39. # not Windows or Mac is ELF.
  40. if target.cpu == 'x86':
  41. asflags = '-f elf32'
  42. elif target.cpu == 'x86_64':
  43. asflags = '-f elf64'
  44. if asflags:
  45. asflags += ' -rnasm -pnasm'
  46. return asflags
  47. set_config('YASM_ASFLAGS', yasm_asflags)
  48. @depends(yasm_asflags)
  49. def have_yasm(value):
  50. if value:
  51. return True
  52. set_config('HAVE_YASM', have_yasm)
  53. # Until the YASM variable is not necessary in old-configure.
  54. add_old_configure_assignment('YASM', have_yasm)
  55. # Android NDK
  56. # ==============================================================
  57. @depends('--disable-compile-environment', build_project, gonkdir, '--help')
  58. def compiling_android(compile_env, build_project, gonkdir, _):
  59. return compile_env and (gonkdir or build_project in ('mobile/android', 'js'))
  60. include('android-ndk.configure', when=compiling_android)
  61. # MacOS deployment target version
  62. # ==============================================================
  63. # This needs to happen before any compilation test is done.
  64. option('--enable-macos-target', env='MACOSX_DEPLOYMENT_TARGET', nargs=1,
  65. default='10.7', help='Set the minimum MacOS version needed at runtime')
  66. @depends('--enable-macos-target', target)
  67. @imports(_from='os', _import='environ')
  68. def macos_target(value, target):
  69. if value and target.os == 'OSX':
  70. # Ensure every compiler process we spawn uses this value.
  71. environ['MACOSX_DEPLOYMENT_TARGET'] = value[0]
  72. return value[0]
  73. if value and value.origin != 'default':
  74. die('--enable-macos-target cannot be used when targeting %s',
  75. target.os)
  76. set_config('MACOSX_DEPLOYMENT_TARGET', macos_target)
  77. add_old_configure_assignment('MACOSX_DEPLOYMENT_TARGET', macos_target)
  78. # Compiler wrappers
  79. # ==============================================================
  80. # Normally, we'd use js_option and automatically have those variables
  81. # propagated to js/src, but things are complicated by possible additional
  82. # wrappers in CC/CXX, and by other subconfigures that do not handle those
  83. # options and do need CC/CXX altered.
  84. option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
  85. help='Enable compiling with wrappers such as distcc and ccache')
  86. option('--with-ccache', env='CCACHE', nargs='?',
  87. help='Enable compiling with ccache')
  88. @depends_if('--with-ccache')
  89. def ccache(value):
  90. if len(value):
  91. return value
  92. # If --with-ccache was given without an explicit value, we default to
  93. # 'ccache'.
  94. return 'ccache'
  95. ccache = check_prog('CCACHE', progs=(), input=ccache)
  96. @depends_if(ccache)
  97. def using_ccache(ccache):
  98. return True
  99. set_config('MOZ_USING_CCACHE', using_ccache)
  100. @depends('--with-compiler-wrapper', ccache)
  101. @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
  102. def compiler_wrapper(wrapper, ccache):
  103. if wrapper:
  104. raw_wrapper = wrapper[0]
  105. wrapper = shell_split(raw_wrapper)
  106. wrapper_program = find_program(wrapper[0])
  107. if not wrapper_program:
  108. die('Cannot find `%s` from the given compiler wrapper `%s`',
  109. wrapper[0], raw_wrapper)
  110. wrapper[0] = wrapper_program
  111. if ccache:
  112. if wrapper:
  113. return tuple([ccache] + wrapper)
  114. else:
  115. return (ccache,)
  116. elif wrapper:
  117. return tuple(wrapper)
  118. add_old_configure_assignment('COMPILER_WRAPPER', compiler_wrapper)
  119. @depends_if(compiler_wrapper)
  120. def using_compiler_wrapper(compiler_wrapper):
  121. return True
  122. set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
  123. # Cross-compilation related things.
  124. # ==============================================================
  125. js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
  126. help='Prefix for the target toolchain')
  127. @depends('--with-toolchain-prefix', target, cross_compiling)
  128. def toolchain_prefix(value, target, cross_compiling):
  129. if value:
  130. return tuple(value)
  131. if cross_compiling:
  132. return ('%s-' % target.toolchain, '%s-' % target.alias)
  133. @depends(toolchain_prefix, target)
  134. def first_toolchain_prefix(toolchain_prefix, target):
  135. # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
  136. # command line/environment (in which case there's only one value in the tuple),
  137. # or when cross-compiling for Android.
  138. if toolchain_prefix and (target.os == 'Android' or len(toolchain_prefix) == 1):
  139. return toolchain_prefix[0]
  140. set_config('TOOLCHAIN_PREFIX', first_toolchain_prefix)
  141. add_old_configure_assignment('TOOLCHAIN_PREFIX', first_toolchain_prefix)
  142. # Compilers
  143. # ==============================================================
  144. include('compilers-util.configure')
  145. def try_preprocess(compiler, language, source):
  146. return try_invoke_compiler(compiler, language, source, ['-E'])
  147. @imports(_from='mozbuild.configure.constants', _import='CompilerType')
  148. @imports(_from='mozbuild.configure.constants',
  149. _import='CPU_preprocessor_checks')
  150. @imports(_from='mozbuild.configure.constants',
  151. _import='kernel_preprocessor_checks')
  152. @imports(_from='textwrap', _import='dedent')
  153. def get_compiler_info(compiler, language):
  154. '''Returns information about the given `compiler` (command line in the
  155. form of a list or tuple), in the given `language`.
  156. The returned information includes:
  157. - the compiler type (msvc, clang-cl, clang or gcc)
  158. - the compiler version
  159. - the compiler supported language
  160. - the compiler supported language version
  161. '''
  162. # Note: MSVC doesn't expose __STDC_VERSION__. It does expose __STDC__,
  163. # but only when given the -Za option, which disables compiler
  164. # extensions.
  165. # Note: We'd normally do a version check for clang, but versions of clang
  166. # in Xcode have a completely different versioning scheme despite exposing
  167. # the version with the same defines.
  168. # So instead, we make things such that the version is missing when the
  169. # clang used is below the minimum supported version (currently clang 3.6).
  170. # We then only include the version information when the C++ compiler
  171. # matches the feature check, so that an unsupported version of clang would
  172. # have no version number.
  173. check = dedent('''\
  174. #if defined(_MSC_VER)
  175. #if defined(__clang__)
  176. %COMPILER "clang-cl"
  177. %VERSION _MSC_FULL_VER
  178. #else
  179. %COMPILER "msvc"
  180. %VERSION _MSC_FULL_VER
  181. #endif
  182. #elif defined(__clang__)
  183. %COMPILER "clang"
  184. # if !__cplusplus || __has_feature(cxx_alignof)
  185. %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
  186. # endif
  187. #elif defined(__GNUC__)
  188. %COMPILER "gcc"
  189. %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
  190. #endif
  191. #if __cplusplus
  192. %cplusplus __cplusplus
  193. #elif __STDC_VERSION__
  194. %STDC_VERSION __STDC_VERSION__
  195. #elif __STDC__
  196. %STDC_VERSION 198900L
  197. #endif
  198. ''')
  199. # While we're doing some preprocessing, we might as well do some more
  200. # preprocessor-based tests at the same time, to check the toolchain
  201. # matches what we want.
  202. for name, preprocessor_checks in (
  203. ('CPU', CPU_preprocessor_checks),
  204. ('KERNEL', kernel_preprocessor_checks),
  205. ):
  206. for n, (value, condition) in enumerate(preprocessor_checks.iteritems()):
  207. check += dedent('''\
  208. #%(if)s %(condition)s
  209. %%%(name)s "%(value)s"
  210. ''' % {
  211. 'if': 'elif' if n else 'if',
  212. 'condition': condition,
  213. 'name': name,
  214. 'value': value,
  215. })
  216. check += '#endif\n'
  217. # Also check for endianness. The advantage of living in modern times is
  218. # that all the modern compilers we support now have __BYTE_ORDER__ defined
  219. # by the preprocessor, except MSVC, which only supports little endian.
  220. check += dedent('''\
  221. #if _MSC_VER || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  222. %ENDIANNESS "little"
  223. #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  224. %ENDIANNESS "big"
  225. #endif
  226. ''')
  227. result = try_preprocess(compiler, language, check)
  228. if not result:
  229. raise FatalCheckError(
  230. 'Unknown compiler or compiler not supported.')
  231. # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
  232. # have non-ASCII characters. Treat the output as bytearray.
  233. data = {}
  234. for line in result.splitlines():
  235. if line.startswith(b'%'):
  236. k, _, v = line.partition(' ')
  237. k = k.lstrip('%')
  238. data[k] = v.replace(' ', '').lstrip('"').rstrip('"')
  239. log.debug('%s = %s', k, data[k])
  240. try:
  241. type = CompilerType(data['COMPILER'])
  242. except:
  243. raise FatalCheckError(
  244. 'Unknown compiler or compiler not supported.')
  245. cplusplus = int(data.get('cplusplus', '0L').rstrip('L'))
  246. stdc_version = int(data.get('STDC_VERSION', '0L').rstrip('L'))
  247. version = data.get('VERSION')
  248. if version and type in ('msvc', 'clang-cl'):
  249. msc_ver = version
  250. version = msc_ver[0:2]
  251. if len(msc_ver) > 2:
  252. version += '.' + msc_ver[2:4]
  253. if len(msc_ver) > 4:
  254. version += '.' + msc_ver[4:]
  255. if version:
  256. version = Version(version)
  257. return namespace(
  258. type=type,
  259. version=version,
  260. cpu=data.get('CPU'),
  261. kernel=data.get('KERNEL'),
  262. endianness=data.get('ENDIANNESS'),
  263. language='C++' if cplusplus else 'C',
  264. language_version=cplusplus if cplusplus else stdc_version,
  265. )
  266. @imports(_from='mozbuild.shellutil', _import='quote')
  267. def check_compiler(compiler, language, target):
  268. info = get_compiler_info(compiler, language)
  269. flags = []
  270. def append_flag(flag):
  271. if flag not in flags:
  272. if info.type == 'clang-cl':
  273. flags.append('-Xclang')
  274. flags.append(flag)
  275. # Check language standards
  276. # --------------------------------------------------------------------
  277. if language != info.language:
  278. raise FatalCheckError(
  279. '`%s` is not a %s compiler.' % (quote(*compiler), language))
  280. # Note: We do a strict version check because there sometimes are backwards
  281. # incompatible changes in the standard, and not all code that compiles as
  282. # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
  283. # example)
  284. if info.language == 'C' and info.language_version != 199901:
  285. if info.type in ('clang-cl', 'clang', 'gcc'):
  286. append_flag('-std=gnu99')
  287. # Note: MSVC, while supporting C++11, still reports 199711L for __cplusplus.
  288. # Note: this is a strict version check because we used to always add
  289. # -std=gnu++11.
  290. if info.language == 'C++':
  291. if info.type in ('clang', 'gcc') and info.language_version != 201103:
  292. append_flag('-std=gnu++11')
  293. # MSVC 2015 headers include C++14 features, but don't guard them
  294. # with appropriate checks.
  295. if info.type == 'clang-cl' and info.language_version != 201402:
  296. append_flag('-std=c++14')
  297. # We force clang-cl to emulate Visual C++ 2015 Update 3 with fallback to
  298. # cl.exe.
  299. if info.type == 'clang-cl' and info.version != '19.00.24213':
  300. # Those flags are direct clang-cl flags that don't need -Xclang, add
  301. # them directly.
  302. flags.append('-fms-compatibility-version=19.00.24213')
  303. flags.append('-fallback')
  304. # Check compiler target
  305. # --------------------------------------------------------------------
  306. if not info.cpu or info.cpu != target.cpu:
  307. if info.type == 'clang':
  308. append_flag('--target=%s' % target.toolchain)
  309. elif info.type == 'gcc':
  310. same_arch_different_bits = (
  311. ('x86', 'x86_64'),
  312. ('ppc', 'ppc64'),
  313. ('sparc', 'sparc64'),
  314. )
  315. if (target.cpu, info.cpu) in same_arch_different_bits:
  316. append_flag('-m32')
  317. elif (info.cpu, target.cpu) in same_arch_different_bits:
  318. append_flag('-m64')
  319. if not info.kernel or info.kernel != target.kernel:
  320. if info.type == 'clang':
  321. append_flag('--target=%s' % target.toolchain)
  322. if not info.endianness or info.endianness != target.endianness:
  323. if info.type == 'clang':
  324. append_flag('--target=%s' % target.toolchain)
  325. return namespace(
  326. type=info.type,
  327. version=info.version,
  328. target_cpu=info.cpu,
  329. target_kernel=info.kernel,
  330. target_endianness=info.endianness,
  331. flags=flags,
  332. )
  333. @imports(_from='__builtin__', _import='open')
  334. @imports('json')
  335. @imports('subprocess')
  336. @imports('sys')
  337. def get_vc_paths(topsrcdir):
  338. def vswhere(args):
  339. encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
  340. return json.loads(subprocess.check_output([os.path.join(topsrcdir, 'build/win32/vswhere.exe'), '-format', 'json'] + args).decode(encoding, 'replace'))
  341. # Then VS2017 and newer.
  342. for install in vswhere(['-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64']):
  343. path = install['installationPath']
  344. tools_version = open(os.path.join(path, r'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt'), 'rb').read().strip()
  345. if tools_version < '14.20.0000':
  346. tools_path = os.path.join(path, r'VC\Tools\MSVC', tools_version, r'bin\HostX64')
  347. yield (Version(install['installationVersion']), {
  348. 'x64': [os.path.join(tools_path, 'x64')],
  349. # The x64->x86 cross toolchain requires DLLs from the native x64 toolchain.
  350. 'x86': [os.path.join(tools_path, 'x86'), os.path.join(tools_path, 'x64')],
  351. })
  352. # Then VS2019 and newer.
  353. # We can only use the VS2017 toolset installed.
  354. for install in vswhere(['-requires', 'Microsoft.VisualStudio.Component.VC.v141.x86.x64']):
  355. path = install['installationPath']
  356. tools_version = open(os.path.join(path, r'VC\Auxiliary\Build\Microsoft.VCToolsVersion.v141.default.txt'), 'rb').read().strip()
  357. tools_path = os.path.join(path, r'VC\Tools\MSVC', tools_version, r'bin\HostX64')
  358. yield (Version(install['installationVersion']), {
  359. 'x64': [os.path.join(tools_path, 'x64')],
  360. # The x64->x86 cross toolchain requires DLLs from the native x64 toolchain.
  361. 'x86': [os.path.join(tools_path, 'x86'), os.path.join(tools_path, 'x64')],
  362. })
  363. js_option('--with-visual-studio-version', nargs=1,
  364. choices=('2017', '2019'),
  365. help='Select a specific Visual Studio version to use')
  366. @depends('--with-visual-studio-version')
  367. def vs_major_version(value):
  368. if value:
  369. return {'2017': 15,
  370. '2019': 16}[value[0]]
  371. @depends(host, target, vs_major_version, check_build_environment, '--with-visual-studio-version')
  372. @imports(_from='__builtin__', _import='sorted')
  373. @imports(_from='operator', _import='itemgetter')
  374. @imports('platform')
  375. def vc_compiler_path(host, target, vs_major_version, env, vs_release_name):
  376. if host.kernel != 'WINNT':
  377. return
  378. vc_target = {
  379. 'x86': 'x86',
  380. 'x86_64': 'x64',
  381. 'arm': 'arm',
  382. }.get(target.cpu)
  383. if vc_target is None:
  384. return
  385. all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
  386. if not all_versions:
  387. return
  388. if vs_major_version:
  389. versions = [d for (v, d) in all_versions if v.major == vs_major_version]
  390. if not versions:
  391. die('Visual Studio %s could not be found!' % vs_release_name)
  392. data = versions[0]
  393. else:
  394. # Choose the newest version.
  395. data = all_versions[-1][1]
  396. paths = data.get(vc_target)
  397. if not paths:
  398. return
  399. return paths
  400. @depends(vc_compiler_path)
  401. @imports('os')
  402. def toolchain_search_path(vc_compiler_path):
  403. if vc_compiler_path:
  404. result = [os.environ.get('PATH')]
  405. result.extend(vc_compiler_path)
  406. # We're going to alter PATH for good in windows.configure, but we also
  407. # need to do it for the valid_compiler() check below.
  408. os.environ['PATH'] = os.pathsep.join(result)
  409. return result
  410. @template
  411. def default_c_compilers(host_or_target):
  412. '''Template defining the set of default C compilers for the host and
  413. target platforms.
  414. `host_or_target` is either `host` or `target` (the @depends functions
  415. from init.configure.
  416. '''
  417. assert host_or_target in (host, target)
  418. @depends(host_or_target, target, toolchain_prefix)
  419. def default_c_compilers(host_or_target, target, toolchain_prefix):
  420. gcc = ('gcc',)
  421. if toolchain_prefix and host_or_target is target:
  422. gcc = tuple('%sgcc' % p for p in toolchain_prefix) + gcc
  423. if host_or_target.kernel == 'WINNT':
  424. return ('cl', 'clang-cl') + gcc + ('clang',)
  425. if host_or_target.kernel == 'Darwin':
  426. return ('clang',)
  427. return gcc + ('clang',)
  428. return default_c_compilers
  429. @template
  430. def default_cxx_compilers(c_compiler):
  431. '''Template defining the set of default C++ compilers for the host and
  432. target platforms.
  433. `c_compiler` is the @depends function returning a Compiler instance for
  434. the desired platform.
  435. Because the build system expects the C and C++ compilers to be from the
  436. same compiler suite, we derive the default C++ compilers from the C
  437. compiler that was found if none was provided.
  438. '''
  439. @depends(c_compiler)
  440. def default_cxx_compilers(c_compiler):
  441. dir = os.path.dirname(c_compiler.compiler)
  442. file = os.path.basename(c_compiler.compiler)
  443. if c_compiler.type == 'gcc':
  444. return (os.path.join(dir, file.replace('gcc', 'g++')),)
  445. if c_compiler.type == 'clang':
  446. return (os.path.join(dir, file.replace('clang', 'clang++')),)
  447. return (c_compiler.compiler,)
  448. return default_cxx_compilers
  449. @template
  450. def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
  451. other_c_compiler=None):
  452. '''Template handling the generic base checks for the compiler for the
  453. given `language` on the given platform (`host_or_target`).
  454. `host_or_target` is either `host` or `target` (the @depends functions
  455. from init.configure.
  456. When the language is 'C++', `c_compiler` is the result of the `compiler`
  457. template for the language 'C' for the same `host_or_target`.
  458. When `host_or_target` is `host`, `other_compiler` is the result of the
  459. `compiler` template for the same `language` for `target`.
  460. When `host_or_target` is `host` and the language is 'C++',
  461. `other_c_compiler` is the result of the `compiler` template for the
  462. language 'C' for `target`.
  463. '''
  464. assert host_or_target in (host, target)
  465. assert language in ('C', 'C++')
  466. assert language == 'C' or c_compiler is not None
  467. assert host_or_target == target or other_compiler is not None
  468. assert language == 'C' or host_or_target == target or \
  469. other_c_compiler is not None
  470. host_or_target_str = {
  471. host: 'host',
  472. target: 'target',
  473. }[host_or_target]
  474. var = {
  475. ('C', target): 'CC',
  476. ('C++', target): 'CXX',
  477. ('C', host): 'HOST_CC',
  478. ('C++', host): 'HOST_CXX',
  479. }[language, host_or_target]
  480. default_compilers = {
  481. 'C': lambda: default_c_compilers(host_or_target),
  482. 'C++': lambda: default_cxx_compilers(c_compiler),
  483. }[language]()
  484. what='the %s %s compiler' % (host_or_target_str, language)
  485. option(env=var, nargs=1, help='Path to %s' % what)
  486. # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
  487. # HOST_CXX variables.
  488. @depends_if(var)
  489. @imports(_from='itertools', _import='takewhile')
  490. @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
  491. def provided_compiler(cmd):
  492. # Historically, the compiler variables have contained more than the
  493. # path to the compiler itself. So for backwards compatibility, try to
  494. # find what is what in there, assuming the first dash-prefixed item is
  495. # a compiler option, the item before that is the compiler, and anything
  496. # before that is a compiler wrapper.
  497. cmd = shell_split(cmd[0])
  498. without_flags = list(takewhile(lambda x: not x.startswith('-'), cmd))
  499. return namespace(
  500. wrapper=without_flags[:-1],
  501. compiler=without_flags[-1],
  502. flags=cmd[len(without_flags):],
  503. )
  504. # Derive the host compiler from the corresponding target compiler when no
  505. # explicit compiler was given and we're not cross compiling. For the C++
  506. # compiler, though, prefer to derive from the host C compiler when it
  507. # doesn't match the target C compiler.
  508. # As a special case, since clang supports all kinds of targets in the same
  509. # executable, when cross compiling with clang, default to the same compiler
  510. # as the target compiler, resetting flags.
  511. if host_or_target == host:
  512. if other_c_compiler is not None:
  513. args = (c_compiler, other_c_compiler)
  514. else:
  515. args = ()
  516. @depends(provided_compiler, other_compiler, cross_compiling, *args)
  517. def provided_compiler(value, other_compiler, cross_compiling, *args):
  518. if value:
  519. return value
  520. c_compiler, other_c_compiler = args if args else (None, None)
  521. if not cross_compiling and c_compiler == other_c_compiler:
  522. return other_compiler
  523. if cross_compiling and other_compiler.type == 'clang':
  524. return namespace(**{
  525. k: [] if k == 'flags' else v
  526. for k, v in other_compiler.__dict__.iteritems()
  527. })
  528. # Normally, we'd use `var` instead of `_var`, but the interaction with
  529. # old-configure complicates things, and for now, we a) can't take the plain
  530. # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
  531. # old-configure AC_SUBST it (because it's autoconf doing it, not us)
  532. compiler = check_prog('_%s' % var, what=what, progs=default_compilers,
  533. input=provided_compiler.compiler,
  534. paths=toolchain_search_path)
  535. @depends(compiler, provided_compiler, compiler_wrapper, host_or_target)
  536. @checking('whether %s can be used' % what, lambda x: bool(x))
  537. @imports(_from='mozbuild.shellutil', _import='quote')
  538. def valid_compiler(compiler, provided_compiler, compiler_wrapper,
  539. host_or_target):
  540. wrapper = list(compiler_wrapper or ())
  541. if provided_compiler:
  542. provided_wrapper = list(provided_compiler.wrapper)
  543. # When doing a subconfigure, the compiler is set by old-configure
  544. # and it contains the wrappers from --with-compiler-wrapper and
  545. # --with-ccache.
  546. if provided_wrapper[:len(wrapper)] == wrapper:
  547. provided_wrapper = provided_wrapper[len(wrapper):]
  548. wrapper.extend(provided_wrapper)
  549. flags = provided_compiler.flags
  550. else:
  551. flags = []
  552. # Ideally, we'd always use the absolute path, but unfortunately, on
  553. # Windows, the compiler is very often in a directory containing spaces.
  554. # Unfortunately, due to the way autoconf does its compiler tests with
  555. # eval, that doesn't work out. So in that case, check that the
  556. # compiler can still be found in $PATH, and use the file name instead
  557. # of the full path.
  558. if quote(compiler) != compiler:
  559. full_path = os.path.abspath(compiler)
  560. compiler = os.path.basename(compiler)
  561. found_compiler = find_program(compiler)
  562. if not found_compiler:
  563. die('%s is not in your $PATH'
  564. % quote(os.path.dirname(full_path)))
  565. if os.path.normcase(find_program(compiler)) != os.path.normcase(
  566. full_path):
  567. die('Found `%s` before `%s` in your $PATH. '
  568. 'Please reorder your $PATH.',
  569. quote(os.path.dirname(found_compiler)),
  570. quote(os.path.dirname(full_path)))
  571. info = check_compiler(wrapper + [compiler] + flags, language,
  572. host_or_target)
  573. # Check that the additional flags we got are enough to not require any
  574. # more flags.
  575. if info.flags:
  576. flags += info.flags
  577. info = check_compiler(wrapper + [compiler] + flags, language,
  578. host_or_target)
  579. if not info.target_cpu or info.target_cpu != host_or_target.cpu:
  580. raise FatalCheckError(
  581. '%s %s compiler target CPU (%s) does not match --%s CPU (%s)'
  582. % (host_or_target_str.capitalize(), language,
  583. info.target_cpu or 'unknown', host_or_target_str,
  584. host_or_target.raw_cpu))
  585. if not info.target_kernel or (info.target_kernel !=
  586. host_or_target.kernel):
  587. raise FatalCheckError(
  588. '%s %s compiler target kernel (%s) does not match --%s kernel (%s)'
  589. % (host_or_target_str.capitalize(), language,
  590. info.target_kernel or 'unknown', host_or_target_str,
  591. host_or_target.kernel))
  592. if not info.target_endianness or (info.target_endianness !=
  593. host_or_target.endianness):
  594. raise FatalCheckError(
  595. '%s %s compiler target endianness (%s) does not match --%s '
  596. 'endianness (%s)'
  597. % (host_or_target_str.capitalize(), language,
  598. info.target_endianness or 'unknown', host_or_target_str,
  599. host_or_target.endianness))
  600. if info.flags:
  601. raise FatalCheckError(
  602. 'Unknown compiler or compiler not supported.')
  603. # Compiler version checks
  604. # ===================================================
  605. # Check the compiler version here instead of in `compiler_version` so
  606. # that the `checking` message doesn't pretend the compiler can be used
  607. # to then bail out one line later.
  608. if info.type == 'gcc' and info.version < '4.8.0':
  609. raise FatalCheckError(
  610. 'Only GCC 4.8 or newer is supported (found version %s).'
  611. % info.version)
  612. # If you want to bump the version check here search for
  613. # __cpp_static_assert above, and see the associated comment.
  614. if info.type == 'clang' and not info.version:
  615. raise FatalCheckError(
  616. 'Only clang/llvm 3.6 or newer is supported.')
  617. if info.type == 'msvc':
  618. # Require MSVC version 15.9.13 or higher
  619. # Treat anything else as untested.
  620. if info.version < '19.16.27021':
  621. raise FatalCheckError(
  622. 'This version (%s) of the MSVC compiler is not '
  623. 'supported.\n'
  624. 'You must install Visual Studio 2017 version '
  625. '15.9.13 or later in order to build.\n'
  626. 'Windows_Build_Prerequisites' % info.version)
  627. if info.version >= '19.20.0':
  628. raise FatalCheckError(
  629. 'This version (%s) of the MSVC compiler is not '
  630. 'supported.\n'
  631. 'You must install Visual Studio 2017 version '
  632. '15.9.13 or later in order to build.\n'
  633. 'Windows_Build_Prerequisites' % info.version)
  634. return namespace(
  635. wrapper=wrapper,
  636. compiler=compiler,
  637. flags=flags,
  638. type=info.type,
  639. version=info.version,
  640. language=language,
  641. )
  642. @depends(valid_compiler)
  643. @checking('%s version' % what)
  644. def compiler_version(compiler):
  645. return compiler.version
  646. if language == 'C++':
  647. @depends(valid_compiler, c_compiler)
  648. def valid_compiler(compiler, c_compiler):
  649. if compiler.type != c_compiler.type:
  650. die('The %s C compiler is %s, while the %s C++ compiler is '
  651. '%s. Need to use the same compiler suite.',
  652. host_or_target_str, c_compiler.type,
  653. host_or_target_str, compiler.type)
  654. if compiler.version != c_compiler.version:
  655. die('The %s C compiler is version %s, while the %s C++ '
  656. 'compiler is version %s. Need to use the same compiler '
  657. 'version.',
  658. host_or_target_str, c_compiler.version,
  659. host_or_target_str, compiler.version)
  660. return compiler
  661. # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
  662. # and the flags that were part of the user input for those variables to
  663. # be provided.
  664. add_old_configure_assignment(var, depends_if(valid_compiler)(
  665. lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
  666. # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
  667. # old-configure to do some of its still existing checks.
  668. if language == 'C':
  669. set_config(
  670. '%s_TYPE' % var, valid_compiler.type)
  671. add_old_configure_assignment(
  672. '%s_TYPE' % var, valid_compiler.type)
  673. add_old_configure_assignment(
  674. '%s_VERSION' % var, valid_compiler.version)
  675. valid_compiler = compiler_class(valid_compiler)
  676. def compiler_error():
  677. raise FatalCheckError('Failed compiling a simple %s source with %s'
  678. % (language, what))
  679. valid_compiler.try_compile(check_msg='%s works' % what,
  680. onerror=compiler_error)
  681. # Set CPP/CXXCPP for both the build system and old-configure. We don't
  682. # need to check this works for preprocessing, because we already relied
  683. # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
  684. # in the first place.
  685. if host_or_target == target:
  686. pp_var = {
  687. 'C': 'CPP',
  688. 'C++': 'CXXCPP',
  689. }[language]
  690. preprocessor = depends_if(valid_compiler)(
  691. lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
  692. set_config(pp_var, preprocessor)
  693. add_old_configure_assignment(pp_var, preprocessor)
  694. if language == 'C':
  695. linker_var = {
  696. target: 'LD',
  697. host: 'HOST_LD',
  698. }[host_or_target]
  699. @deprecated_option(env=linker_var, nargs=1)
  700. def linker(value):
  701. if value:
  702. return value[0]
  703. @depends(valid_compiler, linker)
  704. def unused_linker(compiler, linker):
  705. if linker and compiler.type != 'msvc':
  706. log.warning('The value of %s is not used by this build system.'
  707. % linker_var)
  708. if host_or_target == target:
  709. @depends(valid_compiler)
  710. def is_msvc(compiler):
  711. return compiler.type == 'msvc'
  712. imply_option('LINK', linker, reason='LD', when=is_msvc)
  713. return valid_compiler
  714. c_compiler = compiler('C', target)
  715. cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
  716. host_c_compiler = compiler('C', host, other_compiler=c_compiler)
  717. host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
  718. other_compiler=cxx_compiler,
  719. other_c_compiler=c_compiler)
  720. # Generic compiler-based conditions.
  721. non_msvc_compiler = depends(c_compiler)(lambda info: info.type != 'msvc')
  722. building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc')
  723. include('compile-checks.configure')
  724. @depends(have_64_bit,
  725. try_compile(body='static_assert(sizeof(void *) == 8, "")',
  726. check_msg='for 64-bit OS'))
  727. def check_have_64_bit(have_64_bit, compiler_have_64_bit):
  728. if have_64_bit != compiler_have_64_bit:
  729. configure_error('The target compiler does not agree with configure '
  730. 'about the target bitness.')
  731. @depends(c_compiler)
  732. def default_debug_flags(compiler_info):
  733. # Debug info is ON by default.
  734. if compiler_info.type in ('msvc', 'clang-cl'):
  735. return '-Zi'
  736. return '-g'
  737. option(env='MOZ_DEBUG_FLAGS',
  738. nargs=1,
  739. help='Debug compiler flags')
  740. imply_option('--enable-debug-symbols',
  741. depends_if('--enable-debug')(lambda v: v))
  742. js_option('--enable-debug-symbols',
  743. nargs='?',
  744. default=True,
  745. help='Enable debug symbols using the given compiler flags')
  746. set_config('MOZ_DEBUG_SYMBOLS',
  747. depends_if('--enable-debug-symbols')(lambda _: True))
  748. @depends('MOZ_DEBUG_FLAGS', '--enable-debug-symbols', default_debug_flags)
  749. def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
  750. # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
  751. # --enable-debug-symbols takes precedence. Note, the value of
  752. # --enable-debug-symbols may be implied by --enable-debug.
  753. if len(enable_debug_flags):
  754. return enable_debug_flags[0]
  755. if env_debug_flags:
  756. return env_debug_flags[0]
  757. return default_debug_flags
  758. set_config('MOZ_DEBUG_FLAGS', debug_flags)
  759. add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
  760. # Some standard library headers (notably bionic on Android) declare standard
  761. # functions (e.g. getchar()) and also #define macros for those standard
  762. # functions. libc++ deals with this by doing something like the following
  763. # (explanatory comments added):
  764. #
  765. # #ifdef FUNC
  766. # // Capture the definition of FUNC.
  767. # inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
  768. # #undef FUNC
  769. # // Use a real inline definition.
  770. # inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
  771. # #endif
  772. #
  773. # _LIBCPP_INLINE_VISIBILITY is typically defined as:
  774. #
  775. # __attribute__((__visibility__("hidden"), __always_inline__))
  776. #
  777. # Unfortunately, this interacts badly with our system header wrappers, as the:
  778. #
  779. # #pragma GCC visibility push(default)
  780. #
  781. # that they do prior to including the actual system header is treated by the
  782. # compiler as an explicit declaration of visibility on every function declared
  783. # in the header. Therefore, when the libc++ code above is encountered, it is
  784. # as though the compiler has effectively seen:
  785. #
  786. # int FUNC(...) __attribute__((__visibility__("default")));
  787. # int FUNC(...) __attribute__((__visibility__("hidden")));
  788. #
  789. # and the compiler complains about the mismatched visibility declarations.
  790. #
  791. # However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
  792. # existing definition. We can therefore define it to the empty string (since
  793. # we are properly managing visibility ourselves) and avoid this whole mess.
  794. # Note that we don't need to do this with gcc, as libc++ detects gcc and
  795. # effectively does the same thing we are doing here.
  796. @depends(c_compiler, target)
  797. def libcxx_inline_visibility(c_compiler, target):
  798. if c_compiler.type == 'clang' and target.os == 'Android':
  799. return ''
  800. set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_inline_visibility)
  801. set_define('_LIBCPP_INLINE_VISIBILITY_EXCEPT_GCC49', libcxx_inline_visibility)
  802. @depends(target, check_build_environment)
  803. def visibility_flags(target, env):
  804. if target.os != 'WINNT':
  805. if target.kernel == 'Darwin':
  806. return ('-fvisibility=hidden', '-fvisibility-inlines-hidden')
  807. return ('-I%s/system_wrappers' % os.path.join(env.dist),
  808. '-include',
  809. '%s/config/gcc_hidden.h' % env.topsrcdir)
  810. @depends(target, visibility_flags)
  811. def wrap_system_includes(target, visibility_flags):
  812. if visibility_flags and target.kernel != 'Darwin':
  813. return True
  814. set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE',
  815. depends(visibility_flags)(lambda v: bool(v) or None))
  816. set_define('HAVE_VISIBILITY_ATTRIBUTE',
  817. depends(visibility_flags)(lambda v: bool(v) or None))
  818. set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes)
  819. set_config('VISIBILITY_FLAGS', visibility_flags)
  820. @depends(target)
  821. def is_windows(target):
  822. return target.kernel == 'WINNT'
  823. include('windows.configure', when=is_windows)
  824. include('rust.configure')