Bug 1589288 [wpt PR 19747] - autofocus: Change the timing of 'flush autofocus candida...
[gecko.git] / build / moz.configure / toolchain.configure
blob2c09b4f3b0aeee2f2d4ece55fd5aae10947cb6a5
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/.
7 imply_option('--enable-release', mozilla_official)
8 imply_option('--enable-release', depends_if('MOZ_AUTOMATION')(lambda x: True))
10 js_option('--enable-release',
11           default=milestone.is_release_or_beta,
12           help='{Build|Do not build} with more conservative, release '
13                'engineering-oriented options.{ This may slow down builds.|}')
16 @depends('--enable-release')
17 def developer_options(value):
18     if not value:
19         return True
22 add_old_configure_assignment('DEVELOPER_OPTIONS', developer_options)
23 set_config('DEVELOPER_OPTIONS', developer_options)
25 # Code optimization
26 # ==============================================================
28 js_option('--disable-optimize',
29           nargs='?',
30           help='Disable optimizations via compiler flags')
33 @depends('--enable-optimize', '--help')
34 def moz_optimize(option, _):
35     flags = None
37     if len(option):
38         val = '2'
39         flags = option[0]
40     elif option:
41         val = '1'
42     else:
43         val = None
45     return namespace(
46         optimize=val,
47         flags=flags,
48     )
51 set_config('MOZ_OPTIMIZE', moz_optimize.optimize)
52 add_old_configure_assignment('MOZ_OPTIMIZE', moz_optimize.optimize)
53 add_old_configure_assignment('MOZ_CONFIGURE_OPTIMIZE_FLAGS', moz_optimize.flags)
55 # yasm detection
56 # ==============================================================
57 yasm = check_prog('YASM', ['yasm'], allow_missing=True)
60 @depends_if(yasm)
61 @checking('yasm version')
62 def yasm_version(yasm):
63     version = check_cmd_output(
64         yasm, '--version',
65         onerror=lambda: die('Failed to get yasm version.')
66     ).splitlines()[0].split()[1]
67     return Version(version)
70 @depends(yasm, target)
71 def yasm_asflags(yasm, target):
72     if yasm:
73         asflags = {
74             ('OSX', 'x86'): ['-f', 'macho32'],
75             ('OSX', 'x86_64'): ['-f', 'macho64'],
76             ('WINNT', 'x86'): ['-f', 'win32'],
77             ('WINNT', 'x86_64'): ['-f', 'x64'],
78         }.get((target.os, target.cpu), None)
79         if asflags is None:
80             # We're assuming every x86 platform we support that's
81             # not Windows or Mac is ELF.
82             if target.cpu == 'x86':
83                 asflags = ['-f', 'elf32']
84             elif target.cpu == 'x86_64':
85                 asflags = ['-f', 'elf64']
86         if asflags:
87             asflags += ['-rnasm', '-pnasm']
88         return asflags
91 set_config('YASM_ASFLAGS', yasm_asflags)
94 # Android NDK
95 # ==============================================================
98 @depends('--disable-compile-environment', target)
99 def compiling_android(compile_env, target):
100     return compile_env and target.os == 'Android'
103 include('android-ndk.configure', when=compiling_android)
105 with only_when(target_is_osx):
106     # MacOS deployment target version
107     # ==============================================================
108     # This needs to happen before any compilation test is done.
110     option('--enable-macos-target', env='MACOSX_DEPLOYMENT_TARGET', nargs=1,
111            default='10.9', help='Set the minimum MacOS version needed at runtime')
114     @depends('--enable-macos-target')
115     @imports(_from='os', _import='environ')
116     def macos_target(value):
117         if value:
118             # Ensure every compiler process we spawn uses this value.
119             environ['MACOSX_DEPLOYMENT_TARGET'] = value[0]
120             return value[0]
123     set_config('MACOSX_DEPLOYMENT_TARGET', macos_target)
124     add_old_configure_assignment('MACOSX_DEPLOYMENT_TARGET', macos_target)
127 @depends(host)
128 def host_is_osx(host):
129     if host.os == 'OSX':
130         return True
133 with only_when(host_is_osx | target_is_osx):
134     # MacOS SDK
135     # =========
136     js_option('--with-macos-sdk', env='MACOS_SDK_DIR', nargs=1,
137            help='Location of platform SDK to use')
139     @depends('--with-macos-sdk', host)
140     @imports(_from='os.path', _import='isdir')
141     @imports(_from='biplist', _import='readPlist')
142     def macos_sdk(sdk, host):
143         sdk_min_version = Version('10.11')
144         sdk_max_version = Version('10.14')
146         if sdk:
147             sdk = sdk[0]
148         elif host.os == 'OSX':
149             sdk = check_cmd_output('xcrun', '--show-sdk-path', onerror=lambda: '').rstrip()
150             if not sdk:
151                 die('Could not find the macOS SDK. Please use --with-macos-sdk to give '
152                     'the path to a macOS SDK.')
153         else:
154             die('Need a macOS SDK when targeting macOS. Please use --with-macos-sdk '
155                 'to give the path to a macOS SDK.')
157         if not isdir(sdk):
158             die('SDK not found in %s. When using --with-macos-sdk, you must specify a '
159                 'valid SDK. SDKs are installed when the optional cross-development '
160                 'tools are selected during the Xcode/Developer Tools installation.'
161                 % sdk)
162         obj = readPlist(os.path.join(sdk, 'SDKSettings.plist'))
163         if not obj:
164             die('Error parsing SDKSettings.plist in the SDK directory: %s' % sdk)
165         if 'Version' not in obj:
166             die('Error finding Version information in SDKSettings.plist from the SDK: %s' % sdk)
167         version = Version(obj['Version'])
168         if version < sdk_min_version:
169             die('SDK version "%s" is too old. Please upgrade to at least %s. '
170                 'You may need to point to it using --with-macos-sdk=<path> in your '
171                 'mozconfig. Various SDK versions are available from '
172                 'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_min_version))
173         if version > sdk_max_version:
174             die('SDK version "%s" is unsupported. Please downgrade to version '
175                 '%s. You may need to point to it using --with-macos-sdk=<path> in '
176                 'your mozconfig. Various SDK versions are available from '
177                 'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_max_version))
178         return sdk
180     set_config('MACOS_SDK_DIR', macos_sdk)
183 with only_when(target_is_osx):
184     with only_when(cross_compiling):
185         option('--with-macos-private-frameworks',
186                env="MACOS_PRIVATE_FRAMEWORKS_DIR", nargs=1,
187                help='Location of private frameworks to use')
189         @depends_if('--with-macos-private-frameworks')
190         @imports(_from='os.path', _import='isdir')
191         def macos_private_frameworks(value):
192             if value and not isdir(value[0]):
193                 die('PrivateFrameworks not found not found in %s. When using '
194                     '--with-macos-private-frameworks, you must specify a valid '
195                     'directory', value[0])
196             return value[0]
198     @depends(macos_private_frameworks)
199     def macos_private_frameworks(value):
200         if value:
201             return value
202         return '/System/Library/PrivateFrameworks'
204     set_config('MACOS_PRIVATE_FRAMEWORKS_DIR', macos_private_frameworks)
207 with only_when(host_is_osx):
208     # Xcode state
209     # ===========
210     js_option('--disable-xcode-checks',
211               help='Do not check that Xcode is installed and properly configured')
214     @depends(host, '--disable-xcode-checks')
215     def xcode_path(host, xcode_checks):
216         if host.kernel != 'Darwin' or not xcode_checks:
217             return
219         # xcode-select -p prints the path to the installed Xcode. It
220         # should exit 0 and return non-empty result if Xcode is installed.
222         def bad_xcode_select():
223             die('Could not find installed Xcode; install Xcode from the App '
224                 'Store, run it once to perform initial configuration, and then '
225                 'try again; in the rare case you wish to build without Xcode '
226                 'installed, add the --disable-xcode-checks configure flag')
228         xcode_path = check_cmd_output('xcode-select', '--print-path',
229                                       onerror=bad_xcode_select).strip()
231         if not xcode_path:
232             bad_xcode_select()
234         # Now look for the Command Line Tools.
235         def no_cltools():
236             die('Could not find installed Xcode Command Line Tools; '
237                 'run `xcode-select --install` and follow the instructions '
238                 'to install them then try again; if you wish to build without '
239                 'Xcode Command Line Tools installed, '
240                 'add the --disable-xcode-checks configure flag')
242         check_cmd_output('pkgutil', '--pkg-info',
243                          'com.apple.pkg.CLTools_Executables',
244                          onerror=no_cltools)
246         return xcode_path
249     set_config('XCODE_PATH', xcode_path)
252 # Compiler wrappers
253 # ==============================================================
254 # Normally, we'd use js_option and automatically have those variables
255 # propagated to js/src, but things are complicated by possible additional
256 # wrappers in CC/CXX, and by other subconfigures that do not handle those
257 # options and do need CC/CXX altered.
258 option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
259        help='Enable compiling with wrappers such as distcc and ccache')
261 js_option('--with-ccache', env='CCACHE', nargs='?',
262           help='Enable compiling with ccache')
265 @depends_if('--with-ccache')
266 def ccache(value):
267     if len(value):
268         return value
269     # If --with-ccache was given without an explicit value, we default to
270     # 'ccache'.
271     return 'ccache'
274 ccache = check_prog('CCACHE', progs=(), input=ccache)
276 js_option(env='CCACHE_PREFIX',
277           nargs=1,
278           help='Compiler prefix to use when using ccache')
280 ccache_prefix = depends_if('CCACHE_PREFIX')(lambda prefix: prefix[0])
281 set_config('CCACHE_PREFIX', ccache_prefix)
283 # Distinguish ccache from sccache.
286 @depends_if(ccache)
287 def ccache_is_sccache(ccache):
288     return check_cmd_output(ccache, '--version').startswith('sccache')
291 @depends(ccache, ccache_is_sccache)
292 def using_ccache(ccache, ccache_is_sccache):
293     return ccache and not ccache_is_sccache
296 @depends_if(ccache, ccache_is_sccache)
297 def using_sccache(ccache, ccache_is_sccache):
298     return ccache and ccache_is_sccache
300 js_option(env='RUSTC_WRAPPER', nargs=1,
301           help='Wrap rust compilation with given tool')
303 @depends(ccache, ccache_is_sccache, 'RUSTC_WRAPPER')
304 @imports(_from='textwrap', _import='dedent')
305 @imports('os')
306 def check_sccache_version(ccache, ccache_is_sccache, rustc_wrapper):
307     sccache_min_version = Version('0.2.12')
309     def check_version(path):
310         out = check_cmd_output(path, '--version')
311         version = Version(out.rstrip().split()[-1])
312         if version < sccache_min_version:
313             die(dedent('''\
314             sccache %s or later is required. sccache in use at %s has
315             version %s.
317             Please upgrade or acquire a new version with |./mach bootstrap|.
318             '''), sccache_min_version, path, version)
320     if ccache and ccache_is_sccache:
321         check_version(ccache)
323     if (rustc_wrapper and
324         (os.path.splitext(os.path.basename(rustc_wrapper[0]))[0].lower() ==
325          'sccache')):
326         check_version(rustc_wrapper[0])
328 set_config('MOZ_USING_CCACHE', using_ccache)
329 set_config('MOZ_USING_SCCACHE', using_sccache)
331 option(env='SCCACHE_VERBOSE_STATS',
332        help='Print verbose sccache stats after build')
335 @depends(using_sccache, 'SCCACHE_VERBOSE_STATS')
336 def sccache_verbose_stats(using_sccache, verbose_stats):
337     return using_sccache and bool(verbose_stats)
340 set_config('SCCACHE_VERBOSE_STATS', sccache_verbose_stats)
343 @depends('--with-compiler-wrapper', ccache)
344 @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
345 def compiler_wrapper(wrapper, ccache):
346     if wrapper:
347         raw_wrapper = wrapper[0]
348         wrapper = shell_split(raw_wrapper)
349         wrapper_program = find_program(wrapper[0])
350         if not wrapper_program:
351             die('Cannot find `%s` from the given compiler wrapper `%s`',
352                 wrapper[0], raw_wrapper)
353         wrapper[0] = wrapper_program
355     if ccache:
356         if wrapper:
357             return tuple([ccache] + wrapper)
358         else:
359             return (ccache,)
360     elif wrapper:
361         return tuple(wrapper)
364 @depends_if(compiler_wrapper)
365 def using_compiler_wrapper(compiler_wrapper):
366     return True
369 set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
372 # GC rooting and hazard analysis.
373 # ==============================================================
374 option(env='MOZ_HAZARD', help='Build for the GC rooting hazard analysis')
377 @depends('MOZ_HAZARD')
378 def hazard_analysis(value):
379     if value:
380         return True
383 set_config('MOZ_HAZARD', hazard_analysis)
386 # Cross-compilation related things.
387 # ==============================================================
388 js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
389           help='Prefix for the target toolchain')
392 @depends('--with-toolchain-prefix', target, cross_compiling)
393 def toolchain_prefix(value, target, cross_compiling):
394     if value:
395         return tuple(value)
396     if cross_compiling:
397         return ('%s-' % target.toolchain, '%s-' % target.alias)
400 @depends(toolchain_prefix, target)
401 def first_toolchain_prefix(toolchain_prefix, target):
402     # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
403     # command line/environment (in which case there's only one value in the tuple),
404     # or when cross-compiling for Android or OSX.
405     if toolchain_prefix and (target.os in ('Android', 'OSX') or len(toolchain_prefix) == 1):
406         return toolchain_prefix[0]
409 set_config('TOOLCHAIN_PREFIX', first_toolchain_prefix)
410 add_old_configure_assignment('TOOLCHAIN_PREFIX', first_toolchain_prefix)
413 # Compilers
414 # ==============================================================
415 include('compilers-util.configure')
418 def try_preprocess(compiler, language, source):
419     return try_invoke_compiler(compiler, language, source, ['-E'])
422 @imports(_from='mozbuild.configure.constants', _import='CompilerType')
423 @imports(_from='mozbuild.configure.constants',
424          _import='CPU_preprocessor_checks')
425 @imports(_from='mozbuild.configure.constants',
426          _import='kernel_preprocessor_checks')
427 @imports(_from='mozbuild.configure.constants',
428          _import='OS_preprocessor_checks')
429 @imports(_from='textwrap', _import='dedent')
430 @imports(_from='__builtin__', _import='Exception')
431 def get_compiler_info(compiler, language):
432     '''Returns information about the given `compiler` (command line in the
433     form of a list or tuple), in the given `language`.
435     The returned information includes:
436     - the compiler type (clang-cl, clang or gcc)
437     - the compiler version
438     - the compiler supported language
439     - the compiler supported language version
440     '''
441     # Note: We'd normally do a version check for clang, but versions of clang
442     # in Xcode have a completely different versioning scheme despite exposing
443     # the version with the same defines.
444     # So instead, we make things such that the version is missing when the
445     # clang used is below the minimum supported version (currently clang 4.0,
446     # or 5.0 on mac).
447     # We then only include the version information when the compiler matches
448     # the feature check, so that an unsupported version of clang would have
449     # no version number.
450     check = dedent('''\
451         #if defined(_MSC_VER) && defined(__clang__) && defined(_MT)
452         %COMPILER "clang-cl"
453         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
454         #elif defined(__clang__)
455         %COMPILER "clang"
456         #  if defined(__APPLE__)
457         #    if __has_warning("-Wunguarded-availability")
458         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
459         #    endif
460         #  elif __has_attribute(diagnose_if)
461         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
462         #  endif
463         #elif defined(__GNUC__)
464         %COMPILER "gcc"
465         %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
466         #endif
468         #if __cplusplus
469         %cplusplus __cplusplus
470         #elif __STDC_VERSION__
471         %STDC_VERSION __STDC_VERSION__
472         #endif
473     ''')
475     # While we're doing some preprocessing, we might as well do some more
476     # preprocessor-based tests at the same time, to check the toolchain
477     # matches what we want.
478     for name, preprocessor_checks in (
479         ('CPU', CPU_preprocessor_checks),
480         ('KERNEL', kernel_preprocessor_checks),
481         ('OS', OS_preprocessor_checks),
482     ):
483         for n, (value, condition) in enumerate(preprocessor_checks.iteritems()):
484             check += dedent('''\
485                 #%(if)s %(condition)s
486                 %%%(name)s "%(value)s"
487             ''' % {
488                 'if': 'elif' if n else 'if',
489                 'condition': condition,
490                 'name': name,
491                 'value': value,
492             })
493         check += '#endif\n'
495     # Also check for endianness. The advantage of living in modern times is
496     # that all the modern compilers we support now have __BYTE_ORDER__ defined
497     # by the preprocessor.
498     check += dedent('''\
499         #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
500         %ENDIANNESS "little"
501         #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
502         %ENDIANNESS "big"
503         #endif
504     ''')
506     result = try_preprocess(compiler, language, check)
508     if not result:
509         raise FatalCheckError(
510             'Unknown compiler or compiler not supported.')
512     # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
513     # have non-ASCII characters. Treat the output as bytearray.
514     data = {}
515     for line in result.splitlines():
516         if line.startswith(b'%'):
517             k, _, v = line.partition(' ')
518             k = k.lstrip('%')
519             data[k] = v.replace(' ', '').lstrip('"').rstrip('"')
520             log.debug('%s = %s', k, data[k])
522     try:
523         type = CompilerType(data['COMPILER'])
524     except Exception:
525         raise FatalCheckError(
526             'Unknown compiler or compiler not supported.')
528     cplusplus = int(data.get('cplusplus', '0L').rstrip('L'))
529     stdc_version = int(data.get('STDC_VERSION', '0L').rstrip('L'))
531     version = data.get('VERSION')
532     if version:
533         version = Version(version)
535     return namespace(
536         type=type,
537         version=version,
538         cpu=data.get('CPU'),
539         kernel=data.get('KERNEL'),
540         endianness=data.get('ENDIANNESS'),
541         os=data.get('OS'),
542         language='C++' if cplusplus else 'C',
543         language_version=cplusplus if cplusplus else stdc_version,
544     )
547 def same_arch_different_bits():
548     return (
549         ('x86', 'x86_64'),
550         ('ppc', 'ppc64'),
551         ('sparc', 'sparc64'),
552     )
555 @imports(_from='mozbuild.shellutil', _import='quote')
556 @imports(_from='mozbuild.configure.constants',
557          _import='OS_preprocessor_checks')
558 def check_compiler(compiler, language, target):
559     info = get_compiler_info(compiler, language)
561     flags = []
563     # Check language standards
564     # --------------------------------------------------------------------
565     if language != info.language:
566         raise FatalCheckError(
567             '`%s` is not a %s compiler.' % (quote(*compiler), language))
569     # Note: We do a strict version check because there sometimes are backwards
570     # incompatible changes in the standard, and not all code that compiles as
571     # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
572     # example)
573     if info.language == 'C' and info.language_version != 199901:
574         if info.type == 'clang-cl':
575             flags.append('-Xclang')
576         flags.append('-std=gnu99')
578     # Note: this is a strict version check because we used to always add
579     # -std=gnu++14.
580     cxx14_version = 201402
581     if info.language == 'C++':
582         if info.language_version != cxx14_version:
583             # MSVC headers include C++14 features, but don't guard them
584             # with appropriate checks.
585             if info.type == 'clang-cl':
586                 flags.append('-Xclang')
587                 flags.append('-std=c++14')
588             else:
589                 flags.append('-std=gnu++14')
591     # Check compiler target
592     # --------------------------------------------------------------------
593     has_target = False
594     if info.type == 'clang':
595         if not info.kernel or info.kernel != target.kernel or \
596                 not info.endianness or info.endianness != target.endianness:
597             flags.append('--target=%s' % target.toolchain)
598             has_target = True
600         # Add target flag when there is an OS mismatch (e.g. building for Android on
601         # Linux). However, only do this if the target OS is in our whitelist, to
602         # keep things the same on other platforms.
603         elif target.os in OS_preprocessor_checks and (
604                 not info.os or info.os != target.os):
605             flags.append('--target=%s' % target.toolchain)
606             has_target = True
608     if not has_target and (not info.cpu or info.cpu != target.cpu):
609         same_arch = same_arch_different_bits()
610         if (target.cpu, info.cpu) in same_arch:
611             flags.append('-m32')
612         elif (info.cpu, target.cpu) in same_arch:
613             flags.append('-m64')
614         elif info.type == 'clang-cl' and target.cpu == 'aarch64':
615             flags.append('--target=%s' % target.toolchain)
616         elif info.type == 'clang':
617             flags.append('--target=%s' % target.toolchain)
619     return namespace(
620         type=info.type,
621         version=info.version,
622         target_cpu=info.cpu,
623         target_kernel=info.kernel,
624         target_endianness=info.endianness,
625         target_os=info.os,
626         flags=flags,
627     )
630 @imports(_from='__builtin__', _import='open')
631 @imports('json')
632 @imports('os')
633 def get_vc_paths(topsrcdir):
634     def vswhere(args):
635         program_files = (os.environ.get('PROGRAMFILES(X86)') or
636                          os.environ.get('PROGRAMFILES'))
637         if not program_files:
638             return []
639         vswhere = os.path.join(program_files, 'Microsoft Visual Studio',
640                                'Installer', 'vswhere.exe')
641         if not os.path.exists(vswhere):
642             return []
643         return json.loads(check_cmd_output(vswhere, '-format', 'json', *args))
645     for install in vswhere(['-products', '*', '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64']):
646         path = install['installationPath']
647         tools_version = open(os.path.join(
648             path, r'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt'), 'rb').read().strip()
649         tools_path = os.path.join(
650             path, r'VC\Tools\MSVC', tools_version)
651         yield (Version(install['installationVersion']), tools_path)
654 @depends(host)
655 def host_is_windows(host):
656     if host.kernel == 'WINNT':
657         return True
660 js_option('--with-visual-studio-version', nargs=1,
661           choices=('2017',), when=host_is_windows,
662           help='Select a specific Visual Studio version to use')
665 @depends('--with-visual-studio-version', when=host_is_windows)
666 def vs_major_version(value):
667     if value:
668         return {'2017': 15}[value[0]]
671 js_option(env='VC_PATH', nargs=1, when=host_is_windows,
672           help='Path to the Microsoft Visual C/C++ compiler')
675 @depends(host, vs_major_version, check_build_environment, 'VC_PATH',
676          '--with-visual-studio-version', when=host_is_windows)
677 @imports(_from='__builtin__', _import='sorted')
678 @imports(_from='operator', _import='itemgetter')
679 def vc_compiler_paths_for_version(host, vs_major_version, env, vc_path, vs_release_name):
680     if vc_path and vs_release_name:
681         die('VC_PATH and --with-visual-studio-version cannot be used together.')
682     if vc_path:
683         # Use an arbitrary version, it doesn't matter.
684         all_versions = [(Version('15'), vc_path[0])]
685     else:
686         all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
687     if not all_versions:
688         return
689     if vs_major_version:
690         versions = [d for (v, d) in all_versions if v.major ==
691                     vs_major_version]
692         if not versions:
693             die('Visual Studio %s could not be found!' % vs_release_name)
694         path = versions[0]
695     else:
696         # Choose the newest version.
697         path = all_versions[-1][1]
698     host_dir = {
699         'x86_64': 'HostX64',
700         'x86': 'HostX86',
701     }.get(host.cpu)
702     if host_dir:
703         path = os.path.join(path, 'bin', host_dir)
704         return {
705             'x64': [os.path.join(path, 'x64')],
706             # The cross toolchains require DLLs from the native x64 toolchain.
707             'x86': [os.path.join(path, 'x86'), os.path.join(path, 'x64')],
708             'arm64': [os.path.join(path, 'arm64'), os.path.join(path, 'x64')],
709         }
712 @template
713 def vc_compiler_path_for(host_or_target):
714     @depends(host_or_target, vc_compiler_paths_for_version,
715              when=host_is_windows)
716     def vc_compiler_path(target, paths):
717         vc_target = {
718             'x86': 'x86',
719             'x86_64': 'x64',
720             'arm': 'arm',
721             'aarch64': 'arm64'
722         }.get(target.cpu)
723         if not paths:
724             return
725         return paths.get(vc_target)
726     return vc_compiler_path
729 vc_compiler_path = vc_compiler_path_for(target)
730 host_vc_compiler_path = vc_compiler_path_for(host)
733 @dependable
734 @imports('os')
735 @imports(_from='os', _import='environ')
736 def original_path():
737     return environ['PATH'].split(os.pathsep)
740 @template
741 def toolchain_search_path_for(host_or_target):
742     vc_path = {
743         host: host_vc_compiler_path,
744         target: vc_compiler_path,
745     }[host_or_target]
747     @depends(vc_path, original_path, developer_options, mozbuild_state_path)
748     @imports('os')
749     @imports(_from='os', _import='environ')
750     def toolchain_search_path(vc_compiler_path, original_path, developer_options,
751                               mozbuild_state_path):
752         result = list(original_path)
754         if vc_compiler_path:
755             # The second item, if there is one, is necessary to have in $PATH for
756             # Windows to load the required DLLs from there.
757             if len(vc_compiler_path) > 1:
758                 environ['PATH'] = os.pathsep.join(result + vc_compiler_path[1:])
760             # The first item is where the programs are going to be
761             result.append(vc_compiler_path[0])
763         # Also add in the location to which `mach bootstrap` or
764         # `mach artifact toolchain` installs clang.
765         bootstrapped = []
767         bootstrap_clang_path = os.path.join(mozbuild_state_path, 'clang', 'bin')
768         bootstrapped.append(bootstrap_clang_path)
770         bootstrap_cbindgen_path = os.path.join(mozbuild_state_path, 'cbindgen')
771         bootstrapped.append(bootstrap_cbindgen_path)
773         bootstrap_nasm_path = os.path.join(mozbuild_state_path, 'nasm')
774         bootstrapped.append(bootstrap_nasm_path)
776         # Also add the rustup install directory for cargo/rustc.
777         rustup_path = os.path.expanduser(os.path.join('~', '.cargo', 'bin'))
778         result.append(rustup_path)
780         if developer_options:
781             return bootstrapped + result
782         return result + bootstrapped
783     return toolchain_search_path
786 toolchain_search_path = toolchain_search_path_for(target)
787 host_toolchain_search_path = toolchain_search_path_for(host)
790 # As a workaround until bug 1516228 and bug 1516253 are fixed, set the PATH
791 # variable for the build to contain the toolchain search path.
792 @depends(toolchain_search_path, host_toolchain_search_path)
793 @imports('os')
794 @imports(_from='os', _import='environ')
795 def altered_path(toolchain_search_path, host_toolchain_search_path):
796     path = environ['PATH'].split(os.pathsep)
797     altered_path = list(toolchain_search_path)
798     for p in host_toolchain_search_path:
799         if p not in altered_path:
800             altered_path.append(p)
801     for p in path:
802         if p not in altered_path:
803             altered_path.append(p)
804     return os.pathsep.join(altered_path)
807 set_config('PATH', altered_path)
810 @template
811 def default_c_compilers(host_or_target, other_c_compiler=None):
812     '''Template defining the set of default C compilers for the host and
813     target platforms.
814     `host_or_target` is either `host` or `target` (the @depends functions
815     from init.configure.
816     `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
817     '''
818     assert host_or_target in {host, target}
820     other_c_compiler = () if other_c_compiler is None else (other_c_compiler,)
822     @depends(host_or_target, target, toolchain_prefix, *other_c_compiler)
823     def default_c_compilers(host_or_target, target, toolchain_prefix,
824                             *other_c_compiler):
825         if host_or_target.kernel == 'WINNT':
826             supported = types = ('clang-cl', 'gcc', 'clang')
827         elif host_or_target.kernel == 'Darwin':
828             types = ('clang',)
829             supported = ('clang', 'gcc')
830         else:
831             supported = types = ('clang', 'gcc')
833         info = other_c_compiler[0] if other_c_compiler else None
834         if info and info.type in supported:
835             # When getting default C compilers for the host, we prioritize the
836             # same compiler as the target C compiler.
837             prioritized = info.compiler
838             if info.type == 'gcc':
839                 same_arch = same_arch_different_bits()
840                 if (target.cpu != host_or_target.cpu and
841                         (target.cpu, host_or_target.cpu) not in same_arch and
842                         (host_or_target.cpu, target.cpu) not in same_arch):
843                     # If the target C compiler is GCC, and it can't be used with
844                     # -m32/-m64 for the host, it's probably toolchain-prefixed,
845                     # so we prioritize a raw 'gcc' instead.
846                     prioritized = info.type
848             types = [prioritized] + [t for t in types if t != info.type]
850         gcc = ('gcc',)
851         if toolchain_prefix and host_or_target is target:
852             gcc = tuple('%sgcc' % p for p in toolchain_prefix) + gcc
854         result = []
855         for type in types:
856             if type == 'gcc':
857                 result.extend(gcc)
858             else:
859                 result.append(type)
861         return tuple(result)
863     return default_c_compilers
866 @template
867 def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None):
868     '''Template defining the set of default C++ compilers for the host and
869     target platforms.
870     `c_compiler` is the @depends function returning a Compiler instance for
871     the desired platform.
873     Because the build system expects the C and C++ compilers to be from the
874     same compiler suite, we derive the default C++ compilers from the C
875     compiler that was found if none was provided.
877     We also factor in the target C++ compiler when getting the default host
878     C++ compiler, using the target C++ compiler if the host and target C
879     compilers are the same.
880     '''
882     assert (other_c_compiler is None) == (other_cxx_compiler is None)
883     if other_c_compiler is not None:
884         other_compilers = (other_c_compiler, other_cxx_compiler)
885     else:
886         other_compilers = ()
888     @depends(c_compiler, *other_compilers)
889     def default_cxx_compilers(c_compiler, *other_compilers):
890         if other_compilers:
891             other_c_compiler, other_cxx_compiler = other_compilers
892             if other_c_compiler.compiler == c_compiler.compiler:
893                 return (other_cxx_compiler.compiler,)
895         dir = os.path.dirname(c_compiler.compiler)
896         file = os.path.basename(c_compiler.compiler)
898         if c_compiler.type == 'gcc':
899             return (os.path.join(dir, file.replace('gcc', 'g++')),)
901         if c_compiler.type == 'clang':
902             return (os.path.join(dir, file.replace('clang', 'clang++')),)
904         return (c_compiler.compiler,)
906     return default_cxx_compilers
909 @template
910 def provided_program(env_var):
911     '''Template handling cases where a program can be specified either as a
912     path or as a path with applicable arguments.
913     '''
915     @depends_if(env_var)
916     @imports(_from='itertools', _import='takewhile')
917     @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
918     def provided(cmd):
919         # Assume the first dash-prefixed item (and any subsequent items) are
920         # command-line options, the item before the dash-prefixed item is
921         # the program we're looking for, and anything before that is a wrapper
922         # of some kind (e.g. sccache).
923         cmd = shell_split(cmd[0])
925         without_flags = list(takewhile(lambda x: not x.startswith('-'), cmd))
927         return namespace(
928             wrapper=without_flags[:-1],
929             program=without_flags[-1],
930             flags=cmd[len(without_flags):],
931         )
933     return provided
936 def prepare_flags(host_or_target, macos_sdk):
937     if macos_sdk and host_or_target.os == 'OSX':
938         return ['-isysroot', macos_sdk]
939     return []
942 @template
943 def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
944              other_c_compiler=None):
945     '''Template handling the generic base checks for the compiler for the
946     given `language` on the given platform (`host_or_target`).
947     `host_or_target` is either `host` or `target` (the @depends functions
948     from init.configure.
949     When the language is 'C++', `c_compiler` is the result of the `compiler`
950     template for the language 'C' for the same `host_or_target`.
951     When `host_or_target` is `host`, `other_compiler` is the result of the
952     `compiler` template for the same `language` for `target`.
953     When `host_or_target` is `host` and the language is 'C++',
954     `other_c_compiler` is the result of the `compiler` template for the
955     language 'C' for `target`.
956     '''
957     assert host_or_target in {host, target}
958     assert language in ('C', 'C++')
959     assert language == 'C' or c_compiler is not None
960     assert host_or_target is target or other_compiler is not None
961     assert language == 'C' or host_or_target is target or \
962         other_c_compiler is not None
964     host_or_target_str = {
965         host: 'host',
966         target: 'target',
967     }[host_or_target]
969     var = {
970         ('C', target): 'CC',
971         ('C++', target): 'CXX',
972         ('C', host): 'HOST_CC',
973         ('C++', host): 'HOST_CXX',
974     }[language, host_or_target]
976     default_compilers = {
977         'C': lambda: default_c_compilers(host_or_target, other_compiler),
978         'C++': lambda: default_cxx_compilers(c_compiler, other_c_compiler, other_compiler),
979     }[language]()
981     what = 'the %s %s compiler' % (host_or_target_str, language)
983     option(env=var, nargs=1, help='Path to %s' % what)
985     # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
986     # HOST_CXX variables.
987     provided_compiler = provided_program(var)
989     search_path = {
990         host: host_toolchain_search_path,
991         target: toolchain_search_path,
992     }[host_or_target]
994     # Normally, we'd use `var` instead of `_var`, but the interaction with
995     # old-configure complicates things, and for now, we a) can't take the plain
996     # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
997     # old-configure AC_SUBST it (because it's autoconf doing it, not us)
998     compiler = check_prog('_%s' % var, what=what, progs=default_compilers,
999                           input=provided_compiler.program,
1000                           paths=search_path)
1002     @depends(compiler, provided_compiler, compiler_wrapper, host_or_target, macos_sdk)
1003     @checking('whether %s can be used' % what, lambda x: bool(x))
1004     @imports(_from='mozbuild.shellutil', _import='quote')
1005     def valid_compiler(compiler, provided_compiler, compiler_wrapper,
1006                        host_or_target, macos_sdk):
1007         wrapper = list(compiler_wrapper or ())
1008         if provided_compiler:
1009             provided_wrapper = list(provided_compiler.wrapper)
1010             # When doing a subconfigure, the compiler is set by old-configure
1011             # and it contains the wrappers from --with-compiler-wrapper and
1012             # --with-ccache.
1013             if provided_wrapper[:len(wrapper)] == wrapper:
1014                 provided_wrapper = provided_wrapper[len(wrapper):]
1015             wrapper.extend(provided_wrapper)
1016             flags = provided_compiler.flags
1017         else:
1018             flags = []
1020         if not flags:
1021             flags = prepare_flags(host_or_target, macos_sdk)
1023         info = check_compiler(wrapper + [compiler] + flags, language,
1024                               host_or_target)
1026         # Check that the additional flags we got are enough to not require any
1027         # more flags. If we get an exception, just ignore it; it's liable to be
1028         # invalid command-line flags, which means the compiler we're checking
1029         # doesn't support those command-line flags and will fail one or more of
1030         # the checks below.
1031         try:
1032             if info.flags:
1033                 flags += info.flags
1034                 info = check_compiler(wrapper + [compiler] + flags, language,
1035                                       host_or_target)
1036         except FatalCheckError:
1037             pass
1039         if not info.target_cpu or info.target_cpu != host_or_target.cpu:
1040             raise FatalCheckError(
1041                 '%s %s compiler target CPU (%s) does not match --%s CPU (%s)'
1042                 % (host_or_target_str.capitalize(), language,
1043                    info.target_cpu or 'unknown', host_or_target_str,
1044                    host_or_target.raw_cpu))
1046         if not info.target_kernel or (info.target_kernel !=
1047                                       host_or_target.kernel):
1048             raise FatalCheckError(
1049                 '%s %s compiler target kernel (%s) does not match --%s kernel (%s)'
1050                 % (host_or_target_str.capitalize(), language,
1051                    info.target_kernel or 'unknown', host_or_target_str,
1052                    host_or_target.kernel))
1054         if not info.target_endianness or (info.target_endianness !=
1055                                           host_or_target.endianness):
1056             raise FatalCheckError(
1057                 '%s %s compiler target endianness (%s) does not match --%s '
1058                 'endianness (%s)'
1059                 % (host_or_target_str.capitalize(), language,
1060                    info.target_endianness or 'unknown', host_or_target_str,
1061                    host_or_target.endianness))
1063         # Compiler version checks
1064         # ===================================================
1065         # Check the compiler version here instead of in `compiler_version` so
1066         # that the `checking` message doesn't pretend the compiler can be used
1067         # to then bail out one line later.
1068         if info.type == 'gcc':
1069             if host_or_target.os == 'Android':
1070                 raise FatalCheckError('GCC is not supported on Android.\n'
1071                                       'Please use clang from the Android NDK instead.')
1072             if info.version < '6.1.0':
1073                 raise FatalCheckError(
1074                     'Only GCC 6.1 or newer is supported (found version %s).'
1075                     % info.version)
1077         if info.type == 'clang-cl':
1078             if info.version < '8.0.0':
1079                 raise FatalCheckError(
1080                     'Only clang-cl 8.0 or newer is supported (found version %s)'
1081                     % info.version)
1083         # If you want to bump the version check here search for
1084         # diagnose_if above, and see the associated comment.
1085         if info.type == 'clang' and not info.version:
1086             if host_or_target.os == 'OSX':
1087                 raise FatalCheckError(
1088                     'Only clang/llvm 5.0 or newer is supported.')
1089             raise FatalCheckError(
1090                 'Only clang/llvm 4.0 or newer is supported.')
1092         if info.flags:
1093             raise FatalCheckError(
1094                 'Unknown compiler or compiler not supported.')
1096         return namespace(
1097             wrapper=wrapper,
1098             compiler=compiler,
1099             flags=flags,
1100             type=info.type,
1101             version=info.version,
1102             language=language,
1103         )
1105     @depends(valid_compiler)
1106     @checking('%s version' % what)
1107     def compiler_version(compiler):
1108         return compiler.version
1110     if language == 'C++':
1111         @depends(valid_compiler, c_compiler)
1112         def valid_compiler(compiler, c_compiler):
1113             if compiler.type != c_compiler.type:
1114                 die('The %s C compiler is %s, while the %s C++ compiler is '
1115                     '%s. Need to use the same compiler suite.',
1116                     host_or_target_str, c_compiler.type,
1117                     host_or_target_str, compiler.type)
1119             if compiler.version != c_compiler.version:
1120                 die('The %s C compiler is version %s, while the %s C++ '
1121                     'compiler is version %s. Need to use the same compiler '
1122                     'version.',
1123                     host_or_target_str, c_compiler.version,
1124                     host_or_target_str, compiler.version)
1125             return compiler
1127     # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
1128     # and the flags that were part of the user input for those variables to
1129     # be provided.
1130     add_old_configure_assignment(var, depends_if(valid_compiler)(
1131         lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
1133     if host_or_target is target:
1134         add_old_configure_assignment('ac_cv_prog_%s' % var, depends_if(valid_compiler)(
1135             lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
1136         # We check that it works in python configure already.
1137         add_old_configure_assignment('ac_cv_prog_%s_works' % var.lower(), 'yes')
1138         add_old_configure_assignment(
1139             'ac_cv_prog_%s_cross' % var.lower(),
1140             depends(cross_compiling)(lambda x: 'yes' if x else 'no'))
1141         gcc_like = depends(valid_compiler.type)(lambda x: 'yes' if x in ('gcc', 'clang') else 'no')
1142         add_old_configure_assignment('ac_cv_prog_%s_g' % var.lower(), gcc_like)
1143         if language == 'C':
1144             add_old_configure_assignment('ac_cv_prog_gcc', gcc_like)
1145         if language == 'C++':
1146             add_old_configure_assignment('ac_cv_prog_gxx', gcc_like)
1149     # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
1150     # old-configure to do some of its still existing checks.
1151     if language == 'C':
1152         set_config(
1153             '%s_TYPE' % var, valid_compiler.type)
1154         add_old_configure_assignment(
1155             '%s_TYPE' % var, valid_compiler.type)
1156         set_config(
1157             '%s_VERSION' % var, depends(valid_compiler.version)(lambda v: str(v)))
1159     valid_compiler = compiler_class(valid_compiler, host_or_target)
1161     def compiler_error():
1162         raise FatalCheckError('Failed compiling a simple %s source with %s'
1163                               % (language, what))
1165     valid_compiler.try_compile(check_msg='%s works' % what,
1166                                onerror=compiler_error)
1168     set_config('%s_BASE_FLAGS' % var, valid_compiler.flags)
1170     # Set CPP/CXXCPP for both the build system and old-configure. We don't
1171     # need to check this works for preprocessing, because we already relied
1172     # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
1173     # in the first place.
1174     if host_or_target is target:
1175         pp_var = {
1176             'C': 'CPP',
1177             'C++': 'CXXCPP',
1178         }[language]
1180         preprocessor = depends_if(valid_compiler)(
1181             lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
1183         set_config(pp_var, preprocessor)
1184         add_old_configure_assignment(pp_var, preprocessor)
1186     if language == 'C':
1187         linker_var = {
1188             target: 'LD',
1189             host: 'HOST_LD',
1190         }[host_or_target]
1192         @deprecated_option(env=linker_var, nargs=1)
1193         def linker(value):
1194             if value:
1195                 return value[0]
1197         @depends(linker)
1198         def unused_linker(linker):
1199             if linker:
1200                 log.warning('The value of %s is not used by this build system.'
1201                             % linker_var)
1203     return valid_compiler
1206 c_compiler = compiler('C', target)
1207 cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
1208 host_c_compiler = compiler('C', host, other_compiler=c_compiler)
1209 host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
1210                              other_compiler=cxx_compiler,
1211                              other_c_compiler=c_compiler)
1213 # Generic compiler-based conditions.
1214 building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc')
1217 @depends(cxx_compiler, ccache_prefix)
1218 @imports('os')
1219 def cxx_is_icecream(info, ccache_prefix):
1220     if (os.path.islink(info.compiler) and os.path.basename(
1221             os.readlink(info.compiler)) == 'icecc'):
1222         return True
1223     if ccache_prefix and os.path.basename(ccache_prefix) == 'icecc':
1224         return True
1226 set_config('CXX_IS_ICECREAM', cxx_is_icecream)
1229 @depends(c_compiler)
1230 def msvs_version(info):
1231     # clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to
1232     # be set for GYP on Windows.
1233     if info.type == 'clang-cl':
1234         return '2017'
1236     return ''
1239 set_config('MSVS_VERSION', msvs_version)
1241 include('compile-checks.configure')
1242 include('arm.configure', when=depends(target.cpu)(lambda cpu: cpu == 'arm'))
1245 @depends(host,
1246          host_os_kernel_major_version,
1247          target,
1248          cxx_compiler.try_run(header='#include_next <inttypes.h>'))
1249 def check_have_mac_10_14_sdk(host, version, target, value):
1250     # Only an issue on Mac OS X 10.14 (and probably above).
1251     if host.kernel != 'Darwin' or target.kernel !='Darwin' or version < '18' or value:
1252         return
1254     die('System inttypes.h not found. Please try running '
1255          '`open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg` '
1256          'and following the instructions to install the necessary headers')
1259 @depends(have_64_bit,
1260          try_compile(body='static_assert(sizeof(void *) == 8, "")',
1261                      check_msg='for 64-bit OS'))
1262 def check_have_64_bit(have_64_bit, compiler_have_64_bit):
1263     if have_64_bit != compiler_have_64_bit:
1264         configure_error('The target compiler does not agree with configure '
1265                         'about the target bitness.')
1268 @depends(c_compiler, target)
1269 def default_debug_flags(compiler_info, target):
1270     # Debug info is ON by default.
1271     if compiler_info.type == 'clang-cl':
1272         return '-Z7'
1273     elif target.kernel == 'WINNT' and compiler_info.type == 'clang':
1274         return '-g -gcodeview'
1275     return '-g'
1278 option(env='MOZ_DEBUG_FLAGS',
1279        nargs=1,
1280        help='Debug compiler flags')
1282 imply_option('--enable-debug-symbols',
1283              depends_if('--enable-debug')(lambda v: v))
1285 js_option('--disable-debug-symbols',
1286           nargs='?',
1287           help='Disable debug symbols using the given compiler flags')
1289 set_config('MOZ_DEBUG_SYMBOLS',
1290            depends_if('--enable-debug-symbols')(lambda _: True))
1293 @depends('MOZ_DEBUG_FLAGS', '--enable-debug-symbols', default_debug_flags)
1294 def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
1295     # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
1296     # --enable-debug-symbols takes precedence. Note, the value of
1297     # --enable-debug-symbols may be implied by --enable-debug.
1298     if len(enable_debug_flags):
1299         return enable_debug_flags[0]
1300     if env_debug_flags:
1301         return env_debug_flags[0]
1302     return default_debug_flags
1305 set_config('MOZ_DEBUG_FLAGS', debug_flags)
1306 add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
1309 @depends(c_compiler)
1310 def color_cflags(info):
1311     # We could test compiling with flags. By why incur the overhead when
1312     # color support should always be present in a specific toolchain
1313     # version?
1315     # Code for auto-adding this flag to compiler invocations needs to
1316     # determine if an existing flag isn't already present. That is likely
1317     # using exact string matching on the returned value. So if the return
1318     # value changes to e.g. "<x>=always", exact string match may fail and
1319     # multiple color flags could be added. So examine downstream consumers
1320     # before adding flags to return values.
1321     if info.type == 'gcc':
1322         return '-fdiagnostics-color'
1323     elif info.type == 'clang':
1324         return '-fcolor-diagnostics'
1325     else:
1326         return ''
1329 set_config('COLOR_CFLAGS', color_cflags)
1331 # Some standard library headers (notably bionic on Android) declare standard
1332 # functions (e.g. getchar()) and also #define macros for those standard
1333 # functions.  libc++ deals with this by doing something like the following
1334 # (explanatory comments added):
1336 #   #ifdef FUNC
1337 #   // Capture the definition of FUNC.
1338 #   inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
1339 #   #undef FUNC
1340 #   // Use a real inline definition.
1341 #   inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
1342 #   #endif
1344 # _LIBCPP_INLINE_VISIBILITY is typically defined as:
1346 #   __attribute__((__visibility__("hidden"), __always_inline__))
1348 # Unfortunately, this interacts badly with our system header wrappers, as the:
1350 #   #pragma GCC visibility push(default)
1352 # that they do prior to including the actual system header is treated by the
1353 # compiler as an explicit declaration of visibility on every function declared
1354 # in the header.  Therefore, when the libc++ code above is encountered, it is
1355 # as though the compiler has effectively seen:
1357 #   int FUNC(...) __attribute__((__visibility__("default")));
1358 #   int FUNC(...) __attribute__((__visibility__("hidden")));
1360 # and the compiler complains about the mismatched visibility declarations.
1362 # However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
1363 # existing definition.  We can therefore define it to the empty string (since
1364 # we are properly managing visibility ourselves) and avoid this whole mess.
1365 # Note that we don't need to do this with gcc, as libc++ detects gcc and
1366 # effectively does the same thing we are doing here.
1368 # _LIBCPP_ALWAYS_INLINE needs a similar workarounds, since it too declares
1369 # hidden visibility.
1371 # _LIBCPP_HIDE_FROM_ABI is a macro in libc++ versions in NDKs >=r19.  It too
1372 # declares hidden visibility, but it also declares functions as excluded from
1373 # explicit instantiation (roughly: the function can be unused in the current
1374 # compilation, but does not then trigger an actual definition of the function;
1375 # it is assumed the real definition comes from elsewhere).  We need to replicate
1376 # this setup.
1379 @depends(c_compiler, target)
1380 def libcxx_override_visibility(c_compiler, target):
1381     if c_compiler.type == 'clang' and target.os == 'Android':
1382         return namespace(
1383             empty='',
1384             hide_from_abi='__attribute__((__exclude_from_explicit_instantiation__))',
1385         )
1388 set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_override_visibility.empty)
1389 set_define('_LIBCPP_ALWAYS_INLINE', libcxx_override_visibility.empty)
1391 set_define('_LIBCPP_HIDE_FROM_ABI', libcxx_override_visibility.hide_from_abi)
1393 @depends(target, check_build_environment)
1394 def visibility_flags(target, env):
1395     if target.os != 'WINNT':
1396         if target.kernel == 'Darwin':
1397             return ('-fvisibility=hidden', '-fvisibility-inlines-hidden')
1398         return ('-I%s/system_wrappers' % os.path.join(env.dist),
1399                 '-include',
1400                 '%s/config/gcc_hidden.h' % env.topsrcdir)
1403 @depends(target, visibility_flags)
1404 def wrap_system_includes(target, visibility_flags):
1405     if visibility_flags and target.kernel != 'Darwin':
1406         return True
1409 set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE',
1410            depends(visibility_flags)(lambda v: bool(v) or None))
1411 set_define('HAVE_VISIBILITY_ATTRIBUTE',
1412            depends(visibility_flags)(lambda v: bool(v) or None))
1413 set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes)
1414 set_config('VISIBILITY_FLAGS', visibility_flags)
1417 @template
1418 def depend_cflags(host_or_target_c_compiler):
1419     @depends(host_or_target_c_compiler)
1420     def depend_cflags(host_or_target_c_compiler):
1421         if host_or_target_c_compiler.type != 'clang-cl':
1422             return ['-MD', '-MP', '-MF $(MDDEPDIR)/$(@F).pp']
1423         else:
1424             # clang-cl doesn't accept the normal -MD -MP -MF options that clang
1425             # does, but the underlying cc1 binary understands how to generate
1426             # dependency files.  These options are based on analyzing what the
1427             # normal clang driver sends to cc1 when given the "correct"
1428             # dependency options.
1429             return [
1430                 '-Xclang', '-MP',
1431                 '-Xclang', '-dependency-file',
1432                 '-Xclang', '$(MDDEPDIR)/$(@F).pp',
1433                 '-Xclang', '-MT',
1434                 '-Xclang', '$@'
1435             ]
1437     return depend_cflags
1440 set_config('_DEPEND_CFLAGS', depend_cflags(c_compiler))
1441 set_config('_HOST_DEPEND_CFLAGS', depend_cflags(host_c_compiler))
1444 @depends(c_compiler)
1445 def preprocess_option(compiler):
1446     # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
1447     if compiler.type in ('gcc', 'clang'):
1448         return '-E -o '
1449     else:
1450         return '-P -Fi'
1453 set_config('PREPROCESS_OPTION', preprocess_option)
1456 # We only want to include windows.configure when we are compiling on
1457 # Windows, for Windows.
1460 @depends(target, host)
1461 def is_windows(target, host):
1462     return host.kernel == 'WINNT' and target.kernel == 'WINNT'
1465 include('windows.configure', when=is_windows)
1468 # On Power ISA, determine compiler flags for VMX, VSX and VSX-3.
1470 set_config('PPC_VMX_FLAGS',
1471            ['-maltivec'],
1472            when=depends(target.cpu)(lambda cpu: cpu.startswith('ppc')))
1474 set_config('PPC_VSX_FLAGS',
1475            ['-mvsx'],
1476            when=depends(target.cpu)(lambda cpu: cpu.startswith('ppc')))
1478 set_config('PPC_VSX3_FLAGS',
1479            ['-mvsx','-mcpu=power9'],
1480            when=depends(target.cpu)(lambda cpu: cpu.startswith('ppc')))
1482 # ASAN
1483 # ==============================================================
1485 js_option('--enable-address-sanitizer', help='Enable Address Sanitizer')
1488 @depends(when='--enable-address-sanitizer')
1489 def asan():
1490     return True
1493 add_old_configure_assignment('MOZ_ASAN', asan)
1495 # MSAN
1496 # ==============================================================
1498 js_option('--enable-memory-sanitizer', help='Enable Memory Sanitizer')
1501 @depends(when='--enable-memory-sanitizer')
1502 def msan():
1503     return True
1506 add_old_configure_assignment('MOZ_MSAN', msan)
1508 # TSAN
1509 # ==============================================================
1511 js_option('--enable-thread-sanitizer', help='Enable Thread Sanitizer')
1514 @depends(when='--enable-thread-sanitizer')
1515 def tsan():
1516     return True
1519 add_old_configure_assignment('MOZ_TSAN', tsan)
1521 # UBSAN
1522 # ==============================================================
1524 js_option('--enable-undefined-sanitizer',
1525           nargs='*',
1526           help='Enable UndefinedBehavior Sanitizer')
1528 @depends_if('--enable-undefined-sanitizer')
1529 def ubsan(options):
1530     default_checks = [
1531         'bool',
1532         'bounds',
1533         'vla-bound',
1534     ]
1536     checks = options if len(options) else default_checks
1538     return ','.join(checks)
1540 add_old_configure_assignment('MOZ_UBSAN_CHECKS', ubsan)
1543 js_option('--enable-signed-overflow-sanitizer',
1544           help='Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)')
1547 @depends(when='--enable-signed-overflow-sanitizer')
1548 def ub_signed_overflow_san():
1549     return True
1552 add_old_configure_assignment('MOZ_SIGNED_OVERFLOW_SANITIZE', ub_signed_overflow_san)
1555 js_option('--enable-unsigned-overflow-sanitizer',
1556           help='Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)')
1559 @depends(when='--enable-unsigned-overflow-sanitizer')
1560 def ub_unsigned_overflow_san():
1561     return True
1564 add_old_configure_assignment('MOZ_UNSIGNED_OVERFLOW_SANITIZE', ub_unsigned_overflow_san)
1566 # Security Hardening
1567 # ==============================================================
1569 option('--enable-hardening', env='MOZ_SECURITY_HARDENING',
1570        help='Enables security hardening compiler options')
1573 # This function is a bit confusing. It adds or removes hardening flags in
1574 # three stuations: if --enable-hardening is passed; if --disable-hardening
1575 # is passed, and if no flag is passed.
1577 # At time of this comment writing, all flags are actually added in the
1578 # default no-flag case; making --enable-hardening the same as omitting the
1579 # flag. --disable-hardening will omit the security flags. (However, not all
1580 # possible security flags will be omitted by --disable-hardening, as many are
1581 # compiler-default options we do not explicitly enable.)
1582 @depends('--enable-hardening', '--enable-address-sanitizer',
1583          '--enable-debug', '--enable-optimize', c_compiler, target)
1584 def security_hardening_cflags(hardening_flag, asan, debug, optimize, c_compiler,
1585                               target):
1586     compiler_is_gccish = c_compiler.type in ('gcc', 'clang')
1588     flags = []
1589     ldflags = []
1590     js_flags = []
1591     js_ldflags = []
1593     # ----------------------------------------------------------
1594     # If hardening is explicitly enabled, or not explicitly disabled
1595     if hardening_flag.origin == "default" or hardening_flag:
1596         # FORTIFY_SOURCE ------------------------------------
1597         # Require optimization for FORTIFY_SOURCE. See Bug 1417452
1598         # Also, undefine it before defining it just in case a distro adds it, see Bug 1418398
1599         if compiler_is_gccish and optimize and not asan:
1600             # Don't enable FORTIFY_SOURCE on Android on the top-level, but do enable in js/
1601             if target.os != 'Android':
1602                 flags.append("-U_FORTIFY_SOURCE")
1603                 flags.append("-D_FORTIFY_SOURCE=2")
1604             js_flags.append("-U_FORTIFY_SOURCE")
1605             js_flags.append("-D_FORTIFY_SOURCE=2")
1607         # fstack-protector ------------------------------------
1608         # Enable only if hardening is not disabled and ASAN is
1609         # not on as ASAN will catch the crashes for us
1610         # mingw-clang cross-compile toolchain has bugs with stack protector
1611         mingw_clang = c_compiler.type == 'clang' and target.os == 'WINNT'
1612         if compiler_is_gccish and not asan:
1613             if not mingw_clang:
1614                 flags.append("-fstack-protector-strong")
1615                 ldflags.append("-fstack-protector-strong")
1616                 js_flags.append("-fstack-protector-strong")
1617                 js_ldflags.append("-fstack-protector-strong")
1619         # ftrivial-auto-var-init ------------------------------
1620         # Initialize local variables with a 0xAA pattern in clang debug builds.
1621         # Linux32 fails some xpcshell tests with -ftrivial-auto-var-init
1622         linux32 = target.kernel == 'Linux' and target.cpu == 'x86'
1623         if (c_compiler.type == 'clang' or c_compiler.type == 'clang-cl') and \
1624             c_compiler.version >= '8' and debug and not linux32:
1625             if c_compiler.type == 'clang-cl':
1626                 flags.append('-Xclang')
1627                 js_flags.append('-Xclang')
1628             flags.append('-ftrivial-auto-var-init=pattern')
1629             js_flags.append('-ftrivial-auto-var-init=pattern')
1631         # ASLR ------------------------------------------------
1632         # ASLR (dynamicbase) is enabled by default in clang-cl; but the
1633         # mingw-clang build requires it to be explicitly enabled
1634         if mingw_clang:
1635             ldflags.append("-Wl,--dynamicbase")
1636             js_ldflags.append("-Wl,--dynamicbase")
1638         # Control Flow Guard (CFG) ----------------------------
1639         # On aarch64, this is enabled only with explicit --enable-hardening
1640         # (roughly: automation) due to a dependency on a patched clang-cl.
1641         if c_compiler.type == 'clang-cl' and c_compiler.version >= '8' and \
1642            (target.cpu != 'aarch64' or hardening_flag):
1643             flags.append("-guard:cf")
1644             js_flags.append("-guard:cf")
1645             # nolongjmp is needed because clang doesn't emit the CFG tables of
1646             # setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057
1647             ldflags.append("-guard:cf,nolongjmp")
1648             js_ldflags.append("-guard:cf,nolongjmp")
1650     # ----------------------------------------------------------
1651     # If ASAN _is_ on, undefine FORTIFY_SOURCE just to be safe
1652     if asan:
1653         flags.append("-U_FORTIFY_SOURCE")
1654         js_flags.append("-U_FORTIFY_SOURCE")
1656     # fno-common -----------------------------------------
1657     # Do not merge variables for ASAN; can detect some subtle bugs
1658     if asan:
1659         # clang-cl does not recognize the flag, it must be passed down to clang
1660         if c_compiler.type == 'clang-cl':
1661             flags.append("-Xclang")
1662         flags.append("-fno-common")
1664     return namespace(
1665         flags=flags,
1666         ldflags=ldflags,
1667         js_flags=js_flags,
1668         js_ldflags=js_ldflags,
1669     )
1672 add_old_configure_assignment('MOZ_HARDENING_CFLAGS', security_hardening_cflags.flags)
1673 add_old_configure_assignment('MOZ_HARDENING_LDFLAGS', security_hardening_cflags.ldflags)
1674 add_old_configure_assignment('MOZ_HARDENING_CFLAGS_JS', security_hardening_cflags.js_flags)
1675 add_old_configure_assignment('MOZ_HARDENING_LDFLAGS_JS', security_hardening_cflags.js_ldflags)
1678 # Frame pointers
1679 # ==============================================================
1680 @depends(c_compiler)
1681 def frame_pointer_flags(compiler):
1682     if compiler.type == 'clang-cl':
1683         return namespace(
1684             enable=['-Oy-'],
1685             disable=['-Oy'],
1686         )
1687     return namespace(
1688         enable=['-fno-omit-frame-pointer', '-funwind-tables'],
1689         disable=['-fomit-frame-pointer', '-funwind-tables'],
1690     )
1693 @depends(moz_optimize.optimize, moz_debug, target,
1694          '--enable-memory-sanitizer', '--enable-address-sanitizer',
1695          '--enable-undefined-sanitizer')
1696 def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
1697     return bool(not optimize or debug or msan or asan or ubsan or \
1698         (target.os == 'WINNT' and target.cpu in ('x86', 'aarch64')))
1701 js_option('--enable-frame-pointers', default=frame_pointer_default,
1702           help='{Enable|Disable} frame pointers')
1705 @depends('--enable-frame-pointers', frame_pointer_flags)
1706 def frame_pointer_flags(enable, flags):
1707     if enable:
1708         return flags.enable
1709     return flags.disable
1712 set_config('MOZ_FRAMEPTR_FLAGS', frame_pointer_flags)
1715 # nasm detection
1716 # ==============================================================
1717 nasm = check_prog('NASM', ['nasm'], allow_missing=True, paths=toolchain_search_path)
1720 @depends_if(nasm)
1721 @checking('nasm version')
1722 def nasm_version(nasm):
1723     (retcode, stdout, _) = get_cmd_output(nasm, '-v')
1724     if retcode:
1725         # mac stub binary
1726         return None
1728     version = stdout.splitlines()[0].split()[2]
1729     return Version(version)
1732 @depends_if(nasm_version)
1733 def nasm_major_version(nasm_version):
1734     return str(nasm_version.major)
1737 @depends_if(nasm_version)
1738 def nasm_minor_version(nasm_version):
1739     return str(nasm_version.minor)
1742 set_config('NASM_MAJOR_VERSION', nasm_major_version)
1743 set_config('NASM_MINOR_VERSION', nasm_minor_version)
1746 @depends(nasm, target)
1747 def nasm_asflags(nasm, target):
1748     if nasm:
1749         asflags = {
1750             ('OSX', 'x86'): ['-f', 'macho32'],
1751             ('OSX', 'x86_64'): ['-f', 'macho64'],
1752             ('WINNT', 'x86'): ['-f', 'win32'],
1753             ('WINNT', 'x86_64'): ['-f', 'win64'],
1754         }.get((target.os, target.cpu), None)
1755         if asflags is None:
1756             # We're assuming every x86 platform we support that's
1757             # not Windows or Mac is ELF.
1758             if target.cpu == 'x86':
1759                 asflags = ['-f', 'elf32']
1760             elif target.cpu == 'x86_64':
1761                 asflags = ['-f', 'elf64']
1762         return asflags
1765 set_config('NASM_ASFLAGS', nasm_asflags)
1767 @depends(nasm_asflags)
1768 def have_nasm(value):
1769     if value:
1770         return True
1773 @depends(yasm_asflags)
1774 def have_yasm(yasm_asflags):
1775     if yasm_asflags:
1776         return True
1778 set_config('HAVE_NASM', have_nasm)
1780 set_config('HAVE_YASM', have_yasm)
1781 # Until the YASM variable is not necessary in old-configure.
1782 add_old_configure_assignment('YASM', have_yasm)
1785 # clang-cl integrated assembler support
1786 # ==============================================================
1787 @depends(target)
1788 def clangcl_asflags(target):
1789     asflags = None
1790     if target.os == 'WINNT' and target.cpu == 'aarch64':
1791         asflags = ['--target=aarch64-windows-msvc']
1792     return asflags
1795 set_config('CLANGCL_ASFLAGS', clangcl_asflags)
1798 # Code Coverage
1799 # ==============================================================
1801 js_option('--enable-coverage', env='MOZ_CODE_COVERAGE',
1802           help='Enable code coverage')
1805 @depends('--enable-coverage')
1806 def code_coverage(value):
1807     if value:
1808         return True
1811 set_config('MOZ_CODE_COVERAGE', code_coverage)
1812 set_define('MOZ_CODE_COVERAGE', code_coverage)
1814 @depends(target, c_compiler, vc_path, check_build_environment, when=code_coverage)
1815 @imports('re')
1816 @imports('mozpack.path')
1817 @imports(_from='__builtin__', _import='open')
1818 def coverage_cflags(target, c_compiler, vc_path, build_env):
1819     cflags = ['--coverage']
1821     if c_compiler.type in ('clang', 'clang-cl'):
1822         cflags += [
1823             '-Xclang', '-coverage-no-function-names-in-data',
1824         ]
1826     if target.os == 'WINNT' and c_compiler.type == 'clang-cl':
1827         # The Visual Studio directory is the parent of the Visual C++ directory.
1828         vs_path = os.path.dirname(vc_path)
1830         # We need to get the real path of Visual Studio, which can be in a
1831         # symlinked directory (for example, on automation).
1832         vs_path = mozpack.path.readlink(vs_path)
1833         # Since the -fprofile-exclude-files option in LLVM is a regex, we need to
1834         # have the same path separators.
1835         vs_path = vs_path.replace('/', '\\')
1837         cflags += [
1838             '-fprofile-exclude-files=^{}.*$'.format(re.escape(vs_path)),
1839         ]
1841     response_file_path = os.path.join(build_env.topobjdir, 'code_coverage_cflags')
1843     with open(response_file_path, 'w') as f:
1844         f.write(' '.join(cflags))
1846     return ['@{}'.format(response_file_path)]
1848 set_config('COVERAGE_CFLAGS', coverage_cflags)
1850 # ==============================================================
1852 option(env='RUSTFLAGS',
1853        nargs=1,
1854        help='Rust compiler flags')
1855 set_config('RUSTFLAGS', depends('RUSTFLAGS')(lambda flags: flags))
1858 # Rust compiler flags
1859 # ==============================================================
1861 js_option(env='RUSTC_OPT_LEVEL',
1862           nargs=1,
1863           help='Rust compiler optimization level (-C opt-level=%s)')
1865 # --enable-release kicks in full optimizations.
1866 imply_option('RUSTC_OPT_LEVEL', '2', when='--enable-release')
1869 @depends('RUSTC_OPT_LEVEL', moz_optimize)
1870 def rustc_opt_level(opt_level_option, moz_optimize):
1871     if opt_level_option:
1872         return opt_level_option[0]
1873     else:
1874         return '1' if moz_optimize.optimize else '0'
1877 @depends(rustc_opt_level, debug_rust, '--enable-debug-symbols', '--enable-frame-pointers')
1878 def rust_compile_flags(opt_level, debug_rust, debug_symbols, frame_pointers):
1879     # Cargo currently supports only two interesting profiles for building:
1880     # development and release. Those map (roughly) to --enable-debug and
1881     # --disable-debug in Gecko, respectively.
1882     #
1883     # But we'd also like to support an additional axis of control for
1884     # optimization level. Since Cargo only supports 2 profiles, we're in
1885     # a bit of a bind.
1886     #
1887     # Code here derives various compiler options given other configure options.
1888     # The options defined here effectively override defaults specified in
1889     # Cargo.toml files.
1891     debug_assertions = None
1892     debug_info = None
1894     # opt-level=0 implies -C debug-assertions, which may not be desired
1895     # unless Rust debugging is enabled.
1896     if opt_level == '0' and not debug_rust:
1897         debug_assertions = False
1899     if debug_symbols:
1900         debug_info = '2'
1902     opts = []
1904     if opt_level is not None:
1905         opts.append('opt-level=%s' % opt_level)
1906     if debug_assertions is not None:
1907         opts.append('debug-assertions=%s' %
1908                     ('yes' if debug_assertions else 'no'))
1909     if debug_info is not None:
1910         opts.append('debuginfo=%s' % debug_info)
1911     if frame_pointers:
1912         opts.append('force-frame-pointers=yes')
1914     flags = []
1915     for opt in opts:
1916         flags.extend(['-C', opt])
1918     return flags
1921 # Rust incremental compilation
1922 # ==============================================================
1925 js_option('--disable-cargo-incremental',
1926           help='Disable incremental rust compilation.')
1928 @depends(rustc_opt_level, debug_rust, 'MOZ_AUTOMATION', code_coverage,
1929          '--disable-cargo-incremental', using_sccache, 'RUSTC_WRAPPER')
1930 @imports('os')
1931 def cargo_incremental(opt_level, debug_rust, automation, code_coverage,
1932                       enabled, using_sccache, rustc_wrapper):
1933     """Return a value for the CARGO_INCREMENTAL environment variable."""
1935     if not enabled:
1936         return '0'
1938     # We never want to use incremental compilation in automation.  sccache
1939     # handles our automation use case much better than incremental compilation
1940     # would.
1941     if automation:
1942         return '0'
1944     # Coverage instrumentation doesn't play well with incremental compilation
1945     # https://github.com/rust-lang/rust/issues/50203.
1946     if code_coverage:
1947         return '0'
1949     # Incremental compilation doesn't work as well as it should, and if we're
1950     # using sccache, it's better to use sccache than incremental compilation.
1951     if not using_sccache and rustc_wrapper:
1952         rustc_wrapper = os.path.basename(rustc_wrapper[0])
1953         if os.path.splitext(rustc_wrapper)[0].lower() == 'sccache':
1954             using_sccache = True
1955     if using_sccache:
1956         return '0'
1958     # Incremental compilation is automatically turned on for debug builds, so
1959     # we don't need to do anything special here.
1960     if debug_rust:
1961         return
1963     # --enable-release automatically sets -O2 for Rust code, and people can
1964     # set RUSTC_OPT_LEVEL to 2 or even 3 if they want to profile Rust code.
1965     # Let's assume that if Rust code is using -O2 or higher, we shouldn't
1966     # be using incremental compilation, because we'd be imposing a
1967     # significant runtime cost.
1968     if opt_level not in ('0', '1'):
1969         return
1971     # We're clear to use incremental compilation!
1972     return '1'
1975 set_config('CARGO_INCREMENTAL', cargo_incremental)
1977 # Linker detection
1978 # ==============================================================
1981 @depends(target)
1982 def is_linker_option_enabled(target):
1983     if target.kernel not in ('WINNT', 'SunOS'):
1984         return True
1987 option('--enable-gold',
1988        env='MOZ_FORCE_GOLD',
1989        help='Enable GNU Gold Linker when it is not already the default',
1990        when=is_linker_option_enabled)
1992 imply_option('--enable-linker', 'gold', when='--enable-gold')
1994 @depends(target, developer_options)
1995 def enable_linker_default(target, developer_options):
1996     # x86-64 gold has bugs in how it lays out .note.* sections. See bug 1573820.
1997     # lld is faster, so prefer that for developer builds.
1998     if target.os == 'Android' and target.cpu == 'x86_64':
1999         return 'lld' if developer_options else 'bfd'
2002 js_option('--enable-linker', nargs=1,
2003           help='Select the linker {bfd, gold, ld64, lld, lld-*}{|}',
2004           default=enable_linker_default,
2005           when=is_linker_option_enabled)
2008 @depends('--enable-linker', c_compiler, developer_options, '--enable-gold',
2009          extra_toolchain_flags, target, when=is_linker_option_enabled)
2010 @checking('for linker', lambda x: x.KIND)
2011 @imports('os')
2012 @imports('shutil')
2013 def select_linker(linker, c_compiler, developer_options, enable_gold,
2014                   toolchain_flags, target):
2016     if linker:
2017         linker = linker[0]
2018     else:
2019         linker = None
2021     def is_valid_linker(linker):
2022         if target.kernel == 'Darwin':
2023             valid_linkers = ('ld64', 'lld')
2024         else:
2025             valid_linkers = ('bfd', 'gold', 'lld')
2026         if linker in valid_linkers:
2027             return True
2028         if 'lld' in valid_linkers and linker.startswith('lld-'):
2029             return True
2030         return False
2032     if linker and not is_valid_linker(linker):
2033         # Check that we are trying to use a supported linker
2034         die('Unsupported linker ' + linker)
2036     # Check the kind of linker
2037     version_check = ['-Wl,--version']
2038     cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
2040     def try_linker(linker):
2041         # Generate the compiler flag
2042         if linker == 'ld64':
2043             linker_flag = ['-fuse-ld=ld']
2044         elif linker:
2045             linker_flag = ["-fuse-ld=" + linker]
2046         else:
2047             linker_flag = []
2048         cmd = cmd_base + linker_flag + version_check
2049         if toolchain_flags:
2050             cmd += toolchain_flags
2052         # ld64 doesn't have anything to print out a version. It does print out
2053         # "ld64: For information on command line options please use 'man ld'."
2054         # but that would require doing two attempts, one with --version, that
2055         # would fail, and another with --help.
2056         # Instead, abuse its LD_PRINT_OPTIONS feature to detect a message
2057         # specific to it on stderr when it fails to process --version.
2058         env = dict(os.environ)
2059         env['LD_PRINT_OPTIONS'] = '1'
2060         retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
2061         if retcode == 1 and 'Logging ld64 options' in stderr:
2062             kind = 'ld64'
2064         elif retcode != 0:
2065             return None
2067         elif 'GNU ld' in stdout:
2068             # We are using the normal linker
2069             kind = 'bfd'
2071         elif 'GNU gold' in stdout:
2072             kind = 'gold'
2074         elif 'LLD' in stdout:
2075             kind = 'lld'
2077         else:
2078             kind = 'unknown'
2080         return namespace(
2081             KIND=kind,
2082             LINKER_FLAG=linker_flag,
2083         )
2085     result = try_linker(linker)
2086     if result is None:
2087         if linker:
2088             die("Could not use {} as linker".format(linker))
2089         die("Failed to find a linker")
2091     if (linker is None and enable_gold.origin == 'default' and
2092             developer_options and result.KIND in ('bfd', 'gold')):
2093         # try and use lld if available.
2094         tried = try_linker('lld')
2095         if result.KIND != 'gold' and (tried is None or tried.KIND != 'lld'):
2096             tried = try_linker('gold')
2097             if tried is None or tried.KIND != 'gold':
2098                 tried = None
2099         if tried:
2100             result = tried
2102     # If an explicit linker was given, error out if what we found is different.
2103     if linker and not linker.startswith(result.KIND):
2104         die("Could not use {} as linker".format(linker))
2106     return result
2109 set_config('LINKER_KIND', select_linker.KIND)
2112 @depends_if(select_linker, macos_sdk)
2113 def linker_ldflags(linker, macos_sdk):
2114     flags = list(linker.LINKER_FLAG or [])
2115     if macos_sdk:
2116         if linker.KIND == 'ld64':
2117             flags.append('-Wl,-syslibroot,%s' % macos_sdk)
2118         else:
2119             flags.append('-Wl,--sysroot=%s' % macos_sdk)
2121     return flags
2124 add_old_configure_assignment('LINKER_LDFLAGS', linker_ldflags)
2127 # There's a wrinkle with MinGW: linker configuration is not enabled, so
2128 # `select_linker` is never invoked.  Hard-code around it.
2129 @depends(select_linker, target, c_compiler)
2130 def gcc_use_gnu_ld(select_linker, target, c_compiler):
2131     if select_linker is not None:
2132         return select_linker.KIND in ('bfd', 'gold', 'lld')
2133     if target.kernel == 'WINNT' and c_compiler.type == 'clang':
2134         return True
2135     return None
2138 # GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld.
2139 set_config('GCC_USE_GNU_LD', gcc_use_gnu_ld)
2140 add_old_configure_assignment('GCC_USE_GNU_LD', gcc_use_gnu_ld)
2142 # Assembler detection
2143 # ==============================================================
2145 js_option(env='AS', nargs=1, help='Path to the assembler')
2147 @depends(target, c_compiler)
2148 def as_info(target, c_compiler):
2149     if c_compiler.type == 'clang-cl':
2150         ml = {
2151             'x86': 'ml',
2152             'x86_64': 'ml64',
2153             'aarch64': 'armasm64.exe',
2154         }.get(target.cpu)
2155         return namespace(
2156             type='masm',
2157             names=(ml, )
2158         )
2159     # When building with anything but clang-cl, we just use the C compiler as the assembler.
2160     return namespace(
2161         type='gcc',
2162         names=(c_compiler.compiler, )
2163     )
2165 # One would expect the assembler to be specified merely as a program.  But in
2166 # cases where the assembler is passed down into js/, it can be specified in
2167 # the same way as CC: a program + a list of argument flags.  We might as well
2168 # permit the same behavior in general, even though it seems somewhat unusual.
2169 # So we have to do the same sort of dance as we did above with
2170 # `provided_compiler`.
2171 provided_assembler = provided_program('AS')
2172 assembler = check_prog('_AS', input=provided_assembler.program,
2173                        what='the assembler', progs=as_info.names,
2174                        paths=toolchain_search_path)
2176 @depends(as_info, assembler, provided_assembler, c_compiler)
2177 def as_with_flags(as_info, assembler, provided_assembler, c_compiler):
2178     if provided_assembler:
2179         return provided_assembler.wrapper + \
2180             [provided_assembler.program] + \
2181             provided_assembler.flags
2183     if as_info.type == 'masm':
2184         return assembler
2186     assert as_info.type == 'gcc'
2188     # Need to add compiler wrappers and flags as appropriate.
2189     return c_compiler.wrapper + [assembler] + c_compiler.flags
2192 add_old_configure_assignment('AS', as_with_flags)
2193 add_old_configure_assignment('ac_cv_prog_AS', as_with_flags)
2196 @depends(assembler, c_compiler, extra_toolchain_flags)
2197 @imports('subprocess')
2198 @imports(_from='os', _import='devnull')
2199 def gnu_as(assembler, c_compiler, toolchain_flags):
2200     # clang uses a compatible GNU assembler.
2201     if c_compiler.type == 'clang':
2202         return True
2204     if c_compiler.type == 'gcc':
2205         cmd = [assembler] + c_compiler.flags
2206         if toolchain_flags:
2207             cmd += toolchain_flags
2208         cmd += ['-Wa,--version', '-c', '-o', devnull, '-x', 'assembler', '-']
2209         # We don't actually have to provide any input on stdin, `Popen.communicate` will
2210         # close the stdin pipe.
2211         # clang will error if it uses its integrated assembler for this target,
2212         # so handle failures gracefully.
2213         if 'GNU' in check_cmd_output(*cmd, stdin=subprocess.PIPE, onerror=lambda: ''):
2214             return True
2217 set_config('GNU_AS', gnu_as)
2218 add_old_configure_assignment('GNU_AS', gnu_as)
2221 @depends(as_info, target)
2222 def as_dash_c_flag(as_info, target):
2223     # armasm64 doesn't understand -c.
2224     if as_info.type == 'masm' and target.cpu == 'aarch64':
2225         return ''
2226     else:
2227         return '-c'
2230 set_config('AS_DASH_C_FLAG', as_dash_c_flag)
2233 @depends(as_info, target)
2234 def as_outoption(as_info, target):
2235     # The uses of ASOUTOPTION depend on the spacing for -o/-Fo.
2236     if as_info.type == 'masm' and target.cpu != 'aarch64':
2237         return '-Fo'
2239     return '-o '
2242 set_config('ASOUTOPTION', as_outoption)
2244 # clang plugin handling
2245 # ==============================================================
2247 js_option('--enable-clang-plugin', env='ENABLE_CLANG_PLUGIN',
2248           help="Enable building with the Clang plugin (gecko specific static analyzers)")
2250 add_old_configure_assignment('ENABLE_CLANG_PLUGIN',
2251                              depends_if('--enable-clang-plugin')(lambda _: True))
2253 js_option('--enable-mozsearch-plugin', env='ENABLE_MOZSEARCH_PLUGIN',
2254           help="Enable building with the mozsearch indexer plugin")
2256 add_old_configure_assignment('ENABLE_MOZSEARCH_PLUGIN',
2257                              depends_if('--enable-mozsearch-plugin')(lambda _: True))
2259 # Libstdc++ compatibility hacks
2260 # ==============================================================
2262 js_option('--enable-stdcxx-compat', env='MOZ_STDCXX_COMPAT',
2263           help='Enable compatibility with older libstdc++')
2266 @template
2267 def libstdcxx_version(var, compiler):
2268     @depends(compiler, when='--enable-stdcxx-compat')
2269     @checking(var, lambda v: v and "GLIBCXX_%s" % v.dotted)
2270     @imports(_from='mozbuild.configure.libstdcxx', _import='find_version')
2271     @imports(_from='__builtin__', _import='Exception')
2272     def version(compiler):
2273         try:
2274             result = find_version(
2275                 compiler.wrapper + [compiler.compiler] + compiler.flags)
2276         except Exception:
2277             die("Couldn't determine libstdc++ version")
2278         if result:
2279             return namespace(
2280                 dotted=result[0],
2281                 encoded=str(result[1]),
2282             )
2284     set_config(var, version.encoded)
2285     return version
2288 add_gcc_flag(
2289     '-D_GLIBCXX_USE_CXX11_ABI=0', cxx_compiler,
2290     when=libstdcxx_version(
2291         'MOZ_LIBSTDCXX_TARGET_VERSION', cxx_compiler))
2292 add_gcc_flag(
2293     '-D_GLIBCXX_USE_CXX11_ABI=0', host_cxx_compiler,
2294     when=libstdcxx_version(
2295         'MOZ_LIBSTDCXX_HOST_VERSION', host_cxx_compiler))
2298 # Support various fuzzing options
2299 # ==============================================================
2300 js_option('--enable-fuzzing', help='Enable fuzzing support')
2302 @depends('--enable-fuzzing')
2303 def enable_fuzzing(value):
2304     if value:
2305         return True
2307 @depends(try_compile(body='__AFL_COMPILER;',
2308                      check_msg='for AFL compiler',
2309                      when='--enable-fuzzing'))
2310 def enable_aflfuzzer(afl):
2311     if afl:
2312         return True
2314 @depends(enable_fuzzing,
2315          enable_aflfuzzer,
2316          c_compiler,
2317          target)
2318 def enable_libfuzzer(fuzzing, afl, c_compiler, target):
2319     if fuzzing and not afl and c_compiler.type == 'clang' and target.os != 'Android':
2320         return True
2322 @depends(enable_fuzzing,
2323          enable_aflfuzzer,
2324          enable_libfuzzer)
2325 def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer):
2326     if fuzzing and (afl or libfuzzer):
2327         return True
2329 set_config('FUZZING', enable_fuzzing)
2330 set_define('FUZZING', enable_fuzzing)
2332 set_config('LIBFUZZER', enable_libfuzzer)
2333 set_define('LIBFUZZER', enable_libfuzzer)
2334 add_old_configure_assignment('LIBFUZZER', enable_libfuzzer)
2336 set_config('FUZZING_INTERFACES', enable_fuzzing_interfaces)
2337 set_define('FUZZING_INTERFACES', enable_fuzzing_interfaces)
2338 add_old_configure_assignment('FUZZING_INTERFACES', enable_fuzzing_interfaces)
2341 @depends(c_compiler.try_compile(flags=['-fsanitize=fuzzer-no-link'],
2342          when=enable_fuzzing,
2343          check_msg='whether the C compiler supports -fsanitize=fuzzer-no-link'))
2344 def libfuzzer_flags(value):
2345     if value:
2346         no_link_flag_supported = True
2347         # recommended for (and only supported by) clang >= 6
2348         use_flags = ['-fsanitize=fuzzer-no-link']
2349     else:
2350         no_link_flag_supported = False
2351         use_flags = ['-fsanitize-coverage=trace-pc-guard,trace-cmp']
2353     return namespace(
2354         no_link_flag_supported=no_link_flag_supported,
2355         use_flags=use_flags,
2356     )
2358 set_config('HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK', libfuzzer_flags.no_link_flag_supported)
2359 set_config('LIBFUZZER_FLAGS', libfuzzer_flags.use_flags)
2360 add_old_configure_assignment('LIBFUZZER_FLAGS', libfuzzer_flags.use_flags)
2362 # Shared library building
2363 # ==============================================================
2365 # XXX: The use of makefile constructs in these variables is awful.
2366 @depends(target, c_compiler)
2367 def make_shared_library(target, compiler):
2368     if target.os == 'WINNT':
2369         if compiler.type == 'gcc':
2370             return namespace(
2371                 mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-o', '$@'],
2372                 mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-o', '$@'],
2373             )
2374         elif compiler.type == 'clang':
2375             return namespace(
2376                 mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
2377                 mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
2378             )
2379         else:
2380             linker = [
2381                 '$(LINKER)',
2382                 '-NOLOGO', '-DLL',
2383                 '-OUT:$@',
2384                 '-PDB:$(LINK_PDBFILE)',
2385                 '$(DSO_LDOPTS)'
2386             ]
2387             return namespace(
2388                 mkshlib=linker,
2389                 mkcshlib=linker,
2390             )
2392     cc = ['$(CC)', '$(COMPUTED_C_LDFLAGS)']
2393     cxx = ['$(CXX)', '$(COMPUTED_CXX_LDFLAGS)']
2394     flags = ['$(PGO_CFLAGS)', '$(DSO_PIC_CFLAGS)', '$(DSO_LDOPTS)']
2395     output = ['-o', '$@']
2397     if target.kernel == 'Darwin':
2398         soname = []
2399     elif target.os == 'NetBSD':
2400         soname = ['-Wl,-soname,$(DSO_SONAME)']
2401     else:
2402         assert compiler.type in ('gcc', 'clang')
2404         soname = ['-Wl,-h,$(DSO_SONAME)']
2406     return namespace(
2407         mkshlib=cxx + flags + soname + output,
2408         mkcshlib=cc + flags + soname + output,
2409     )
2412 set_config('MKSHLIB', make_shared_library.mkshlib)
2413 set_config('MKCSHLIB', make_shared_library.mkcshlib)
2416 @depends(c_compiler, toolchain_prefix, when=target_is_windows)
2417 def rc_names(c_compiler, toolchain_prefix):
2418     if c_compiler.type in ('gcc', 'clang'):
2419         return tuple('%s%s' % (p, 'windres')
2420                      for p in ('',) + (toolchain_prefix or ()))
2421     return ('rc',)
2424 check_prog('RC', rc_names, paths=sdk_bin_path)
2427 @depends(link, toolchain_prefix)
2428 def ar_config(link, toolchain_prefix):
2429     if link:  # if LINKER is set, it's either for lld-link or link
2430         if 'lld-link' in link:
2431             return namespace(
2432                 names=('llvm-lib',),
2433                 flags=('-llvmlibthin', '-out:$@'),
2434             )
2435         else:
2436             return namespace(
2437                 names=('lib',),
2438                 flags=('-NOLOGO', '-OUT:$@'),
2439             )
2440     return namespace(
2441         names=tuple('%s%s' % (p, 'ar')
2442                    for p in (toolchain_prefix or ()) + ('',)),
2443         flags=('crs', '$@'),
2444     )
2447 ar = check_prog('AR', ar_config.names, paths=toolchain_search_path)
2449 add_old_configure_assignment('AR', ar)
2451 set_config('AR_FLAGS', ar_config.flags)