Bug 1837620 - Part 1.5: Rename IC flag to 'mayHaveFoldedStub' to make it clear that...
[gecko.git] / build / moz.configure / toolchain.configure
blob335bfc888125b9f380818fdd50e446234dc6469e
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         val = "2"
19         flags = option[0]
20     elif option:
21         val = "1"
22     else:
23         val = None
25     return namespace(
26         optimize=val,
27         flags=flags,
28     )
31 set_config("MOZ_OPTIMIZE", moz_optimize.optimize)
32 add_old_configure_assignment("MOZ_OPTIMIZE", moz_optimize.optimize)
33 add_old_configure_assignment("MOZ_CONFIGURE_OPTIMIZE_FLAGS", moz_optimize.flags)
35 # Android NDK
36 # ==============================================================
39 @depends("--disable-compile-environment", target)
40 def compiling_android(compile_env, target):
41     return compile_env and target.os == "Android"
44 include("android-ndk.configure", when=compiling_android)
46 with only_when(target_is_osx):
47     # MacOS deployment target version
48     # ==============================================================
49     # This needs to happen before any compilation test is done.
51     option(
52         "--enable-macos-target",
53         env="MACOSX_DEPLOYMENT_TARGET",
54         nargs=1,
55         default=depends(target, developer_options)
56         # We continue to target 10.12 on Intel, but can target 11.0 for
57         # aarch64 since the earliest hardware was released alongside 11.0.
58         # For local builds, we want to target 10.12 regardless of the
59         # underlying platform to catch any errors or warnings that wouldn't
60         # show up when targeting 11.0, since these would later show up on
61         # CI for Intel builds.
62         (lambda t, d: "11.0" if (t.cpu == "aarch64" and not d) else "10.12"),
63         help="Set the minimum MacOS version needed at runtime{|}",
64     )
66     @depends_if("--enable-macos-target", developer_options)
67     def macos_target(value, _):
68         return value[0]
71 with only_when(host_is_osx | target_is_osx):
72     # MacOS SDK
73     # =========
74     option(
75         "--with-macos-sdk",
76         env="MACOS_SDK_DIR",
77         nargs=1,
78         help="Location of platform SDK to use",
79     )
81     @imports("plistlib")
82     @imports(_from="__builtin__", _import="open")
83     @imports(_from="__builtin__", _import="Exception")
84     def get_sdk_version(sdk):
85         with open(os.path.join(sdk, "SDKSettings.plist"), "rb") as plist:
86             obj = plistlib.load(plist)
87         if not obj:
88             raise Exception(
89                 "Error parsing SDKSettings.plist in the SDK directory: %s" % sdk
90             )
91         if "Version" not in obj:
92             raise Exception(
93                 "Error finding Version information in SDKSettings.plist from the SDK: %s"
94                 % sdk
95             )
96         return Version(obj["Version"])
98     def sdk_min_version():
99         return "13.3"
101     @depends(
102         "--with-macos-sdk",
103         host,
104         bootstrap_path(
105             "MacOSX{}.sdk".format(sdk_min_version()),
106             when=depends("--with-macos-sdk")(lambda x: not x),
107             allow_failure=True,
108         ),
109     )
110     @imports(_from="__builtin__", _import="Exception")
111     @imports(_from="os.path", _import="isdir")
112     @imports(_from="os", _import="listdir")
113     def macos_sdk(sdk, host, bootstrapped):
114         if bootstrapped:
115             sdk = [bootstrapped]
116         if sdk:
117             sdk = sdk[0]
118             try:
119                 version = get_sdk_version(sdk)
120             except Exception as e:
121                 die(e)
122         elif host.os == "OSX":
123             sdk = check_cmd_output(
124                 "xcrun", "--show-sdk-path", onerror=lambda: ""
125             ).rstrip()
126             if not sdk:
127                 die(
128                     "Could not find the macOS SDK. Please use --with-macos-sdk to give "
129                     "the path to a macOS SDK."
130                 )
131             # Scan the parent directory xcrun returns for the most recent SDK.
132             sdk_dir = os.path.dirname(sdk)
133             versions = []
134             for d in listdir(sdk_dir):
135                 if d.lower().startswith("macos"):
136                     try:
137                         sdk = os.path.join(sdk_dir, d)
138                         versions.append((get_sdk_version(sdk), sdk))
139                     except Exception:
140                         pass
141             version, sdk = max(versions)
142         else:
143             die(
144                 "Need a macOS SDK when targeting macOS. Please use --with-macos-sdk "
145                 "to give the path to a macOS SDK."
146             )
148         if not isdir(sdk):
149             die(
150                 "SDK not found in %s. When using --with-macos-sdk, you must specify a "
151                 "valid SDK. SDKs are installed when the optional cross-development "
152                 "tools are selected during the Xcode/Developer Tools installation."
153                 % sdk
154             )
155         if version < Version(sdk_min_version()):
156             die(
157                 'SDK version "%s" is too old. Please upgrade to at least %s. Try '
158                 "updating your system Xcode." % (version, sdk_min_version())
159             )
160         return sdk
162     set_config("MACOS_SDK_DIR", macos_sdk)
165 with only_when(target_is_osx):
166     with only_when(cross_compiling):
167         option(
168             "--with-macos-private-frameworks",
169             env="MACOS_PRIVATE_FRAMEWORKS_DIR",
170             nargs=1,
171             help="Location of private frameworks to use",
172         )
174         @depends_if("--with-macos-private-frameworks")
175         @imports(_from="os.path", _import="isdir")
176         def macos_private_frameworks(value):
177             if value and not isdir(value[0]):
178                 die(
179                     "PrivateFrameworks not found not found in %s. When using "
180                     "--with-macos-private-frameworks, you must specify a valid "
181                     "directory",
182                     value[0],
183                 )
184             return value[0]
186     @depends(macos_private_frameworks, macos_sdk)
187     def macos_private_frameworks(value, sdk):
188         if value:
189             return value
190         return os.path.join(sdk or "/", "System/Library/PrivateFrameworks")
192     set_config("MACOS_PRIVATE_FRAMEWORKS_DIR", macos_private_frameworks)
195 # GC rooting and hazard analysis.
196 # ==============================================================
197 option(env="MOZ_HAZARD", help="Build for the GC rooting hazard analysis")
200 @depends("MOZ_HAZARD")
201 def hazard_analysis(value):
202     if value:
203         return True
206 set_config("MOZ_HAZARD", hazard_analysis)
209 # Cross-compilation related things.
210 # ==============================================================
211 option(
212     "--with-toolchain-prefix",
213     env="TOOLCHAIN_PREFIX",
214     nargs=1,
215     help="Prefix for the target toolchain",
219 @depends("--with-toolchain-prefix", host, target, cross_compiling)
220 def toolchain_prefix(value, host, target, cross_compiling):
221     if value:
222         return tuple(value)
223     # We don't want a toolchain prefix by default when building on mac for mac.
224     if cross_compiling and not (target.os == "OSX" and host.os == "OSX"):
225         return ("%s-" % target.toolchain, "%s-" % target.alias)
228 @depends(toolchain_prefix, target)
229 def first_toolchain_prefix(toolchain_prefix, target):
230     # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
231     # command line/environment (in which case there's only one value in the tuple),
232     # or when cross-compiling for Android or OSX.
233     if toolchain_prefix and (
234         target.os in ("Android", "OSX") or len(toolchain_prefix) == 1
235     ):
236         return toolchain_prefix[0]
239 set_config("TOOLCHAIN_PREFIX", first_toolchain_prefix)
240 add_old_configure_assignment("TOOLCHAIN_PREFIX", first_toolchain_prefix)
243 # Compilers
244 # ==============================================================
245 include("compilers-util.configure")
248 def try_preprocess(
249     configure_cache, compiler, language, source, onerror=None, wrapper=[]
251     return try_invoke_compiler(
252         configure_cache, compiler, language, source, ["-E"], onerror, wrapper
253     )
256 @imports(_from="mozbuild.configure.constants", _import="CompilerType")
257 @imports(_from="mozbuild.configure.constants", _import="CPU_preprocessor_checks")
258 @imports(_from="mozbuild.configure.constants", _import="kernel_preprocessor_checks")
259 @imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks")
260 @imports(_from="textwrap", _import="dedent")
261 @imports(_from="__builtin__", _import="Exception")
262 def get_compiler_info(configure_cache, compiler, language):
263     """Returns information about the given `compiler` (command line in the
264     form of a list or tuple), in the given `language`.
266     The returned information includes:
267     - the compiler type (clang-cl, clang or gcc)
268     - the compiler version
269     - the compiler supported language
270     - the compiler supported language version
271     """
272     # Xcode clang versions are different from the underlying llvm version (they
273     # instead are aligned with the Xcode version). Fortunately, we can tell
274     # apart plain clang from Xcode clang, and convert the Xcode clang version
275     # into the more or less corresponding plain clang version.
276     check = dedent(
277         """\
278         #if defined(_MSC_VER) && defined(__clang__) && defined(_MT)
279         %COMPILER "clang-cl"
280         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
281         #elif defined(__clang__)
282         %COMPILER "clang"
283         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
284         #  ifdef __apple_build_version__
285         %XCODE 1
286         #  endif
287         #elif defined(__GNUC__) && !defined(__MINGW32__)
288         %COMPILER "gcc"
289         %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
290         #endif
292         #if __cplusplus
293         %cplusplus __cplusplus
294         #elif __STDC_VERSION__
295         %STDC_VERSION __STDC_VERSION__
296         #endif
297     """
298     )
300     # While we're doing some preprocessing, we might as well do some more
301     # preprocessor-based tests at the same time, to check the toolchain
302     # matches what we want.
303     for name, preprocessor_checks in (
304         ("CPU", CPU_preprocessor_checks),
305         ("KERNEL", kernel_preprocessor_checks),
306         ("OS", OS_preprocessor_checks),
307     ):
308         for n, (value, condition) in enumerate(preprocessor_checks.items()):
309             check += dedent(
310                 """\
311                 #%(if)s %(condition)s
312                 %%%(name)s "%(value)s"
313             """
314                 % {
315                     "if": "elif" if n else "if",
316                     "condition": condition,
317                     "name": name,
318                     "value": value,
319                 }
320             )
321         check += "#endif\n"
323     # Also check for endianness. The advantage of living in modern times is
324     # that all the modern compilers we support now have __BYTE_ORDER__ defined
325     # by the preprocessor.
326     check += dedent(
327         """\
328         #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
329         %ENDIANNESS "little"
330         #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
331         %ENDIANNESS "big"
332         #endif
333     """
334     )
336     result = try_preprocess(configure_cache, compiler, language, check)
338     if not result:
339         raise FatalCheckError("Unknown compiler or compiler not supported.")
341     # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
342     # have non-ASCII characters. Treat the output as bytearray.
343     data = {}
344     for line in result.splitlines():
345         if line.startswith("%"):
346             k, _, v = line.partition(" ")
347             k = k.lstrip("%")
348             data[k] = v.replace(" ", "").lstrip('"').rstrip('"')
349             log.debug("%s = %s", k, data[k])
351     try:
352         type = CompilerType(data["COMPILER"])
353     except Exception:
354         raise FatalCheckError("Unknown compiler or compiler not supported.")
356     cplusplus = int(data.get("cplusplus", "0L").rstrip("L"))
357     stdc_version = int(data.get("STDC_VERSION", "0L").rstrip("L"))
359     version = data.get("VERSION")
360     if version:
361         version = Version(version)
362         if data.get("XCODE"):
363             # Derived from https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
364             # with enough granularity for major.minor version checks further
365             # down the line
366             if version < "9.1":
367                 version = Version("4.0.0.or.less")
368             elif version < "10.0":
369                 version = Version("5.0.2")
370             elif version < "10.0.1":
371                 version = Version("6.0.1")
372             elif version < "11.0":
373                 version = Version("7.0.0")
374             elif version < "11.0.3":
375                 version = Version("8.0.0")
376             elif version < "12.0":
377                 version = Version("9.0.0")
378             elif version < "12.0.5":
379                 version = Version("10.0.0")
380             elif version < "13.0":
381                 version = Version("11.1.0")
382             elif version < "13.0.1":
383                 version = Version("12.0.0")
384             elif version < "14.0":
385                 version = Version("13.0.0")
386             elif version < "15.0":
387                 version = Version("14.0.0")
388             else:
389                 version = Version("14.0.0.or.more")
391     return namespace(
392         type=type,
393         version=version,
394         cpu=data.get("CPU"),
395         kernel=data.get("KERNEL"),
396         endianness=data.get("ENDIANNESS"),
397         os=data.get("OS"),
398         language="C++" if cplusplus else "C",
399         language_version=cplusplus if cplusplus else stdc_version,
400         xcode=bool(data.get("XCODE")),
401     )
404 def same_arch_different_bits():
405     return (
406         ("x86", "x86_64"),
407         ("ppc", "ppc64"),
408         ("sparc", "sparc64"),
409     )
412 @imports(_from="mozbuild.shellutil", _import="quote")
413 @imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks")
414 def check_compiler(configure_cache, compiler, language, target, android_version):
415     info = get_compiler_info(configure_cache, compiler, language)
417     flags = []
419     # Check language standards
420     # --------------------------------------------------------------------
421     if language != info.language:
422         raise FatalCheckError(
423             "`%s` is not a %s compiler." % (quote(*compiler), language)
424         )
426     # Note: We do a strict version check because there sometimes are backwards
427     # incompatible changes in the standard, and not all code that compiles as
428     # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
429     # example)
430     if info.language == "C" and info.language_version != 199901:
431         if info.type == "clang-cl":
432             flags.append("-Xclang")
433         flags.append("-std=gnu99")
435     cxx17_version = 201703
436     if info.language == "C++":
437         if info.language_version != cxx17_version:
438             # MSVC headers include C++17 features, but don't guard them
439             # with appropriate checks.
440             if info.type == "clang-cl":
441                 flags.append("-Xclang")
442                 flags.append("-std=c++17")
443             else:
444                 flags.append("-std=gnu++17")
446     # Check compiler target
447     # --------------------------------------------------------------------
448     has_target = False
449     if target.os == "Android" and android_version:
450         # This makes clang define __ANDROID_API__ and use versioned library
451         # directories from the NDK.
452         toolchain = "%s%d" % (target.toolchain, android_version)
453     else:
454         toolchain = target.toolchain
456     if info.type == "clang":
457         # Add the target explicitly when the target is aarch64 macosx, because
458         # the Xcode clang target is named differently, and we need to work around
459         # https://github.com/rust-lang/rust-bindgen/issues/1871 and
460         # https://github.com/alexcrichton/cc-rs/issues/542 so we always want
461         # the target on the command line, even if the compiler would default to
462         # that.
463         if info.xcode and target.os == "OSX" and target.cpu == "aarch64":
464             if "--target=arm64-apple-darwin" not in compiler:
465                 flags.append("--target=arm64-apple-darwin")
466             has_target = True
468         elif (
469             not info.kernel
470             or info.kernel != target.kernel
471             or not info.endianness
472             or info.endianness != target.endianness
473         ):
474             flags.append("--target=%s" % toolchain)
475             has_target = True
477         # Add target flag when there is an OS mismatch (e.g. building for Android on
478         # Linux). However, only do this if the target OS is in our whitelist, to
479         # keep things the same on other platforms.
480         elif target.os in OS_preprocessor_checks and (
481             not info.os or info.os != target.os
482         ):
483             flags.append("--target=%s" % toolchain)
484             has_target = True
486     if not has_target and (not info.cpu or info.cpu != target.cpu):
487         same_arch = same_arch_different_bits()
488         if (target.cpu, info.cpu) in same_arch:
489             flags.append("-m32")
490         elif (info.cpu, target.cpu) in same_arch:
491             flags.append("-m64")
492         elif info.type == "clang-cl" and target.cpu == "aarch64":
493             flags.append("--target=%s" % toolchain)
494         elif info.type == "clang":
495             flags.append("--target=%s" % toolchain)
497     return namespace(
498         type=info.type,
499         version=info.version,
500         target_cpu=info.cpu,
501         target_kernel=info.kernel,
502         target_endianness=info.endianness,
503         target_os=info.os,
504         flags=flags,
505     )
508 @imports(_from="__builtin__", _import="open")
509 @imports("json")
510 @imports("os")
511 def get_vc_paths(topsrcdir):
512     def vswhere(args):
513         program_files = os.environ.get("PROGRAMFILES(X86)") or os.environ.get(
514             "PROGRAMFILES"
515         )
516         if not program_files:
517             return []
518         vswhere = os.path.join(
519             program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe"
520         )
521         if not os.path.exists(vswhere):
522             return []
523         return json.loads(check_cmd_output(vswhere, "-format", "json", *args))
525     for install in vswhere(
526         [
527             "-products",
528             "*",
529             "-requires",
530             "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
531         ]
532     ):
533         path = install["installationPath"]
534         tools_version = (
535             open(
536                 os.path.join(
537                     path, r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"
538                 ),
539                 "r",
540             )
541             .read()
542             .strip()
543         )
544         tools_path = os.path.join(path, r"VC\Tools\MSVC", tools_version)
545         yield (Version(install["installationVersion"]), tools_path)
548 @depends(target, host)
549 def is_windows(target, host):
550     return host.kernel == "WINNT" or target.kernel == "WINNT"
553 # Calling this a sysroot is a little weird, but it's the terminology clang went
554 # with with its -winsysroot flag.
555 option(
556     env="WINSYSROOT",
557     nargs=1,
558     when=is_windows,
559     help='Path to a Windows "sysroot" (directory containing MSVC, SDKs)',
563 @depends(
564     "WINSYSROOT",
565     bootstrap_path(
566         "vs",
567         when=depends("WINSYSROOT", when=is_windows)(lambda x: not x),
568     ),
569     when=is_windows,
571 def winsysroot(winsysroot, bootstrapped):
572     if bootstrapped:
573         return bootstrapped
574     if winsysroot:
575         return winsysroot[0]
578 option(
579     env="VC_PATH",
580     nargs=1,
581     when=is_windows,
582     help="Path to the Microsoft Visual C/C++ compiler",
586 @depends(
587     host,
588     build_environment,
589     "VC_PATH",
590     winsysroot,
591     when=is_windows,
593 @imports("os")
594 @imports(_from="operator", _import="itemgetter")
595 def vc_compiler_paths_for_version(host, env, vc_path, winsysroot):
596     if winsysroot:
597         if vc_path:
598             die("WINSYSROOT and VC_PATH cannot be set together.")
599         base_vc_path = os.path.join(winsysroot, "VC", "Tools", "MSVC")
600         versions = os.listdir(base_vc_path)
601         vc_path = [os.path.join(base_vc_path, str(max(Version(v) for v in versions)))]
602     if vc_path:
603         # Use an arbitrary version, it doesn't matter.
604         all_versions = [(Version("15"), vc_path[0])]
605     elif host.kernel != "WINNT":
606         # Don't try to do anything when VC_PATH is not set on cross-compiles.
607         return
608     else:
609         all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
610     if not all_versions:
611         return
612     # Choose the newest version.
613     path = all_versions[-1][1]
614     host_dir = {
615         "x86_64": "Hostx64",
616         "x86": "Hostx86",
617     }.get(host.cpu)
618     if host_dir:
619         path = os.path.join(path, "bin", host_dir)
620         return {
621             "x64": [os.path.join(path, "x64")],
622             # The cross toolchains require DLLs from the native x64 toolchain.
623             "x86": [os.path.join(path, "x86"), os.path.join(path, "x64")],
624             "arm64": [os.path.join(path, "arm64"), os.path.join(path, "x64")],
625         }
628 @depends(target, vc_compiler_paths_for_version, when=is_windows)
629 def vc_compiler_path(target, paths):
630     vc_target = {
631         "x86": "x86",
632         "x86_64": "x64",
633         "arm": "arm",
634         "aarch64": "arm64",
635     }.get(target.cpu)
636     if not paths:
637         return
638     return paths.get(vc_target)
641 @depends(vc_compiler_path, original_path)
642 @imports("os")
643 @imports(_from="os", _import="environ")
644 def vc_toolchain_search_path(vc_compiler_path, original_path):
645     result = list(original_path)
647     if vc_compiler_path:
648         # The second item, if there is one, is necessary to have in $PATH for
649         # Windows to load the required DLLs from there.
650         if len(vc_compiler_path) > 1:
651             environ["PATH"] = os.pathsep.join(result + vc_compiler_path[1:])
653         # The first item is where the programs are going to be
654         result.append(vc_compiler_path[0])
656     return result
659 @depends_if(vc_compiler_path, when=is_windows)
660 def vc_compiler_version(vc_compiler_path):
661     version = Version(
662         os.path.basename(
663             os.path.dirname(os.path.dirname(os.path.dirname(vc_compiler_path[0])))
664         )
665     )
666     # MSVC path with version 14.x is actually version 19.x
667     if version.major == 14:
668         return Version(f"19.{version.minor}")
671 @depends_if(vc_compiler_version)
672 def is_vs2019_or_more(vc_compiler_version):
673     return vc_compiler_version >= Version("19.20")
676 add_old_configure_assignment("IS_VS2019_OR_MORE", is_vs2019_or_more)
679 @depends_if(vc_compiler_version)
680 def msvs_version(vc_compiler_version):
681     # clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to
682     # be set for GYP on Windows.
683     if vc_compiler_version >= Version("19.30"):
684         return "2022"
685     if vc_compiler_version >= Version("19.20"):
686         return "2019"
687     if vc_compiler_version >= Version("19.10"):
688         return "2017"
690     return ""
693 set_config("MSVS_VERSION", msvs_version)
696 clang_search_path = bootstrap_search_path("clang/bin")
699 @depends(
700     bootstrap_search_path("rustc/bin", when="MOZ_AUTOMATION"),
701     bootstrap_search_path_order,
702     original_path,
704 @imports("os")
705 @imports(_from="os", _import="environ")
706 def rust_search_path(rust_path, search_order, original_path):
707     result = list(rust_path or original_path)
708     # Also add the rustup install directory for cargo/rustc.
709     cargo_home = environ.get("CARGO_HOME", "")
710     if cargo_home:
711         cargo_home = os.path.abspath(cargo_home)
712     else:
713         cargo_home = os.path.expanduser(os.path.join("~", ".cargo"))
714     rustup_path = os.path.join(cargo_home, "bin")
715     if search_order == "prepend":
716         result.insert(0, rustup_path)
717     else:
718         result.append(rustup_path)
719     return result
722 # Prepend the mozilla-build msys2 path, since otherwise we can get mismatched
723 # cygwin dll errors during configure if we get called from another msys2
724 # environment, see bug 1801826.
725 @depends(mozillabuild_bin_paths, clang_search_path, target, original_path)
726 @imports("os")
727 def altered_path(mozillabuild_bin_paths, clang_search_path, target, original_path):
728     altered_path = mozillabuild_bin_paths
729     if target.kernel == "Darwin":
730         # The rust compiler wants to execute dsymutil, but it does so in a
731         # non-configurable way (https://github.com/rust-lang/rust/issues/52728)
732         # so we add the clang path.
733         path = clang_search_path
734     else:
735         path = original_path
736     for p in path:
737         if p not in altered_path:
738             altered_path.append(p)
739     return os.pathsep.join(altered_path)
742 set_config("PATH", altered_path)
745 # Compiler wrappers
746 # ==============================================================
747 option(
748     "--with-compiler-wrapper",
749     env="COMPILER_WRAPPER",
750     nargs=1,
751     help="Enable compiling with wrappers such as distcc and ccache",
754 option("--with-ccache", env="CCACHE", nargs="?", help="Enable compiling with ccache")
757 @depends_if("--with-ccache")
758 def ccache(value):
759     if len(value):
760         return value
761     # If --with-ccache was given without an explicit value, we default to
762     # 'ccache'.
763     return "ccache"
766 ccache = check_prog(
767     "CCACHE",
768     progs=(),
769     input=ccache,
770     paths=bootstrap_search_path(
771         "sccache", when=depends("CCACHE")(lambda c: len(c) and c[0] == "sccache")
772     ),
773     allow_missing=True,
776 option(env="CCACHE_PREFIX", nargs=1, help="Compiler prefix to use when using ccache")
778 ccache_prefix = depends_if("CCACHE_PREFIX")(lambda prefix: prefix[0])
779 set_config("CCACHE_PREFIX", ccache_prefix)
781 # Distinguish ccache from sccache.
784 @depends_if(ccache)
785 def ccache_is_sccache(ccache):
786     return check_cmd_output(ccache, "--version").startswith("sccache")
789 @depends(ccache, ccache_is_sccache)
790 def using_ccache(ccache, ccache_is_sccache):
791     return ccache and not ccache_is_sccache
794 @depends_if(ccache, ccache_is_sccache)
795 def using_sccache(ccache, ccache_is_sccache):
796     return ccache and ccache_is_sccache
799 option(env="RUSTC_WRAPPER", nargs=1, help="Wrap rust compilation with given tool")
802 @depends(ccache, ccache_is_sccache, "RUSTC_WRAPPER")
803 @imports(_from="textwrap", _import="dedent")
804 @imports("os")
805 def check_sccache_version(ccache, ccache_is_sccache, rustc_wrapper):
806     sccache_min_version = Version("0.2.13")
808     def check_version(path):
809         out = check_cmd_output(path, "--version")
810         version = Version(out.rstrip().split()[-1])
811         if version < sccache_min_version:
812             die(
813                 dedent(
814                     """\
815             sccache %s or later is required. sccache in use at %s has
816             version %s.
818             Please upgrade or acquire a new version with |./mach bootstrap|.
819             """
820                 ),
821                 sccache_min_version,
822                 path,
823                 version,
824             )
826     if ccache and ccache_is_sccache:
827         check_version(ccache)
829     if rustc_wrapper and (
830         os.path.splitext(os.path.basename(rustc_wrapper[0]))[0].lower() == "sccache"
831     ):
832         check_version(rustc_wrapper[0])
835 set_config("MOZ_USING_CCACHE", using_ccache)
836 set_config("MOZ_USING_SCCACHE", using_sccache)
838 option(env="SCCACHE_VERBOSE_STATS", help="Print verbose sccache stats after build")
841 @depends(using_sccache, "SCCACHE_VERBOSE_STATS")
842 def sccache_verbose_stats(using_sccache, verbose_stats):
843     return using_sccache and bool(verbose_stats)
846 set_config("SCCACHE_VERBOSE_STATS", sccache_verbose_stats)
849 @depends("--with-compiler-wrapper", ccache)
850 @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
851 def compiler_wrapper(wrapper, ccache):
852     if wrapper:
853         raw_wrapper = wrapper[0]
854         wrapper = shell_split(raw_wrapper)
855         wrapper_program = find_program(wrapper[0])
856         if not wrapper_program:
857             die(
858                 "Cannot find `%s` from the given compiler wrapper `%s`",
859                 wrapper[0],
860                 raw_wrapper,
861             )
862         wrapper[0] = wrapper_program
864     if ccache:
865         if wrapper:
866             return tuple([ccache] + wrapper)
867         else:
868             return (ccache,)
869     elif wrapper:
870         return tuple(wrapper)
873 @depends_if(compiler_wrapper)
874 def using_compiler_wrapper(compiler_wrapper):
875     return True
878 set_config("MOZ_USING_COMPILER_WRAPPER", using_compiler_wrapper)
881 @dependable
882 def wasm():
883     return split_triplet("wasm32-wasi", allow_wasi=True)
886 @template
887 def default_c_compilers(host_or_target, other_c_compiler=None):
888     """Template defining the set of default C compilers for the host and
889     target platforms.
890     `host_or_target` is either `host` or `target` (the @depends functions
891     from init.configure.
892     `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
893     """
894     assert host_or_target in {host, target, wasm}
896     other_c_compiler = () if other_c_compiler is None else (other_c_compiler,)
898     @depends(host_or_target, target, toolchain_prefix, *other_c_compiler)
899     def default_c_compilers(
900         host_or_target, target, toolchain_prefix, *other_c_compiler
901     ):
902         if host_or_target.kernel == "WINNT":
903             if host_or_target.abi:
904                 if host_or_target.abi == "msvc":
905                     supported = types = ("clang-cl",)
906                 elif host_or_target.abi == "mingw":
907                     supported = types = ("clang",)
908             else:
909                 supported = types = ("clang-cl", "clang")
910         elif host_or_target.kernel == "Darwin":
911             types = ("clang",)
912             supported = ("clang", "gcc")
913         elif host_or_target.kernel == "WASI":
914             supported = types = ("clang",)
915         else:
916             supported = types = ("clang", "gcc")
918         info = other_c_compiler[0] if other_c_compiler else None
919         if info and info.type in supported:
920             # When getting default C compilers for the host, we prioritize the
921             # same compiler as the target C compiler.
922             prioritized = info.compiler
923             if info.type == "gcc":
924                 same_arch = same_arch_different_bits()
925                 if (
926                     target.cpu != host_or_target.cpu
927                     and (target.cpu, host_or_target.cpu) not in same_arch
928                     and (host_or_target.cpu, target.cpu) not in same_arch
929                 ):
930                     # If the target C compiler is GCC, and it can't be used with
931                     # -m32/-m64 for the host, it's probably toolchain-prefixed,
932                     # so we prioritize a raw 'gcc' instead.
933                     prioritized = info.type
934             if target.os != "WINNT" and host_or_target.os == "WINNT":
935                 # When cross-compiling on Windows, don't prioritize. We'll fallback
936                 # to checking for clang-cl first.
937                 pass
938             else:
939                 types = [prioritized] + [t for t in types if t != info.type]
941         gcc = ("gcc",)
942         if toolchain_prefix and host_or_target is target:
943             gcc = tuple("%sgcc" % p for p in toolchain_prefix) + gcc
945         result = []
946         for type in types:
947             if type == "gcc":
948                 result.extend(gcc)
949             else:
950                 result.append(type)
952         return tuple(result)
954     return default_c_compilers
957 @template
958 def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None):
959     """Template defining the set of default C++ compilers for the host and
960     target platforms.
961     `c_compiler` is the @depends function returning a Compiler instance for
962     the desired platform.
964     Because the build system expects the C and C++ compilers to be from the
965     same compiler suite, we derive the default C++ compilers from the C
966     compiler that was found if none was provided.
968     We also factor in the target C++ compiler when getting the default host
969     C++ compiler, using the target C++ compiler if the host and target C
970     compilers are the same.
971     """
973     assert (other_c_compiler is None) == (other_cxx_compiler is None)
974     if other_c_compiler is not None:
975         other_compilers = (other_c_compiler, other_cxx_compiler)
976     else:
977         other_compilers = ()
979     @depends(c_compiler, *other_compilers)
980     def default_cxx_compilers(c_compiler, *other_compilers):
981         if other_compilers:
982             other_c_compiler, other_cxx_compiler = other_compilers
983             if other_c_compiler.compiler == c_compiler.compiler:
984                 return (other_cxx_compiler.compiler,)
986         dir = os.path.dirname(c_compiler.compiler)
987         file = os.path.basename(c_compiler.compiler)
989         if c_compiler.type == "gcc":
990             return (os.path.join(dir, file.replace("gcc", "g++")),)
992         if c_compiler.type == "clang":
993             return (os.path.join(dir, file.replace("clang", "clang++")),)
995         return (c_compiler.compiler,)
997     return default_cxx_compilers
1000 @template
1001 def provided_program(env_var, when=None):
1002     """Template handling cases where a program can be specified either as a
1003     path or as a path with applicable arguments.
1004     """
1006     @depends_if(env_var, when=when)
1007     @imports(_from="itertools", _import="takewhile")
1008     @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
1009     def provided(cmd):
1010         # Assume the first dash-prefixed item (and any subsequent items) are
1011         # command-line options, the item before the dash-prefixed item is
1012         # the program we're looking for, and anything before that is a wrapper
1013         # of some kind (e.g. sccache).
1014         cmd = shell_split(cmd[0])
1016         without_flags = list(takewhile(lambda x: not x.startswith("-"), cmd))
1018         return namespace(
1019             wrapper=without_flags[:-1],
1020             program=without_flags[-1],
1021             flags=cmd[len(without_flags) :],
1022         )
1024     return provided
1027 @template
1028 def sysroot(host_or_target, target_sysroot=None):
1029     assert target_sysroot or host_or_target is target
1030     bootstrap_target_when = target_is_linux_or_wasi
1031     if host_or_target is host:
1032         host_or_target_str = "host"
1033         opt = "--with-host-sysroot"
1034         env = "HOST_SYSROOT"
1035         when = depends(host)(lambda h: h.kernel == "Linux")
1036         # Only bootstrap a host sysroot when using a bootstrapped target sysroot
1037         # or when the target doesn't use a bootstrapped sysroot in the first place.
1038         @depends(when, bootstrap_target_when, target_sysroot.bootstrapped)
1039         def bootstrap_when(when, bootstrap_target_when, bootstrapped):
1040             return when and (bootstrapped or not bootstrap_target_when)
1042     else:
1043         assert host_or_target is target
1044         host_or_target_str = "target"
1045         opt = "--with-sysroot"
1046         env = "SYSROOT"
1047         when = target_is_linux_or_wasi
1048         bootstrap_when = bootstrap_target_when
1050     option(
1051         opt,
1052         env=env,
1053         nargs=1,
1054         when=when,
1055         help="Use the given sysroot directory for %s build" % host_or_target_str,
1056     )
1058     sysroot_input = depends(opt, when=when)(lambda x: x)
1059     bootstrap_sysroot = depends(bootstrap_when, sysroot_input)(
1060         # Only bootstrap when no flag was explicitly given (either --with or --without)
1061         lambda bootstrap, input: bootstrap
1062         and not input
1063         and input.origin == "default"
1064     )
1066     @depends(
1067         sysroot_input,
1068         host_or_target,
1069         macos_sdk,
1070         bootstrap_path(
1071             depends(host_or_target)(lambda t: "sysroot-{}".format(t.toolchain)),
1072             when=bootstrap_sysroot,
1073         ),
1074     )
1075     @imports("os")
1076     def sysroot(sysroot_input, host_or_target, macos_sdk, path):
1077         version = None
1078         if sysroot_input:
1079             path = sysroot_input[0]
1080         elif host_or_target.kernel == "Darwin" and macos_sdk:
1081             path = macos_sdk
1082         if path:
1083             # Find the version of libstdc++ headears in the sysroot
1084             include = os.path.join(path, "usr/include/c++")
1085             if os.path.isdir(include):
1086                 with os.scandir(include) as d:
1087                     version = max(Version(e.name) for e in d if e.is_dir())
1088             log.info("Using %s sysroot in %s", host_or_target_str, path)
1089         return namespace(
1090             path=path,
1091             bootstrapped=bool(path and not sysroot_input),
1092             stdcxx_version=version,
1093         )
1095     return sysroot
1098 target_sysroot = sysroot(target)
1101 # Use `system_lib_option` instead of `option` for options that enable building
1102 # with a system library for which the development headers are not available in
1103 # the bootstrapped sysroots.
1104 @template
1105 def system_lib_option(name, *args, **kwargs):
1106     option(name, *args, **kwargs)
1108     @depends(name, target_sysroot.bootstrapped)
1109     def no_system_lib_in_sysroot(value, bootstrapped):
1110         if bootstrapped and value:
1111             die(
1112                 "%s is not supported with bootstrapped sysroot. "
1113                 "Drop the option, or use --without-sysroot or --disable-bootstrap",
1114                 value.format(name),
1115             )
1118 host_sysroot = sysroot(host, target_sysroot)
1121 @template
1122 def multiarch_dir(host_or_target):
1123     sysroot = {
1124         host: host_sysroot,
1125         target: target_sysroot,
1126     }[host_or_target]
1128     @depends(host_or_target, when=sysroot.path)
1129     def multiarch_dir(target):
1130         if target.cpu == "x86":
1131             # Turn e.g. i686-linux-gnu into i386-linux-gnu
1132             return target.toolchain.replace(target.raw_cpu, "i386")
1133         return target.toolchain
1135     return multiarch_dir
1138 target_multiarch_dir = multiarch_dir(target)
1139 host_multiarch_dir = multiarch_dir(host)
1142 def minimum_gcc_version():
1143     return Version("8.1.0")
1146 @template
1147 def compiler(
1148     language,
1149     host_or_target,
1150     c_compiler=None,
1151     other_compiler=None,
1152     other_c_compiler=None,
1154     """Template handling the generic base checks for the compiler for the
1155     given `language` on the given platform (`host_or_target`).
1156     `host_or_target` is either `host` or `target` (the @depends functions
1157     from init.configure.
1158     When the language is 'C++', `c_compiler` is the result of the `compiler`
1159     template for the language 'C' for the same `host_or_target`.
1160     When `host_or_target` is `host`, `other_compiler` is the result of the
1161     `compiler` template for the same `language` for `target`.
1162     When `host_or_target` is `host` and the language is 'C++',
1163     `other_c_compiler` is the result of the `compiler` template for the
1164     language 'C' for `target`.
1165     """
1166     assert host_or_target in {host, target, wasm}
1167     assert language in ("C", "C++")
1168     assert language == "C" or c_compiler is not None
1169     assert host_or_target is target or other_compiler is not None
1170     assert language == "C" or host_or_target is target or other_c_compiler is not None
1172     host_or_target_str = {
1173         host: "host",
1174         target: "target",
1175         wasm: "wasm",
1176     }[host_or_target]
1178     sysroot = {
1179         host: host_sysroot,
1180         target: target_sysroot,
1181         wasm: dependable(lambda: namespace(path=None)),
1182     }[host_or_target]
1184     multiarch_dir = {
1185         host: host_multiarch_dir,
1186         target: target_multiarch_dir,
1187         wasm: never,
1188     }[host_or_target]
1190     var = {
1191         ("C", target): "CC",
1192         ("C++", target): "CXX",
1193         ("C", host): "HOST_CC",
1194         ("C++", host): "HOST_CXX",
1195         ("C", wasm): "WASM_CC",
1196         ("C++", wasm): "WASM_CXX",
1197     }[language, host_or_target]
1199     default_compilers = {
1200         "C": lambda: default_c_compilers(host_or_target, other_compiler),
1201         "C++": lambda: default_cxx_compilers(
1202             c_compiler, other_c_compiler, other_compiler
1203         ),
1204     }[language]()
1206     what = "the %s %s compiler" % (host_or_target_str, language)
1208     option(env=var, nargs=1, help="Path to %s" % what)
1210     # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
1211     # HOST_CXX variables.
1212     provided_compiler = provided_program(var)
1214     # Normally, we'd use `var` instead of `_var`, but the interaction with
1215     # old-configure complicates things, and for now, we a) can't take the plain
1216     # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
1217     # old-configure AC_SUBST it (because it's autoconf doing it, not us)
1218     compiler = check_prog(
1219         "_%s" % var,
1220         what=what,
1221         progs=default_compilers,
1222         input=provided_compiler.program,
1223         paths=clang_search_path,
1224     )
1226     @depends(
1227         configure_cache,
1228         compiler,
1229         provided_compiler,
1230         compiler_wrapper,
1231         host_or_target,
1232         sysroot,
1233         macos_target,
1234         android_version,
1235         vc_compiler_version,
1236         multiarch_dir,
1237         winsysroot,
1238         host,
1239     )
1240     @checking("whether %s can be used" % what, lambda x: bool(x))
1241     @imports(_from="mozbuild.shellutil", _import="quote")
1242     @imports("os")
1243     def valid_compiler(
1244         configure_cache,
1245         compiler,
1246         provided_compiler,
1247         compiler_wrapper,
1248         host_or_target,
1249         sysroot,
1250         macos_target,
1251         android_version,
1252         vc_compiler_version,
1253         multiarch_dir,
1254         winsysroot,
1255         host,
1256     ):
1257         wrapper = list(compiler_wrapper or ())
1258         flags = []
1259         if sysroot.path:
1260             if host_or_target.kernel == "Darwin":
1261                 # While --sysroot and -isysroot are roughly equivalent, when not using
1262                 # -isysroot on mac, clang takes the SDKROOT environment variable into
1263                 # consideration, which may be set by python and break things.
1264                 flags.extend(("-isysroot", sysroot.path))
1265             else:
1266                 flags.extend(("--sysroot", sysroot.path))
1267         if host_or_target.os == "OSX" and macos_target:
1268             flags.append("-mmacosx-version-min=%s" % macos_target)
1269         if provided_compiler:
1270             wrapper.extend(provided_compiler.wrapper)
1271             flags.extend(provided_compiler.flags)
1273         info = check_compiler(
1274             configure_cache,
1275             wrapper + [compiler] + flags,
1276             language,
1277             host_or_target,
1278             android_version,
1279         )
1281         # When not given an explicit compatibility version, clang-cl tries
1282         # to get one from MSVC, which might not even be the one used by the
1283         # build. And when it can't find one, its default might also not match
1284         # what the build is using. So if we were able to figure out the version
1285         # we're building with, explicitly use that.
1286         # This also means that, as a side effect, clang-cl will not try to find
1287         # MSVC, which saves a little overhead.
1288         if info.type == "clang-cl" and vc_compiler_version:
1289             flags.append(f"-fms-compatibility-version={vc_compiler_version}")
1291         if info.type == "clang" and language == "C++" and host_or_target.os == "OSX":
1292             flags.append("-stdlib=libc++")
1294         # Check that the additional flags we got are enough to not require any
1295         # more flags. If we get an exception, just ignore it; it's liable to be
1296         # invalid command-line flags, which means the compiler we're checking
1297         # doesn't support those command-line flags and will fail one or more of
1298         # the checks below.
1299         try:
1300             if info.flags:
1301                 flags += info.flags
1302                 info = check_compiler(
1303                     configure_cache,
1304                     wrapper + [compiler] + flags,
1305                     language,
1306                     host_or_target,
1307                     android_version,
1308                 )
1309         except FatalCheckError:
1310             pass
1312         if not info.target_cpu or info.target_cpu != host_or_target.cpu:
1313             raise FatalCheckError(
1314                 "%s %s compiler target CPU (%s) does not match --%s CPU (%s)"
1315                 % (
1316                     host_or_target_str.capitalize(),
1317                     language,
1318                     info.target_cpu or "unknown",
1319                     host_or_target_str,
1320                     host_or_target.raw_cpu,
1321                 )
1322             )
1324         if not info.target_kernel or (info.target_kernel != host_or_target.kernel):
1325             raise FatalCheckError(
1326                 "%s %s compiler target kernel (%s) does not match --%s kernel (%s)"
1327                 % (
1328                     host_or_target_str.capitalize(),
1329                     language,
1330                     info.target_kernel or "unknown",
1331                     host_or_target_str,
1332                     host_or_target.kernel,
1333                 )
1334             )
1336         if not info.target_endianness or (
1337             info.target_endianness != host_or_target.endianness
1338         ):
1339             raise FatalCheckError(
1340                 "%s %s compiler target endianness (%s) does not match --%s "
1341                 "endianness (%s)"
1342                 % (
1343                     host_or_target_str.capitalize(),
1344                     language,
1345                     info.target_endianness or "unknown",
1346                     host_or_target_str,
1347                     host_or_target.endianness,
1348                 )
1349             )
1351         # Compiler version checks
1352         # ===================================================
1353         # Check the compiler version here instead of in `compiler_version` so
1354         # that the `checking` message doesn't pretend the compiler can be used
1355         # to then bail out one line later.
1356         if info.type == "gcc":
1357             if host_or_target.os == "Android":
1358                 raise FatalCheckError(
1359                     "GCC is not supported on Android.\n"
1360                     "Please use clang from the Android NDK instead."
1361                 )
1362             gcc_version = minimum_gcc_version()
1363             if info.version < gcc_version:
1364                 raise FatalCheckError(
1365                     "Only GCC %d.%d or newer is supported (found version %s)."
1366                     % (gcc_version.major, gcc_version.minor, info.version)
1367                 )
1369             # Force GCC to use the C++ headers from the sysroot, and to prefer the
1370             # sysroot system headers to /usr/include.
1371             # Non-Debian GCC also doesn't look at headers in multiarch directory.
1372             if sysroot.bootstrapped and sysroot.stdcxx_version:
1373                 version = sysroot.stdcxx_version
1374                 for path in (
1375                     "usr/include/c++/{}".format(version),
1376                     "usr/include/{}/c++/{}".format(multiarch_dir, version),
1377                     "usr/include/{}".format(multiarch_dir),
1378                     "usr/include",
1379                 ):
1380                     flags.extend(("-isystem", os.path.join(sysroot.path, path)))
1382         if info.type == "clang-cl":
1383             if info.version < "9.0.0":
1384                 raise FatalCheckError(
1385                     "Only clang-cl 9.0 or newer is supported (found version %s)"
1386                     % info.version
1387                 )
1388             if winsysroot and host.os != "WINNT":
1389                 overlay = os.path.join(winsysroot, "overlay.yaml")
1390                 if os.path.exists(overlay):
1391                     overlay_flags = ["-Xclang", "-ivfsoverlay", "-Xclang", overlay]
1392                     if info.version >= "16.0" or (
1393                         # clang-cl 15 normally doesn't support the root-relative
1394                         # overlay we use, but the bootstrapped clang-cl 15 is patched
1395                         # to support it, so check we're using a patched version.
1396                         info.version >= "15.0"
1397                         and try_preprocess(
1398                             configure_cache,
1399                             [compiler] + flags + overlay_flags,
1400                             language,
1401                             "",
1402                             onerror=lambda: False,
1403                             wrapper=wrapper,
1404                         )
1405                     ):
1406                         flags.extend(overlay_flags)
1408         if (info.type, host_or_target.abi) in (
1409             ("clang", "msvc"),
1410             ("clang-cl", "mingw"),
1411         ):
1412             raise FatalCheckError("Unknown compiler or compiler not supported.")
1414         # If you want to bump the version check here ensure the version
1415         # is known for Xcode in get_compiler_info.
1416         if info.type == "clang" and info.version < "7.0":
1417             raise FatalCheckError(
1418                 "Only clang/llvm 7.0 or newer is supported (found version %s)."
1419                 % info.version
1420             )
1422         if host_or_target.kernel == "WASI":
1423             if info.type != "clang":
1424                 raise FatalCheckError(
1425                     "Only clang is supported for %s" % host_or_target.alias
1426                 )
1427             if info.version < "8.0":
1428                 raise FatalCheckError(
1429                     "Only clang/llvm 8.0 or newer is supported for %s (found version %s)."
1430                     % (host_or_target.alias, info.version)
1431                 )
1433         if host_or_target.os == "Android":
1434             # Need at least clang 13 for compiler-rt/libunwind being the default.
1435             if info.type == "clang" and info.version < "13.0":
1436                 raise FatalCheckError(
1437                     "Only clang/llvm 13.0 or newer is supported for %s (found version %s)."
1438                     % (host_or_target.alias, info.version)
1439                 )
1441         if info.flags:
1442             raise FatalCheckError("Unknown compiler or compiler not supported.")
1444         return namespace(
1445             wrapper=wrapper,
1446             compiler=compiler,
1447             flags=flags,
1448             type=info.type,
1449             version=info.version,
1450             language=language,
1451         )
1453     @depends(valid_compiler)
1454     @checking("%s version" % what)
1455     def compiler_version(compiler):
1456         return compiler.version
1458     if language == "C++":
1460         @depends(valid_compiler, c_compiler)
1461         def valid_compiler(compiler, c_compiler):
1462             if compiler.type != c_compiler.type:
1463                 die(
1464                     "The %s C compiler is %s, while the %s C++ compiler is "
1465                     "%s. Need to use the same compiler suite.",
1466                     host_or_target_str,
1467                     c_compiler.type,
1468                     host_or_target_str,
1469                     compiler.type,
1470                 )
1472             if compiler.version != c_compiler.version:
1473                 die(
1474                     "The %s C compiler is version %s, while the %s C++ "
1475                     "compiler is version %s. Need to use the same compiler "
1476                     "version.",
1477                     host_or_target_str,
1478                     c_compiler.version,
1479                     host_or_target_str,
1480                     compiler.version,
1481                 )
1482             return compiler
1484     # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
1485     # and the flags that were part of the user input for those variables to
1486     # be provided.
1487     add_old_configure_assignment(
1488         var,
1489         depends_if(valid_compiler)(
1490             lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1491         ),
1492     )
1494     if host_or_target is target:
1495         add_old_configure_assignment(
1496             "ac_cv_prog_%s" % var,
1497             depends_if(valid_compiler)(
1498                 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1499             ),
1500         )
1501         # We check that it works in python configure already.
1502         add_old_configure_assignment("ac_cv_prog_%s_works" % var.lower(), "yes")
1503         add_old_configure_assignment(
1504             "ac_cv_prog_%s_cross" % var.lower(),
1505             depends(cross_compiling)(lambda x: "yes" if x else "no"),
1506         )
1507         gcc_like = depends(valid_compiler.type)(
1508             lambda x: "yes" if x in ("gcc", "clang") else "no"
1509         )
1510         add_old_configure_assignment("ac_cv_prog_%s_g" % var.lower(), gcc_like)
1511         if language == "C":
1512             add_old_configure_assignment("ac_cv_prog_gcc", gcc_like)
1513         if language == "C++":
1514             add_old_configure_assignment("ac_cv_prog_gxx", gcc_like)
1516     # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
1517     # old-configure to do some of its still existing checks.
1518     if language == "C":
1519         set_config("%s_TYPE" % var, valid_compiler.type)
1520         add_old_configure_assignment("%s_TYPE" % var, valid_compiler.type)
1521         set_config(
1522             "%s_VERSION" % var, depends(valid_compiler.version)(lambda v: str(v))
1523         )
1525     valid_compiler = compiler_class(valid_compiler, host_or_target)
1527     def compiler_error():
1528         raise FatalCheckError(
1529             "Failed compiling a simple %s source with %s" % (language, what)
1530         )
1532     valid_compiler.try_compile(check_msg="%s works" % what, onerror=compiler_error)
1534     set_config("%s_BASE_FLAGS" % var, valid_compiler.flags)
1536     # Set CPP/CXXCPP for both the build system and old-configure. We don't
1537     # need to check this works for preprocessing, because we already relied
1538     # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
1539     # in the first place.
1540     if host_or_target is target:
1541         pp_var = {
1542             "C": "CPP",
1543             "C++": "CXXCPP",
1544         }[language]
1546         preprocessor = depends_if(valid_compiler)(
1547             lambda x: list(x.wrapper) + [x.compiler, "-E"] + list(x.flags)
1548         )
1550         set_config(pp_var, preprocessor)
1551         add_old_configure_assignment(pp_var, preprocessor)
1553     if language == "C":
1554         linker_var = {
1555             target: "LD",
1556             host: "HOST_LD",
1557         }.get(host_or_target)
1559         if linker_var:
1561             @deprecated_option(env=linker_var, nargs=1)
1562             def linker(value):
1563                 if value:
1564                     return value[0]
1566             @depends(linker)
1567             def unused_linker(linker):
1568                 if linker:
1569                     log.warning(
1570                         "The value of %s is not used by this build system." % linker_var
1571                     )
1573     return valid_compiler
1576 c_compiler = compiler("C", target)
1577 cxx_compiler = compiler("C++", target, c_compiler=c_compiler)
1578 host_c_compiler = compiler("C", host, other_compiler=c_compiler)
1579 host_cxx_compiler = compiler(
1580     "C++",
1581     host,
1582     c_compiler=host_c_compiler,
1583     other_compiler=cxx_compiler,
1584     other_c_compiler=c_compiler,
1588 @template
1589 def windows_abi(host_or_target, c_compiler):
1590     @depends(host_or_target)
1591     def windows_abi(host_or_target):
1592         if host_or_target.os == "WINNT":
1593             return host_or_target.abi
1595     @depends(host_or_target, windows_abi)
1596     def need_windows_abi_from_compiler(host_or_target, windows_abi):
1597         return host_or_target.os == "WINNT" and windows_abi is None
1599     @depends(host_or_target, c_compiler, when=need_windows_abi_from_compiler)
1600     def windows_abi_from_compiler(host_or_target, c_compiler):
1601         if host_or_target.os == "WINNT":
1602             if c_compiler.type == "clang-cl":
1603                 return "msvc"
1604             return "mingw"
1606     return windows_abi | windows_abi_from_compiler
1609 target_windows_abi = windows_abi(target, c_compiler)
1610 host_windows_abi = windows_abi(host, host_c_compiler)
1613 # Generic compiler-based conditions.
1614 building_with_gcc = depends(c_compiler)(lambda info: info.type == "gcc")
1617 @depends(cxx_compiler, ccache_prefix)
1618 @imports("os")
1619 def cxx_is_icecream(info, ccache_prefix):
1620     if (
1621         os.path.islink(info.compiler)
1622         and os.path.basename(os.readlink(info.compiler)) == "icecc"
1623     ):
1624         return True
1625     if ccache_prefix and os.path.basename(ccache_prefix) == "icecc":
1626         return True
1629 set_config("CXX_IS_ICECREAM", cxx_is_icecream)
1632 # Linker detection
1633 # ==============================================================
1634 # The policy is as follows:
1635 # For Windows:
1636 # - the linker is picked via the LINKER environment variable per windows.configure,
1637 #   but ought to be llvm-lld in any case.
1638 # For macOS:
1639 # - the linker is lld if the clang used is >= 15 (per LLVM version, not Xcode version).
1640 # - the linker is also lld on local developer builds if the clang used is >= 13 (per LLVM
1641 #   version, not Xcode version)
1642 # - otherwise the linker is ld64, either from XCode on macOS, or from cctools-ports when
1643 #   cross-compiling.
1644 # For other OSes:
1645 # - on local developer builds: lld is used if present. Otherwise gold is used if present
1646 #   otherwise, BFD ld is used.
1647 # - on release/official builds: whatever the compiler uses by default, except on Android
1648 #   (see enable_linker_default below). Usually what the compiler uses by default is BFD
1649 #   ld, except with the Android NDK compiler, where the default varies depending on the
1650 #   NDK version. The default also varies by platform and clang version.
1651 #   lld is not used by default on Linux and Android because it introduces layout changes
1652 #   that prevent elfhack from working. See e.g.
1653 #   https://bugzilla.mozilla.org/show_bug.cgi?id=1563654#c2.
1654 @template
1655 def is_not_winnt_or_sunos(host_or_target):
1656     @depends(host_or_target)
1657     def is_not_winnt_or_sunos(host_or_target):
1658         if host_or_target.kernel not in ("WINNT", "SunOS"):
1659             return True
1661     return is_not_winnt_or_sunos
1664 is_linker_option_enabled = is_not_winnt_or_sunos(target)
1667 @deprecated_option("--enable-gold", env="MOZ_FORCE_GOLD", when=is_linker_option_enabled)
1668 def enable_gold(value):
1669     if value:
1670         die("--enable-gold is deprecated, use --enable-linker=gold instead")
1671     else:
1672         die("--disable-gold is deprecated, use --enable-linker=something_else instead")
1675 @depends(target, developer_options)
1676 def enable_linker_default(target, developer_options):
1677     # Recent versions of clang default to lld when targetting Android, but we don't
1678     # want that as the default for non developer builds (see above).
1679     # So we want to force the default to whatever it was with older versions of clang,
1680     # but with caveats/workarounds:
1681     # - x86-64 gold has bugs in how it lays out .note.* sections. See bug 1573820.
1682     # - x86-32 gold has a bug when assembly files are built. See bug 1651699.
1683     # That leaves us with aarch64 and armv7, which respectively defaulted to
1684     # bfd and gold.
1685     # On developer builds, select_linker will pick lld if it's not the default.
1686     if target.os == "Android" and not developer_options:
1687         return "bfd" if target.cpu in ("x86", "x86_64", "aarch64") else "gold"
1690 option(
1691     "--enable-linker",
1692     nargs=1,
1693     help="Select the linker {bfd, gold, ld64, lld, lld-*, mold}{|}",
1694     default=enable_linker_default,
1695     when=is_linker_option_enabled,
1699 # No-op to enable depending on --enable-linker from default_elfhack in
1700 # toolkit/moz.configure.
1701 @depends("--enable-linker", when=is_linker_option_enabled)
1702 def enable_linker(linker):
1703     return linker
1706 @template
1707 def select_linker_tmpl(host_or_target):
1708     if host_or_target is target:
1709         deps = depends(
1710             "--enable-linker",
1711             c_compiler,
1712             developer_options,
1713             extra_toolchain_flags,
1714             target,
1715             when=is_linker_option_enabled,
1716         )
1717         host_or_target_str = "target"
1718     else:
1719         deps = depends(
1720             dependable(None),
1721             host_c_compiler,
1722             developer_options,
1723             dependable(None),
1724             host,
1725             when=is_not_winnt_or_sunos(host_or_target),
1726         )
1727         host_or_target_str = "host"
1729     @deps
1730     @checking(f"for {host_or_target_str} linker", lambda x: x.KIND)
1731     @imports("os")
1732     @imports("shutil")
1733     def select_linker(linker, c_compiler, developer_options, toolchain_flags, target):
1735         if linker:
1736             linker = linker[0]
1737         else:
1738             linker = None
1740         def is_valid_linker(linker):
1741             if target.kernel == "Darwin":
1742                 valid_linkers = ("ld64", "lld")
1743             else:
1744                 valid_linkers = ("bfd", "gold", "lld", "mold")
1745             if linker in valid_linkers:
1746                 return True
1747             if "lld" in valid_linkers and linker.startswith("lld-"):
1748                 return True
1749             return False
1751         if linker and not is_valid_linker(linker):
1752             # Check that we are trying to use a supported linker
1753             die("Unsupported linker " + linker)
1755         # Check the kind of linker
1756         version_check = ["-Wl,--version"]
1757         cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
1759         def try_linker(linker):
1760             # Generate the compiler flag
1761             if linker == "ld64":
1762                 linker_flag = ["-fuse-ld=ld"]
1763             elif linker:
1764                 linker_flag = ["-fuse-ld=" + linker]
1765             else:
1766                 linker_flag = []
1767             cmd = cmd_base + linker_flag + version_check
1768             if toolchain_flags:
1769                 cmd += toolchain_flags
1771             # ld64 doesn't have anything to print out a version. It does print out
1772             # "ld64: For information on command line options please use 'man ld'."
1773             # but that would require doing two attempts, one with --version, that
1774             # would fail, and another with --help.
1775             # Instead, abuse its LD_PRINT_OPTIONS feature to detect a message
1776             # specific to it on stderr when it fails to process --version.
1777             env = dict(os.environ)
1778             env["LD_PRINT_OPTIONS"] = "1"
1779             # Some locales might not print out the strings we are looking for, so
1780             # ensure consistent output.
1781             env["LC_ALL"] = "C"
1782             retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
1783             if retcode == 1 and "Logging ld64 options" in stderr:
1784                 kind = "ld64"
1786             elif retcode != 0:
1787                 return None
1789             elif "mold" in stdout:
1790                 kind = "mold"
1792             elif "GNU ld" in stdout:
1793                 # We are using the normal linker
1794                 kind = "bfd"
1796             elif "GNU gold" in stdout:
1797                 kind = "gold"
1799             elif "LLD" in stdout:
1800                 kind = "lld"
1802             else:
1803                 kind = "unknown"
1805             if kind == "unknown" or is_valid_linker(kind):
1806                 return namespace(
1807                     KIND=kind,
1808                     LINKER_FLAG=linker_flag,
1809                 )
1811         result = try_linker(linker)
1812         if result is None and linker:
1813             die("Could not use {} as linker".format(linker))
1815         if (
1816             linker is None
1817             and target.kernel == "Darwin"
1818             and c_compiler.type == "clang"
1819             and (
1820                 (developer_options and c_compiler.version >= "13.0")
1821                 or c_compiler.version >= "15.0"
1822             )
1823         ):
1824             result = try_linker("lld")
1825         elif (
1826             linker is None
1827             and (
1828                 developer_options
1829                 or (host_or_target_str == "host" and c_compiler.type == "clang")
1830             )
1831             and (result is None or result.KIND in ("bfd", "gold"))
1832         ):
1833             # try and use lld if available.
1834             tried = try_linker("lld")
1835             if (result is None or result.KIND != "gold") and (
1836                 tried is None or tried.KIND != "lld"
1837             ):
1838                 tried = try_linker("gold")
1839                 if tried is None or tried.KIND != "gold":
1840                     tried = None
1841             if tried:
1842                 result = tried
1844         if result is None:
1845             die("Failed to find an adequate linker")
1847         # If an explicit linker was given, error out if what we found is different.
1848         if linker and not linker.startswith(result.KIND):
1849             die("Could not use {} as linker".format(linker))
1851         return result
1853     return select_linker
1856 select_linker = select_linker_tmpl(target)
1857 set_config("LINKER_KIND", select_linker.KIND)
1860 @template
1861 def linker_ldflags_tmpl(host_or_target):
1862     if host_or_target is target:
1863         deps = depends_if(
1864             select_linker,
1865             target,
1866             target_sysroot,
1867             target_multiarch_dir,
1868             android_sysroot,
1869             android_version,
1870             c_compiler,
1871             developer_options,
1872         )
1873     else:
1874         deps = depends_if(
1875             select_linker_tmpl(host),
1876             host,
1877             host_sysroot,
1878             host_multiarch_dir,
1879             dependable(None),
1880             dependable(None),
1881             host_c_compiler,
1882             developer_options,
1883         )
1885     @deps
1886     @imports("os")
1887     def linker_ldflags(
1888         linker,
1889         target,
1890         sysroot,
1891         multiarch_dir,
1892         android_sysroot,
1893         android_version,
1894         c_compiler,
1895         developer_options,
1896     ):
1897         flags = list((linker and linker.LINKER_FLAG) or [])
1898         # rpath-link is irrelevant to wasm, see for more info https://github.com/emscripten-core/emscripten/issues/11076.
1899         if sysroot.path and multiarch_dir and target.os != "WASI":
1900             for d in ("lib", "usr/lib"):
1901                 multiarch_lib_dir = os.path.join(sysroot.path, d, multiarch_dir)
1902                 if os.path.exists(multiarch_lib_dir):
1903                     # Non-Debian-patched binutils linkers (both BFD and gold) don't lookup
1904                     # in multi-arch directories.
1905                     flags.append("-Wl,-rpath-link,%s" % multiarch_lib_dir)
1906                     # GCC also needs -L.
1907                     if c_compiler.type == "gcc":
1908                         flags.append("-L%s" % multiarch_lib_dir)
1909             if (
1910                 c_compiler.type == "gcc"
1911                 and sysroot.bootstrapped
1912                 and sysroot.stdcxx_version
1913             ):
1914                 flags.append(
1915                     "-L{}/usr/lib/gcc/{}/{}".format(
1916                         sysroot.path, multiarch_dir, sysroot.stdcxx_version
1917                     )
1918                 )
1919         if android_sysroot:
1920             # BFD/gold linkers need a manual --rpath-link for indirect
1921             # dependencies.
1922             flags += [
1923                 "-Wl,--rpath-link={}/usr/lib/{}".format(
1924                     android_sysroot, target.toolchain
1925                 ),
1926                 "-Wl,--rpath-link={}/usr/lib/{}/{}".format(
1927                     android_sysroot, target.toolchain, android_version
1928                 ),
1929             ]
1930         if (
1931             developer_options
1932             and linker
1933             and linker.KIND == "lld"
1934             and target.kernel != "WINNT"
1935         ):
1936             flags.append("-Wl,-O0")
1937         return flags
1939     return linker_ldflags
1942 linker_ldflags = linker_ldflags_tmpl(target)
1943 add_old_configure_assignment("LINKER_LDFLAGS", linker_ldflags)
1945 add_old_configure_assignment("HOST_LINKER_LDFLAGS", linker_ldflags_tmpl(host))
1948 # There's a wrinkle with MinGW: linker configuration is not enabled, so
1949 # `select_linker` is never invoked.  Hard-code around it.
1950 @depends(select_linker, target, c_compiler)
1951 def gcc_use_gnu_ld(select_linker, target, c_compiler):
1952     if select_linker is not None and target.kernel != "Darwin":
1953         return select_linker.KIND in ("bfd", "gold", "lld", "mold")
1954     if target.kernel == "WINNT" and c_compiler.type == "clang":
1955         return True
1956     return None
1959 # GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld.
1960 set_config("GCC_USE_GNU_LD", gcc_use_gnu_ld)
1961 add_old_configure_assignment("GCC_USE_GNU_LD", gcc_use_gnu_ld)
1964 include("compile-checks.configure")
1965 include("arm.configure", when=depends(target.cpu)(lambda cpu: cpu == "arm"))
1968 @depends(
1969     have_64_bit,
1970     try_compile(
1971         body='static_assert(sizeof(void *) == 8, "")', check_msg="for 64-bit OS"
1972     ),
1974 def check_have_64_bit(have_64_bit, compiler_have_64_bit):
1975     if have_64_bit != compiler_have_64_bit:
1976         configure_error(
1977             "The target compiler does not agree with configure "
1978             "about the target bitness."
1979         )
1982 @depends(cxx_compiler, target)
1983 def needs_libstdcxx_newness_check(cxx_compiler, target):
1984     # We only have to care about this on Linux and MinGW.
1985     if cxx_compiler.type == "clang-cl":
1986         return
1988     if target.kernel not in ("Linux", "WINNT"):
1989         return
1991     if target.os == "Android":
1992         return
1994     return True
1997 def die_on_old_libstdcxx():
1998     die(
1999         "The libstdc++ in use is not new enough.  Please run "
2000         "./mach bootstrap to update your compiler, or update your system "
2001         "libstdc++ installation."
2002     )
2005 try_compile(
2006     includes=["cstddef"],
2007     body="\n".join(
2008         [
2009             # _GLIBCXX_RELEASE showed up in libstdc++ 7.
2010             "#if defined(__GLIBCXX__) && !defined(_GLIBCXX_RELEASE)",
2011             "#  error libstdc++ not new enough",
2012             "#endif",
2013             "#if defined(_GLIBCXX_RELEASE)",
2014             "#  if _GLIBCXX_RELEASE < %d" % minimum_gcc_version().major,
2015             "#    error libstdc++ not new enough",
2016             "#  else",
2017             "     (void) 0",
2018             "#  endif",
2019             "#endif",
2020         ]
2021     ),
2022     check_msg="for new enough STL headers from libstdc++",
2023     when=needs_libstdcxx_newness_check,
2024     onerror=die_on_old_libstdcxx,
2028 @depends(c_compiler, target)
2029 def default_debug_flags(compiler_info, target):
2030     # Debug info is ON by default.
2031     if compiler_info.type == "clang-cl":
2032         return "-Z7"
2033     elif target.kernel == "WINNT" and compiler_info.type == "clang":
2034         return "-g -gcodeview"
2035     # The oldest versions of supported compilers default to DWARF-4, but
2036     # newer versions may default to DWARF-5 or newer (e.g. clang 14), which
2037     # Valgrind doesn't support. Force-use DWARF-4.
2038     return "-gdwarf-4"
2041 option(env="MOZ_DEBUG_FLAGS", nargs=1, help="Debug compiler flags")
2043 imply_option("--enable-debug-symbols", depends_if("--enable-debug")(lambda v: v))
2045 option(
2046     "--disable-debug-symbols",
2047     nargs="?",
2048     help="Disable debug symbols using the given compiler flags",
2051 set_config("MOZ_DEBUG_SYMBOLS", depends_if("--enable-debug-symbols")(lambda _: True))
2054 @depends("MOZ_DEBUG_FLAGS", "--enable-debug-symbols", default_debug_flags)
2055 def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
2056     # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
2057     # --enable-debug-symbols takes precedence. Note, the value of
2058     # --enable-debug-symbols may be implied by --enable-debug.
2059     if len(enable_debug_flags):
2060         return enable_debug_flags[0]
2061     if env_debug_flags:
2062         return env_debug_flags[0]
2063     return default_debug_flags
2066 set_config("MOZ_DEBUG_FLAGS", debug_flags)
2067 add_old_configure_assignment("MOZ_DEBUG_FLAGS", debug_flags)
2070 @depends(c_compiler, host)
2071 @imports(
2072     _from="mach.logging", _import="enable_blessed", _as="_enable_ansi_escape_codes"
2074 def color_cflags(info, host):
2075     # We could test compiling with flags. By why incur the overhead when
2076     # color support should always be present in a specific toolchain
2077     # version?
2079     # Code for auto-adding this flag to compiler invocations needs to
2080     # determine if an existing flag isn't already present. That is likely
2081     # using exact string matching on the returned value. So if the return
2082     # value changes to e.g. "<x>=always", exact string match may fail and
2083     # multiple color flags could be added. So examine downstream consumers
2084     # before adding flags to return values.
2085     if info.type == "gcc":
2086         return "-fdiagnostics-color"
2087     elif info.type in ["clang", "clang-cl"]:
2088         if host.os == "WINNT" and _enable_ansi_escape_codes():
2089             return "-fcolor-diagnostics -fansi-escape-codes"
2090         else:
2091             return "-fcolor-diagnostics"
2092     else:
2093         return ""
2096 set_config("COLOR_CFLAGS", color_cflags)
2098 # Some standard library headers (notably bionic on Android) declare standard
2099 # functions (e.g. getchar()) and also #define macros for those standard
2100 # functions.  libc++ deals with this by doing something like the following
2101 # (explanatory comments added):
2103 #   #ifdef FUNC
2104 #   // Capture the definition of FUNC.
2105 #   inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
2106 #   #undef FUNC
2107 #   // Use a real inline definition.
2108 #   inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
2109 #   #endif
2111 # _LIBCPP_INLINE_VISIBILITY is typically defined as:
2113 #   __attribute__((__visibility__("hidden"), __always_inline__))
2115 # Unfortunately, this interacts badly with our system header wrappers, as the:
2117 #   #pragma GCC visibility push(default)
2119 # that they do prior to including the actual system header is treated by the
2120 # compiler as an explicit declaration of visibility on every function declared
2121 # in the header.  Therefore, when the libc++ code above is encountered, it is
2122 # as though the compiler has effectively seen:
2124 #   int FUNC(...) __attribute__((__visibility__("default")));
2125 #   int FUNC(...) __attribute__((__visibility__("hidden")));
2127 # and the compiler complains about the mismatched visibility declarations.
2129 # However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
2130 # existing definition.  We can therefore define it to the empty string (since
2131 # we are properly managing visibility ourselves) and avoid this whole mess.
2132 # Note that we don't need to do this with gcc, as libc++ detects gcc and
2133 # effectively does the same thing we are doing here.
2135 # _LIBCPP_ALWAYS_INLINE needs a similar workarounds, since it too declares
2136 # hidden visibility.
2138 # _LIBCPP_HIDE_FROM_ABI is a macro in libc++ versions in NDKs >=r19.  It too
2139 # declares hidden visibility, but it also declares functions as excluded from
2140 # explicit instantiation (roughly: the function can be unused in the current
2141 # compilation, but does not then trigger an actual definition of the function;
2142 # it is assumed the real definition comes from elsewhere).  We need to replicate
2143 # this setup.
2146 @depends(c_compiler, target)
2147 def libcxx_override_visibility(c_compiler, target):
2148     if c_compiler.type == "clang" and target.os == "Android":
2149         return namespace(
2150             empty="",
2151             hide_from_abi="__attribute__((__exclude_from_explicit_instantiation__))",
2152         )
2155 set_define("_LIBCPP_INLINE_VISIBILITY", libcxx_override_visibility.empty)
2156 set_define("_LIBCPP_ALWAYS_INLINE", libcxx_override_visibility.empty)
2158 set_define("_LIBCPP_HIDE_FROM_ABI", libcxx_override_visibility.hide_from_abi)
2161 @depends(target, build_environment)
2162 def visibility_flags(target, env):
2163     if target.os != "WINNT":
2164         if target.kernel == "Darwin":
2165             return ("-fvisibility=hidden", "-fvisibility-inlines-hidden")
2166         return (
2167             "-I%s/system_wrappers" % os.path.join(env.dist),
2168             "-include",
2169             "%s/config/gcc_hidden.h" % env.topsrcdir,
2170         )
2173 @depends(target, visibility_flags)
2174 def wrap_system_includes(target, visibility_flags):
2175     if visibility_flags and target.kernel != "Darwin":
2176         return True
2179 set_define(
2180     "HAVE_VISIBILITY_HIDDEN_ATTRIBUTE",
2181     depends(visibility_flags)(lambda v: bool(v) or None),
2183 set_define(
2184     "HAVE_VISIBILITY_ATTRIBUTE", depends(visibility_flags)(lambda v: bool(v) or None)
2186 set_config("WRAP_SYSTEM_INCLUDES", wrap_system_includes)
2187 set_config("VISIBILITY_FLAGS", visibility_flags)
2190 @template
2191 def depend_cflags(host_or_target_c_compiler):
2192     @depends(host_or_target_c_compiler)
2193     def depend_cflags(host_or_target_c_compiler):
2194         if host_or_target_c_compiler.type != "clang-cl":
2195             return ["-MD", "-MP", "-MF $(MDDEPDIR)/$(@F).pp"]
2196         else:
2197             # clang-cl doesn't accept the normal -MD -MP -MF options that clang
2198             # does, but the underlying cc1 binary understands how to generate
2199             # dependency files.  These options are based on analyzing what the
2200             # normal clang driver sends to cc1 when given the "correct"
2201             # dependency options.
2202             return [
2203                 "-Xclang",
2204                 "-MP",
2205                 "-Xclang",
2206                 "-dependency-file",
2207                 "-Xclang",
2208                 "$(MDDEPDIR)/$(@F).pp",
2209                 "-Xclang",
2210                 "-MT",
2211                 "-Xclang",
2212                 "$@",
2213             ]
2215     return depend_cflags
2218 set_config("_DEPEND_CFLAGS", depend_cflags(c_compiler))
2219 set_config("_HOST_DEPEND_CFLAGS", depend_cflags(host_c_compiler))
2222 @depends(c_compiler)
2223 def preprocess_option(compiler):
2224     # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
2225     if compiler.type in ("gcc", "clang"):
2226         return "-E -o "
2227     else:
2228         return "-P -Fi"
2231 set_config("PREPROCESS_OPTION", preprocess_option)
2234 # We only want to include windows.configure when we are compiling on
2235 # Windows, or for Windows.
2236 include("windows.configure", when=is_windows)
2239 # On Power ISA, determine compiler flags for VMX, VSX and VSX-3.
2241 set_config(
2242     "PPC_VMX_FLAGS",
2243     ["-maltivec"],
2244     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2247 set_config(
2248     "PPC_VSX_FLAGS",
2249     ["-mvsx"],
2250     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2253 set_config(
2254     "PPC_VSX3_FLAGS",
2255     ["-mvsx", "-mcpu=power9"],
2256     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2259 # ASAN
2260 # ==============================================================
2262 option("--enable-address-sanitizer", help="Enable Address Sanitizer")
2265 @depends(when="--enable-address-sanitizer")
2266 def asan():
2267     return True
2270 add_old_configure_assignment("MOZ_ASAN", asan)
2272 # MSAN
2273 # ==============================================================
2275 option("--enable-memory-sanitizer", help="Enable Memory Sanitizer")
2278 @depends(when="--enable-memory-sanitizer")
2279 def msan():
2280     return True
2283 add_old_configure_assignment("MOZ_MSAN", msan)
2285 # TSAN
2286 # ==============================================================
2288 option("--enable-thread-sanitizer", help="Enable Thread Sanitizer")
2291 @depends(when="--enable-thread-sanitizer")
2292 def tsan():
2293     return True
2296 add_old_configure_assignment("MOZ_TSAN", tsan)
2298 # UBSAN
2299 # ==============================================================
2301 option(
2302     "--enable-undefined-sanitizer", nargs="*", help="Enable UndefinedBehavior Sanitizer"
2306 @depends_if("--enable-undefined-sanitizer")
2307 def ubsan(options):
2308     default_checks = [
2309         "bool",
2310         "bounds",
2311         "enum",
2312         "function",
2313         "integer-divide-by-zero",
2314         "object-size",
2315         "pointer-overflow",
2316         "return",
2317         "vla-bound",
2318     ]
2320     checks = options if len(options) else default_checks
2322     return ",".join(checks)
2325 add_old_configure_assignment("MOZ_UBSAN_CHECKS", ubsan)
2328 option(
2329     "--enable-signed-overflow-sanitizer",
2330     help="Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)",
2334 @depends(when="--enable-signed-overflow-sanitizer")
2335 def ub_signed_overflow_san():
2336     return True
2339 add_old_configure_assignment("MOZ_SIGNED_OVERFLOW_SANITIZE", ub_signed_overflow_san)
2342 option(
2343     "--enable-unsigned-overflow-sanitizer",
2344     help="Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)",
2348 @depends(when="--enable-unsigned-overflow-sanitizer")
2349 def ub_unsigned_overflow_san():
2350     return True
2353 add_old_configure_assignment("MOZ_UNSIGNED_OVERFLOW_SANITIZE", ub_unsigned_overflow_san)
2355 # Security Hardening
2356 # ==============================================================
2358 option(
2359     "--enable-hardening",
2360     env="MOZ_SECURITY_HARDENING",
2361     help="Enables security hardening compiler options",
2365 # This function is a bit confusing. It adds or removes hardening flags in
2366 # three stuations: if --enable-hardening is passed; if --disable-hardening
2367 # is passed, and if no flag is passed.
2369 # At time of this comment writing, all flags are actually added in the
2370 # default no-flag case; making --enable-hardening the same as omitting the
2371 # flag. --disable-hardening will omit the security flags. (However, not all
2372 # possible security flags will be omitted by --disable-hardening, as many are
2373 # compiler-default options we do not explicitly enable.)
2374 @depends(
2375     "--enable-hardening",
2376     "--enable-address-sanitizer",
2377     "--enable-debug",
2378     "--enable-optimize",
2379     c_compiler,
2380     target,
2382 def security_hardening_cflags(
2383     hardening_flag, asan, debug, optimize, c_compiler, target
2385     compiler_is_gccish = c_compiler.type in ("gcc", "clang")
2386     mingw_clang = c_compiler.type == "clang" and target.os == "WINNT"
2388     flags = []
2389     ldflags = []
2390     trivial_auto_var_init = []
2392     # WASI compiler doesn't support security hardening cflags
2393     if target.os == "WASI":
2394         return
2396     # ----------------------------------------------------------
2397     # If hardening is explicitly enabled, or not explicitly disabled
2398     if hardening_flag.origin == "default" or hardening_flag:
2399         # FORTIFY_SOURCE ------------------------------------
2400         # Require optimization for FORTIFY_SOURCE. See Bug 1417452
2401         # Also, undefine it before defining it just in case a distro adds it, see Bug 1418398
2402         if compiler_is_gccish and optimize and not asan:
2403             flags.append("-U_FORTIFY_SOURCE")
2404             flags.append("-D_FORTIFY_SOURCE=2")
2406         # fstack-protector ------------------------------------
2407         # Enable only if hardening is not disabled and ASAN is
2408         # not on as ASAN will catch the crashes for us
2409         if compiler_is_gccish and not asan:
2410             flags.append("-fstack-protector-strong")
2411             ldflags.append("-fstack-protector-strong")
2413             if (
2414                 c_compiler.type == "clang"
2415                 and c_compiler.version >= "11.0.1"
2416                 and target.os not in ("WINNT", "OSX")
2417                 and target.cpu in ("x86", "x86_64", "ppc64", "s390x")
2418             ):
2419                 flags.append("-fstack-clash-protection")
2420                 ldflags.append("-fstack-clash-protection")
2422         # ftrivial-auto-var-init ------------------------------
2423         # Initialize local variables with a 0xAA pattern in clang builds.
2424         # Linux32 fails some xpcshell tests with -ftrivial-auto-var-init
2425         linux32 = target.kernel == "Linux" and target.cpu == "x86"
2426         if (
2427             (c_compiler.type == "clang" or c_compiler.type == "clang-cl")
2428             and c_compiler.version >= "8"
2429             and not linux32
2430         ):
2431             if c_compiler.type == "clang-cl":
2432                 trivial_auto_var_init.append("-Xclang")
2433             trivial_auto_var_init.append("-ftrivial-auto-var-init=pattern")
2434             # Always enable on debug builds.
2435             if debug:
2436                 flags.extend(trivial_auto_var_init)
2438         # ASLR ------------------------------------------------
2439         # ASLR (dynamicbase) is enabled by default in clang-cl; but the
2440         # mingw-clang build requires it to be explicitly enabled
2441         if mingw_clang:
2442             ldflags.append("-Wl,--dynamicbase")
2444         # Control Flow Guard (CFG) ----------------------------
2445         if (
2446             c_compiler.type == "clang-cl"
2447             and c_compiler.version >= "8"
2448             and (target.cpu != "aarch64" or c_compiler.version >= "8.0.1")
2449         ):
2450             if target.cpu == "aarch64" and c_compiler.version >= "10.0.0":
2451                 # The added checks in clang 10 make arm64 builds crash. (Bug 1639318)
2452                 flags.append("-guard:cf,nochecks")
2453             else:
2454                 flags.append("-guard:cf")
2455             # nolongjmp is needed because clang doesn't emit the CFG tables of
2456             # setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057
2457             ldflags.append("-guard:cf,nolongjmp")
2459     # ----------------------------------------------------------
2460     # If ASAN _is_ on, disable FORTIFY_SOURCE just to be safe
2461     if asan:
2462         flags.append("-D_FORTIFY_SOURCE=0")
2464     # fno-common -----------------------------------------
2465     # Do not merge variables for ASAN; can detect some subtle bugs
2466     if asan:
2467         # clang-cl does not recognize the flag, it must be passed down to clang
2468         if c_compiler.type == "clang-cl":
2469             flags.append("-Xclang")
2470         flags.append("-fno-common")
2472     return namespace(
2473         flags=flags,
2474         ldflags=ldflags,
2475         trivial_auto_var_init=trivial_auto_var_init,
2476     )
2479 set_config("MOZ_HARDENING_CFLAGS", security_hardening_cflags.flags)
2480 set_config("MOZ_HARDENING_LDFLAGS", security_hardening_cflags.ldflags)
2481 set_config(
2482     "MOZ_TRIVIAL_AUTO_VAR_INIT",
2483     security_hardening_cflags.trivial_auto_var_init,
2487 # Intel Control-flow Enforcement Technology
2488 # ==============================================================
2489 # We keep this separate from the hardening flags above, because we want to be
2490 # able to easily remove the flags in the build files for certain executables.
2491 @depends(c_compiler, target)
2492 def cet_ldflags(c_compiler, target):
2493     ldflags = []
2494     if (
2495         c_compiler.type == "clang-cl"
2496         and c_compiler.version >= "11"
2497         and target.cpu == "x86_64"
2498     ):
2499         ldflags.append("-CETCOMPAT")
2500     return ldflags
2503 set_config("MOZ_CETCOMPAT_LDFLAGS", cet_ldflags)
2505 # Frame pointers
2506 # ==============================================================
2507 @depends(c_compiler)
2508 def frame_pointer_flags(compiler):
2509     if compiler.type == "clang-cl":
2510         return namespace(
2511             enable=["-Oy-"],
2512             disable=["-Oy"],
2513         )
2514     return namespace(
2515         enable=["-fno-omit-frame-pointer", "-funwind-tables"],
2516         disable=["-fomit-frame-pointer", "-funwind-tables"],
2517     )
2520 @depends(
2521     moz_optimize.optimize,
2522     moz_debug,
2523     target,
2524     "--enable-memory-sanitizer",
2525     "--enable-address-sanitizer",
2526     "--enable-undefined-sanitizer",
2528 def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
2529     return bool(
2530         not optimize
2531         or debug
2532         or msan
2533         or asan
2534         or ubsan
2535         or (target.os == "WINNT" and target.cpu in ("x86", "aarch64"))
2536         or target.os == "OSX"
2537     )
2540 option(
2541     "--enable-frame-pointers",
2542     default=frame_pointer_default,
2543     help="{Enable|Disable} frame pointers",
2547 @depends("--enable-frame-pointers", frame_pointer_flags)
2548 def frame_pointer_flags(enable, flags):
2549     if enable:
2550         return flags.enable
2551     return flags.disable
2554 set_config("MOZ_FRAMEPTR_FLAGS", frame_pointer_flags)
2557 # Code Coverage
2558 # ==============================================================
2560 option("--enable-coverage", env="MOZ_CODE_COVERAGE", help="Enable code coverage")
2563 @depends("--enable-coverage")
2564 def code_coverage(value):
2565     if value:
2566         return True
2569 set_config("MOZ_CODE_COVERAGE", code_coverage)
2570 set_define("MOZ_CODE_COVERAGE", code_coverage)
2573 @depends(target, c_compiler, build_environment, when=code_coverage)
2574 @imports("os")
2575 @imports("re")
2576 @imports(_from="__builtin__", _import="open")
2577 def coverage_cflags(target, c_compiler, build_env):
2578     cflags = ["--coverage"]
2580     # clang 11 no longer accepts this flag (its behavior became the default)
2581     if c_compiler.type in ("clang", "clang-cl") and c_compiler.version < "11.0.0":
2582         cflags += [
2583             "-Xclang",
2584             "-coverage-no-function-names-in-data",
2585         ]
2587     exclude = []
2588     if target.os == "WINNT" and c_compiler.type == "clang-cl":
2589         # VS files
2590         exclude.append("^.*[vV][sS]20[0-9]{2}.*$")
2591         # Files in fetches directory.
2592         exclude.append("^.*[\\\\/]fetches[\\\\/].*$")
2593     elif target.os == "OSX":
2594         # Files in fetches directory.
2595         exclude.append("^.*/fetches/.*$")
2596     elif target.os == "GNU":
2597         # Files in fetches directory.
2598         exclude.append("^.*/fetches/.*$")
2599         # Files in /usr/
2600         exclude.append("^/usr/.*$")
2602     if exclude:
2603         exclude = ";".join(exclude)
2604         cflags += [
2605             f"-fprofile-exclude-files={exclude}",
2606         ]
2608     response_file_path = os.path.join(build_env.topobjdir, "code_coverage_cflags")
2610     with open(response_file_path, "w") as f:
2611         f.write(" ".join(cflags))
2613     return ["@{}".format(response_file_path)]
2616 set_config("COVERAGE_CFLAGS", coverage_cflags)
2618 # Assembler detection
2619 # ==============================================================
2621 option(env="AS", nargs=1, help="Path to the assembler")
2624 @depends(target, c_compiler)
2625 def as_info(target, c_compiler):
2626     if c_compiler.type == "clang-cl":
2627         ml = {
2628             "x86": "ml.exe",
2629             "x86_64": "ml64.exe",
2630             "aarch64": "armasm64.exe",
2631         }.get(target.cpu)
2632         return namespace(type="masm", names=(ml,))
2633     # When building with anything but clang-cl, we just use the C compiler as the assembler.
2634     return namespace(type="gcc", names=(c_compiler.compiler,))
2637 # One would expect the assembler to be specified merely as a program.  But in
2638 # cases where the assembler is passed down into js/, it can be specified in
2639 # the same way as CC: a program + a list of argument flags.  We might as well
2640 # permit the same behavior in general, even though it seems somewhat unusual.
2641 # So we have to do the same sort of dance as we did above with
2642 # `provided_compiler`.
2643 provided_assembler = provided_program("AS")
2644 assembler = check_prog(
2645     "_AS",
2646     input=provided_assembler.program,
2647     what="the assembler",
2648     progs=as_info.names,
2649     paths=vc_toolchain_search_path,
2653 @depends(as_info, assembler, provided_assembler, c_compiler)
2654 def as_with_flags(as_info, assembler, provided_assembler, c_compiler):
2655     if provided_assembler:
2656         return provided_assembler.wrapper + [assembler] + provided_assembler.flags
2658     if as_info.type == "masm":
2659         return assembler
2661     assert as_info.type == "gcc"
2663     # Need to add compiler wrappers and flags as appropriate.
2664     return c_compiler.wrapper + [assembler] + c_compiler.flags
2667 set_config("AS", as_with_flags)
2670 @depends(assembler, c_compiler, extra_toolchain_flags)
2671 @imports("subprocess")
2672 @imports(_from="os", _import="devnull")
2673 def gnu_as(assembler, c_compiler, toolchain_flags):
2674     # clang uses a compatible GNU assembler.
2675     if c_compiler.type == "clang":
2676         return True
2678     if c_compiler.type == "gcc":
2679         cmd = [assembler] + c_compiler.flags
2680         if toolchain_flags:
2681             cmd += toolchain_flags
2682         cmd += ["-Wa,--version", "-c", "-o", devnull, "-x", "assembler", "-"]
2683         # We don't actually have to provide any input on stdin, `Popen.communicate` will
2684         # close the stdin pipe.
2685         # clang will error if it uses its integrated assembler for this target,
2686         # so handle failures gracefully.
2687         if "GNU" in check_cmd_output(*cmd, stdin=subprocess.PIPE, onerror=lambda: ""):
2688             return True
2691 set_config("GNU_AS", gnu_as)
2694 @depends(as_info, target)
2695 def as_dash_c_flag(as_info, target):
2696     # armasm64 doesn't understand -c.
2697     if as_info.type == "masm" and target.cpu == "aarch64":
2698         return ""
2699     else:
2700         return "-c"
2703 set_config("AS_DASH_C_FLAG", as_dash_c_flag)
2706 @depends(as_info, target)
2707 def as_outoption(as_info, target):
2708     # The uses of ASOUTOPTION depend on the spacing for -o/-Fo.
2709     if as_info.type == "masm" and target.cpu != "aarch64":
2710         return "-Fo"
2712     return "-o "
2715 set_config("ASOUTOPTION", as_outoption)
2717 # clang plugin handling
2718 # ==============================================================
2720 option(
2721     "--enable-clang-plugin",
2722     env="ENABLE_CLANG_PLUGIN",
2723     help="Enable building with the Clang plugin (gecko specific static analyzers)",
2726 add_old_configure_assignment(
2727     "ENABLE_CLANG_PLUGIN", depends_if("--enable-clang-plugin")(lambda _: True)
2731 @depends(host_c_compiler, c_compiler, when="--enable-clang-plugin")
2732 def llvm_config(host_c_compiler, c_compiler):
2733     clang = None
2734     for compiler in (host_c_compiler, c_compiler):
2735         if compiler and compiler.type == "clang":
2736             clang = compiler.compiler
2737             break
2738         elif compiler and compiler.type == "clang-cl":
2739             clang = os.path.join(os.path.dirname(compiler.compiler), "clang")
2740             break
2742     if not clang:
2743         die("Cannot --enable-clang-plugin when not building with clang")
2744     llvm_config = "llvm-config"
2745     out = check_cmd_output(clang, "--print-prog-name=llvm-config", onerror=lambda: None)
2746     if out:
2747         llvm_config = out.rstrip()
2748     return (llvm_config,)
2751 llvm_config = check_prog(
2752     "LLVM_CONFIG",
2753     llvm_config,
2754     what="llvm-config",
2755     when="--enable-clang-plugin",
2756     paths=clang_search_path,
2759 add_old_configure_assignment("LLVM_CONFIG", llvm_config)
2762 option(
2763     "--enable-clang-plugin-alpha",
2764     env="ENABLE_CLANG_PLUGIN_ALPHA",
2765     help="Enable static analysis with clang-plugin alpha checks.",
2769 @depends("--enable-clang-plugin", "--enable-clang-plugin-alpha")
2770 def check_clang_plugin_alpha(enable_clang_plugin, enable_clang_plugin_alpha):
2771     if enable_clang_plugin_alpha:
2772         if enable_clang_plugin:
2773             return True
2774         die("Cannot enable clang-plugin alpha checkers without --enable-clang-plugin.")
2777 add_old_configure_assignment("ENABLE_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha)
2778 set_define("MOZ_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha)
2780 option(
2781     "--enable-mozsearch-plugin",
2782     env="ENABLE_MOZSEARCH_PLUGIN",
2783     help="Enable building with the mozsearch indexer plugin",
2786 add_old_configure_assignment(
2787     "ENABLE_MOZSEARCH_PLUGIN", depends_if("--enable-mozsearch-plugin")(lambda _: True)
2790 # Libstdc++ compatibility hacks
2791 # ==============================================================
2793 @depends(target, host)
2794 def target_or_host_is_linux(target, host):
2795     return any(t.os == "GNU" and t.kernel == "Linux" for t in (target, host))
2798 option(
2799     "--enable-stdcxx-compat",
2800     env="MOZ_STDCXX_COMPAT",
2801     help="Enable compatibility with older libstdc++",
2802     when=target_or_host_is_linux,
2806 @depends("--enable-stdcxx-compat", when=target_or_host_is_linux)
2807 def stdcxx_compat(value):
2808     if value:
2809         return True
2812 set_config("MOZ_STDCXX_COMPAT", True, when=stdcxx_compat)
2813 add_flag(
2814     "-D_GLIBCXX_USE_CXX11_ABI=0",
2815     cxx_compiler,
2816     when=stdcxx_compat,
2818 add_flag(
2819     "-D_GLIBCXX_USE_CXX11_ABI=0",
2820     host_cxx_compiler,
2821     when=stdcxx_compat,
2825 # Support various fuzzing options
2826 # ==============================================================
2827 option("--enable-fuzzing", help="Enable fuzzing support")
2830 @depends(build_project)
2831 def js_build(build_project):
2832     return build_project == "js"
2835 option(
2836     "--enable-js-fuzzilli",
2837     when=js_build,
2838     help="Enable fuzzilli support for the JS engine",
2842 option(
2843     "--enable-snapshot-fuzzing",
2844     help="Enable experimental snapshot fuzzing support",
2848 imply_option("--enable-fuzzing", True, when="--enable-snapshot-fuzzing")
2851 @depends("--enable-snapshot-fuzzing")
2852 def enable_snapshot_fuzzing(value):
2853     if value:
2854         return True
2857 @depends("--enable-fuzzing")
2858 def enable_fuzzing(value):
2859     if value:
2860         return True
2863 @depends("--enable-js-fuzzilli", when=js_build)
2864 def enable_js_fuzzilli(value):
2865     if value:
2866         return True
2869 @depends(enable_fuzzing, enable_snapshot_fuzzing)
2870 def check_aflfuzzer(fuzzing, snapshot_fuzzing):
2871     if fuzzing and not snapshot_fuzzing:
2872         return True
2875 @depends(
2876     try_compile(
2877         body="__AFL_COMPILER;", check_msg="for AFL compiler", when=check_aflfuzzer
2878     )
2880 def enable_aflfuzzer(afl):
2881     if afl:
2882         return True
2885 @depends(enable_fuzzing, enable_aflfuzzer, enable_snapshot_fuzzing, c_compiler, target)
2886 def enable_libfuzzer(fuzzing, afl, snapshot_fuzzing, c_compiler, target):
2887     if (
2888         fuzzing
2889         and not afl
2890         and not snapshot_fuzzing
2891         and c_compiler.type == "clang"
2892         and target.os != "Android"
2893     ):
2894         return True
2897 @depends(enable_fuzzing, enable_aflfuzzer, enable_libfuzzer, enable_js_fuzzilli)
2898 def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer, enable_js_fuzzilli):
2899     if fuzzing and (afl or libfuzzer) and not enable_js_fuzzilli:
2900         return True
2903 set_config("FUZZING", enable_fuzzing)
2904 set_define("FUZZING", enable_fuzzing)
2906 set_config("LIBFUZZER", enable_libfuzzer)
2907 set_define("LIBFUZZER", enable_libfuzzer)
2908 add_old_configure_assignment("LIBFUZZER", enable_libfuzzer)
2910 set_config("AFLFUZZ", enable_aflfuzzer)
2911 set_define("AFLFUZZ", enable_aflfuzzer)
2913 set_config("FUZZING_INTERFACES", enable_fuzzing_interfaces)
2914 set_define("FUZZING_INTERFACES", enable_fuzzing_interfaces)
2915 add_old_configure_assignment("FUZZING_INTERFACES", enable_fuzzing_interfaces)
2917 set_config("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
2918 set_define("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
2920 set_config("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
2921 set_define("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
2924 @depends(
2925     c_compiler.try_compile(
2926         flags=["-fsanitize=fuzzer-no-link"],
2927         when=enable_fuzzing,
2928         check_msg="whether the C compiler supports -fsanitize=fuzzer-no-link",
2929     ),
2930     tsan,
2931     enable_js_fuzzilli,
2933 def libfuzzer_flags(value, tsan, enable_js_fuzzilli):
2934     if tsan:
2935         # With ThreadSanitizer, we should not use any libFuzzer instrumentation because
2936         # it is incompatible (e.g. there are races on global sanitizer coverage counters).
2937         # Instead we use an empty set of flags here but still build the fuzzing targets.
2938         # With this setup, we can still run files through these targets in TSan builds,
2939         # e.g. those obtained from regular fuzzing.
2940         # This code can be removed once libFuzzer has been made compatible with TSan.
2941         #
2942         # Also, this code needs to be kept in sync with certain gyp files, currently:
2943         #   - dom/media/webrtc/transport/third_party/nICEr/nicer.gyp
2944         return namespace(no_link_flag_supported=False, use_flags=[])
2946     if enable_js_fuzzilli:
2947         # Fuzzilli comes with its own trace-pc interceptors and flag requirements.
2948         no_link_flag_supported = False
2949         use_flags = ["-fsanitize-coverage=trace-pc-guard", "-g"]
2950     elif value:
2951         no_link_flag_supported = True
2952         # recommended for (and only supported by) clang >= 6
2953         use_flags = ["-fsanitize=fuzzer-no-link"]
2954     else:
2955         no_link_flag_supported = False
2956         use_flags = ["-fsanitize-coverage=trace-pc-guard,trace-cmp"]
2958     return namespace(
2959         no_link_flag_supported=no_link_flag_supported,
2960         use_flags=use_flags,
2961     )
2964 set_config("HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK", libfuzzer_flags.no_link_flag_supported)
2965 set_config("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
2966 add_old_configure_assignment("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
2968 # Shared library building
2969 # ==============================================================
2971 # XXX: The use of makefile constructs in these variables is awful.
2972 @depends(target, c_compiler)
2973 def make_shared_library(target, compiler):
2974     if target.os == "WINNT":
2975         if compiler.type == "gcc":
2976             return namespace(
2977                 mkshlib=["$(CXX)", "$(DSO_LDOPTS)", "-o", "$@"],
2978                 mkcshlib=["$(CC)", "$(DSO_LDOPTS)", "-o", "$@"],
2979             )
2980         elif compiler.type == "clang":
2981             return namespace(
2982                 mkshlib=[
2983                     "$(CXX)",
2984                     "$(DSO_LDOPTS)",
2985                     "-Wl,-pdb,$(LINK_PDBFILE)",
2986                     "-o",
2987                     "$@",
2988                 ],
2989                 mkcshlib=[
2990                     "$(CC)",
2991                     "$(DSO_LDOPTS)",
2992                     "-Wl,-pdb,$(LINK_PDBFILE)",
2993                     "-o",
2994                     "$@",
2995                 ],
2996             )
2997         else:
2998             linker = [
2999                 "$(LINKER)",
3000                 "-NOLOGO",
3001                 "-DLL",
3002                 "-OUT:$@",
3003                 "-PDB:$(LINK_PDBFILE)",
3004                 "$(DSO_LDOPTS)",
3005             ]
3006             return namespace(
3007                 mkshlib=linker,
3008                 mkcshlib=linker,
3009             )
3011     cc = ["$(CC)", "$(COMPUTED_C_LDFLAGS)"]
3012     cxx = ["$(CXX)", "$(COMPUTED_CXX_LDFLAGS)"]
3013     flags = ["$(PGO_CFLAGS)", "$(DSO_LDOPTS)"]
3014     output = ["-o", "$@"]
3016     if target.kernel == "Darwin":
3017         soname = []
3018     elif target.os == "NetBSD":
3019         soname = ["-Wl,-soname,$(DSO_SONAME)"]
3020     else:
3021         assert compiler.type in ("gcc", "clang")
3023         soname = ["-Wl,-h,$(DSO_SONAME)"]
3025     return namespace(
3026         mkshlib=cxx + flags + soname + output,
3027         mkcshlib=cc + flags + soname + output,
3028     )
3031 set_config("MKSHLIB", make_shared_library.mkshlib)
3032 set_config("MKCSHLIB", make_shared_library.mkcshlib)
3035 @depends(c_compiler, toolchain_prefix, when=target_is_windows)
3036 def rc_names(c_compiler, toolchain_prefix):
3037     if c_compiler.type in ("gcc", "clang"):
3038         return tuple("%s%s" % (p, "windres") for p in ("",) + (toolchain_prefix or ()))
3039     return ("llvm-rc",)
3042 check_prog("RC", rc_names, paths=clang_search_path, when=target_is_windows)
3045 @template
3046 def ar_config(c_compiler, toolchain_prefix=None):
3047     if not toolchain_prefix:
3048         toolchain_prefix = dependable(None)
3050     @depends(toolchain_prefix, c_compiler)
3051     def ar_config(toolchain_prefix, c_compiler):
3052         if c_compiler.type == "clang-cl":
3053             return namespace(
3054                 names=("llvm-lib",),
3055                 flags=("-llvmlibthin", "-out:$@"),
3056             )
3058         names = tuple("%s%s" % (p, "ar") for p in (toolchain_prefix or ()) + ("",))
3059         if c_compiler.type == "clang":
3060             # Get the llvm-ar path as per the output from clang --print-prog-name=llvm-ar
3061             # so that we directly get the one under the clang directory, rather than one
3062             # that might be in /usr/bin and that might point to one from a different version
3063             # of clang.
3064             out = check_cmd_output(
3065                 c_compiler.compiler, "--print-prog-name=llvm-ar", onerror=lambda: None
3066             )
3067             llvm_ar = out.rstrip() if out else "llvm-ar"
3068             names = (llvm_ar,) + names
3070         return namespace(
3071             names=names,
3072             flags=("crs", "$@"),
3073         )
3075     return ar_config
3078 target_ar_config = ar_config(c_compiler, toolchain_prefix)
3080 check_prog("AR", target_ar_config.names, paths=clang_search_path)
3082 set_config("AR_FLAGS", target_ar_config.flags)
3084 host_ar_config = ar_config(host_c_compiler)
3086 check_prog("HOST_AR", host_ar_config.names, paths=clang_search_path)
3089 @depends(toolchain_prefix, c_compiler)
3090 def nm_names(toolchain_prefix, c_compiler):
3091     names = tuple("%s%s" % (p, "nm") for p in (toolchain_prefix or ()) + ("",))
3092     if c_compiler.type == "clang":
3093         # Get the llvm-nm path as per the output from clang --print-prog-name=llvm-nm
3094         # so that we directly get the one under the clang directory, rather than one
3095         # that might be in /usr/bin and that might point to one from a different version
3096         # of clang.
3097         out = check_cmd_output(
3098             c_compiler.compiler, "--print-prog-name=llvm-nm", onerror=lambda: None
3099         )
3100         llvm_nm = out.rstrip() if out else "llvm-nm"
3101         names = (llvm_nm,) + names
3103     return names
3106 check_prog("NM", nm_names, paths=clang_search_path, when=target_has_linux_kernel)
3109 option("--enable-cpp-rtti", help="Enable C++ RTTI")
3111 add_old_configure_assignment("_MOZ_USE_RTTI", "1", when="--enable-cpp-rtti")
3114 option(
3115     "--enable-path-remapping",
3116     nargs="*",
3117     choices=("c", "rust"),
3118     help="Enable remapping source and object paths in compiled outputs.",
3122 @depends("--enable-path-remapping")
3123 def path_remapping(value):
3124     if len(value):
3125         return value
3126     if bool(value):
3127         return ["c", "rust"]
3128     return []
3131 @depends(
3132     target,
3133     build_environment,
3134     target_sysroot.path,
3135     valid_windows_sdk_dir,
3136     vc_path,
3137     when="--enable-path-remapping",
3139 def path_remappings(target, build_env, sysroot_path, windows_sdk_dir, vc_path):
3140     win = target.kernel == "WINNT"
3142     # The prefix maps are processed in the order they're specified on the
3143     # command line.  Therefore, to accommodate object directories in the source
3144     # directory, it's important that we map the topobjdir before the topsrcdir,
3145     # 'cuz we might have /src/obj/=/o/ and /src/=/s/.  The various other
3146     # directories might be subdirectories of topsrcdir as well, so they come
3147     # earlier still.
3149     path_remappings = []
3151     # We will have only one sysroot or SDK, so all can have the same mnemonic: K
3152     # for "kit" (since S is taken for "source").  See
3153     # https://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html
3154     # for how to use the Windows `subst` command to map these in debuggers and
3155     # IDEs.
3156     if sysroot_path:
3157         path_remappings.append((sysroot_path, "k:/" if win else "/sysroot/"))
3158     if windows_sdk_dir:
3159         path_remappings.append(
3160             (windows_sdk_dir.path, "k:/" if win else "/windows_sdk/")
3161         )
3162     if vc_path:
3163         path_remappings.append((vc_path, "v:/" if win else "/vc/"))
3165     path_remappings += [
3166         (build_env.topobjdir, "o:/" if win else "/topobjdir/"),
3167         (build_env.topsrcdir, "s:/" if win else "/topsrcdir/"),
3168     ]
3170     path_remappings = [
3171         (normsep(old).rstrip("/") + "/", new) for old, new in path_remappings
3172     ]
3174     # It is tempting to sort these, but we want the order to be the same across
3175     # machines so that we can share cache hits.  Therefore we reject bad
3176     # configurations rather than trying to make the configuration good.
3177     for i in range(len(path_remappings) - 1):
3178         p = path_remappings[i][0]
3179         for q, _ in path_remappings[i + 1 :]:
3180             if q.startswith(p):
3181                 die(f"Cannot remap paths because {p} is an ancestor of {q}")
3183     return path_remappings
3186 set_config("MMX_FLAGS", ["-mmmx"])
3187 set_config("SSE_FLAGS", ["-msse"])
3188 set_config("SSE2_FLAGS", ["-msse2"])
3189 set_config("SSSE3_FLAGS", ["-mssse3"])
3190 set_config("SSE4_2_FLAGS", ["-msse4.2"])
3191 set_config("FMA_FLAGS", ["-mfma"])
3192 set_config("AVX2_FLAGS", ["-mavx2"])