Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / build / moz.configure / toolchain.configure
blob0d0249b215d997bbb92a5ffed2a607988a10e5a6
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 # Code optimization
8 # ==============================================================
10 option("--disable-optimize", nargs="?", help="Disable optimizations via compiler flags")
13 @depends("--enable-optimize")
14 def moz_optimize(option):
15     flags = None
17     if len(option):
18         flags = option[0]
19         if "-O0" in flags:
20             val = None
21         else:
22             val = "2"
23     elif option:
24         val = "1"
25     else:
26         val = None
28     return namespace(
29         optimize=val,
30         flags=flags,
31     )
34 set_config("MOZ_OPTIMIZE", moz_optimize.optimize)
35 add_old_configure_assignment("MOZ_OPTIMIZE", moz_optimize.optimize)
36 add_old_configure_assignment("MOZ_CONFIGURE_OPTIMIZE_FLAGS", moz_optimize.flags)
38 # Android NDK
39 # ==============================================================
42 @depends("--disable-compile-environment", target)
43 def compiling_android(compile_env, target):
44     return compile_env and target.os == "Android"
47 include("android-ndk.configure", when=compiling_android)
49 with only_when(target_is_osx):
50     # MacOS deployment target version
51     # ==============================================================
52     # This needs to happen before any compilation test is done.
54     option(
55         "--enable-macos-target",
56         env="MACOSX_DEPLOYMENT_TARGET",
57         nargs=1,
58         default=depends(target, developer_options)
59         # We continue to target 10.15 on Intel, but can target 11.0 for
60         # aarch64 since the earliest hardware was released alongside 11.0.
61         # For local builds, we want to target 10.15 regardless of the
62         # underlying platform to catch any errors or warnings that wouldn't
63         # show up when targeting 11.0, since these would later show up on
64         # CI for Intel builds.
65         (lambda t, d: "11.0" if (t.cpu == "aarch64" and not d) else "10.15"),
66         help="Set the minimum MacOS version needed at runtime{|}",
67     )
69     @depends_if("--enable-macos-target", developer_options)
70     def macos_target(value, _):
71         return value[0]
74 @imports("plistlib")
75 @imports(_from="__builtin__", _import="open")
76 @imports(_from="__builtin__", _import="Exception")
77 def get_sdk_version(sdk):
78     with open(os.path.join(sdk, "SDKSettings.plist"), "rb") as plist:
79         obj = plistlib.load(plist)
80     if not obj:
81         raise Exception(
82             "Error parsing SDKSettings.plist in the SDK directory: %s" % sdk
83         )
84     if "Version" not in obj:
85         raise Exception(
86             "Error finding Version information in SDKSettings.plist from the SDK: %s"
87             % sdk
88         )
89     return Version(obj["Version"])
92 with only_when(host_is_osx | target_is_osx):
93     # MacOS SDK
94     # =========
95     option(
96         "--with-macos-sdk",
97         env="MACOS_SDK_DIR",
98         nargs=1,
99         help="Location of platform SDK to use",
100     )
102     def mac_sdk_min_version():
103         return "14.4"
105     @depends(
106         "--with-macos-sdk",
107         host,
108         bootstrap_path(
109             "MacOSX{}.sdk".format(mac_sdk_min_version()),
110             when=depends("--with-macos-sdk")(lambda x: not x),
111             allow_failure=True,
112         ),
113     )
114     @imports(_from="__builtin__", _import="Exception")
115     @imports(_from="os.path", _import="isdir")
116     @imports(_from="os", _import="listdir")
117     def macos_sdk(sdk, host, bootstrapped):
118         if bootstrapped:
119             sdk = [bootstrapped]
120         if sdk:
121             sdk = sdk[0]
122             try:
123                 version = get_sdk_version(sdk)
124             except Exception as e:
125                 die(e)
126         elif host.os == "OSX":
127             sdk = check_cmd_output(
128                 "xcrun", "--show-sdk-path", onerror=lambda: ""
129             ).rstrip()
130             if not sdk:
131                 die(
132                     "Could not find the macOS SDK. Please use --with-macos-sdk to give "
133                     "the path to a macOS SDK."
134                 )
135             # Scan the parent directory xcrun returns for the most recent SDK.
136             sdk_dir = os.path.dirname(sdk)
137             versions = []
138             for d in listdir(sdk_dir):
139                 if d.lower().startswith("macos"):
140                     try:
141                         sdk = os.path.join(sdk_dir, d)
142                         versions.append((get_sdk_version(sdk), sdk))
143                     except Exception:
144                         pass
145             version, sdk = max(versions)
146         else:
147             die(
148                 "Need a macOS SDK when targeting macOS. Please use --with-macos-sdk "
149                 "to give the path to a macOS SDK."
150             )
152         if not isdir(sdk):
153             die(
154                 "SDK not found in %s. When using --with-macos-sdk, you must specify a "
155                 "valid SDK. SDKs are installed when the optional cross-development "
156                 "tools are selected during the Xcode/Developer Tools installation."
157                 % sdk
158             )
159         if version < Version(mac_sdk_min_version()):
160             die(
161                 'SDK version "%s" is too old. Please upgrade to at least %s. Try '
162                 "updating your system Xcode." % (version, mac_sdk_min_version())
163             )
164         return sdk
166     set_config("MACOS_SDK_DIR", macos_sdk)
169 with only_when(target_is_osx):
170     with only_when(cross_compiling):
171         option(
172             "--with-macos-private-frameworks",
173             env="MACOS_PRIVATE_FRAMEWORKS_DIR",
174             nargs=1,
175             help="Location of private frameworks to use",
176         )
178         @depends_if("--with-macos-private-frameworks")
179         @imports(_from="os.path", _import="isdir")
180         def macos_private_frameworks(value):
181             if value and not isdir(value[0]):
182                 die(
183                     "PrivateFrameworks not found not found in %s. When using "
184                     "--with-macos-private-frameworks, you must specify a valid "
185                     "directory",
186                     value[0],
187                 )
188             return value[0]
190     @depends(macos_private_frameworks, macos_sdk)
191     def macos_private_frameworks(value, sdk):
192         if value:
193             return value
194         return os.path.join(sdk or "/", "System/Library/PrivateFrameworks")
196     set_config("MACOS_PRIVATE_FRAMEWORKS_DIR", macos_private_frameworks)
199 with only_when(target_is_ios):
200     # iOS deployment target version
201     # ==============================================================
202     # This needs to happen before any compilation test is done.
204     option(
205         "--enable-ios-target",
206         env="IPHONEOS_DEPLOYMENT_TARGET",
207         nargs=1,
208         default="17.4",
209         help="Set the minimum iOS version needed at runtime",
210     )
212     @depends_if("--enable-ios-target")
213     def ios_target(value):
214         return value[0]
217 with only_when(target_is_ios):
218     # MacOS SDK
219     # =========
220     option(
221         "--with-ios-sdk",
222         env="IPHONEOS_SDK_DIR",
223         nargs=1,
224         help="Location of platform SDK to use",
225     )
227     def ios_sdk_min_version():
228         return "17.4"
230     @depends(target)
231     def ios_sdk_name(target):
232         return "iPhone{}{}.sdk".format(
233             "Simulator" if target.raw_os == "ios-sim" else "OS",
234             ios_sdk_min_version(),
235         )
237     @depends(
238         "--with-ios-sdk",
239         host,
240         target,
241         bootstrap_path(ios_sdk_name, when=depends("--with-ios-sdk")(lambda x: not x)),
242     )
243     @imports(_from="__builtin__", _import="Exception")
244     @imports(_from="os.path", _import="isdir")
245     @imports(_from="os", _import="listdir")
246     def ios_sdk(sdk, host, target, bootstrapped):
247         if bootstrapped:
248             sdk = [bootstrapped]
249         if sdk:
250             sdk = sdk[0]
251             try:
252                 version = get_sdk_version(sdk)
253             except Exception as e:
254                 die(e)
255         elif host.os == "OSX":
256             sdk_name = "iphonesimulator" if target.raw_os == "ios-sim" else "iphoneos"
257             sdk = check_cmd_output(
258                 "xcrun", "--show-sdk-path", "--sdk", sdk_name, onerror=lambda: ""
259             ).rstrip()
260             if not sdk:
261                 die(
262                     "Could not find the iOS SDK. Please use --with-ios-sdk to give "
263                     "the path to a iOS SDK."
264                 )
265             # Scan the parent directory xcrun returns for the most recent SDK.
266             sdk_dir = os.path.dirname(sdk)
267             versions = []
268             for d in listdir(sdk_dir):
269                 if d.lower().startswith(sdk_name):
270                     try:
271                         sdk = os.path.join(sdk_dir, d)
272                         versions.append((get_sdk_version(sdk), sdk))
273                     except Exception:
274                         pass
275             version, sdk = max(versions)
276         else:
277             die(
278                 "Need an iOS SDK when targeting iOS. Please use --with-ios-sdk "
279                 "to give the path to a iOS SDK."
280             )
282         if not isdir(sdk):
283             die(
284                 "SDK not found in %s. When using --with-ios-sdk, you must specify a "
285                 "valid SDK. SDKs are installed when the optional cross-development "
286                 "tools are selected during the Xcode installation." % sdk
287             )
288         if version < Version(ios_sdk_min_version()):
289             die(
290                 'SDK version "%s" is too old. Please upgrade to at least %s. Try '
291                 "updating your system Xcode." % (version, ios_sdk_min_version())
292             )
293         return sdk
295     set_config("IPHONEOS_SDK_DIR", ios_sdk)
298 # GC rooting and hazard analysis.
299 # ==============================================================
300 option(env="MOZ_HAZARD", help="Build for the GC rooting hazard analysis")
303 @depends("MOZ_HAZARD")
304 def hazard_analysis(value):
305     if value:
306         return True
309 set_config("MOZ_HAZARD", hazard_analysis)
312 # Cross-compilation related things.
313 # ==============================================================
314 option(
315     "--with-toolchain-prefix",
316     env="TOOLCHAIN_PREFIX",
317     nargs=1,
318     help="Prefix for the target toolchain",
322 @depends("--with-toolchain-prefix", host, target, cross_compiling)
323 def toolchain_prefix(value, host, target, cross_compiling):
324     if value:
325         return tuple(value)
326     # We don't want a toolchain prefix by default when building on mac for mac.
327     if cross_compiling and not (target.os == "OSX" and host.os == "OSX"):
328         return ("%s-" % target.toolchain, "%s-" % target.alias)
331 @depends(toolchain_prefix, target)
332 def first_toolchain_prefix(toolchain_prefix, target):
333     # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
334     # command line/environment (in which case there's only one value in the tuple),
335     # or when cross-compiling for Android or OSX.
336     if toolchain_prefix and (
337         target.os in ("Android", "OSX") or len(toolchain_prefix) == 1
338     ):
339         return toolchain_prefix[0]
342 set_config("TOOLCHAIN_PREFIX", first_toolchain_prefix)
343 add_old_configure_assignment("TOOLCHAIN_PREFIX", first_toolchain_prefix)
346 # Compilers
347 # ==============================================================
348 include("compilers-util.configure")
351 def try_preprocess(
352     configure_cache, compiler, language, source, onerror=None, wrapper=[]
354     return try_invoke_compiler(
355         configure_cache, compiler, language, source, ["-E"], onerror, wrapper
356     )
359 @imports(_from="mozbuild.configure.constants", _import="CompilerType")
360 @imports(_from="mozbuild.configure.constants", _import="CPU_preprocessor_checks")
361 @imports(_from="mozbuild.configure.constants", _import="kernel_preprocessor_checks")
362 @imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks")
363 @imports(_from="textwrap", _import="dedent")
364 @imports(_from="__builtin__", _import="Exception")
365 def get_compiler_info(configure_cache, compiler, language):
366     """Returns information about the given `compiler` (command line in the
367     form of a list or tuple), in the given `language`.
369     The returned information includes:
370     - the compiler type (clang-cl, clang or gcc)
371     - the compiler version
372     - the compiler supported language
373     - the compiler supported language version
374     """
375     # Xcode clang versions are different from the underlying llvm version (they
376     # instead are aligned with the Xcode version). Fortunately, we can tell
377     # apart plain clang from Xcode clang, and convert the Xcode clang version
378     # into the more or less corresponding plain clang version.
379     check = dedent(
380         """\
381         #if defined(_MSC_VER) && defined(__clang__) && defined(_MT)
382         %COMPILER "clang-cl"
383         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
384         #elif defined(__clang__)
385         %COMPILER "clang"
386         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
387         #  ifdef __apple_build_version__
388         %XCODE 1
389         #  endif
390         #elif defined(__GNUC__) && !defined(__MINGW32__)
391         %COMPILER "gcc"
392         %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
393         #endif
395         #if __cplusplus
396         %cplusplus __cplusplus
397         #elif __STDC_VERSION__
398         %STDC_VERSION __STDC_VERSION__
399         #endif
400     """
401     )
403     # While we're doing some preprocessing, we might as well do some more
404     # preprocessor-based tests at the same time, to check the toolchain
405     # matches what we want.
406     for name, preprocessor_checks in (
407         ("CPU", CPU_preprocessor_checks),
408         ("KERNEL", kernel_preprocessor_checks),
409         ("OS", OS_preprocessor_checks),
410     ):
411         for n, (value, condition) in enumerate(preprocessor_checks.items()):
412             check += dedent(
413                 """\
414                 #%(if)s %(condition)s
415                 %%%(name)s "%(value)s"
416             """
417                 % {
418                     "if": "elif" if n else "if",
419                     "condition": condition,
420                     "name": name,
421                     "value": value,
422                 }
423             )
424         check += "#endif\n"
426     # Also check for endianness. The advantage of living in modern times is
427     # that all the modern compilers we support now have __BYTE_ORDER__ defined
428     # by the preprocessor.
429     check += dedent(
430         """\
431         #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
432         %ENDIANNESS "little"
433         #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
434         %ENDIANNESS "big"
435         #endif
436     """
437     )
439     result = try_preprocess(configure_cache, compiler, language, check)
441     if not result:
442         raise FatalCheckError("Unknown compiler or compiler not supported.")
444     # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
445     # have non-ASCII characters. Treat the output as bytearray.
446     data = {}
447     for line in result.splitlines():
448         if line.startswith("%"):
449             k, _, v = line.partition(" ")
450             k = k.lstrip("%")
451             data[k] = v.replace(" ", "").lstrip('"').rstrip('"')
452             log.debug("%s = %s", k, data[k])
454     try:
455         type = CompilerType(data["COMPILER"])
456     except Exception:
457         raise FatalCheckError("Unknown compiler or compiler not supported.")
459     cplusplus = int(data.get("cplusplus", "0L").rstrip("L"))
460     stdc_version = int(data.get("STDC_VERSION", "0L").rstrip("L"))
462     version = data.get("VERSION")
463     if version:
464         version = Version(version)
465         if data.get("XCODE"):
466             # Derived from https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
467             # with enough granularity for major.minor version checks further
468             # down the line
469             if version < "9.1":
470                 version = Version("4.0.0.or.less")
471             elif version < "10.0":
472                 version = Version("5.0.2")
473             elif version < "10.0.1":
474                 version = Version("6.0.1")
475             elif version < "11.0":
476                 version = Version("7.0.0")
477             elif version < "11.0.3":
478                 version = Version("8.0.0")
479             elif version < "12.0":
480                 version = Version("9.0.0")
481             elif version < "12.0.5":
482                 version = Version("10.0.0")
483             elif version < "13.0":
484                 version = Version("11.1.0")
485             elif version < "13.0.1":
486                 version = Version("12.0.0")
487             elif version < "14.0":
488                 version = Version("13.0.0")
489             elif version < "15.0":
490                 version = Version("14.0.0")
491             else:
492                 version = Version("14.0.0.or.more")
494     return namespace(
495         type=type,
496         version=version,
497         cpu=data.get("CPU"),
498         kernel=data.get("KERNEL"),
499         endianness=data.get("ENDIANNESS"),
500         os=data.get("OS"),
501         language="C++" if cplusplus else "C",
502         language_version=cplusplus if cplusplus else stdc_version,
503         xcode=bool(data.get("XCODE")),
504     )
507 def same_arch_different_bits():
508     return (
509         ("x86", "x86_64"),
510         ("ppc", "ppc64"),
511         ("sparc", "sparc64"),
512     )
515 @imports(_from="mozbuild.shellutil", _import="quote")
516 @imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks")
517 def check_compiler(configure_cache, compiler, language, target, android_version):
518     info = get_compiler_info(configure_cache, compiler, language)
520     flags = []
522     # Check language standards
523     # --------------------------------------------------------------------
524     if language != info.language:
525         raise FatalCheckError(
526             "`%s` is not a %s compiler." % (quote(*compiler), language)
527         )
529     # Note: We do a strict version check because there sometimes are backwards
530     # incompatible changes in the standard, and not all code that compiles as
531     # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
532     # example)
533     if info.language == "C" and info.language_version != 199901:
534         if info.type == "clang-cl":
535             flags.append("-Xclang")
536         flags.append("-std=gnu99")
538     cxx17_version = 201703
539     if info.language == "C++":
540         if info.language_version != cxx17_version:
541             # MSVC headers include C++17 features, but don't guard them
542             # with appropriate checks.
543             if info.type == "clang-cl":
544                 flags.append("-Xclang")
545                 flags.append("-std=c++17")
546             else:
547                 flags.append("-std=gnu++17")
549     # Check compiler target
550     # --------------------------------------------------------------------
551     has_target = False
552     if target.os == "Android" and android_version:
553         # This makes clang define __ANDROID_API__ and use versioned library
554         # directories from the NDK.
555         toolchain = "%s%d" % (target.toolchain, android_version)
556     else:
557         toolchain = target.toolchain
559     if info.type == "clang":
560         # Add the target explicitly when the target is aarch64 macosx, because
561         # the Xcode clang target is named differently, and we need to work around
562         # https://github.com/rust-lang/rust-bindgen/issues/1871 and
563         # https://github.com/alexcrichton/cc-rs/issues/542 so we always want
564         # the target on the command line, even if the compiler would default to
565         # that.
566         if info.xcode and target.os == "OSX" and target.cpu == "aarch64":
567             if "--target=arm64-apple-darwin" not in compiler:
568                 flags.append("--target=arm64-apple-darwin")
569             has_target = True
570         elif target.os == "iOS":
571             target_flag = "--target=%s" % toolchain
572             if target_flag not in compiler:
573                 flags.append(target_flag)
574             has_target = True
575         elif (
576             not info.kernel
577             or info.kernel != target.kernel
578             or not info.endianness
579             or info.endianness != target.endianness
580         ):
581             flags.append("--target=%s" % toolchain)
582             has_target = True
584         # Add target flag when there is an OS mismatch (e.g. building for Android on
585         # Linux). However, only do this if the target OS is in our whitelist, to
586         # keep things the same on other platforms.
587         elif target.os in OS_preprocessor_checks and (
588             not info.os or info.os != target.os
589         ):
590             flags.append("--target=%s" % toolchain)
591             has_target = True
593     if not has_target and (not info.cpu or info.cpu != target.cpu):
594         same_arch = same_arch_different_bits()
595         if (target.cpu, info.cpu) in same_arch:
596             flags.append("-m32")
597         elif (info.cpu, target.cpu) in same_arch:
598             flags.append("-m64")
599         elif info.type == "clang-cl" and target.cpu == "aarch64":
600             flags.append("--target=%s" % toolchain)
601         elif info.type == "clang":
602             flags.append("--target=%s" % toolchain)
604     return namespace(
605         type=info.type,
606         version=info.version,
607         target_cpu=info.cpu,
608         target_kernel=info.kernel,
609         target_endianness=info.endianness,
610         target_os=info.os,
611         flags=flags,
612     )
615 @imports(_from="__builtin__", _import="open")
616 @imports("json")
617 @imports("os")
618 def get_vc_paths(topsrcdir):
619     def vswhere(args):
620         program_files = os.environ.get("PROGRAMFILES(X86)") or os.environ.get(
621             "PROGRAMFILES"
622         )
623         if not program_files:
624             return []
625         vswhere = os.path.join(
626             program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe"
627         )
628         if not os.path.exists(vswhere):
629             return []
630         return json.loads(check_cmd_output(vswhere, "-format", "json", *args))
632     for install in vswhere(
633         [
634             "-products",
635             "*",
636             "-requires",
637             "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
638         ]
639     ):
640         path = install["installationPath"]
641         tools_version = (
642             open(
643                 os.path.join(
644                     path, r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"
645                 ),
646                 "r",
647             )
648             .read()
649             .strip()
650         )
651         tools_path = os.path.join(path, r"VC\Tools\MSVC", tools_version)
652         yield (Version(install["installationVersion"]), tools_path)
655 @depends(target, host)
656 def is_windows(target, host):
657     return host.kernel == "WINNT" or target.kernel == "WINNT"
660 # Calling this a sysroot is a little weird, but it's the terminology clang went
661 # with with its -winsysroot flag.
662 option(
663     env="WINSYSROOT",
664     nargs=1,
665     when=is_windows,
666     help='Path to a Windows "sysroot" (directory containing MSVC, SDKs)',
670 @depends(
671     "WINSYSROOT",
672     bootstrap_path(
673         "vs",
674         when=depends("WINSYSROOT", when=is_windows)(lambda x: not x),
675     ),
676     when=is_windows,
678 def winsysroot(winsysroot, bootstrapped):
679     if bootstrapped:
680         return bootstrapped
681     if winsysroot:
682         return winsysroot[0]
685 option(
686     env="VC_PATH",
687     nargs=1,
688     when=is_windows,
689     help="Path to the Microsoft Visual C/C++ compiler",
693 @depends(
694     host,
695     build_environment,
696     "VC_PATH",
697     winsysroot,
698     when=is_windows,
700 @imports("os")
701 @imports(_from="operator", _import="itemgetter")
702 def vc_compiler_paths_for_version(host, env, vc_path, winsysroot):
703     if winsysroot:
704         if vc_path:
705             die("WINSYSROOT and VC_PATH cannot be set together.")
706         base_vc_path = os.path.join(winsysroot, "VC", "Tools", "MSVC")
707         versions = os.listdir(base_vc_path)
708         vc_path = [os.path.join(base_vc_path, str(max(Version(v) for v in versions)))]
709     if vc_path:
710         # Use an arbitrary version, it doesn't matter.
711         all_versions = [(Version("15"), vc_path[0])]
712     elif host.kernel != "WINNT":
713         # Don't try to do anything when VC_PATH is not set on cross-compiles.
714         return
715     else:
716         all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
717     if not all_versions:
718         return
719     # Choose the newest version.
720     path = all_versions[-1][1]
721     host_dir = {
722         "x86_64": "Hostx64",
723         "x86": "Hostx86",
724     }.get(host.cpu)
725     if host_dir:
726         path = os.path.join(path, "bin", host_dir)
727         return {
728             "x64": [os.path.join(path, "x64")],
729             # The cross toolchains require DLLs from the native x64 toolchain.
730             "x86": [os.path.join(path, "x86"), os.path.join(path, "x64")],
731             "arm64": [os.path.join(path, "arm64"), os.path.join(path, "x64")],
732         }
735 @depends(target, host, vc_compiler_paths_for_version, when=is_windows)
736 def vc_compiler_path(target, host, paths):
737     cpu = target.cpu if target.os == "WINNT" else host.cpu
738     vc_target = {
739         "x86": "x86",
740         "x86_64": "x64",
741         "arm": "arm",
742         "aarch64": "arm64",
743     }.get(cpu)
744     if not paths:
745         return
746     return paths.get(vc_target)
749 @depends(vc_compiler_path, original_path)
750 @imports("os")
751 @imports(_from="os", _import="environ")
752 def vc_toolchain_search_path(vc_compiler_path, original_path):
753     result = list(original_path)
755     if vc_compiler_path:
756         # The second item, if there is one, is necessary to have in $PATH for
757         # Windows to load the required DLLs from there.
758         if len(vc_compiler_path) > 1:
759             environ["PATH"] = os.pathsep.join(result + vc_compiler_path[1:])
761         # The first item is where the programs are going to be
762         result.append(vc_compiler_path[0])
764     return result
767 @depends_if(vc_compiler_path, when=is_windows)
768 def vc_compiler_version(vc_compiler_path):
769     version = Version(
770         os.path.basename(
771             os.path.dirname(os.path.dirname(os.path.dirname(vc_compiler_path[0])))
772         )
773     )
774     # MSVC path with version 14.x is actually version 19.x
775     if version.major == 14:
776         return Version(f"19.{version.minor}")
779 @depends_if(vc_compiler_version)
780 def msvs_version(vc_compiler_version):
781     # clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to
782     # be set for GYP on Windows.
783     if vc_compiler_version >= Version("19.30"):
784         return "2022"
785     configure_error("Only Visual Studio 2022 or newer are supported")
787     return ""
790 set_config("MSVS_VERSION", msvs_version)
793 clang_search_path = bootstrap_search_path("clang/bin")
796 @depends(
797     bootstrap_search_path("rustc/bin", when="MOZ_AUTOMATION"),
798     original_path,
800 @imports("os")
801 @imports(_from="os", _import="environ")
802 def rust_search_path(rust_path, original_path):
803     result = list(rust_path or original_path)
804     # Also add the rustup install directory for cargo/rustc.
805     cargo_home = environ.get("CARGO_HOME", "")
806     if cargo_home:
807         cargo_home = os.path.abspath(cargo_home)
808     else:
809         cargo_home = os.path.expanduser(os.path.join("~", ".cargo"))
810     rustup_path = os.path.join(cargo_home, "bin")
811     result.insert(0, rustup_path)
812     return result
815 # Prepend the mozilla-build msys2 path, since otherwise we can get mismatched
816 # cygwin dll errors during configure if we get called from another msys2
817 # environment, see bug 1801826.
818 @depends(
819     mozillabuild_bin_paths, clang_search_path, rust_search_path, target, original_path
821 @imports("os")
822 def altered_path(
823     mozillabuild_bin_paths, clang_search_path, rust_search_path, target, original_path
825     altered_path = mozillabuild_bin_paths
826     if target.kernel == "Darwin":
827         # The rust compiler wants to execute dsymutil, but it does so in a
828         # non-configurable way (https://github.com/rust-lang/rust/issues/52728)
829         # so we add the clang path.
830         path = clang_search_path
831     else:
832         path = original_path
833     # cargo needs the rust search path to find cargo-$subcommand.
834     path += rust_search_path
835     for p in path:
836         if p not in altered_path:
837             altered_path.append(p)
838     return os.pathsep.join(altered_path)
841 set_config("PATH", altered_path)
844 # Compiler wrappers
845 # ==============================================================
846 option(
847     "--with-compiler-wrapper",
848     env="COMPILER_WRAPPER",
849     nargs=1,
850     help="Enable compiling with wrappers such as distcc and ccache",
853 option("--with-ccache", env="CCACHE", nargs="?", help="Enable compiling with ccache")
856 @depends_if("--with-ccache")
857 def ccache(value):
858     if len(value):
859         return value
860     # If --with-ccache was given without an explicit value, we default to
861     # 'ccache'.
862     return "ccache"
865 ccache = check_prog(
866     "CCACHE",
867     progs=(),
868     input=ccache,
869     paths=bootstrap_search_path(
870         "sccache", when=depends("CCACHE")(lambda c: len(c) and c[0] == "sccache")
871     ),
872     allow_missing=True,
875 option(env="CCACHE_PREFIX", nargs=1, help="Compiler prefix to use when using ccache")
877 ccache_prefix = depends_if("CCACHE_PREFIX")(lambda prefix: prefix[0])
878 set_config("CCACHE_PREFIX", ccache_prefix)
880 # Distinguish ccache from sccache.
883 @depends_if(ccache)
884 def ccache_is_sccache(ccache):
885     return check_cmd_output(ccache, "--version").startswith("sccache")
888 @depends(ccache, ccache_is_sccache)
889 def using_ccache(ccache, ccache_is_sccache):
890     return ccache and not ccache_is_sccache
893 @depends_if(ccache, ccache_is_sccache)
894 def using_sccache(ccache, ccache_is_sccache):
895     return ccache and ccache_is_sccache
898 option(env="RUSTC_WRAPPER", nargs=1, help="Wrap rust compilation with given tool")
901 @depends(ccache, ccache_is_sccache, "RUSTC_WRAPPER")
902 @imports(_from="textwrap", _import="dedent")
903 @imports("os")
904 def check_sccache_version(ccache, ccache_is_sccache, rustc_wrapper):
905     sccache_min_version = Version("0.2.13")
907     def check_version(path):
908         out = check_cmd_output(path, "--version")
909         version = Version(out.rstrip().split()[-1])
910         if version < sccache_min_version:
911             die(
912                 dedent(
913                     """\
914             sccache %s or later is required. sccache in use at %s has
915             version %s.
917             Please upgrade or acquire a new version with |./mach bootstrap|.
918             """
919                 ),
920                 sccache_min_version,
921                 path,
922                 version,
923             )
925     if ccache and ccache_is_sccache:
926         check_version(ccache)
928     if rustc_wrapper and (
929         os.path.splitext(os.path.basename(rustc_wrapper[0]))[0].lower() == "sccache"
930     ):
931         check_version(rustc_wrapper[0])
934 set_config("MOZ_USING_CCACHE", using_ccache)
935 set_config("MOZ_USING_SCCACHE", using_sccache)
937 option(env="SCCACHE_VERBOSE_STATS", help="Print verbose sccache stats after build")
940 @depends(using_sccache, "SCCACHE_VERBOSE_STATS")
941 def sccache_verbose_stats(using_sccache, verbose_stats):
942     return using_sccache and bool(verbose_stats)
945 set_config("SCCACHE_VERBOSE_STATS", sccache_verbose_stats)
948 @depends("--with-compiler-wrapper", ccache)
949 @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
950 def compiler_wrapper(wrapper, ccache):
951     if wrapper:
952         raw_wrapper = wrapper[0]
953         wrapper = shell_split(raw_wrapper)
954         wrapper_program = find_program(wrapper[0])
955         if not wrapper_program:
956             die(
957                 "Cannot find `%s` from the given compiler wrapper `%s`",
958                 wrapper[0],
959                 raw_wrapper,
960             )
961         wrapper[0] = wrapper_program
963     if ccache:
964         if wrapper:
965             return tuple([ccache] + wrapper)
966         else:
967             return (ccache,)
968     elif wrapper:
969         return tuple(wrapper)
972 @depends_if(compiler_wrapper)
973 def using_compiler_wrapper(compiler_wrapper):
974     return True
977 set_config("MOZ_USING_COMPILER_WRAPPER", using_compiler_wrapper)
980 @dependable
981 def wasm():
982     return split_triplet("wasm32-wasi", allow_wasi=True)
985 @template
986 def default_c_compilers(host_or_target, other_c_compiler=None):
987     """Template defining the set of default C compilers for the host and
988     target platforms.
989     `host_or_target` is either `host` or `target` (the @depends functions
990     from init.configure.
991     `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
992     """
993     assert host_or_target in {host, target, wasm}
995     other_c_compiler = () if other_c_compiler is None else (other_c_compiler,)
997     @depends(host_or_target, target, toolchain_prefix, *other_c_compiler)
998     def default_c_compilers(
999         host_or_target, target, toolchain_prefix, *other_c_compiler
1000     ):
1001         if host_or_target.kernel == "WINNT":
1002             if host_or_target.abi:
1003                 if host_or_target.abi == "msvc":
1004                     supported = types = ("clang-cl",)
1005                 elif host_or_target.abi == "mingw":
1006                     supported = types = ("clang",)
1007             else:
1008                 supported = types = ("clang-cl", "clang")
1009         elif host_or_target.kernel == "Darwin":
1010             types = ("clang",)
1011             supported = ("clang", "gcc")
1012         elif host_or_target.kernel == "WASI":
1013             supported = types = ("clang",)
1014         else:
1015             supported = types = ("clang", "gcc")
1017         info = other_c_compiler[0] if other_c_compiler else None
1018         if info and info.type in supported:
1019             # When getting default C compilers for the host, we prioritize the
1020             # same compiler as the target C compiler.
1021             prioritized = info.compiler
1022             if info.type == "gcc":
1023                 same_arch = same_arch_different_bits()
1024                 if (
1025                     target.cpu != host_or_target.cpu
1026                     and (target.cpu, host_or_target.cpu) not in same_arch
1027                     and (host_or_target.cpu, target.cpu) not in same_arch
1028                 ):
1029                     # If the target C compiler is GCC, and it can't be used with
1030                     # -m32/-m64 for the host, it's probably toolchain-prefixed,
1031                     # so we prioritize a raw 'gcc' instead.
1032                     prioritized = info.type
1033             if target.os != "WINNT" and host_or_target.os == "WINNT":
1034                 # When cross-compiling on Windows, don't prioritize. We'll fallback
1035                 # to checking for clang-cl first.
1036                 pass
1037             else:
1038                 types = [prioritized] + [t for t in types if t != info.type]
1040         gcc = ("gcc",)
1041         if toolchain_prefix and host_or_target is target:
1042             gcc = tuple("%sgcc" % p for p in toolchain_prefix) + gcc
1044         result = []
1045         for type in types:
1046             if type == "gcc":
1047                 result.extend(gcc)
1048             else:
1049                 result.append(type)
1051         return tuple(result)
1053     return default_c_compilers
1056 @template
1057 def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None):
1058     """Template defining the set of default C++ compilers for the host and
1059     target platforms.
1060     `c_compiler` is the @depends function returning a Compiler instance for
1061     the desired platform.
1063     Because the build system expects the C and C++ compilers to be from the
1064     same compiler suite, we derive the default C++ compilers from the C
1065     compiler that was found if none was provided.
1067     We also factor in the target C++ compiler when getting the default host
1068     C++ compiler, using the target C++ compiler if the host and target C
1069     compilers are the same.
1070     """
1072     assert (other_c_compiler is None) == (other_cxx_compiler is None)
1073     if other_c_compiler is not None:
1074         other_compilers = (other_c_compiler, other_cxx_compiler)
1075     else:
1076         other_compilers = ()
1078     @depends(c_compiler, *other_compilers)
1079     def default_cxx_compilers(c_compiler, *other_compilers):
1080         if other_compilers:
1081             other_c_compiler, other_cxx_compiler = other_compilers
1082             if other_c_compiler.compiler == c_compiler.compiler:
1083                 return (other_cxx_compiler.compiler,)
1085         dir = os.path.dirname(c_compiler.compiler)
1086         file = os.path.basename(c_compiler.compiler)
1088         if c_compiler.type == "gcc":
1089             return (os.path.join(dir, file.replace("gcc", "g++")),)
1091         if c_compiler.type == "clang":
1092             return (os.path.join(dir, file.replace("clang", "clang++")),)
1094         return (c_compiler.compiler,)
1096     return default_cxx_compilers
1099 @template
1100 def provided_program(env_var, when=None):
1101     """Template handling cases where a program can be specified either as a
1102     path or as a path with applicable arguments.
1103     """
1105     @depends_if(env_var, when=when)
1106     @imports(_from="itertools", _import="takewhile")
1107     @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
1108     def provided(cmd):
1109         # Assume the first dash-prefixed item (and any subsequent items) are
1110         # command-line options, the item before the dash-prefixed item is
1111         # the program we're looking for, and anything before that is a wrapper
1112         # of some kind (e.g. sccache).
1113         cmd = shell_split(cmd[0])
1115         without_flags = list(takewhile(lambda x: not x.startswith("-"), cmd))
1117         return namespace(
1118             wrapper=without_flags[:-1],
1119             program=without_flags[-1],
1120             flags=cmd[len(without_flags) :],
1121         )
1123     return provided
1126 @template
1127 def sysroot(host_or_target, target_sysroot=None):
1128     assert target_sysroot or host_or_target is target
1129     bootstrap_target_when = target_is_linux_or_wasi
1130     if host_or_target is host:
1131         host_or_target_str = "host"
1132         opt = "--with-host-sysroot"
1133         env = "HOST_SYSROOT"
1134         when = depends(host)(lambda h: h.kernel == "Linux")
1136         # Only bootstrap a host sysroot when using a bootstrapped target sysroot
1137         # or when the target doesn't use a bootstrapped sysroot in the first place.
1138         @depends(when, bootstrap_target_when, target_sysroot.bootstrapped)
1139         def bootstrap_when(when, bootstrap_target_when, bootstrapped):
1140             return when and (bootstrapped or not bootstrap_target_when)
1142     else:
1143         assert host_or_target is target
1144         host_or_target_str = "target"
1145         opt = "--with-sysroot"
1146         env = "SYSROOT"
1147         when = target_is_linux_or_wasi
1148         bootstrap_when = bootstrap_target_when
1150     option(
1151         opt,
1152         env=env,
1153         nargs=1,
1154         when=when,
1155         help="Use the given sysroot directory for %s build" % host_or_target_str,
1156     )
1158     sysroot_input = depends(opt, when=when)(lambda x: x)
1159     bootstrap_sysroot = depends(bootstrap_when, sysroot_input)(
1160         # Only bootstrap when no flag was explicitly given (either --with or --without)
1161         lambda bootstrap, input: bootstrap
1162         and not input
1163         and input.origin == "default"
1164     )
1166     @depends(
1167         sysroot_input,
1168         host_or_target,
1169         macos_sdk,
1170         ios_sdk,
1171         bootstrap_path(
1172             depends(host_or_target)(lambda t: "sysroot-{}".format(t.toolchain)),
1173             when=bootstrap_sysroot,
1174         ),
1175     )
1176     @imports("os")
1177     def sysroot(sysroot_input, host_or_target, macos_sdk, ios_sdk, path):
1178         version = None
1179         if sysroot_input:
1180             path = sysroot_input[0]
1181         elif host_or_target.os == "OSX" and macos_sdk:
1182             path = macos_sdk
1183         elif host_or_target.os == "iOS" and ios_sdk:
1184             path = ios_sdk
1185         if path:
1186             # Find the version of libstdc++ headears in the sysroot
1187             include = os.path.join(path, "usr/include/c++")
1188             if os.path.isdir(include):
1189                 with os.scandir(include) as d:
1190                     version = max(Version(e.name) for e in d if e.is_dir())
1191             log.info("Using %s sysroot in %s", host_or_target_str, path)
1192         return namespace(
1193             path=path,
1194             bootstrapped=bool(path and not sysroot_input),
1195             stdcxx_version=version,
1196         )
1198     return sysroot
1201 target_sysroot = sysroot(target)
1204 # Use `system_lib_option` instead of `option` for options that enable building
1205 # with a system library for which the development headers are not available in
1206 # the bootstrapped sysroots.
1207 @template
1208 def system_lib_option(name, *args, **kwargs):
1209     option(name, *args, **kwargs)
1211     @depends(name, target_sysroot.bootstrapped)
1212     def no_system_lib_in_sysroot(value, bootstrapped):
1213         if bootstrapped and value:
1214             die(
1215                 "%s is not supported with bootstrapped sysroot. "
1216                 "Drop the option, or use --without-sysroot or --disable-bootstrap",
1217                 value.format(name),
1218             )
1221 host_sysroot = sysroot(host, target_sysroot)
1224 @template
1225 def multiarch_dir(host_or_target):
1226     sysroot = {
1227         host: host_sysroot,
1228         target: target_sysroot,
1229     }[host_or_target]
1231     @depends(host_or_target, when=sysroot.path)
1232     def multiarch_dir(target):
1233         if target.cpu == "x86":
1234             # Turn e.g. i686-linux-gnu into i386-linux-gnu
1235             return target.toolchain.replace(target.raw_cpu, "i386")
1236         return target.toolchain
1238     return multiarch_dir
1241 target_multiarch_dir = multiarch_dir(target)
1242 host_multiarch_dir = multiarch_dir(host)
1245 def minimum_gcc_version():
1246     return Version("8.1.0")
1249 @template
1250 def compiler(
1251     language,
1252     host_or_target,
1253     c_compiler=None,
1254     other_compiler=None,
1255     other_c_compiler=None,
1257     """Template handling the generic base checks for the compiler for the
1258     given `language` on the given platform (`host_or_target`).
1259     `host_or_target` is either `host` or `target` (the @depends functions
1260     from init.configure.
1261     When the language is 'C++', `c_compiler` is the result of the `compiler`
1262     template for the language 'C' for the same `host_or_target`.
1263     When `host_or_target` is `host`, `other_compiler` is the result of the
1264     `compiler` template for the same `language` for `target`.
1265     When `host_or_target` is `host` and the language is 'C++',
1266     `other_c_compiler` is the result of the `compiler` template for the
1267     language 'C' for `target`.
1268     """
1269     assert host_or_target in {host, target, wasm}
1270     assert language in ("C", "C++")
1271     assert language == "C" or c_compiler is not None
1272     assert host_or_target is target or other_compiler is not None
1273     assert language == "C" or host_or_target is target or other_c_compiler is not None
1275     host_or_target_str = {
1276         host: "host",
1277         target: "target",
1278         wasm: "wasm",
1279     }[host_or_target]
1281     sysroot = {
1282         host: host_sysroot,
1283         target: target_sysroot,
1284         wasm: dependable(lambda: namespace(path=None)),
1285     }[host_or_target]
1287     multiarch_dir = {
1288         host: host_multiarch_dir,
1289         target: target_multiarch_dir,
1290         wasm: never,
1291     }[host_or_target]
1293     var = {
1294         ("C", target): "CC",
1295         ("C++", target): "CXX",
1296         ("C", host): "HOST_CC",
1297         ("C++", host): "HOST_CXX",
1298         ("C", wasm): "WASM_CC",
1299         ("C++", wasm): "WASM_CXX",
1300     }[language, host_or_target]
1302     default_compilers = {
1303         "C": lambda: default_c_compilers(host_or_target, other_compiler),
1304         "C++": lambda: default_cxx_compilers(
1305             c_compiler, other_c_compiler, other_compiler
1306         ),
1307     }[language]()
1309     what = "the %s %s compiler" % (host_or_target_str, language)
1311     option(env=var, nargs=1, help="Path to %s" % what)
1313     # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
1314     # HOST_CXX variables.
1315     provided_compiler = provided_program(var)
1317     # Normally, we'd use `var` instead of `_var`, but the interaction with
1318     # old-configure complicates things, and for now, we a) can't take the plain
1319     # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
1320     # old-configure AC_SUBST it (because it's autoconf doing it, not us)
1321     compiler = check_prog(
1322         "_%s" % var,
1323         what=what,
1324         progs=default_compilers,
1325         input=provided_compiler.program,
1326         paths=clang_search_path,
1327     )
1329     @depends(
1330         configure_cache,
1331         compiler,
1332         provided_compiler,
1333         compiler_wrapper,
1334         host_or_target,
1335         sysroot,
1336         macos_target,
1337         ios_target,
1338         android_version,
1339         vc_compiler_version,
1340         multiarch_dir,
1341         winsysroot,
1342         host,
1343     )
1344     @checking("whether %s can be used" % what, lambda x: bool(x))
1345     @imports(_from="mozbuild.shellutil", _import="quote")
1346     @imports("os")
1347     def valid_compiler(
1348         configure_cache,
1349         compiler,
1350         provided_compiler,
1351         compiler_wrapper,
1352         host_or_target,
1353         sysroot,
1354         macos_target,
1355         ios_target,
1356         android_version,
1357         vc_compiler_version,
1358         multiarch_dir,
1359         winsysroot,
1360         host,
1361     ):
1362         wrapper = list(compiler_wrapper or ())
1363         flags = []
1364         if sysroot.path:
1365             if host_or_target.kernel == "Darwin":
1366                 # While --sysroot and -isysroot are roughly equivalent, when not using
1367                 # -isysroot on mac, clang takes the SDKROOT environment variable into
1368                 # consideration, which may be set by python and break things.
1369                 flags.extend(("-isysroot", sysroot.path))
1370             else:
1371                 flags.extend(("--sysroot", sysroot.path))
1372         if provided_compiler:
1373             wrapper.extend(provided_compiler.wrapper)
1374             flags.extend(provided_compiler.flags)
1376         info = check_compiler(
1377             configure_cache,
1378             wrapper + [compiler] + flags,
1379             language,
1380             host_or_target,
1381             android_version,
1382         )
1384         if host_or_target.os == "OSX" and macos_target:
1385             flags.append("-mmacosx-version-min=%s" % macos_target)
1386         if host_or_target.os == "iOS" and ios_target:
1387             flags.append("-mios-version-min=%s" % ios_target)
1389         # When not given an explicit compatibility version, clang-cl tries
1390         # to get one from MSVC, which might not even be the one used by the
1391         # build. And when it can't find one, its default might also not match
1392         # what the build is using. So if we were able to figure out the version
1393         # we're building with, explicitly use that.
1394         # This also means that, as a side effect, clang-cl will not try to find
1395         # MSVC, which saves a little overhead.
1396         if info.type == "clang-cl" and vc_compiler_version:
1397             flags.append(f"-fms-compatibility-version={vc_compiler_version}")
1399         if info.type == "clang" and language == "C++" and host_or_target.os == "OSX":
1400             flags.append("-stdlib=libc++")
1402         # Check that the additional flags we got are enough to not require any
1403         # more flags. If we get an exception, just ignore it; it's liable to be
1404         # invalid command-line flags, which means the compiler we're checking
1405         # doesn't support those command-line flags and will fail one or more of
1406         # the checks below.
1407         try:
1408             if info.flags:
1409                 flags += info.flags
1410                 info = check_compiler(
1411                     configure_cache,
1412                     wrapper + [compiler] + flags,
1413                     language,
1414                     host_or_target,
1415                     android_version,
1416                 )
1417         except FatalCheckError:
1418             pass
1420         if not info.target_cpu or info.target_cpu != host_or_target.cpu:
1421             raise FatalCheckError(
1422                 "%s %s compiler target CPU (%s) does not match --%s CPU (%s)"
1423                 % (
1424                     host_or_target_str.capitalize(),
1425                     language,
1426                     info.target_cpu or "unknown",
1427                     host_or_target_str,
1428                     host_or_target.raw_cpu,
1429                 )
1430             )
1432         if not info.target_kernel or (info.target_kernel != host_or_target.kernel):
1433             raise FatalCheckError(
1434                 "%s %s compiler target kernel (%s) does not match --%s kernel (%s)"
1435                 % (
1436                     host_or_target_str.capitalize(),
1437                     language,
1438                     info.target_kernel or "unknown",
1439                     host_or_target_str,
1440                     host_or_target.kernel,
1441                 )
1442             )
1444         if not info.target_endianness or (
1445             info.target_endianness != host_or_target.endianness
1446         ):
1447             raise FatalCheckError(
1448                 "%s %s compiler target endianness (%s) does not match --%s "
1449                 "endianness (%s)"
1450                 % (
1451                     host_or_target_str.capitalize(),
1452                     language,
1453                     info.target_endianness or "unknown",
1454                     host_or_target_str,
1455                     host_or_target.endianness,
1456                 )
1457             )
1459         # Compiler version checks
1460         # ===================================================
1461         # Check the compiler version here instead of in `compiler_version` so
1462         # that the `checking` message doesn't pretend the compiler can be used
1463         # to then bail out one line later.
1464         if info.type == "gcc":
1465             if host_or_target.os == "Android":
1466                 raise FatalCheckError(
1467                     "GCC is not supported on Android.\n"
1468                     "Please use clang from the Android NDK instead."
1469                 )
1470             gcc_version = minimum_gcc_version()
1471             if info.version < gcc_version:
1472                 raise FatalCheckError(
1473                     "Only GCC %d.%d or newer is supported (found version %s)."
1474                     % (gcc_version.major, gcc_version.minor, info.version)
1475                 )
1477             # Force GCC to use the C++ headers from the sysroot, and to prefer the
1478             # sysroot system headers to /usr/include.
1479             # Non-Debian GCC also doesn't look at headers in multiarch directory.
1480             if sysroot.bootstrapped and sysroot.stdcxx_version:
1481                 version = sysroot.stdcxx_version
1482                 for path in (
1483                     "usr/include/c++/{}".format(version),
1484                     "usr/include/{}/c++/{}".format(multiarch_dir, version),
1485                     "usr/include/{}".format(multiarch_dir),
1486                     "usr/include",
1487                 ):
1488                     flags.extend(("-isystem", os.path.join(sysroot.path, path)))
1490         if info.type == "clang-cl":
1491             if info.version < "9.0.0":
1492                 raise FatalCheckError(
1493                     "Only clang-cl 9.0 or newer is supported (found version %s)"
1494                     % info.version
1495                 )
1496             if winsysroot and host.os != "WINNT":
1497                 overlay = os.path.join(winsysroot, "overlay.yaml")
1498                 if os.path.exists(overlay):
1499                     overlay_flags = ["-Xclang", "-ivfsoverlay", "-Xclang", overlay]
1500                     if info.version >= "16.0" or (
1501                         # clang-cl 15 normally doesn't support the root-relative
1502                         # overlay we use, but the bootstrapped clang-cl 15 is patched
1503                         # to support it, so check we're using a patched version.
1504                         info.version >= "15.0"
1505                         and try_preprocess(
1506                             configure_cache,
1507                             [compiler] + flags + overlay_flags,
1508                             language,
1509                             "",
1510                             onerror=lambda: False,
1511                             wrapper=wrapper,
1512                         )
1513                     ):
1514                         flags.extend(overlay_flags)
1516         if (info.type, host_or_target.abi) in (
1517             ("clang", "msvc"),
1518             ("clang-cl", "mingw"),
1519         ):
1520             raise FatalCheckError("Unknown compiler or compiler not supported.")
1522         # If you want to bump the version check here ensure the version
1523         # is known for Xcode in get_compiler_info.
1524         if info.type == "clang" and info.version < "8.0":
1525             raise FatalCheckError(
1526                 "Only clang/llvm 8.0 or newer is supported (found version %s)."
1527                 % info.version
1528             )
1530         if host_or_target.kernel == "WASI":
1531             if info.type != "clang":
1532                 raise FatalCheckError(
1533                     "Only clang is supported for %s" % host_or_target.alias
1534                 )
1535             if info.version < "8.0":
1536                 raise FatalCheckError(
1537                     "Only clang/llvm 8.0 or newer is supported for %s (found version %s)."
1538                     % (host_or_target.alias, info.version)
1539                 )
1541         if host_or_target.os == "Android":
1542             # Need at least clang 13 for compiler-rt/libunwind being the default.
1543             if info.type == "clang" and info.version < "13.0":
1544                 raise FatalCheckError(
1545                     "Only clang/llvm 13.0 or newer is supported for %s (found version %s)."
1546                     % (host_or_target.alias, info.version)
1547                 )
1549         if info.flags:
1550             raise FatalCheckError("Unknown compiler or compiler not supported.")
1552         return namespace(
1553             wrapper=wrapper,
1554             compiler=compiler,
1555             flags=flags,
1556             type=info.type,
1557             version=info.version,
1558             language=language,
1559         )
1561     @depends(valid_compiler)
1562     @checking("%s version" % what)
1563     def compiler_version(compiler):
1564         return compiler.version
1566     if language == "C++":
1568         @depends(valid_compiler, c_compiler)
1569         def valid_compiler(compiler, c_compiler):
1570             if compiler.type != c_compiler.type:
1571                 die(
1572                     "The %s C compiler is %s, while the %s C++ compiler is "
1573                     "%s. Need to use the same compiler suite.",
1574                     host_or_target_str,
1575                     c_compiler.type,
1576                     host_or_target_str,
1577                     compiler.type,
1578                 )
1580             if compiler.version != c_compiler.version:
1581                 die(
1582                     "The %s C compiler is version %s, while the %s C++ "
1583                     "compiler is version %s. Need to use the same compiler "
1584                     "version.",
1585                     host_or_target_str,
1586                     c_compiler.version,
1587                     host_or_target_str,
1588                     compiler.version,
1589                 )
1590             return compiler
1592     # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
1593     # and the flags that were part of the user input for those variables to
1594     # be provided.
1595     add_old_configure_assignment(
1596         var,
1597         depends_if(valid_compiler)(
1598             lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1599         ),
1600     )
1602     if host_or_target is target:
1603         add_old_configure_assignment(
1604             "ac_cv_prog_%s" % var,
1605             depends_if(valid_compiler)(
1606                 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1607             ),
1608         )
1609         # We check that it works in python configure already.
1610         add_old_configure_assignment("ac_cv_prog_%s_works" % var.lower(), "yes")
1611         add_old_configure_assignment(
1612             "ac_cv_prog_%s_cross" % var.lower(),
1613             depends(cross_compiling)(lambda x: "yes" if x else "no"),
1614         )
1615         gcc_like = depends(valid_compiler.type)(
1616             lambda x: "yes" if x in ("gcc", "clang") else "no"
1617         )
1618         add_old_configure_assignment("ac_cv_prog_%s_g" % var.lower(), gcc_like)
1619         if language == "C":
1620             add_old_configure_assignment("ac_cv_prog_gcc", gcc_like)
1621         if language == "C++":
1622             add_old_configure_assignment("ac_cv_prog_gxx", gcc_like)
1624     # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
1625     # old-configure to do some of its still existing checks.
1626     if language == "C":
1627         set_config("%s_TYPE" % var, valid_compiler.type)
1628         add_old_configure_assignment("%s_TYPE" % var, valid_compiler.type)
1629         set_config(
1630             "%s_VERSION" % var, depends(valid_compiler.version)(lambda v: str(v))
1631         )
1633     valid_compiler = compiler_class(valid_compiler, host_or_target)
1635     def compiler_error():
1636         raise FatalCheckError(
1637             "Failed compiling a simple %s source with %s" % (language, what)
1638         )
1640     valid_compiler.try_compile(check_msg="%s works" % what, onerror=compiler_error)
1642     set_config("%s_BASE_FLAGS" % var, valid_compiler.flags)
1644     # Set CPP/CXXCPP for both the build system and old-configure. We don't
1645     # need to check this works for preprocessing, because we already relied
1646     # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
1647     # in the first place.
1648     if host_or_target is target:
1649         pp_var = {
1650             "C": "CPP",
1651             "C++": "CXXCPP",
1652         }[language]
1654         preprocessor = depends_if(valid_compiler)(
1655             lambda x: list(x.wrapper) + [x.compiler, "-E"] + list(x.flags)
1656         )
1658         set_config(pp_var, preprocessor)
1659         add_old_configure_assignment(pp_var, preprocessor)
1661     if language == "C":
1662         linker_var = {
1663             target: "LD",
1664             host: "HOST_LD",
1665         }.get(host_or_target)
1667         if linker_var:
1669             @deprecated_option(env=linker_var, nargs=1)
1670             def linker(value):
1671                 if value:
1672                     return value[0]
1674             @depends(linker)
1675             def unused_linker(linker):
1676                 if linker:
1677                     log.warning(
1678                         "The value of %s is not used by this build system." % linker_var
1679                     )
1681     return valid_compiler
1684 c_compiler = compiler("C", target)
1685 cxx_compiler = compiler("C++", target, c_compiler=c_compiler)
1686 host_c_compiler = compiler("C", host, other_compiler=c_compiler)
1687 host_cxx_compiler = compiler(
1688     "C++",
1689     host,
1690     c_compiler=host_c_compiler,
1691     other_compiler=cxx_compiler,
1692     other_c_compiler=c_compiler,
1696 @template
1697 def windows_abi(host_or_target, c_compiler):
1698     @depends(host_or_target)
1699     def windows_abi(host_or_target):
1700         if host_or_target.os == "WINNT":
1701             return host_or_target.abi
1703     @depends(host_or_target, windows_abi)
1704     def need_windows_abi_from_compiler(host_or_target, windows_abi):
1705         return host_or_target.os == "WINNT" and windows_abi is None
1707     @depends(host_or_target, c_compiler, when=need_windows_abi_from_compiler)
1708     def windows_abi_from_compiler(host_or_target, c_compiler):
1709         if host_or_target.os == "WINNT":
1710             if c_compiler.type == "clang-cl":
1711                 return "msvc"
1712             return "mingw"
1714     return windows_abi | windows_abi_from_compiler
1717 target_windows_abi = windows_abi(target, c_compiler)
1718 host_windows_abi = windows_abi(host, host_c_compiler)
1721 # Generic compiler-based conditions.
1722 building_with_gcc = depends(c_compiler)(lambda info: info.type == "gcc")
1723 building_with_gnu_cc = depends(c_compiler)(lambda info: info.type != "clang-cl")
1726 @depends(cxx_compiler, ccache_prefix)
1727 @imports("os")
1728 def cxx_is_icecream(info, ccache_prefix):
1729     if (
1730         os.path.islink(info.compiler)
1731         and os.path.basename(os.readlink(info.compiler)) == "icecc"
1732     ):
1733         return True
1734     if ccache_prefix and os.path.basename(ccache_prefix) == "icecc":
1735         return True
1738 set_config("CXX_IS_ICECREAM", cxx_is_icecream)
1741 # Libstdc++ compatibility hacks
1742 # ==============================================================
1744 @depends(target, host)
1745 def target_or_host_is_linux(target, host):
1746     return any(t.os == "GNU" and t.kernel == "Linux" for t in (target, host))
1749 option(
1750     "--enable-stdcxx-compat",
1751     env="MOZ_STDCXX_COMPAT",
1752     help="Enable compatibility with older libstdc++",
1753     when=target_or_host_is_linux,
1757 @depends("--enable-stdcxx-compat", when=target_or_host_is_linux)
1758 def stdcxx_compat(value):
1759     if value:
1760         return True
1763 set_config("MOZ_STDCXX_COMPAT", True, when=stdcxx_compat)
1766 # Linker detection
1767 # ==============================================================
1768 # The policy is as follows:
1769 # For Windows:
1770 # - the linker is picked via the LINKER environment variable per windows.configure,
1771 #   but ought to be lld-link in any case.
1772 # For macOS:
1773 # - the linker is lld if the clang used is >= 15 (per LLVM version, not Xcode version).
1774 # - the linker is also lld on local developer builds if the clang used is >= 13 (per LLVM
1775 #   version, not Xcode version)
1776 # - otherwise the linker is ld64, either from XCode on macOS, or from cctools-ports when
1777 #   cross-compiling.
1778 # For other OSes:
1779 # - on local developer builds: lld if present and the compiler is clang. Otherwise gold
1780 #   is used if present otherwise, whatever the compiler uses by default.
1781 # - on release/official builds: whatever the compiler uses by default, except when the
1782 #   compiler is clang, in which case lld is preferred when it's new enough.
1783 @template
1784 def is_not_winnt_or_sunos(host_or_target):
1785     @depends(host_or_target)
1786     def is_not_winnt_or_sunos(host_or_target):
1787         if host_or_target.kernel not in ("WINNT", "SunOS"):
1788             return True
1790     return is_not_winnt_or_sunos
1793 is_linker_option_enabled = is_not_winnt_or_sunos(target)
1796 @deprecated_option("--enable-gold", env="MOZ_FORCE_GOLD", when=is_linker_option_enabled)
1797 def enable_gold(value):
1798     if value:
1799         die("--enable-gold is deprecated, use --enable-linker=gold instead")
1800     else:
1801         die("--disable-gold is deprecated, use --enable-linker=something_else instead")
1804 option(
1805     "--enable-linker",
1806     nargs=1,
1807     help="Select the linker {bfd, gold, ld64, lld, lld-*, mold}",
1808     when=is_linker_option_enabled,
1812 # No-op to enable depending on --enable-linker from default_elfhack in
1813 # toolkit/moz.configure.
1814 @depends("--enable-linker", when=is_linker_option_enabled)
1815 def enable_linker(linker):
1816     return linker
1819 @template
1820 def select_linker_tmpl(host_or_target):
1821     if host_or_target is target:
1822         deps = depends(
1823             "--enable-linker",
1824             c_compiler,
1825             developer_options,
1826             extra_toolchain_flags,
1827             target,
1828             stdcxx_compat,
1829             when=is_linker_option_enabled,
1830         )
1831         host_or_target_str = "target"
1832     else:
1833         deps = depends(
1834             dependable(None),
1835             host_c_compiler,
1836             developer_options,
1837             dependable(None),
1838             host,
1839             stdcxx_compat,
1840             when=is_not_winnt_or_sunos(host_or_target),
1841         )
1842         host_or_target_str = "host"
1844     @deps
1845     @checking(f"for {host_or_target_str} linker", lambda x: x.KIND)
1846     @imports("os")
1847     @imports("shutil")
1848     def select_linker(
1849         linker, c_compiler, developer_options, toolchain_flags, target, stdcxx_compat
1850     ):
1851         if linker:
1852             linker = linker[0]
1853         else:
1854             linker = None
1856         def is_valid_linker(linker):
1857             if target.kernel == "Darwin":
1858                 valid_linkers = ("ld64", "lld")
1859             else:
1860                 valid_linkers = ("bfd", "gold", "lld", "mold")
1861             if linker in valid_linkers:
1862                 return True
1863             if "lld" in valid_linkers and linker.startswith("lld-"):
1864                 return True
1865             return False
1867         if linker and not is_valid_linker(linker):
1868             # Check that we are trying to use a supported linker
1869             die("Unsupported linker " + linker)
1871         # Check the kind of linker
1872         version_check = ["-Wl,--version"]
1873         cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
1875         def try_linker(linker):
1876             # Generate the compiler flag
1877             if linker == "ld64":
1878                 linker_flag = ["-fuse-ld=ld"]
1879             elif linker:
1880                 linker_flag = ["-fuse-ld=" + linker]
1881             else:
1882                 linker_flag = []
1883             cmd = cmd_base + linker_flag + version_check
1884             if toolchain_flags:
1885                 cmd += toolchain_flags
1887             # ld64 doesn't have anything to print out a version. It does print out
1888             # "ld64: For information on command line options please use 'man ld'."
1889             # but that would require doing two attempts, one with --version, that
1890             # would fail, and another with --help.
1891             # Instead, abuse its LD_PRINT_OPTIONS feature to detect a message
1892             # specific to it on stderr when it fails to process --version.
1893             env = dict(os.environ)
1894             env["LD_PRINT_OPTIONS"] = "1"
1895             # Some locales might not print out the strings we are looking for, so
1896             # ensure consistent output.
1897             env["LC_ALL"] = "C"
1898             retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
1899             if retcode == 1 and "Logging ld64 options" in stderr:
1900                 kind = "ld64"
1902             elif retcode != 0:
1903                 return None
1905             elif "mold" in stdout:
1906                 kind = "mold"
1908             elif "GNU ld" in stdout:
1909                 # We are using the normal linker
1910                 kind = "bfd"
1912             elif "GNU gold" in stdout:
1913                 kind = "gold"
1915             elif "LLD" in stdout:
1916                 kind = "lld"
1918             else:
1919                 kind = "unknown"
1921             if kind == "unknown" or is_valid_linker(kind):
1922                 return namespace(
1923                     KIND=kind,
1924                     LINKER_FLAG=linker_flag,
1925                 )
1927         result = None
1928         if linker:
1929             result = try_linker(linker)
1930             if result is None:
1931                 die("Could not use {} as linker".format(linker))
1933         if (
1934             result is None
1935             and c_compiler.type == "clang"
1936             and (
1937                 (
1938                     target.kernel != "Darwin"
1939                     and (
1940                         developer_options
1941                         or host_or_target_str == "host"
1942                         or c_compiler.version >= "15.0"
1943                     )
1944                 )
1945                 or (
1946                     target.kernel == "Darwin"
1947                     and (
1948                         (developer_options and c_compiler.version >= "13.0")
1949                         or c_compiler.version >= "15.0"
1950                     )
1951                 )
1952             )
1953         ):
1954             result = try_linker("lld")
1956         if result is None and developer_options and not stdcxx_compat:
1957             result = try_linker("gold")
1959         if result is None:
1960             result = try_linker(None)
1962         if result is None:
1963             die("Failed to find an adequate linker")
1965         if stdcxx_compat and result.KIND == "gold":
1966             die("--enable-stdcxx-compat is not compatible with the gold linker")
1968         # If an explicit linker was given, error out if what we found is different.
1969         if linker and not linker.startswith(result.KIND):
1970             die("Could not use {} as linker".format(linker))
1972         return result
1974     return select_linker
1977 select_linker = select_linker_tmpl(target)
1978 set_config("LINKER_KIND", select_linker.KIND)
1981 @template
1982 def linker_ldflags_tmpl(host_or_target):
1983     if host_or_target is target:
1984         deps = depends_if(
1985             select_linker,
1986             target,
1987             target_sysroot,
1988             target_multiarch_dir,
1989             android_sysroot,
1990             android_version,
1991             c_compiler,
1992             developer_options,
1993         )
1994     else:
1995         deps = depends_if(
1996             select_linker_tmpl(host),
1997             host,
1998             host_sysroot,
1999             host_multiarch_dir,
2000             dependable(None),
2001             dependable(None),
2002             host_c_compiler,
2003             developer_options,
2004         )
2006     @deps
2007     @imports("os")
2008     def linker_ldflags(
2009         linker,
2010         target,
2011         sysroot,
2012         multiarch_dir,
2013         android_sysroot,
2014         android_version,
2015         c_compiler,
2016         developer_options,
2017     ):
2018         flags = list((linker and linker.LINKER_FLAG) or [])
2019         # rpath-link is irrelevant to wasm, see for more info https://github.com/emscripten-core/emscripten/issues/11076.
2020         if sysroot.path and multiarch_dir and target.os != "WASI":
2021             for d in ("lib", "usr/lib"):
2022                 multiarch_lib_dir = os.path.join(sysroot.path, d, multiarch_dir)
2023                 if os.path.exists(multiarch_lib_dir):
2024                     # Non-Debian-patched binutils linkers (both BFD and gold) don't lookup
2025                     # in multi-arch directories.
2026                     flags.append("-Wl,-rpath-link,%s" % multiarch_lib_dir)
2027                     # GCC also needs -L.
2028                     if c_compiler.type == "gcc":
2029                         flags.append("-L%s" % multiarch_lib_dir)
2030             if (
2031                 c_compiler.type == "gcc"
2032                 and sysroot.bootstrapped
2033                 and sysroot.stdcxx_version
2034             ):
2035                 flags.append(
2036                     "-L{}/usr/lib/gcc/{}/{}".format(
2037                         sysroot.path, multiarch_dir, sysroot.stdcxx_version
2038                     )
2039                 )
2040         if android_sysroot:
2041             # BFD/gold linkers need a manual --rpath-link for indirect
2042             # dependencies.
2043             flags += [
2044                 "-Wl,--rpath-link={}/usr/lib/{}".format(
2045                     android_sysroot, target.toolchain
2046                 ),
2047                 "-Wl,--rpath-link={}/usr/lib/{}/{}".format(
2048                     android_sysroot, target.toolchain, android_version
2049                 ),
2050             ]
2051         if (
2052             developer_options
2053             and linker
2054             and linker.KIND == "lld"
2055             and target.kernel != "WINNT"
2056         ):
2057             flags.append("-Wl,-O0")
2058         return flags
2060     return linker_ldflags
2063 linker_ldflags = linker_ldflags_tmpl(target)
2064 add_old_configure_assignment("LINKER_LDFLAGS", linker_ldflags)
2066 host_linker_ldflags = linker_ldflags_tmpl(host)
2067 add_old_configure_assignment("HOST_LINKER_LDFLAGS", host_linker_ldflags)
2070 # There's a wrinkle with MinGW: linker configuration is not enabled, so
2071 # `select_linker` is never invoked.  Hard-code around it.
2072 @depends(select_linker, target, c_compiler)
2073 def gcc_use_gnu_ld(select_linker, target, c_compiler):
2074     if select_linker is not None and target.kernel != "Darwin":
2075         return select_linker.KIND in ("bfd", "gold", "lld", "mold")
2076     if target.kernel == "WINNT" and c_compiler.type == "clang":
2077         return True
2078     return None
2081 # GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld.
2082 set_config("GCC_USE_GNU_LD", gcc_use_gnu_ld)
2083 add_old_configure_assignment("GCC_USE_GNU_LD", gcc_use_gnu_ld)
2086 include("compile-checks.configure")
2087 include("arm.configure", when=depends(target.cpu)(lambda cpu: cpu == "arm"))
2090 @depends(
2091     have_64_bit,
2092     try_compile(
2093         body='static_assert(sizeof(void *) == 8, "")', check_msg="for 64-bit OS"
2094     ),
2096 def check_have_64_bit(have_64_bit, compiler_have_64_bit):
2097     if have_64_bit != compiler_have_64_bit:
2098         configure_error(
2099             "The target compiler does not agree with configure "
2100             "about the target bitness."
2101         )
2104 @depends(cxx_compiler, target)
2105 def needs_libstdcxx_newness_check(cxx_compiler, target):
2106     # We only have to care about this on Linux and MinGW.
2107     if cxx_compiler.type == "clang-cl":
2108         return
2110     if target.kernel not in ("Linux", "WINNT"):
2111         return
2113     if target.os == "Android":
2114         return
2116     return True
2119 def die_on_old_libstdcxx():
2120     die(
2121         "The libstdc++ in use is not new enough.  Please run "
2122         "./mach bootstrap to update your compiler, or update your system "
2123         "libstdc++ installation."
2124     )
2127 try_compile(
2128     includes=["cstddef"],
2129     body="\n".join(
2130         [
2131             # _GLIBCXX_RELEASE showed up in libstdc++ 7.
2132             "#if defined(__GLIBCXX__) && !defined(_GLIBCXX_RELEASE)",
2133             "#  error libstdc++ not new enough",
2134             "#endif",
2135             "#if defined(_GLIBCXX_RELEASE)",
2136             "#  if _GLIBCXX_RELEASE < %d" % minimum_gcc_version().major,
2137             "#    error libstdc++ not new enough",
2138             "#  else",
2139             "     (void) 0",
2140             "#  endif",
2141             "#endif",
2142         ]
2143     ),
2144     check_msg="for new enough STL headers from libstdc++",
2145     when=needs_libstdcxx_newness_check,
2146     onerror=die_on_old_libstdcxx,
2150 @depends(c_compiler, target)
2151 def default_debug_flags(compiler_info, target):
2152     # Debug info is ON by default.
2153     if compiler_info.type == "clang-cl":
2154         return "-Z7"
2155     elif target.kernel == "WINNT" and compiler_info.type == "clang":
2156         return "-g -gcodeview"
2157     # The oldest versions of supported compilers default to DWARF-4, but
2158     # newer versions may default to DWARF-5 or newer (e.g. clang 14), which
2159     # Valgrind doesn't support. Force-use DWARF-4.
2160     return "-gdwarf-4"
2163 option(env="MOZ_DEBUG_FLAGS", nargs=1, help="Debug compiler flags")
2165 imply_option("--enable-debug-symbols", depends_if("--enable-debug")(lambda v: v))
2167 option(
2168     "--disable-debug-symbols",
2169     nargs="?",
2170     help="Disable debug symbols using the given compiler flags",
2173 set_config("MOZ_DEBUG_SYMBOLS", depends_if("--enable-debug-symbols")(lambda _: True))
2176 @depends("MOZ_DEBUG_FLAGS", "--enable-debug-symbols", default_debug_flags)
2177 def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
2178     # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
2179     # --enable-debug-symbols takes precedence. Note, the value of
2180     # --enable-debug-symbols may be implied by --enable-debug.
2181     if len(enable_debug_flags):
2182         return enable_debug_flags[0]
2183     if env_debug_flags:
2184         return env_debug_flags[0]
2185     return default_debug_flags
2188 set_config("MOZ_DEBUG_FLAGS", debug_flags)
2189 add_old_configure_assignment("MOZ_DEBUG_FLAGS", debug_flags)
2192 @depends(c_compiler, host)
2193 @imports(
2194     _from="mach.logging", _import="enable_blessed", _as="_enable_ansi_escape_codes"
2196 def color_cflags(info, host):
2197     # We could test compiling with flags. By why incur the overhead when
2198     # color support should always be present in a specific toolchain
2199     # version?
2201     # Code for auto-adding this flag to compiler invocations needs to
2202     # determine if an existing flag isn't already present. That is likely
2203     # using exact string matching on the returned value. So if the return
2204     # value changes to e.g. "<x>=always", exact string match may fail and
2205     # multiple color flags could be added. So examine downstream consumers
2206     # before adding flags to return values.
2207     if info.type == "gcc":
2208         return "-fdiagnostics-color"
2209     elif info.type in ["clang", "clang-cl"]:
2210         if host.os == "WINNT" and _enable_ansi_escape_codes():
2211             return "-fcolor-diagnostics -fansi-escape-codes"
2212         else:
2213             return "-fcolor-diagnostics"
2214     else:
2215         return ""
2218 set_config("COLOR_CFLAGS", color_cflags)
2220 # Some standard library headers (notably bionic on Android) declare standard
2221 # functions (e.g. getchar()) and also #define macros for those standard
2222 # functions.  libc++ deals with this by doing something like the following
2223 # (explanatory comments added):
2225 #   #ifdef FUNC
2226 #   // Capture the definition of FUNC.
2227 #   inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
2228 #   #undef FUNC
2229 #   // Use a real inline definition.
2230 #   inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
2231 #   #endif
2233 # _LIBCPP_INLINE_VISIBILITY is typically defined as:
2235 #   __attribute__((__visibility__("hidden"), __always_inline__))
2237 # Unfortunately, this interacts badly with our system header wrappers, as the:
2239 #   #pragma GCC visibility push(default)
2241 # that they do prior to including the actual system header is treated by the
2242 # compiler as an explicit declaration of visibility on every function declared
2243 # in the header.  Therefore, when the libc++ code above is encountered, it is
2244 # as though the compiler has effectively seen:
2246 #   int FUNC(...) __attribute__((__visibility__("default")));
2247 #   int FUNC(...) __attribute__((__visibility__("hidden")));
2249 # and the compiler complains about the mismatched visibility declarations.
2251 # However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
2252 # existing definition.  We can therefore define it to the empty string (since
2253 # we are properly managing visibility ourselves) and avoid this whole mess.
2254 # Note that we don't need to do this with gcc, as libc++ detects gcc and
2255 # effectively does the same thing we are doing here.
2257 # _LIBCPP_ALWAYS_INLINE needs a similar workarounds, since it too declares
2258 # hidden visibility.
2260 # _LIBCPP_HIDE_FROM_ABI is a macro in libc++ versions in NDKs >=r19.  It too
2261 # declares hidden visibility, but it also declares functions as excluded from
2262 # explicit instantiation (roughly: the function can be unused in the current
2263 # compilation, but does not then trigger an actual definition of the function;
2264 # it is assumed the real definition comes from elsewhere).  We need to replicate
2265 # this setup.
2268 @depends(c_compiler, target)
2269 def libcxx_override_visibility(c_compiler, target):
2270     if c_compiler.type == "clang" and target.os == "Android":
2271         return namespace(
2272             empty="",
2273             hide_from_abi="__attribute__((__exclude_from_explicit_instantiation__))",
2274         )
2277 set_define("_LIBCPP_INLINE_VISIBILITY", libcxx_override_visibility.empty)
2278 set_define("_LIBCPP_ALWAYS_INLINE", libcxx_override_visibility.empty)
2280 set_define("_LIBCPP_HIDE_FROM_ABI", libcxx_override_visibility.hide_from_abi)
2283 @depends(target, build_environment)
2284 def visibility_flags(target, env):
2285     if target.os != "WINNT":
2286         if target.kernel == "Darwin":
2287             return ("-fvisibility=hidden", "-fvisibility-inlines-hidden")
2288         return (
2289             "-I%s/system_wrappers" % os.path.join(env.dist),
2290             "-include",
2291             "%s/config/gcc_hidden.h" % env.topsrcdir,
2292         )
2295 @depends(target, visibility_flags)
2296 def wrap_system_includes(target, visibility_flags):
2297     if visibility_flags and target.kernel != "Darwin":
2298         return True
2301 set_define(
2302     "HAVE_VISIBILITY_HIDDEN_ATTRIBUTE",
2303     depends(visibility_flags)(lambda v: bool(v) or None),
2305 set_define(
2306     "HAVE_VISIBILITY_ATTRIBUTE", depends(visibility_flags)(lambda v: bool(v) or None)
2308 set_config("WRAP_SYSTEM_INCLUDES", wrap_system_includes)
2309 set_config("VISIBILITY_FLAGS", visibility_flags)
2312 # try harder, when checking for __thread support, see bug 521750 comment #33 and below
2313 # We pass linker_optimize_flags to the linker because if dead_strip is
2314 # enabled, the linker in xcode 4.1 will crash. Without this it would crash when
2315 # linking XUL.
2318 @depends(target, c_compiler)
2319 def check_thread(target, c_compiler):
2320     if target.cpu in ("mips32", "mips64"):
2321         # mips builds fail with TLS variables because of a binutils bug.
2322         # See bug 528687
2323         return False
2324     if target.os == "Android":
2325         # The custom dynamic linker doesn't support TLS variables
2326         return False
2327     if target.kernel == "OpenBSD":
2328         # OpenBSD doesn't have TLS support, and the test succeeds with clang++
2329         return False
2330     return c_compiler.type != "clang-cl"
2333 set_define(
2334     "HAVE_THREAD_TLS_KEYWORD",
2335     try_link(
2336         body="static __thread bool tlsIsMainThread = false; return tlsIsMainThread;",
2337         flags=linker_optimize_flags.ldflags,
2338         check_msg="for __thread keyword for TLS variables",
2339         when=check_thread,
2340     ),
2344 @template
2345 def depend_cflags(host_or_target_c_compiler):
2346     @depends(host_or_target_c_compiler)
2347     def depend_cflags(host_or_target_c_compiler):
2348         if host_or_target_c_compiler.type != "clang-cl":
2349             return ["-MD", "-MP", "-MF $(MDDEPDIR)/$(@F).pp"]
2350         else:
2351             # clang-cl doesn't accept the normal -MD -MP -MF options that clang
2352             # does, but the underlying cc1 binary understands how to generate
2353             # dependency files.  These options are based on analyzing what the
2354             # normal clang driver sends to cc1 when given the "correct"
2355             # dependency options.
2356             return [
2357                 "-Xclang",
2358                 "-MP",
2359                 "-Xclang",
2360                 "-dependency-file",
2361                 "-Xclang",
2362                 "$(MDDEPDIR)/$(@F).pp",
2363                 "-Xclang",
2364                 "-MT",
2365                 "-Xclang",
2366                 "$@",
2367             ]
2369     return depend_cflags
2372 set_config("_DEPEND_CFLAGS", depend_cflags(c_compiler))
2373 set_config("_HOST_DEPEND_CFLAGS", depend_cflags(host_c_compiler))
2376 @depends(c_compiler)
2377 def preprocess_option(compiler):
2378     # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
2379     if compiler.type in ("gcc", "clang"):
2380         return "-E -o "
2381     else:
2382         return "-P -Fi"
2385 set_config("PREPROCESS_OPTION", preprocess_option)
2388 # We only want to include windows.configure when we are compiling on
2389 # Windows, or for Windows.
2390 include("windows.configure", when=is_windows)
2393 # On Power ISA, determine compiler flags for VMX, VSX and VSX-3.
2395 set_config(
2396     "PPC_VMX_FLAGS",
2397     ["-maltivec"],
2398     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2401 set_config(
2402     "PPC_VSX_FLAGS",
2403     ["-mvsx"],
2404     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2407 set_config(
2408     "PPC_VSX3_FLAGS",
2409     ["-mvsx", "-mcpu=power9"],
2410     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2413 # ASAN
2414 # ==============================================================
2416 option("--enable-address-sanitizer", help="Enable Address Sanitizer")
2419 @depends(when="--enable-address-sanitizer")
2420 def asan():
2421     return True
2424 add_old_configure_assignment("MOZ_ASAN", asan)
2426 # MSAN
2427 # ==============================================================
2429 option("--enable-memory-sanitizer", help="Enable Memory Sanitizer")
2432 @depends(when="--enable-memory-sanitizer")
2433 def msan():
2434     return True
2437 add_old_configure_assignment("MOZ_MSAN", msan)
2439 # TSAN
2440 # ==============================================================
2442 option("--enable-thread-sanitizer", help="Enable Thread Sanitizer")
2445 @depends(when="--enable-thread-sanitizer")
2446 def tsan():
2447     return True
2450 add_old_configure_assignment("MOZ_TSAN", tsan)
2452 # UBSAN
2453 # ==============================================================
2455 option(
2456     "--enable-undefined-sanitizer", nargs="*", help="Enable UndefinedBehavior Sanitizer"
2460 @depends("--enable-undefined-sanitizer", moz_optimize.optimize)
2461 def ubsan(options, optimize):
2462     if not options:
2463         return
2465     default_checks = [
2466         "bool",
2467         "bounds",
2468         "enum",
2469         "function",
2470         "integer-divide-by-zero",
2471         "pointer-overflow",
2472         "return",
2473         "vla-bound",
2474     ]
2476     # adding object-size generates a warning if -O0 is set
2477     if optimize:
2478         default_checks.append("object-size")
2480     checks = options if len(options) else default_checks
2482     return ",".join(checks)
2485 add_old_configure_assignment("MOZ_UBSAN_CHECKS", ubsan)
2488 option(
2489     "--enable-signed-overflow-sanitizer",
2490     help="Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)",
2494 @depends(when="--enable-signed-overflow-sanitizer")
2495 def ub_signed_overflow_san():
2496     return True
2499 add_old_configure_assignment("MOZ_SIGNED_OVERFLOW_SANITIZE", ub_signed_overflow_san)
2502 option(
2503     "--enable-unsigned-overflow-sanitizer",
2504     help="Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)",
2508 @depends(when="--enable-unsigned-overflow-sanitizer")
2509 def ub_unsigned_overflow_san():
2510     return True
2513 add_old_configure_assignment("MOZ_UNSIGNED_OVERFLOW_SANITIZE", ub_unsigned_overflow_san)
2516 # Security Hardening
2517 # ==============================================================
2519 option(
2520     "--enable-hardening",
2521     env="MOZ_SECURITY_HARDENING",
2522     help="Enables security hardening compiler options",
2526 # This function is a bit confusing. It adds or removes hardening flags in
2527 # three stuations: if --enable-hardening is passed; if --disable-hardening
2528 # is passed, and if no flag is passed.
2530 # At time of this comment writing, all flags are actually added in the
2531 # default no-flag case; making --enable-hardening the same as omitting the
2532 # flag. --disable-hardening will omit the security flags. (However, not all
2533 # possible security flags will be omitted by --disable-hardening, as many are
2534 # compiler-default options we do not explicitly enable.)
2535 @depends(
2536     "--enable-hardening",
2537     "--enable-address-sanitizer",
2538     "--enable-debug",
2539     "--enable-optimize",
2540     c_compiler,
2541     target,
2543 def security_hardening_cflags(
2544     hardening_flag, asan, debug, optimize, c_compiler, target
2546     compiler_is_gccish = c_compiler.type in ("gcc", "clang")
2547     mingw_clang = c_compiler.type == "clang" and target.os == "WINNT"
2549     flags = []
2550     ldflags = []
2551     trivial_auto_var_init = []
2553     # WASI compiler doesn't support security hardening cflags
2554     if target.os == "WASI":
2555         return
2557     # ----------------------------------------------------------
2558     # If hardening is explicitly enabled, or not explicitly disabled
2559     if hardening_flag.origin == "default" or hardening_flag:
2560         # FORTIFY_SOURCE ------------------------------------
2561         # Require optimization for FORTIFY_SOURCE. See Bug 1417452
2562         # Also, undefine it before defining it just in case a distro adds it, see Bug 1418398
2563         if compiler_is_gccish and optimize and not asan:
2564             flags.append("-U_FORTIFY_SOURCE")
2565             flags.append("-D_FORTIFY_SOURCE=2")
2567         # fstack-protector ------------------------------------
2568         # Enable only if hardening is not disabled and ASAN is
2569         # not on as ASAN will catch the crashes for us
2570         if compiler_is_gccish and not asan:
2571             flags.append("-fstack-protector-strong")
2572             ldflags.append("-fstack-protector-strong")
2574             if (
2575                 c_compiler.type == "clang"
2576                 and c_compiler.version >= "11.0.1"
2577                 and target.os not in ("WINNT", "OSX", "OpenBSD")
2578                 and target.cpu in ("x86", "x86_64", "ppc64", "s390x")
2579             ):
2580                 flags.append("-fstack-clash-protection")
2581                 ldflags.append("-fstack-clash-protection")
2583         # ftrivial-auto-var-init ------------------------------
2584         # Initialize local variables with a 0xAA pattern in clang builds.
2585         # Linux32 fails some xpcshell tests with -ftrivial-auto-var-init
2586         linux32 = target.kernel == "Linux" and target.cpu == "x86"
2587         if (
2588             (c_compiler.type == "clang" or c_compiler.type == "clang-cl")
2589             and c_compiler.version >= "8"
2590             and not linux32
2591         ):
2592             if c_compiler.type == "clang-cl":
2593                 trivial_auto_var_init.append("-Xclang")
2594             trivial_auto_var_init.append("-ftrivial-auto-var-init=pattern")
2595             # Always enable on debug builds.
2596             if debug:
2597                 flags.extend(trivial_auto_var_init)
2599         # ASLR ------------------------------------------------
2600         # ASLR (dynamicbase) is enabled by default in clang-cl; but the
2601         # mingw-clang build requires it to be explicitly enabled
2602         if mingw_clang:
2603             ldflags.append("-Wl,--dynamicbase")
2605         # Control Flow Guard (CFG) ----------------------------
2606         if (
2607             c_compiler.type == "clang-cl"
2608             and c_compiler.version >= "8"
2609             and (target.cpu != "aarch64" or c_compiler.version >= "8.0.1")
2610         ):
2611             if target.cpu == "aarch64" and c_compiler.version >= "10.0.0":
2612                 # The added checks in clang 10 make arm64 builds crash. (Bug 1639318)
2613                 flags.append("-guard:cf,nochecks")
2614             else:
2615                 flags.append("-guard:cf")
2616             # nolongjmp is needed because clang doesn't emit the CFG tables of
2617             # setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057
2618             ldflags.append("-guard:cf,nolongjmp")
2620     # ----------------------------------------------------------
2621     # If ASAN _is_ on, disable FORTIFY_SOURCE just to be safe
2622     if asan:
2623         flags.append("-D_FORTIFY_SOURCE=0")
2625     # fno-common -----------------------------------------
2626     # Do not merge variables for ASAN; can detect some subtle bugs
2627     if asan:
2628         # clang-cl does not recognize the flag, it must be passed down to clang
2629         if c_compiler.type == "clang-cl":
2630             flags.append("-Xclang")
2631         flags.append("-fno-common")
2633     return namespace(
2634         flags=flags,
2635         ldflags=ldflags,
2636         trivial_auto_var_init=trivial_auto_var_init,
2637     )
2640 set_config("MOZ_HARDENING_CFLAGS", security_hardening_cflags.flags)
2641 set_config("MOZ_HARDENING_LDFLAGS", security_hardening_cflags.ldflags)
2642 set_config(
2643     "MOZ_TRIVIAL_AUTO_VAR_INIT",
2644     security_hardening_cflags.trivial_auto_var_init,
2648 # Intel Control-flow Enforcement Technology
2649 # ==============================================================
2650 # We keep this separate from the hardening flags above, because we want to be
2651 # able to easily remove the flags in the build files for certain executables.
2652 @depends(c_compiler, target)
2653 def cet_ldflags(c_compiler, target):
2654     ldflags = []
2655     if (
2656         c_compiler.type == "clang-cl"
2657         and c_compiler.version >= "11"
2658         and target.cpu == "x86_64"
2659     ):
2660         ldflags.append("-CETCOMPAT")
2661     return ldflags
2664 set_config("MOZ_CETCOMPAT_LDFLAGS", cet_ldflags)
2667 # Frame pointers
2668 # ==============================================================
2669 @depends(c_compiler)
2670 def frame_pointer_flags(compiler):
2671     if compiler.type == "clang-cl":
2672         return namespace(
2673             enable=["-Oy-"],
2674             disable=["-Oy"],
2675         )
2676     return namespace(
2677         enable=["-fno-omit-frame-pointer", "-funwind-tables"],
2678         disable=["-fomit-frame-pointer", "-funwind-tables"],
2679     )
2682 @depends(
2683     moz_optimize.optimize,
2684     moz_debug,
2685     target,
2686     "--enable-memory-sanitizer",
2687     "--enable-address-sanitizer",
2688     "--enable-undefined-sanitizer",
2690 def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
2691     return bool(
2692         not optimize
2693         or debug
2694         or msan
2695         or asan
2696         or ubsan
2697         or (target.os == "WINNT" and target.cpu in ("x86", "aarch64"))
2698         or target.os == "OSX"
2699     )
2702 option(
2703     "--enable-frame-pointers",
2704     default=frame_pointer_default,
2705     help="{Enable|Disable} frame pointers",
2709 @depends("--enable-frame-pointers", frame_pointer_flags)
2710 def frame_pointer_flags(enable, flags):
2711     if enable:
2712         return flags.enable
2713     return flags.disable
2716 set_config("MOZ_FRAMEPTR_FLAGS", frame_pointer_flags)
2719 # Stack unwinding without frame pointers
2720 # ==============================================================
2723 have_unwind = check_symbol(
2724     "_Unwind_Backtrace", when=check_header("unwind.h", when=target_is_unix)
2728 # Code Coverage
2729 # ==============================================================
2731 option("--enable-coverage", env="MOZ_CODE_COVERAGE", help="Enable code coverage")
2734 @depends("--enable-coverage")
2735 def code_coverage(value):
2736     if value:
2737         return True
2740 set_config("MOZ_CODE_COVERAGE", code_coverage)
2741 set_define("MOZ_CODE_COVERAGE", code_coverage)
2744 @depends(target, c_compiler, build_environment, when=code_coverage)
2745 @imports("os")
2746 @imports("re")
2747 @imports(_from="__builtin__", _import="open")
2748 def coverage_cflags(target, c_compiler, build_env):
2749     cflags = ["--coverage"]
2751     # clang 11 no longer accepts this flag (its behavior became the default)
2752     if c_compiler.type in ("clang", "clang-cl") and c_compiler.version < "11.0.0":
2753         cflags += [
2754             "-Xclang",
2755             "-coverage-no-function-names-in-data",
2756         ]
2758     exclude = []
2759     if target.os == "WINNT" and c_compiler.type == "clang-cl":
2760         # VS files
2761         exclude.append("^.*[vV][sS]20[0-9]{2}.*$")
2762         # Files in fetches directory.
2763         exclude.append("^.*[\\\\/]fetches[\\\\/].*$")
2764     elif target.os == "OSX":
2765         # Files in fetches directory.
2766         exclude.append("^.*/fetches/.*$")
2767     elif target.os == "GNU":
2768         # Files in fetches directory.
2769         exclude.append("^.*/fetches/.*$")
2770         # Files in /usr/
2771         exclude.append("^/usr/.*$")
2773     if exclude:
2774         exclude = ";".join(exclude)
2775         cflags += [
2776             f"-fprofile-exclude-files={exclude}",
2777         ]
2779     response_file_path = os.path.join(build_env.topobjdir, "code_coverage_cflags")
2781     with open(response_file_path, "w") as f:
2782         f.write(" ".join(cflags))
2784     return ["@{}".format(response_file_path)]
2787 set_config("COVERAGE_CFLAGS", coverage_cflags)
2789 # Assembler detection
2790 # ==============================================================
2792 option(env="AS", nargs=1, help="Path to the assembler")
2795 @depends(target, c_compiler)
2796 def as_info(target, c_compiler):
2797     if c_compiler.type == "clang-cl":
2798         ml = {
2799             "x86": "ml.exe",
2800             "x86_64": "ml64.exe",
2801             "aarch64": "armasm64.exe",
2802         }.get(target.cpu)
2803         return namespace(type="masm", names=(ml,))
2804     # When building with anything but clang-cl, we just use the C compiler as the assembler.
2805     return namespace(type="gcc", names=(c_compiler.compiler,))
2808 # One would expect the assembler to be specified merely as a program.  But in
2809 # cases where the assembler is passed down into js/, it can be specified in
2810 # the same way as CC: a program + a list of argument flags.  We might as well
2811 # permit the same behavior in general, even though it seems somewhat unusual.
2812 # So we have to do the same sort of dance as we did above with
2813 # `provided_compiler`.
2814 provided_assembler = provided_program("AS")
2815 assembler = check_prog(
2816     "_AS",
2817     input=provided_assembler.program,
2818     what="the assembler",
2819     progs=as_info.names,
2820     paths=vc_toolchain_search_path,
2824 @depends(as_info, assembler, provided_assembler, c_compiler)
2825 def as_with_flags(as_info, assembler, provided_assembler, c_compiler):
2826     if provided_assembler:
2827         return provided_assembler.wrapper + [assembler] + provided_assembler.flags
2829     if as_info.type == "masm":
2830         return assembler
2832     assert as_info.type == "gcc"
2834     # Need to add compiler wrappers and flags as appropriate.
2835     return c_compiler.wrapper + [assembler] + c_compiler.flags
2838 set_config("AS", as_with_flags)
2841 @depends(assembler, c_compiler, extra_toolchain_flags)
2842 @imports("subprocess")
2843 @imports(_from="os", _import="devnull")
2844 def gnu_as(assembler, c_compiler, toolchain_flags):
2845     # clang uses a compatible GNU assembler.
2846     if c_compiler.type == "clang":
2847         return True
2849     if c_compiler.type == "gcc":
2850         cmd = [assembler] + c_compiler.flags
2851         if toolchain_flags:
2852             cmd += toolchain_flags
2853         cmd += ["-Wa,--version", "-c", "-o", devnull, "-x", "assembler", "-"]
2854         # We don't actually have to provide any input on stdin, `Popen.communicate` will
2855         # close the stdin pipe.
2856         # clang will error if it uses its integrated assembler for this target,
2857         # so handle failures gracefully.
2858         if "GNU" in check_cmd_output(*cmd, stdin=subprocess.PIPE, onerror=lambda: ""):
2859             return True
2862 set_config("GNU_AS", gnu_as)
2865 @depends(as_info, target)
2866 def as_dash_c_flag(as_info, target):
2867     # armasm64 doesn't understand -c.
2868     if as_info.type == "masm" and target.cpu == "aarch64":
2869         return ""
2870     else:
2871         return "-c"
2874 set_config("AS_DASH_C_FLAG", as_dash_c_flag)
2877 @depends(as_info, target)
2878 def as_outoption(as_info, target):
2879     # The uses of ASOUTOPTION depend on the spacing for -o/-Fo.
2880     if as_info.type == "masm" and target.cpu != "aarch64":
2881         return "-Fo"
2883     return "-o "
2886 set_config("ASOUTOPTION", as_outoption)
2888 # clang plugin handling
2889 # ==============================================================
2891 option(
2892     "--enable-clang-plugin",
2893     env="ENABLE_CLANG_PLUGIN",
2894     help="Enable building with the Clang plugin (gecko specific static analyzers)",
2897 add_old_configure_assignment(
2898     "ENABLE_CLANG_PLUGIN", depends_if("--enable-clang-plugin")(lambda _: True)
2902 @depends(host_c_compiler, c_compiler, when="--enable-clang-plugin")
2903 def llvm_config(host_c_compiler, c_compiler):
2904     clang = None
2905     for compiler in (host_c_compiler, c_compiler):
2906         if compiler and compiler.type == "clang":
2907             clang = compiler.compiler
2908             break
2909         elif compiler and compiler.type == "clang-cl":
2910             clang = os.path.join(os.path.dirname(compiler.compiler), "clang")
2911             break
2913     if not clang:
2914         die("Cannot --enable-clang-plugin when not building with clang")
2915     llvm_config = "llvm-config"
2916     out = check_cmd_output(clang, "--print-prog-name=llvm-config", onerror=lambda: None)
2917     if out:
2918         llvm_config = out.rstrip()
2919     return (llvm_config,)
2922 llvm_config = check_prog(
2923     "LLVM_CONFIG",
2924     llvm_config,
2925     what="llvm-config",
2926     when="--enable-clang-plugin",
2927     paths=clang_search_path,
2930 add_old_configure_assignment("LLVM_CONFIG", llvm_config)
2933 option(
2934     "--enable-clang-plugin-alpha",
2935     env="ENABLE_CLANG_PLUGIN_ALPHA",
2936     help="Enable static analysis with clang-plugin alpha checks.",
2940 @depends("--enable-clang-plugin", "--enable-clang-plugin-alpha")
2941 def check_clang_plugin_alpha(enable_clang_plugin, enable_clang_plugin_alpha):
2942     if enable_clang_plugin_alpha:
2943         if enable_clang_plugin:
2944             return True
2945         die("Cannot enable clang-plugin alpha checkers without --enable-clang-plugin.")
2948 add_old_configure_assignment("ENABLE_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha)
2949 set_define("MOZ_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha)
2951 option(
2952     "--enable-mozsearch-plugin",
2953     env="ENABLE_MOZSEARCH_PLUGIN",
2954     help="Enable building with the mozsearch indexer plugin",
2957 add_old_configure_assignment(
2958     "ENABLE_MOZSEARCH_PLUGIN", depends_if("--enable-mozsearch-plugin")(lambda _: True)
2961 # Use the old libstdc++ ABI
2962 # ==============================================================
2963 add_flag(
2964     "-D_GLIBCXX_USE_CXX11_ABI=0",
2965     cxx_compiler,
2966     when=stdcxx_compat,
2968 add_flag(
2969     "-D_GLIBCXX_USE_CXX11_ABI=0",
2970     host_cxx_compiler,
2971     when=stdcxx_compat,
2975 # Support various fuzzing options
2976 # ==============================================================
2977 option("--enable-fuzzing", help="Enable fuzzing support")
2980 @depends(build_project)
2981 def js_build(build_project):
2982     return build_project == "js"
2985 option(
2986     "--enable-js-fuzzilli",
2987     when=js_build,
2988     help="Enable fuzzilli support for the JS engine",
2992 option(
2993     "--enable-snapshot-fuzzing",
2994     help="Enable experimental snapshot fuzzing support",
2998 imply_option("--enable-fuzzing", True, when="--enable-snapshot-fuzzing")
3001 @depends("--enable-snapshot-fuzzing")
3002 def enable_snapshot_fuzzing(value):
3003     if value:
3004         return True
3007 @depends("--enable-fuzzing", enable_snapshot_fuzzing)
3008 def enable_fuzzing(value, snapshot_fuzzing):
3009     if value or snapshot_fuzzing:
3010         return True
3013 @depends("--enable-js-fuzzilli", when=js_build)
3014 def enable_js_fuzzilli(value):
3015     if value:
3016         return True
3019 @depends(enable_fuzzing, enable_snapshot_fuzzing)
3020 def check_aflfuzzer(fuzzing, snapshot_fuzzing):
3021     if fuzzing and not snapshot_fuzzing:
3022         return True
3025 @depends(
3026     try_compile(
3027         body="__AFL_COMPILER;", check_msg="for AFL compiler", when=check_aflfuzzer
3028     )
3030 def enable_aflfuzzer(afl):
3031     if afl:
3032         return True
3035 @depends(enable_fuzzing, enable_aflfuzzer, enable_snapshot_fuzzing, c_compiler, target)
3036 def enable_libfuzzer(fuzzing, afl, snapshot_fuzzing, c_compiler, target):
3037     if (
3038         fuzzing
3039         and not afl
3040         and not snapshot_fuzzing
3041         and c_compiler.type == "clang"
3042         and target.os != "Android"
3043     ):
3044         return True
3047 @depends(enable_fuzzing, enable_aflfuzzer, enable_libfuzzer, enable_js_fuzzilli)
3048 def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer, enable_js_fuzzilli):
3049     if fuzzing and (afl or libfuzzer) and not enable_js_fuzzilli:
3050         return True
3053 set_config("FUZZING", enable_fuzzing)
3054 set_define("FUZZING", enable_fuzzing)
3056 set_config("LIBFUZZER", enable_libfuzzer)
3057 set_define("LIBFUZZER", enable_libfuzzer)
3058 add_old_configure_assignment("LIBFUZZER", enable_libfuzzer)
3060 set_config("AFLFUZZ", enable_aflfuzzer)
3061 set_define("AFLFUZZ", enable_aflfuzzer)
3063 set_config("FUZZING_INTERFACES", enable_fuzzing_interfaces)
3064 set_define("FUZZING_INTERFACES", enable_fuzzing_interfaces)
3065 add_old_configure_assignment("FUZZING_INTERFACES", enable_fuzzing_interfaces)
3067 set_config("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
3068 set_define("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
3070 set_config("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
3071 set_define("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
3074 @depends(
3075     c_compiler.try_compile(
3076         flags=["-fsanitize=fuzzer-no-link"],
3077         when=enable_fuzzing,
3078         check_msg="whether the C compiler supports -fsanitize=fuzzer-no-link",
3079     ),
3080     tsan,
3081     enable_js_fuzzilli,
3083 def libfuzzer_flags(value, tsan, enable_js_fuzzilli):
3084     if tsan:
3085         # With ThreadSanitizer, we should not use any libFuzzer instrumentation because
3086         # it is incompatible (e.g. there are races on global sanitizer coverage counters).
3087         # Instead we use an empty set of flags here but still build the fuzzing targets.
3088         # With this setup, we can still run files through these targets in TSan builds,
3089         # e.g. those obtained from regular fuzzing.
3090         # This code can be removed once libFuzzer has been made compatible with TSan.
3091         #
3092         # Also, this code needs to be kept in sync with certain gyp files, currently:
3093         #   - dom/media/webrtc/transport/third_party/nICEr/nicer.gyp
3094         return namespace(no_link_flag_supported=False, use_flags=[])
3096     if enable_js_fuzzilli:
3097         # Fuzzilli comes with its own trace-pc interceptors and flag requirements.
3098         no_link_flag_supported = False
3099         use_flags = ["-fsanitize-coverage=trace-pc-guard", "-g"]
3100     elif value:
3101         no_link_flag_supported = True
3102         # recommended for (and only supported by) clang >= 6
3103         use_flags = ["-fsanitize=fuzzer-no-link"]
3104     else:
3105         no_link_flag_supported = False
3106         use_flags = ["-fsanitize-coverage=trace-pc-guard,trace-cmp"]
3108     return namespace(
3109         no_link_flag_supported=no_link_flag_supported,
3110         use_flags=use_flags,
3111     )
3114 set_config("HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK", libfuzzer_flags.no_link_flag_supported)
3115 set_config("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
3116 add_old_configure_assignment("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
3118 # Shared library building
3119 # ==============================================================
3122 # XXX: The use of makefile constructs in these variables is awful.
3123 @depends(target, c_compiler)
3124 def make_shared_library(target, compiler):
3125     if target.os == "WINNT":
3126         if compiler.type == "gcc":
3127             return namespace(
3128                 mkshlib=["$(CXX)", "$(DSO_LDOPTS)", "-o", "$@"],
3129                 mkcshlib=["$(CC)", "$(DSO_LDOPTS)", "-o", "$@"],
3130             )
3131         elif compiler.type == "clang":
3132             return namespace(
3133                 mkshlib=[
3134                     "$(CXX)",
3135                     "$(DSO_LDOPTS)",
3136                     "-Wl,-pdb,$(LINK_PDBFILE)",
3137                     "-o",
3138                     "$@",
3139                 ],
3140                 mkcshlib=[
3141                     "$(CC)",
3142                     "$(DSO_LDOPTS)",
3143                     "-Wl,-pdb,$(LINK_PDBFILE)",
3144                     "-o",
3145                     "$@",
3146                 ],
3147             )
3148         else:
3149             linker = [
3150                 "$(LINKER)",
3151                 "-NOLOGO",
3152                 "-DLL",
3153                 "-OUT:$@",
3154                 "-PDB:$(LINK_PDBFILE)",
3155                 "$(DSO_LDOPTS)",
3156             ]
3157             return namespace(
3158                 mkshlib=linker,
3159                 mkcshlib=linker,
3160             )
3162     cc = ["$(CC)", "$(COMPUTED_C_LDFLAGS)"]
3163     cxx = ["$(CXX)", "$(COMPUTED_CXX_LDFLAGS)"]
3164     flags = ["$(DSO_LDOPTS)"]
3165     output = ["-o", "$@"]
3167     if target.kernel == "Darwin":
3168         soname = []
3169     elif target.os == "NetBSD":
3170         soname = ["-Wl,-soname,$(DSO_SONAME)"]
3171     else:
3172         assert compiler.type in ("gcc", "clang")
3174         soname = ["-Wl,-h,$(DSO_SONAME)"]
3176     return namespace(
3177         mkshlib=cxx + flags + soname + output,
3178         mkcshlib=cc + flags + soname + output,
3179     )
3182 set_config("MKSHLIB", make_shared_library.mkshlib)
3183 set_config("MKCSHLIB", make_shared_library.mkcshlib)
3186 @depends(c_compiler, toolchain_prefix, when=target_is_windows)
3187 def rc_names(c_compiler, toolchain_prefix):
3188     if c_compiler.type in ("gcc", "clang"):
3189         return tuple("%s%s" % (p, "windres") for p in ("",) + (toolchain_prefix or ()))
3190     return ("llvm-rc",)
3193 check_prog("RC", rc_names, paths=clang_search_path, when=target_is_windows)
3196 @template
3197 def ar_config(c_compiler, toolchain_prefix=None):
3198     if not toolchain_prefix:
3199         toolchain_prefix = dependable(None)
3201     @depends(toolchain_prefix, c_compiler)
3202     def ar_config(toolchain_prefix, c_compiler):
3203         if c_compiler.type == "clang-cl":
3204             return namespace(
3205                 names=("llvm-lib",),
3206                 flags=("-llvmlibthin", "-out:$@"),
3207             )
3209         names = tuple("%s%s" % (p, "ar") for p in (toolchain_prefix or ()) + ("",))
3210         if c_compiler.type == "clang":
3211             # Get the llvm-ar path as per the output from clang --print-prog-name=llvm-ar
3212             # so that we directly get the one under the clang directory, rather than one
3213             # that might be in /usr/bin and that might point to one from a different version
3214             # of clang.
3215             out = check_cmd_output(
3216                 c_compiler.compiler, "--print-prog-name=llvm-ar", onerror=lambda: None
3217             )
3218             llvm_ar = out.rstrip() if out else "llvm-ar"
3219             names = (llvm_ar,) + names
3221         return namespace(
3222             names=names,
3223             flags=("crs", "$@"),
3224         )
3226     return ar_config
3229 target_ar_config = ar_config(c_compiler, toolchain_prefix)
3231 target_ar = check_prog("AR", target_ar_config.names, paths=clang_search_path)
3233 set_config("AR_FLAGS", target_ar_config.flags)
3236 @depends(c_compiler, extra_toolchain_flags, target_ar, target_ar_config)
3237 @checking("whether ar supports response files")
3238 @imports("os")
3239 @imports(_from="tempfile", _import="mkstemp")
3240 @imports(_from="__builtin__", _import="FileNotFoundError")
3241 @imports(_from="mozbuild.configure.util", _import="LineIO")
3242 def ar_supports_response_files(c_compiler, extra_toolchain_flags, ar, ar_config):
3243     lib_path = list_path = None
3244     try:
3245         fd, obj_path = mkstemp(prefix="conftest.", suffix=".o")
3246         os.close(fd)
3247         if (
3248             try_invoke_compiler(
3249                 # No configure_cache because it would not create the
3250                 # expected output file.
3251                 None,
3252                 [c_compiler.compiler] + c_compiler.flags,
3253                 c_compiler.language,
3254                 "void foo() {}",
3255                 ["-c", "-o", obj_path] + (extra_toolchain_flags or []),
3256                 wrapper=c_compiler.wrapper,
3257                 onerror=lambda: None,
3258             )
3259             is not None
3260         ):
3261             fd, list_path = mkstemp(prefix="conftest.", suffix=".list")
3262             with os.fdopen(fd, "w") as list:
3263                 list.write(obj_path)
3264                 log.debug("Creating `%s` with content:", list_path)
3265                 log.debug("| %s", obj_path)
3266             fd, lib_path = mkstemp(prefix="conftest.", suffix=".a")
3267             os.close(fd)
3268             os.remove(lib_path)
3269             ar_command = (
3270                 [ar]
3271                 + [x.replace("$@", lib_path) for x in ar_config.flags]
3272                 + ["@" + list_path]
3273             )
3274             result = check_cmd_output(*ar_command, onerror=lambda: None)
3275             return result is not None
3276     finally:
3277         for cleanup_path in (obj_path, list_path, lib_path):
3278             if cleanup_path:
3279                 try:
3280                     os.remove(cleanup_path)
3281                 except FileNotFoundError:
3282                     pass
3285 set_config("AR_SUPPORTS_RESPONSE_FILE", True, when=ar_supports_response_files)
3287 host_ar_config = ar_config(host_c_compiler)
3289 check_prog("HOST_AR", host_ar_config.names, paths=clang_search_path)
3292 @depends(toolchain_prefix, c_compiler)
3293 def nm_names(toolchain_prefix, c_compiler):
3294     names = tuple("%s%s" % (p, "nm") for p in (toolchain_prefix or ()) + ("",))
3295     if c_compiler.type == "clang":
3296         # Get the llvm-nm path as per the output from clang --print-prog-name=llvm-nm
3297         # so that we directly get the one under the clang directory, rather than one
3298         # that might be in /usr/bin and that might point to one from a different version
3299         # of clang.
3300         out = check_cmd_output(
3301             c_compiler.compiler, "--print-prog-name=llvm-nm", onerror=lambda: None
3302         )
3303         llvm_nm = out.rstrip() if out else "llvm-nm"
3304         names = (llvm_nm,) + names
3306     return names
3309 check_prog("NM", nm_names, paths=clang_search_path, when=target_has_linux_kernel)
3312 option("--enable-cpp-rtti", help="Enable C++ RTTI")
3314 add_old_configure_assignment("_MOZ_USE_RTTI", "1", when="--enable-cpp-rtti")
3317 option(
3318     "--enable-path-remapping",
3319     nargs="*",
3320     choices=("c", "rust"),
3321     help="Enable remapping source and object paths in compiled outputs.",
3325 @depends("--enable-path-remapping")
3326 def path_remapping(value):
3327     if len(value):
3328         return value
3329     if bool(value):
3330         return ["c", "rust"]
3331     return []
3334 @depends(
3335     target,
3336     build_environment,
3337     target_sysroot.path,
3338     valid_windows_sdk_dir,
3339     vc_path,
3340     when="--enable-path-remapping",
3342 def path_remappings(target, build_env, sysroot_path, windows_sdk_dir, vc_path):
3343     win = target.kernel == "WINNT"
3345     # The prefix maps are processed in the order they're specified on the
3346     # command line.  Therefore, to accommodate object directories in the source
3347     # directory, it's important that we map the topobjdir before the topsrcdir,
3348     # 'cuz we might have /src/obj/=/o/ and /src/=/s/.  The various other
3349     # directories might be subdirectories of topsrcdir as well, so they come
3350     # earlier still.
3352     path_remappings = []
3354     # We will have only one sysroot or SDK, so all can have the same mnemonic: K
3355     # for "kit" (since S is taken for "source").  See
3356     # https://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html
3357     # for how to use the Windows `subst` command to map these in debuggers and
3358     # IDEs.
3359     if sysroot_path:
3360         path_remappings.append((sysroot_path, "k:/" if win else "/sysroot/"))
3361     if windows_sdk_dir:
3362         path_remappings.append(
3363             (windows_sdk_dir.path, "k:/" if win else "/windows_sdk/")
3364         )
3365     if vc_path:
3366         path_remappings.append((vc_path, "v:/" if win else "/vc/"))
3368     path_remappings += [
3369         (build_env.topobjdir, "o:/" if win else "/topobjdir/"),
3370         (build_env.topsrcdir, "s:/" if win else "/topsrcdir/"),
3371     ]
3373     path_remappings = [
3374         (normsep(old).rstrip("/") + "/", new) for old, new in path_remappings
3375     ]
3377     # It is tempting to sort these, but we want the order to be the same across
3378     # machines so that we can share cache hits.  Therefore we reject bad
3379     # configurations rather than trying to make the configuration good.
3380     for i in range(len(path_remappings) - 1):
3381         p = path_remappings[i][0]
3382         for q, _ in path_remappings[i + 1 :]:
3383             if q.startswith(p):
3384                 die(f"Cannot remap paths because {p} is an ancestor of {q}")
3386     return path_remappings
3389 @depends(target)
3390 def is_intel_target(target):
3391     return target.cpu in ("x86", "x86_64")
3394 @depends(target)
3395 def is_aarch64_target(target):
3396     return target.cpu == "aarch64"
3399 set_config("MMX_FLAGS", ["-mmmx"])
3400 set_config("SSE_FLAGS", ["-msse"])
3401 set_config("SSE2_FLAGS", ["-msse2"])
3402 set_config("SSSE3_FLAGS", ["-mssse3"])
3403 set_config("SSE4_2_FLAGS", ["-msse4.2"])
3404 set_config("FMA_FLAGS", ["-mfma"])
3405 set_config("AVX2_FLAGS", ["-mavx2"])
3406 set_config(
3407     "AVXVNNI_FLAGS",
3408     ["-mavxvnni"],
3409     try_compile(
3410         check_msg="for -mavxvnni support", flags=["-mavxvnni"], when=is_intel_target
3411     ),
3413 set_config(
3414     "AVX512BW_FLAGS",
3415     ["-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3416     try_compile(
3417         check_msg="for -mavx512bw support",
3418         flags=["-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3419         when=is_intel_target,
3420     ),
3423 # AVX512VNNI can be based on either avx512bw or avx512vbmi. We choose the
3424 # former.
3425 set_config(
3426     "AVX512VNNI_FLAGS",
3427     ["-mavx512vnni", "-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3428     try_compile(
3429         check_msg="for -mavx512vnni support",
3430         flags=["-mavx512vnni", "-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3431         when=is_intel_target,
3432     ),
3436 set_config(
3437     "NEON_I8MM_FLAGS",
3438     ["-march=armv8.2-a+i8mm"],
3439     try_compile(
3440         check_msg="for i8mm target feature",
3441         flags=["-march=armv8.2-a+i8mm"],
3442         when=is_aarch64_target,
3443     ),
3446 # dtrace support
3448 option("--enable-dtrace", help="Build with dtrace support")
3450 dtrace = check_header(
3451     "sys/sdt.h",
3452     when="--enable-dtrace",
3453     onerror=lambda: die("dtrace enabled but sys/sdt.h not found"),
3456 set_config("HAVE_DTRACE", True, when=dtrace)
3457 set_define("INCLUDE_MOZILLA_DTRACE", True, when=dtrace)