no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / build / moz.configure / toolchain.configure
blobe1bf3a20a2bd0cc5cb18ed9cc52aa2ca5e9d89c8
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.15 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.15 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.15"),
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 "14.0"
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(
726     mozillabuild_bin_paths, clang_search_path, rust_search_path, target, original_path
728 @imports("os")
729 def altered_path(
730     mozillabuild_bin_paths, clang_search_path, rust_search_path, target, original_path
732     altered_path = mozillabuild_bin_paths
733     if target.kernel == "Darwin":
734         # The rust compiler wants to execute dsymutil, but it does so in a
735         # non-configurable way (https://github.com/rust-lang/rust/issues/52728)
736         # so we add the clang path.
737         path = clang_search_path
738     else:
739         path = original_path
740     # cargo needs the rust search path to find cargo-$subcommand.
741     path += rust_search_path
742     for p in path:
743         if p not in altered_path:
744             altered_path.append(p)
745     return os.pathsep.join(altered_path)
748 set_config("PATH", altered_path)
751 # Compiler wrappers
752 # ==============================================================
753 option(
754     "--with-compiler-wrapper",
755     env="COMPILER_WRAPPER",
756     nargs=1,
757     help="Enable compiling with wrappers such as distcc and ccache",
760 option("--with-ccache", env="CCACHE", nargs="?", help="Enable compiling with ccache")
763 @depends_if("--with-ccache")
764 def ccache(value):
765     if len(value):
766         return value
767     # If --with-ccache was given without an explicit value, we default to
768     # 'ccache'.
769     return "ccache"
772 ccache = check_prog(
773     "CCACHE",
774     progs=(),
775     input=ccache,
776     paths=bootstrap_search_path(
777         "sccache", when=depends("CCACHE")(lambda c: len(c) and c[0] == "sccache")
778     ),
779     allow_missing=True,
782 option(env="CCACHE_PREFIX", nargs=1, help="Compiler prefix to use when using ccache")
784 ccache_prefix = depends_if("CCACHE_PREFIX")(lambda prefix: prefix[0])
785 set_config("CCACHE_PREFIX", ccache_prefix)
787 # Distinguish ccache from sccache.
790 @depends_if(ccache)
791 def ccache_is_sccache(ccache):
792     return check_cmd_output(ccache, "--version").startswith("sccache")
795 @depends(ccache, ccache_is_sccache)
796 def using_ccache(ccache, ccache_is_sccache):
797     return ccache and not ccache_is_sccache
800 @depends_if(ccache, ccache_is_sccache)
801 def using_sccache(ccache, ccache_is_sccache):
802     return ccache and ccache_is_sccache
805 option(env="RUSTC_WRAPPER", nargs=1, help="Wrap rust compilation with given tool")
808 @depends(ccache, ccache_is_sccache, "RUSTC_WRAPPER")
809 @imports(_from="textwrap", _import="dedent")
810 @imports("os")
811 def check_sccache_version(ccache, ccache_is_sccache, rustc_wrapper):
812     sccache_min_version = Version("0.2.13")
814     def check_version(path):
815         out = check_cmd_output(path, "--version")
816         version = Version(out.rstrip().split()[-1])
817         if version < sccache_min_version:
818             die(
819                 dedent(
820                     """\
821             sccache %s or later is required. sccache in use at %s has
822             version %s.
824             Please upgrade or acquire a new version with |./mach bootstrap|.
825             """
826                 ),
827                 sccache_min_version,
828                 path,
829                 version,
830             )
832     if ccache and ccache_is_sccache:
833         check_version(ccache)
835     if rustc_wrapper and (
836         os.path.splitext(os.path.basename(rustc_wrapper[0]))[0].lower() == "sccache"
837     ):
838         check_version(rustc_wrapper[0])
841 set_config("MOZ_USING_CCACHE", using_ccache)
842 set_config("MOZ_USING_SCCACHE", using_sccache)
844 option(env="SCCACHE_VERBOSE_STATS", help="Print verbose sccache stats after build")
847 @depends(using_sccache, "SCCACHE_VERBOSE_STATS")
848 def sccache_verbose_stats(using_sccache, verbose_stats):
849     return using_sccache and bool(verbose_stats)
852 set_config("SCCACHE_VERBOSE_STATS", sccache_verbose_stats)
855 @depends("--with-compiler-wrapper", ccache)
856 @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
857 def compiler_wrapper(wrapper, ccache):
858     if wrapper:
859         raw_wrapper = wrapper[0]
860         wrapper = shell_split(raw_wrapper)
861         wrapper_program = find_program(wrapper[0])
862         if not wrapper_program:
863             die(
864                 "Cannot find `%s` from the given compiler wrapper `%s`",
865                 wrapper[0],
866                 raw_wrapper,
867             )
868         wrapper[0] = wrapper_program
870     if ccache:
871         if wrapper:
872             return tuple([ccache] + wrapper)
873         else:
874             return (ccache,)
875     elif wrapper:
876         return tuple(wrapper)
879 @depends_if(compiler_wrapper)
880 def using_compiler_wrapper(compiler_wrapper):
881     return True
884 set_config("MOZ_USING_COMPILER_WRAPPER", using_compiler_wrapper)
887 @dependable
888 def wasm():
889     return split_triplet("wasm32-wasi", allow_wasi=True)
892 @template
893 def default_c_compilers(host_or_target, other_c_compiler=None):
894     """Template defining the set of default C compilers for the host and
895     target platforms.
896     `host_or_target` is either `host` or `target` (the @depends functions
897     from init.configure.
898     `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
899     """
900     assert host_or_target in {host, target, wasm}
902     other_c_compiler = () if other_c_compiler is None else (other_c_compiler,)
904     @depends(host_or_target, target, toolchain_prefix, *other_c_compiler)
905     def default_c_compilers(
906         host_or_target, target, toolchain_prefix, *other_c_compiler
907     ):
908         if host_or_target.kernel == "WINNT":
909             if host_or_target.abi:
910                 if host_or_target.abi == "msvc":
911                     supported = types = ("clang-cl",)
912                 elif host_or_target.abi == "mingw":
913                     supported = types = ("clang",)
914             else:
915                 supported = types = ("clang-cl", "clang")
916         elif host_or_target.kernel == "Darwin":
917             types = ("clang",)
918             supported = ("clang", "gcc")
919         elif host_or_target.kernel == "WASI":
920             supported = types = ("clang",)
921         else:
922             supported = types = ("clang", "gcc")
924         info = other_c_compiler[0] if other_c_compiler else None
925         if info and info.type in supported:
926             # When getting default C compilers for the host, we prioritize the
927             # same compiler as the target C compiler.
928             prioritized = info.compiler
929             if info.type == "gcc":
930                 same_arch = same_arch_different_bits()
931                 if (
932                     target.cpu != host_or_target.cpu
933                     and (target.cpu, host_or_target.cpu) not in same_arch
934                     and (host_or_target.cpu, target.cpu) not in same_arch
935                 ):
936                     # If the target C compiler is GCC, and it can't be used with
937                     # -m32/-m64 for the host, it's probably toolchain-prefixed,
938                     # so we prioritize a raw 'gcc' instead.
939                     prioritized = info.type
940             if target.os != "WINNT" and host_or_target.os == "WINNT":
941                 # When cross-compiling on Windows, don't prioritize. We'll fallback
942                 # to checking for clang-cl first.
943                 pass
944             else:
945                 types = [prioritized] + [t for t in types if t != info.type]
947         gcc = ("gcc",)
948         if toolchain_prefix and host_or_target is target:
949             gcc = tuple("%sgcc" % p for p in toolchain_prefix) + gcc
951         result = []
952         for type in types:
953             if type == "gcc":
954                 result.extend(gcc)
955             else:
956                 result.append(type)
958         return tuple(result)
960     return default_c_compilers
963 @template
964 def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None):
965     """Template defining the set of default C++ compilers for the host and
966     target platforms.
967     `c_compiler` is the @depends function returning a Compiler instance for
968     the desired platform.
970     Because the build system expects the C and C++ compilers to be from the
971     same compiler suite, we derive the default C++ compilers from the C
972     compiler that was found if none was provided.
974     We also factor in the target C++ compiler when getting the default host
975     C++ compiler, using the target C++ compiler if the host and target C
976     compilers are the same.
977     """
979     assert (other_c_compiler is None) == (other_cxx_compiler is None)
980     if other_c_compiler is not None:
981         other_compilers = (other_c_compiler, other_cxx_compiler)
982     else:
983         other_compilers = ()
985     @depends(c_compiler, *other_compilers)
986     def default_cxx_compilers(c_compiler, *other_compilers):
987         if other_compilers:
988             other_c_compiler, other_cxx_compiler = other_compilers
989             if other_c_compiler.compiler == c_compiler.compiler:
990                 return (other_cxx_compiler.compiler,)
992         dir = os.path.dirname(c_compiler.compiler)
993         file = os.path.basename(c_compiler.compiler)
995         if c_compiler.type == "gcc":
996             return (os.path.join(dir, file.replace("gcc", "g++")),)
998         if c_compiler.type == "clang":
999             return (os.path.join(dir, file.replace("clang", "clang++")),)
1001         return (c_compiler.compiler,)
1003     return default_cxx_compilers
1006 @template
1007 def provided_program(env_var, when=None):
1008     """Template handling cases where a program can be specified either as a
1009     path or as a path with applicable arguments.
1010     """
1012     @depends_if(env_var, when=when)
1013     @imports(_from="itertools", _import="takewhile")
1014     @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
1015     def provided(cmd):
1016         # Assume the first dash-prefixed item (and any subsequent items) are
1017         # command-line options, the item before the dash-prefixed item is
1018         # the program we're looking for, and anything before that is a wrapper
1019         # of some kind (e.g. sccache).
1020         cmd = shell_split(cmd[0])
1022         without_flags = list(takewhile(lambda x: not x.startswith("-"), cmd))
1024         return namespace(
1025             wrapper=without_flags[:-1],
1026             program=without_flags[-1],
1027             flags=cmd[len(without_flags) :],
1028         )
1030     return provided
1033 @template
1034 def sysroot(host_or_target, target_sysroot=None):
1035     assert target_sysroot or host_or_target is target
1036     bootstrap_target_when = target_is_linux_or_wasi
1037     if host_or_target is host:
1038         host_or_target_str = "host"
1039         opt = "--with-host-sysroot"
1040         env = "HOST_SYSROOT"
1041         when = depends(host)(lambda h: h.kernel == "Linux")
1043         # Only bootstrap a host sysroot when using a bootstrapped target sysroot
1044         # or when the target doesn't use a bootstrapped sysroot in the first place.
1045         @depends(when, bootstrap_target_when, target_sysroot.bootstrapped)
1046         def bootstrap_when(when, bootstrap_target_when, bootstrapped):
1047             return when and (bootstrapped or not bootstrap_target_when)
1049     else:
1050         assert host_or_target is target
1051         host_or_target_str = "target"
1052         opt = "--with-sysroot"
1053         env = "SYSROOT"
1054         when = target_is_linux_or_wasi
1055         bootstrap_when = bootstrap_target_when
1057     option(
1058         opt,
1059         env=env,
1060         nargs=1,
1061         when=when,
1062         help="Use the given sysroot directory for %s build" % host_or_target_str,
1063     )
1065     sysroot_input = depends(opt, when=when)(lambda x: x)
1066     bootstrap_sysroot = depends(bootstrap_when, sysroot_input)(
1067         # Only bootstrap when no flag was explicitly given (either --with or --without)
1068         lambda bootstrap, input: bootstrap
1069         and not input
1070         and input.origin == "default"
1071     )
1073     @depends(
1074         sysroot_input,
1075         host_or_target,
1076         macos_sdk,
1077         bootstrap_path(
1078             depends(host_or_target)(lambda t: "sysroot-{}".format(t.toolchain)),
1079             when=bootstrap_sysroot,
1080         ),
1081     )
1082     @imports("os")
1083     def sysroot(sysroot_input, host_or_target, macos_sdk, path):
1084         version = None
1085         if sysroot_input:
1086             path = sysroot_input[0]
1087         elif host_or_target.kernel == "Darwin" and macos_sdk:
1088             path = macos_sdk
1089         if path:
1090             # Find the version of libstdc++ headears in the sysroot
1091             include = os.path.join(path, "usr/include/c++")
1092             if os.path.isdir(include):
1093                 with os.scandir(include) as d:
1094                     version = max(Version(e.name) for e in d if e.is_dir())
1095             log.info("Using %s sysroot in %s", host_or_target_str, path)
1096         return namespace(
1097             path=path,
1098             bootstrapped=bool(path and not sysroot_input),
1099             stdcxx_version=version,
1100         )
1102     return sysroot
1105 target_sysroot = sysroot(target)
1108 # Use `system_lib_option` instead of `option` for options that enable building
1109 # with a system library for which the development headers are not available in
1110 # the bootstrapped sysroots.
1111 @template
1112 def system_lib_option(name, *args, **kwargs):
1113     option(name, *args, **kwargs)
1115     @depends(name, target_sysroot.bootstrapped)
1116     def no_system_lib_in_sysroot(value, bootstrapped):
1117         if bootstrapped and value:
1118             die(
1119                 "%s is not supported with bootstrapped sysroot. "
1120                 "Drop the option, or use --without-sysroot or --disable-bootstrap",
1121                 value.format(name),
1122             )
1125 host_sysroot = sysroot(host, target_sysroot)
1128 @template
1129 def multiarch_dir(host_or_target):
1130     sysroot = {
1131         host: host_sysroot,
1132         target: target_sysroot,
1133     }[host_or_target]
1135     @depends(host_or_target, when=sysroot.path)
1136     def multiarch_dir(target):
1137         if target.cpu == "x86":
1138             # Turn e.g. i686-linux-gnu into i386-linux-gnu
1139             return target.toolchain.replace(target.raw_cpu, "i386")
1140         return target.toolchain
1142     return multiarch_dir
1145 target_multiarch_dir = multiarch_dir(target)
1146 host_multiarch_dir = multiarch_dir(host)
1149 def minimum_gcc_version():
1150     return Version("8.1.0")
1153 @template
1154 def compiler(
1155     language,
1156     host_or_target,
1157     c_compiler=None,
1158     other_compiler=None,
1159     other_c_compiler=None,
1161     """Template handling the generic base checks for the compiler for the
1162     given `language` on the given platform (`host_or_target`).
1163     `host_or_target` is either `host` or `target` (the @depends functions
1164     from init.configure.
1165     When the language is 'C++', `c_compiler` is the result of the `compiler`
1166     template for the language 'C' for the same `host_or_target`.
1167     When `host_or_target` is `host`, `other_compiler` is the result of the
1168     `compiler` template for the same `language` for `target`.
1169     When `host_or_target` is `host` and the language is 'C++',
1170     `other_c_compiler` is the result of the `compiler` template for the
1171     language 'C' for `target`.
1172     """
1173     assert host_or_target in {host, target, wasm}
1174     assert language in ("C", "C++")
1175     assert language == "C" or c_compiler is not None
1176     assert host_or_target is target or other_compiler is not None
1177     assert language == "C" or host_or_target is target or other_c_compiler is not None
1179     host_or_target_str = {
1180         host: "host",
1181         target: "target",
1182         wasm: "wasm",
1183     }[host_or_target]
1185     sysroot = {
1186         host: host_sysroot,
1187         target: target_sysroot,
1188         wasm: dependable(lambda: namespace(path=None)),
1189     }[host_or_target]
1191     multiarch_dir = {
1192         host: host_multiarch_dir,
1193         target: target_multiarch_dir,
1194         wasm: never,
1195     }[host_or_target]
1197     var = {
1198         ("C", target): "CC",
1199         ("C++", target): "CXX",
1200         ("C", host): "HOST_CC",
1201         ("C++", host): "HOST_CXX",
1202         ("C", wasm): "WASM_CC",
1203         ("C++", wasm): "WASM_CXX",
1204     }[language, host_or_target]
1206     default_compilers = {
1207         "C": lambda: default_c_compilers(host_or_target, other_compiler),
1208         "C++": lambda: default_cxx_compilers(
1209             c_compiler, other_c_compiler, other_compiler
1210         ),
1211     }[language]()
1213     what = "the %s %s compiler" % (host_or_target_str, language)
1215     option(env=var, nargs=1, help="Path to %s" % what)
1217     # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
1218     # HOST_CXX variables.
1219     provided_compiler = provided_program(var)
1221     # Normally, we'd use `var` instead of `_var`, but the interaction with
1222     # old-configure complicates things, and for now, we a) can't take the plain
1223     # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
1224     # old-configure AC_SUBST it (because it's autoconf doing it, not us)
1225     compiler = check_prog(
1226         "_%s" % var,
1227         what=what,
1228         progs=default_compilers,
1229         input=provided_compiler.program,
1230         paths=clang_search_path,
1231     )
1233     @depends(
1234         configure_cache,
1235         compiler,
1236         provided_compiler,
1237         compiler_wrapper,
1238         host_or_target,
1239         sysroot,
1240         macos_target,
1241         android_version,
1242         vc_compiler_version,
1243         multiarch_dir,
1244         winsysroot,
1245         host,
1246     )
1247     @checking("whether %s can be used" % what, lambda x: bool(x))
1248     @imports(_from="mozbuild.shellutil", _import="quote")
1249     @imports("os")
1250     def valid_compiler(
1251         configure_cache,
1252         compiler,
1253         provided_compiler,
1254         compiler_wrapper,
1255         host_or_target,
1256         sysroot,
1257         macos_target,
1258         android_version,
1259         vc_compiler_version,
1260         multiarch_dir,
1261         winsysroot,
1262         host,
1263     ):
1264         wrapper = list(compiler_wrapper or ())
1265         flags = []
1266         if sysroot.path:
1267             if host_or_target.kernel == "Darwin":
1268                 # While --sysroot and -isysroot are roughly equivalent, when not using
1269                 # -isysroot on mac, clang takes the SDKROOT environment variable into
1270                 # consideration, which may be set by python and break things.
1271                 flags.extend(("-isysroot", sysroot.path))
1272             else:
1273                 flags.extend(("--sysroot", sysroot.path))
1274         if provided_compiler:
1275             wrapper.extend(provided_compiler.wrapper)
1276             flags.extend(provided_compiler.flags)
1278         info = check_compiler(
1279             configure_cache,
1280             wrapper + [compiler] + flags,
1281             language,
1282             host_or_target,
1283             android_version,
1284         )
1286         if host_or_target.os == "OSX" and macos_target:
1287             flags.append("-mmacosx-version-min=%s" % macos_target)
1289         # When not given an explicit compatibility version, clang-cl tries
1290         # to get one from MSVC, which might not even be the one used by the
1291         # build. And when it can't find one, its default might also not match
1292         # what the build is using. So if we were able to figure out the version
1293         # we're building with, explicitly use that.
1294         # This also means that, as a side effect, clang-cl will not try to find
1295         # MSVC, which saves a little overhead.
1296         if info.type == "clang-cl" and vc_compiler_version:
1297             flags.append(f"-fms-compatibility-version={vc_compiler_version}")
1299         if info.type == "clang" and language == "C++" and host_or_target.os == "OSX":
1300             flags.append("-stdlib=libc++")
1302         # Check that the additional flags we got are enough to not require any
1303         # more flags. If we get an exception, just ignore it; it's liable to be
1304         # invalid command-line flags, which means the compiler we're checking
1305         # doesn't support those command-line flags and will fail one or more of
1306         # the checks below.
1307         try:
1308             if info.flags:
1309                 flags += info.flags
1310                 info = check_compiler(
1311                     configure_cache,
1312                     wrapper + [compiler] + flags,
1313                     language,
1314                     host_or_target,
1315                     android_version,
1316                 )
1317         except FatalCheckError:
1318             pass
1320         if not info.target_cpu or info.target_cpu != host_or_target.cpu:
1321             raise FatalCheckError(
1322                 "%s %s compiler target CPU (%s) does not match --%s CPU (%s)"
1323                 % (
1324                     host_or_target_str.capitalize(),
1325                     language,
1326                     info.target_cpu or "unknown",
1327                     host_or_target_str,
1328                     host_or_target.raw_cpu,
1329                 )
1330             )
1332         if not info.target_kernel or (info.target_kernel != host_or_target.kernel):
1333             raise FatalCheckError(
1334                 "%s %s compiler target kernel (%s) does not match --%s kernel (%s)"
1335                 % (
1336                     host_or_target_str.capitalize(),
1337                     language,
1338                     info.target_kernel or "unknown",
1339                     host_or_target_str,
1340                     host_or_target.kernel,
1341                 )
1342             )
1344         if not info.target_endianness or (
1345             info.target_endianness != host_or_target.endianness
1346         ):
1347             raise FatalCheckError(
1348                 "%s %s compiler target endianness (%s) does not match --%s "
1349                 "endianness (%s)"
1350                 % (
1351                     host_or_target_str.capitalize(),
1352                     language,
1353                     info.target_endianness or "unknown",
1354                     host_or_target_str,
1355                     host_or_target.endianness,
1356                 )
1357             )
1359         # Compiler version checks
1360         # ===================================================
1361         # Check the compiler version here instead of in `compiler_version` so
1362         # that the `checking` message doesn't pretend the compiler can be used
1363         # to then bail out one line later.
1364         if info.type == "gcc":
1365             if host_or_target.os == "Android":
1366                 raise FatalCheckError(
1367                     "GCC is not supported on Android.\n"
1368                     "Please use clang from the Android NDK instead."
1369                 )
1370             gcc_version = minimum_gcc_version()
1371             if info.version < gcc_version:
1372                 raise FatalCheckError(
1373                     "Only GCC %d.%d or newer is supported (found version %s)."
1374                     % (gcc_version.major, gcc_version.minor, info.version)
1375                 )
1377             # Force GCC to use the C++ headers from the sysroot, and to prefer the
1378             # sysroot system headers to /usr/include.
1379             # Non-Debian GCC also doesn't look at headers in multiarch directory.
1380             if sysroot.bootstrapped and sysroot.stdcxx_version:
1381                 version = sysroot.stdcxx_version
1382                 for path in (
1383                     "usr/include/c++/{}".format(version),
1384                     "usr/include/{}/c++/{}".format(multiarch_dir, version),
1385                     "usr/include/{}".format(multiarch_dir),
1386                     "usr/include",
1387                 ):
1388                     flags.extend(("-isystem", os.path.join(sysroot.path, path)))
1390         if info.type == "clang-cl":
1391             if info.version < "9.0.0":
1392                 raise FatalCheckError(
1393                     "Only clang-cl 9.0 or newer is supported (found version %s)"
1394                     % info.version
1395                 )
1396             if winsysroot and host.os != "WINNT":
1397                 overlay = os.path.join(winsysroot, "overlay.yaml")
1398                 if os.path.exists(overlay):
1399                     overlay_flags = ["-Xclang", "-ivfsoverlay", "-Xclang", overlay]
1400                     if info.version >= "16.0" or (
1401                         # clang-cl 15 normally doesn't support the root-relative
1402                         # overlay we use, but the bootstrapped clang-cl 15 is patched
1403                         # to support it, so check we're using a patched version.
1404                         info.version >= "15.0"
1405                         and try_preprocess(
1406                             configure_cache,
1407                             [compiler] + flags + overlay_flags,
1408                             language,
1409                             "",
1410                             onerror=lambda: False,
1411                             wrapper=wrapper,
1412                         )
1413                     ):
1414                         flags.extend(overlay_flags)
1416         if (info.type, host_or_target.abi) in (
1417             ("clang", "msvc"),
1418             ("clang-cl", "mingw"),
1419         ):
1420             raise FatalCheckError("Unknown compiler or compiler not supported.")
1422         # If you want to bump the version check here ensure the version
1423         # is known for Xcode in get_compiler_info.
1424         if info.type == "clang" and info.version < "8.0":
1425             raise FatalCheckError(
1426                 "Only clang/llvm 8.0 or newer is supported (found version %s)."
1427                 % info.version
1428             )
1430         if host_or_target.kernel == "WASI":
1431             if info.type != "clang":
1432                 raise FatalCheckError(
1433                     "Only clang is supported for %s" % host_or_target.alias
1434                 )
1435             if info.version < "8.0":
1436                 raise FatalCheckError(
1437                     "Only clang/llvm 8.0 or newer is supported for %s (found version %s)."
1438                     % (host_or_target.alias, info.version)
1439                 )
1441         if host_or_target.os == "Android":
1442             # Need at least clang 13 for compiler-rt/libunwind being the default.
1443             if info.type == "clang" and info.version < "13.0":
1444                 raise FatalCheckError(
1445                     "Only clang/llvm 13.0 or newer is supported for %s (found version %s)."
1446                     % (host_or_target.alias, info.version)
1447                 )
1449         if info.flags:
1450             raise FatalCheckError("Unknown compiler or compiler not supported.")
1452         return namespace(
1453             wrapper=wrapper,
1454             compiler=compiler,
1455             flags=flags,
1456             type=info.type,
1457             version=info.version,
1458             language=language,
1459         )
1461     @depends(valid_compiler)
1462     @checking("%s version" % what)
1463     def compiler_version(compiler):
1464         return compiler.version
1466     if language == "C++":
1468         @depends(valid_compiler, c_compiler)
1469         def valid_compiler(compiler, c_compiler):
1470             if compiler.type != c_compiler.type:
1471                 die(
1472                     "The %s C compiler is %s, while the %s C++ compiler is "
1473                     "%s. Need to use the same compiler suite.",
1474                     host_or_target_str,
1475                     c_compiler.type,
1476                     host_or_target_str,
1477                     compiler.type,
1478                 )
1480             if compiler.version != c_compiler.version:
1481                 die(
1482                     "The %s C compiler is version %s, while the %s C++ "
1483                     "compiler is version %s. Need to use the same compiler "
1484                     "version.",
1485                     host_or_target_str,
1486                     c_compiler.version,
1487                     host_or_target_str,
1488                     compiler.version,
1489                 )
1490             return compiler
1492     # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
1493     # and the flags that were part of the user input for those variables to
1494     # be provided.
1495     add_old_configure_assignment(
1496         var,
1497         depends_if(valid_compiler)(
1498             lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1499         ),
1500     )
1502     if host_or_target is target:
1503         add_old_configure_assignment(
1504             "ac_cv_prog_%s" % var,
1505             depends_if(valid_compiler)(
1506                 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1507             ),
1508         )
1509         # We check that it works in python configure already.
1510         add_old_configure_assignment("ac_cv_prog_%s_works" % var.lower(), "yes")
1511         add_old_configure_assignment(
1512             "ac_cv_prog_%s_cross" % var.lower(),
1513             depends(cross_compiling)(lambda x: "yes" if x else "no"),
1514         )
1515         gcc_like = depends(valid_compiler.type)(
1516             lambda x: "yes" if x in ("gcc", "clang") else "no"
1517         )
1518         add_old_configure_assignment("ac_cv_prog_%s_g" % var.lower(), gcc_like)
1519         if language == "C":
1520             add_old_configure_assignment("ac_cv_prog_gcc", gcc_like)
1521         if language == "C++":
1522             add_old_configure_assignment("ac_cv_prog_gxx", gcc_like)
1524     # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
1525     # old-configure to do some of its still existing checks.
1526     if language == "C":
1527         set_config("%s_TYPE" % var, valid_compiler.type)
1528         add_old_configure_assignment("%s_TYPE" % var, valid_compiler.type)
1529         set_config(
1530             "%s_VERSION" % var, depends(valid_compiler.version)(lambda v: str(v))
1531         )
1533     valid_compiler = compiler_class(valid_compiler, host_or_target)
1535     def compiler_error():
1536         raise FatalCheckError(
1537             "Failed compiling a simple %s source with %s" % (language, what)
1538         )
1540     valid_compiler.try_compile(check_msg="%s works" % what, onerror=compiler_error)
1542     set_config("%s_BASE_FLAGS" % var, valid_compiler.flags)
1544     # Set CPP/CXXCPP for both the build system and old-configure. We don't
1545     # need to check this works for preprocessing, because we already relied
1546     # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
1547     # in the first place.
1548     if host_or_target is target:
1549         pp_var = {
1550             "C": "CPP",
1551             "C++": "CXXCPP",
1552         }[language]
1554         preprocessor = depends_if(valid_compiler)(
1555             lambda x: list(x.wrapper) + [x.compiler, "-E"] + list(x.flags)
1556         )
1558         set_config(pp_var, preprocessor)
1559         add_old_configure_assignment(pp_var, preprocessor)
1561     if language == "C":
1562         linker_var = {
1563             target: "LD",
1564             host: "HOST_LD",
1565         }.get(host_or_target)
1567         if linker_var:
1569             @deprecated_option(env=linker_var, nargs=1)
1570             def linker(value):
1571                 if value:
1572                     return value[0]
1574             @depends(linker)
1575             def unused_linker(linker):
1576                 if linker:
1577                     log.warning(
1578                         "The value of %s is not used by this build system." % linker_var
1579                     )
1581     return valid_compiler
1584 c_compiler = compiler("C", target)
1585 cxx_compiler = compiler("C++", target, c_compiler=c_compiler)
1586 host_c_compiler = compiler("C", host, other_compiler=c_compiler)
1587 host_cxx_compiler = compiler(
1588     "C++",
1589     host,
1590     c_compiler=host_c_compiler,
1591     other_compiler=cxx_compiler,
1592     other_c_compiler=c_compiler,
1596 @template
1597 def windows_abi(host_or_target, c_compiler):
1598     @depends(host_or_target)
1599     def windows_abi(host_or_target):
1600         if host_or_target.os == "WINNT":
1601             return host_or_target.abi
1603     @depends(host_or_target, windows_abi)
1604     def need_windows_abi_from_compiler(host_or_target, windows_abi):
1605         return host_or_target.os == "WINNT" and windows_abi is None
1607     @depends(host_or_target, c_compiler, when=need_windows_abi_from_compiler)
1608     def windows_abi_from_compiler(host_or_target, c_compiler):
1609         if host_or_target.os == "WINNT":
1610             if c_compiler.type == "clang-cl":
1611                 return "msvc"
1612             return "mingw"
1614     return windows_abi | windows_abi_from_compiler
1617 target_windows_abi = windows_abi(target, c_compiler)
1618 host_windows_abi = windows_abi(host, host_c_compiler)
1621 # Generic compiler-based conditions.
1622 building_with_gcc = depends(c_compiler)(lambda info: info.type == "gcc")
1625 @depends(cxx_compiler, ccache_prefix)
1626 @imports("os")
1627 def cxx_is_icecream(info, ccache_prefix):
1628     if (
1629         os.path.islink(info.compiler)
1630         and os.path.basename(os.readlink(info.compiler)) == "icecc"
1631     ):
1632         return True
1633     if ccache_prefix and os.path.basename(ccache_prefix) == "icecc":
1634         return True
1637 set_config("CXX_IS_ICECREAM", cxx_is_icecream)
1640 # Linker detection
1641 # ==============================================================
1642 # The policy is as follows:
1643 # For Windows:
1644 # - the linker is picked via the LINKER environment variable per windows.configure,
1645 #   but ought to be lld-link in any case.
1646 # For macOS:
1647 # - the linker is lld if the clang used is >= 15 (per LLVM version, not Xcode version).
1648 # - the linker is also lld on local developer builds if the clang used is >= 13 (per LLVM
1649 #   version, not Xcode version)
1650 # - otherwise the linker is ld64, either from XCode on macOS, or from cctools-ports when
1651 #   cross-compiling.
1652 # For other OSes:
1653 # - on local developer builds: lld if present and the compiler is clang. Otherwise gold
1654 #   is used if present otherwise, whatever the compiler uses by default.
1655 # - on release/official builds: whatever the compiler uses by default, except when the
1656 #   compiler is clang, in which case lld is preferred when it's new enough.
1657 @template
1658 def is_not_winnt_or_sunos(host_or_target):
1659     @depends(host_or_target)
1660     def is_not_winnt_or_sunos(host_or_target):
1661         if host_or_target.kernel not in ("WINNT", "SunOS"):
1662             return True
1664     return is_not_winnt_or_sunos
1667 is_linker_option_enabled = is_not_winnt_or_sunos(target)
1670 @deprecated_option("--enable-gold", env="MOZ_FORCE_GOLD", when=is_linker_option_enabled)
1671 def enable_gold(value):
1672     if value:
1673         die("--enable-gold is deprecated, use --enable-linker=gold instead")
1674     else:
1675         die("--disable-gold is deprecated, use --enable-linker=something_else instead")
1678 option(
1679     "--enable-linker",
1680     nargs=1,
1681     help="Select the linker {bfd, gold, ld64, lld, lld-*, mold}",
1682     when=is_linker_option_enabled,
1686 # No-op to enable depending on --enable-linker from default_elfhack in
1687 # toolkit/moz.configure.
1688 @depends("--enable-linker", when=is_linker_option_enabled)
1689 def enable_linker(linker):
1690     return linker
1693 @template
1694 def select_linker_tmpl(host_or_target):
1695     if host_or_target is target:
1696         deps = depends(
1697             "--enable-linker",
1698             c_compiler,
1699             developer_options,
1700             extra_toolchain_flags,
1701             target,
1702             when=is_linker_option_enabled,
1703         )
1704         host_or_target_str = "target"
1705     else:
1706         deps = depends(
1707             dependable(None),
1708             host_c_compiler,
1709             developer_options,
1710             dependable(None),
1711             host,
1712             when=is_not_winnt_or_sunos(host_or_target),
1713         )
1714         host_or_target_str = "host"
1716     @deps
1717     @checking(f"for {host_or_target_str} linker", lambda x: x.KIND)
1718     @imports("os")
1719     @imports("shutil")
1720     def select_linker(linker, c_compiler, developer_options, toolchain_flags, target):
1721         if linker:
1722             linker = linker[0]
1723         else:
1724             linker = None
1726         def is_valid_linker(linker):
1727             if target.kernel == "Darwin":
1728                 valid_linkers = ("ld64", "lld")
1729             else:
1730                 valid_linkers = ("bfd", "gold", "lld", "mold")
1731             if linker in valid_linkers:
1732                 return True
1733             if "lld" in valid_linkers and linker.startswith("lld-"):
1734                 return True
1735             return False
1737         if linker and not is_valid_linker(linker):
1738             # Check that we are trying to use a supported linker
1739             die("Unsupported linker " + linker)
1741         # Check the kind of linker
1742         version_check = ["-Wl,--version"]
1743         cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
1745         def try_linker(linker):
1746             # Generate the compiler flag
1747             if linker == "ld64":
1748                 linker_flag = ["-fuse-ld=ld"]
1749             elif linker:
1750                 linker_flag = ["-fuse-ld=" + linker]
1751             else:
1752                 linker_flag = []
1753             cmd = cmd_base + linker_flag + version_check
1754             if toolchain_flags:
1755                 cmd += toolchain_flags
1757             # ld64 doesn't have anything to print out a version. It does print out
1758             # "ld64: For information on command line options please use 'man ld'."
1759             # but that would require doing two attempts, one with --version, that
1760             # would fail, and another with --help.
1761             # Instead, abuse its LD_PRINT_OPTIONS feature to detect a message
1762             # specific to it on stderr when it fails to process --version.
1763             env = dict(os.environ)
1764             env["LD_PRINT_OPTIONS"] = "1"
1765             # Some locales might not print out the strings we are looking for, so
1766             # ensure consistent output.
1767             env["LC_ALL"] = "C"
1768             retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
1769             if retcode == 1 and "Logging ld64 options" in stderr:
1770                 kind = "ld64"
1772             elif retcode != 0:
1773                 return None
1775             elif "mold" in stdout:
1776                 kind = "mold"
1778             elif "GNU ld" in stdout:
1779                 # We are using the normal linker
1780                 kind = "bfd"
1782             elif "GNU gold" in stdout:
1783                 kind = "gold"
1785             elif "LLD" in stdout:
1786                 kind = "lld"
1788             else:
1789                 kind = "unknown"
1791             if kind == "unknown" or is_valid_linker(kind):
1792                 return namespace(
1793                     KIND=kind,
1794                     LINKER_FLAG=linker_flag,
1795                 )
1797         result = None
1798         if linker:
1799             result = try_linker(linker)
1800             if result is None:
1801                 die("Could not use {} as linker".format(linker))
1803         if (
1804             result is None
1805             and c_compiler.type == "clang"
1806             and (
1807                 (
1808                     target.kernel != "Darwin"
1809                     and (
1810                         developer_options
1811                         or host_or_target_str == "host"
1812                         or c_compiler.version >= "15.0"
1813                     )
1814                 )
1815                 or (
1816                     target.kernel == "Darwin"
1817                     and (
1818                         (developer_options and c_compiler.version >= "13.0")
1819                         or c_compiler.version >= "15.0"
1820                     )
1821                 )
1822             )
1823         ):
1824             result = try_linker("lld")
1826         if result is None and developer_options:
1827             result = try_linker("gold")
1829         if result is None:
1830             result = try_linker(None)
1832         if result is None:
1833             die("Failed to find an adequate linker")
1835         # If an explicit linker was given, error out if what we found is different.
1836         if linker and not linker.startswith(result.KIND):
1837             die("Could not use {} as linker".format(linker))
1839         return result
1841     return select_linker
1844 select_linker = select_linker_tmpl(target)
1845 set_config("LINKER_KIND", select_linker.KIND)
1848 @template
1849 def linker_ldflags_tmpl(host_or_target):
1850     if host_or_target is target:
1851         deps = depends_if(
1852             select_linker,
1853             target,
1854             target_sysroot,
1855             target_multiarch_dir,
1856             android_sysroot,
1857             android_version,
1858             c_compiler,
1859             developer_options,
1860         )
1861     else:
1862         deps = depends_if(
1863             select_linker_tmpl(host),
1864             host,
1865             host_sysroot,
1866             host_multiarch_dir,
1867             dependable(None),
1868             dependable(None),
1869             host_c_compiler,
1870             developer_options,
1871         )
1873     @deps
1874     @imports("os")
1875     def linker_ldflags(
1876         linker,
1877         target,
1878         sysroot,
1879         multiarch_dir,
1880         android_sysroot,
1881         android_version,
1882         c_compiler,
1883         developer_options,
1884     ):
1885         flags = list((linker and linker.LINKER_FLAG) or [])
1886         # rpath-link is irrelevant to wasm, see for more info https://github.com/emscripten-core/emscripten/issues/11076.
1887         if sysroot.path and multiarch_dir and target.os != "WASI":
1888             for d in ("lib", "usr/lib"):
1889                 multiarch_lib_dir = os.path.join(sysroot.path, d, multiarch_dir)
1890                 if os.path.exists(multiarch_lib_dir):
1891                     # Non-Debian-patched binutils linkers (both BFD and gold) don't lookup
1892                     # in multi-arch directories.
1893                     flags.append("-Wl,-rpath-link,%s" % multiarch_lib_dir)
1894                     # GCC also needs -L.
1895                     if c_compiler.type == "gcc":
1896                         flags.append("-L%s" % multiarch_lib_dir)
1897             if (
1898                 c_compiler.type == "gcc"
1899                 and sysroot.bootstrapped
1900                 and sysroot.stdcxx_version
1901             ):
1902                 flags.append(
1903                     "-L{}/usr/lib/gcc/{}/{}".format(
1904                         sysroot.path, multiarch_dir, sysroot.stdcxx_version
1905                     )
1906                 )
1907         if android_sysroot:
1908             # BFD/gold linkers need a manual --rpath-link for indirect
1909             # dependencies.
1910             flags += [
1911                 "-Wl,--rpath-link={}/usr/lib/{}".format(
1912                     android_sysroot, target.toolchain
1913                 ),
1914                 "-Wl,--rpath-link={}/usr/lib/{}/{}".format(
1915                     android_sysroot, target.toolchain, android_version
1916                 ),
1917             ]
1918         if (
1919             developer_options
1920             and linker
1921             and linker.KIND == "lld"
1922             and target.kernel != "WINNT"
1923         ):
1924             flags.append("-Wl,-O0")
1925         return flags
1927     return linker_ldflags
1930 linker_ldflags = linker_ldflags_tmpl(target)
1931 add_old_configure_assignment("LINKER_LDFLAGS", linker_ldflags)
1933 host_linker_ldflags = linker_ldflags_tmpl(host)
1934 add_old_configure_assignment("HOST_LINKER_LDFLAGS", host_linker_ldflags)
1937 # There's a wrinkle with MinGW: linker configuration is not enabled, so
1938 # `select_linker` is never invoked.  Hard-code around it.
1939 @depends(select_linker, target, c_compiler)
1940 def gcc_use_gnu_ld(select_linker, target, c_compiler):
1941     if select_linker is not None and target.kernel != "Darwin":
1942         return select_linker.KIND in ("bfd", "gold", "lld", "mold")
1943     if target.kernel == "WINNT" and c_compiler.type == "clang":
1944         return True
1945     return None
1948 # GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld.
1949 set_config("GCC_USE_GNU_LD", gcc_use_gnu_ld)
1950 add_old_configure_assignment("GCC_USE_GNU_LD", gcc_use_gnu_ld)
1953 include("compile-checks.configure")
1954 include("arm.configure", when=depends(target.cpu)(lambda cpu: cpu == "arm"))
1957 @depends(
1958     have_64_bit,
1959     try_compile(
1960         body='static_assert(sizeof(void *) == 8, "")', check_msg="for 64-bit OS"
1961     ),
1963 def check_have_64_bit(have_64_bit, compiler_have_64_bit):
1964     if have_64_bit != compiler_have_64_bit:
1965         configure_error(
1966             "The target compiler does not agree with configure "
1967             "about the target bitness."
1968         )
1971 @depends(cxx_compiler, target)
1972 def needs_libstdcxx_newness_check(cxx_compiler, target):
1973     # We only have to care about this on Linux and MinGW.
1974     if cxx_compiler.type == "clang-cl":
1975         return
1977     if target.kernel not in ("Linux", "WINNT"):
1978         return
1980     if target.os == "Android":
1981         return
1983     return True
1986 def die_on_old_libstdcxx():
1987     die(
1988         "The libstdc++ in use is not new enough.  Please run "
1989         "./mach bootstrap to update your compiler, or update your system "
1990         "libstdc++ installation."
1991     )
1994 try_compile(
1995     includes=["cstddef"],
1996     body="\n".join(
1997         [
1998             # _GLIBCXX_RELEASE showed up in libstdc++ 7.
1999             "#if defined(__GLIBCXX__) && !defined(_GLIBCXX_RELEASE)",
2000             "#  error libstdc++ not new enough",
2001             "#endif",
2002             "#if defined(_GLIBCXX_RELEASE)",
2003             "#  if _GLIBCXX_RELEASE < %d" % minimum_gcc_version().major,
2004             "#    error libstdc++ not new enough",
2005             "#  else",
2006             "     (void) 0",
2007             "#  endif",
2008             "#endif",
2009         ]
2010     ),
2011     check_msg="for new enough STL headers from libstdc++",
2012     when=needs_libstdcxx_newness_check,
2013     onerror=die_on_old_libstdcxx,
2017 @depends(c_compiler, target)
2018 def default_debug_flags(compiler_info, target):
2019     # Debug info is ON by default.
2020     if compiler_info.type == "clang-cl":
2021         return "-Z7"
2022     elif target.kernel == "WINNT" and compiler_info.type == "clang":
2023         return "-g -gcodeview"
2024     # The oldest versions of supported compilers default to DWARF-4, but
2025     # newer versions may default to DWARF-5 or newer (e.g. clang 14), which
2026     # Valgrind doesn't support. Force-use DWARF-4.
2027     return "-gdwarf-4"
2030 option(env="MOZ_DEBUG_FLAGS", nargs=1, help="Debug compiler flags")
2032 imply_option("--enable-debug-symbols", depends_if("--enable-debug")(lambda v: v))
2034 option(
2035     "--disable-debug-symbols",
2036     nargs="?",
2037     help="Disable debug symbols using the given compiler flags",
2040 set_config("MOZ_DEBUG_SYMBOLS", depends_if("--enable-debug-symbols")(lambda _: True))
2043 @depends("MOZ_DEBUG_FLAGS", "--enable-debug-symbols", default_debug_flags)
2044 def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
2045     # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
2046     # --enable-debug-symbols takes precedence. Note, the value of
2047     # --enable-debug-symbols may be implied by --enable-debug.
2048     if len(enable_debug_flags):
2049         return enable_debug_flags[0]
2050     if env_debug_flags:
2051         return env_debug_flags[0]
2052     return default_debug_flags
2055 set_config("MOZ_DEBUG_FLAGS", debug_flags)
2056 add_old_configure_assignment("MOZ_DEBUG_FLAGS", debug_flags)
2059 @depends(c_compiler, host)
2060 @imports(
2061     _from="mach.logging", _import="enable_blessed", _as="_enable_ansi_escape_codes"
2063 def color_cflags(info, host):
2064     # We could test compiling with flags. By why incur the overhead when
2065     # color support should always be present in a specific toolchain
2066     # version?
2068     # Code for auto-adding this flag to compiler invocations needs to
2069     # determine if an existing flag isn't already present. That is likely
2070     # using exact string matching on the returned value. So if the return
2071     # value changes to e.g. "<x>=always", exact string match may fail and
2072     # multiple color flags could be added. So examine downstream consumers
2073     # before adding flags to return values.
2074     if info.type == "gcc":
2075         return "-fdiagnostics-color"
2076     elif info.type in ["clang", "clang-cl"]:
2077         if host.os == "WINNT" and _enable_ansi_escape_codes():
2078             return "-fcolor-diagnostics -fansi-escape-codes"
2079         else:
2080             return "-fcolor-diagnostics"
2081     else:
2082         return ""
2085 set_config("COLOR_CFLAGS", color_cflags)
2087 # Some standard library headers (notably bionic on Android) declare standard
2088 # functions (e.g. getchar()) and also #define macros for those standard
2089 # functions.  libc++ deals with this by doing something like the following
2090 # (explanatory comments added):
2092 #   #ifdef FUNC
2093 #   // Capture the definition of FUNC.
2094 #   inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
2095 #   #undef FUNC
2096 #   // Use a real inline definition.
2097 #   inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
2098 #   #endif
2100 # _LIBCPP_INLINE_VISIBILITY is typically defined as:
2102 #   __attribute__((__visibility__("hidden"), __always_inline__))
2104 # Unfortunately, this interacts badly with our system header wrappers, as the:
2106 #   #pragma GCC visibility push(default)
2108 # that they do prior to including the actual system header is treated by the
2109 # compiler as an explicit declaration of visibility on every function declared
2110 # in the header.  Therefore, when the libc++ code above is encountered, it is
2111 # as though the compiler has effectively seen:
2113 #   int FUNC(...) __attribute__((__visibility__("default")));
2114 #   int FUNC(...) __attribute__((__visibility__("hidden")));
2116 # and the compiler complains about the mismatched visibility declarations.
2118 # However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
2119 # existing definition.  We can therefore define it to the empty string (since
2120 # we are properly managing visibility ourselves) and avoid this whole mess.
2121 # Note that we don't need to do this with gcc, as libc++ detects gcc and
2122 # effectively does the same thing we are doing here.
2124 # _LIBCPP_ALWAYS_INLINE needs a similar workarounds, since it too declares
2125 # hidden visibility.
2127 # _LIBCPP_HIDE_FROM_ABI is a macro in libc++ versions in NDKs >=r19.  It too
2128 # declares hidden visibility, but it also declares functions as excluded from
2129 # explicit instantiation (roughly: the function can be unused in the current
2130 # compilation, but does not then trigger an actual definition of the function;
2131 # it is assumed the real definition comes from elsewhere).  We need to replicate
2132 # this setup.
2135 @depends(c_compiler, target)
2136 def libcxx_override_visibility(c_compiler, target):
2137     if c_compiler.type == "clang" and target.os == "Android":
2138         return namespace(
2139             empty="",
2140             hide_from_abi="__attribute__((__exclude_from_explicit_instantiation__))",
2141         )
2144 set_define("_LIBCPP_INLINE_VISIBILITY", libcxx_override_visibility.empty)
2145 set_define("_LIBCPP_ALWAYS_INLINE", libcxx_override_visibility.empty)
2147 set_define("_LIBCPP_HIDE_FROM_ABI", libcxx_override_visibility.hide_from_abi)
2150 @depends(target, build_environment)
2151 def visibility_flags(target, env):
2152     if target.os != "WINNT":
2153         if target.kernel == "Darwin":
2154             return ("-fvisibility=hidden", "-fvisibility-inlines-hidden")
2155         return (
2156             "-I%s/system_wrappers" % os.path.join(env.dist),
2157             "-include",
2158             "%s/config/gcc_hidden.h" % env.topsrcdir,
2159         )
2162 @depends(target, visibility_flags)
2163 def wrap_system_includes(target, visibility_flags):
2164     if visibility_flags and target.kernel != "Darwin":
2165         return True
2168 set_define(
2169     "HAVE_VISIBILITY_HIDDEN_ATTRIBUTE",
2170     depends(visibility_flags)(lambda v: bool(v) or None),
2172 set_define(
2173     "HAVE_VISIBILITY_ATTRIBUTE", depends(visibility_flags)(lambda v: bool(v) or None)
2175 set_config("WRAP_SYSTEM_INCLUDES", wrap_system_includes)
2176 set_config("VISIBILITY_FLAGS", visibility_flags)
2179 @template
2180 def depend_cflags(host_or_target_c_compiler):
2181     @depends(host_or_target_c_compiler)
2182     def depend_cflags(host_or_target_c_compiler):
2183         if host_or_target_c_compiler.type != "clang-cl":
2184             return ["-MD", "-MP", "-MF $(MDDEPDIR)/$(@F).pp"]
2185         else:
2186             # clang-cl doesn't accept the normal -MD -MP -MF options that clang
2187             # does, but the underlying cc1 binary understands how to generate
2188             # dependency files.  These options are based on analyzing what the
2189             # normal clang driver sends to cc1 when given the "correct"
2190             # dependency options.
2191             return [
2192                 "-Xclang",
2193                 "-MP",
2194                 "-Xclang",
2195                 "-dependency-file",
2196                 "-Xclang",
2197                 "$(MDDEPDIR)/$(@F).pp",
2198                 "-Xclang",
2199                 "-MT",
2200                 "-Xclang",
2201                 "$@",
2202             ]
2204     return depend_cflags
2207 set_config("_DEPEND_CFLAGS", depend_cflags(c_compiler))
2208 set_config("_HOST_DEPEND_CFLAGS", depend_cflags(host_c_compiler))
2211 @depends(c_compiler)
2212 def preprocess_option(compiler):
2213     # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
2214     if compiler.type in ("gcc", "clang"):
2215         return "-E -o "
2216     else:
2217         return "-P -Fi"
2220 set_config("PREPROCESS_OPTION", preprocess_option)
2223 # We only want to include windows.configure when we are compiling on
2224 # Windows, or for Windows.
2225 include("windows.configure", when=is_windows)
2228 # On Power ISA, determine compiler flags for VMX, VSX and VSX-3.
2230 set_config(
2231     "PPC_VMX_FLAGS",
2232     ["-maltivec"],
2233     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2236 set_config(
2237     "PPC_VSX_FLAGS",
2238     ["-mvsx"],
2239     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2242 set_config(
2243     "PPC_VSX3_FLAGS",
2244     ["-mvsx", "-mcpu=power9"],
2245     when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2248 # ASAN
2249 # ==============================================================
2251 option("--enable-address-sanitizer", help="Enable Address Sanitizer")
2254 @depends(when="--enable-address-sanitizer")
2255 def asan():
2256     return True
2259 add_old_configure_assignment("MOZ_ASAN", asan)
2261 # MSAN
2262 # ==============================================================
2264 option("--enable-memory-sanitizer", help="Enable Memory Sanitizer")
2267 @depends(when="--enable-memory-sanitizer")
2268 def msan():
2269     return True
2272 add_old_configure_assignment("MOZ_MSAN", msan)
2274 # TSAN
2275 # ==============================================================
2277 option("--enable-thread-sanitizer", help="Enable Thread Sanitizer")
2280 @depends(when="--enable-thread-sanitizer")
2281 def tsan():
2282     return True
2285 add_old_configure_assignment("MOZ_TSAN", tsan)
2287 # UBSAN
2288 # ==============================================================
2290 option(
2291     "--enable-undefined-sanitizer", nargs="*", help="Enable UndefinedBehavior Sanitizer"
2295 @depends_if("--enable-undefined-sanitizer")
2296 def ubsan(options):
2297     default_checks = [
2298         "bool",
2299         "bounds",
2300         "enum",
2301         "function",
2302         "integer-divide-by-zero",
2303         "object-size",
2304         "pointer-overflow",
2305         "return",
2306         "vla-bound",
2307     ]
2309     checks = options if len(options) else default_checks
2311     return ",".join(checks)
2314 add_old_configure_assignment("MOZ_UBSAN_CHECKS", ubsan)
2317 option(
2318     "--enable-signed-overflow-sanitizer",
2319     help="Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)",
2323 @depends(when="--enable-signed-overflow-sanitizer")
2324 def ub_signed_overflow_san():
2325     return True
2328 add_old_configure_assignment("MOZ_SIGNED_OVERFLOW_SANITIZE", ub_signed_overflow_san)
2331 option(
2332     "--enable-unsigned-overflow-sanitizer",
2333     help="Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)",
2337 @depends(when="--enable-unsigned-overflow-sanitizer")
2338 def ub_unsigned_overflow_san():
2339     return True
2342 add_old_configure_assignment("MOZ_UNSIGNED_OVERFLOW_SANITIZE", ub_unsigned_overflow_san)
2345 # Security Hardening
2346 # ==============================================================
2348 option(
2349     "--enable-hardening",
2350     env="MOZ_SECURITY_HARDENING",
2351     help="Enables security hardening compiler options",
2355 # This function is a bit confusing. It adds or removes hardening flags in
2356 # three stuations: if --enable-hardening is passed; if --disable-hardening
2357 # is passed, and if no flag is passed.
2359 # At time of this comment writing, all flags are actually added in the
2360 # default no-flag case; making --enable-hardening the same as omitting the
2361 # flag. --disable-hardening will omit the security flags. (However, not all
2362 # possible security flags will be omitted by --disable-hardening, as many are
2363 # compiler-default options we do not explicitly enable.)
2364 @depends(
2365     "--enable-hardening",
2366     "--enable-address-sanitizer",
2367     "--enable-debug",
2368     "--enable-optimize",
2369     c_compiler,
2370     target,
2372 def security_hardening_cflags(
2373     hardening_flag, asan, debug, optimize, c_compiler, target
2375     compiler_is_gccish = c_compiler.type in ("gcc", "clang")
2376     mingw_clang = c_compiler.type == "clang" and target.os == "WINNT"
2378     flags = []
2379     ldflags = []
2380     trivial_auto_var_init = []
2382     # WASI compiler doesn't support security hardening cflags
2383     if target.os == "WASI":
2384         return
2386     # ----------------------------------------------------------
2387     # If hardening is explicitly enabled, or not explicitly disabled
2388     if hardening_flag.origin == "default" or hardening_flag:
2389         # FORTIFY_SOURCE ------------------------------------
2390         # Require optimization for FORTIFY_SOURCE. See Bug 1417452
2391         # Also, undefine it before defining it just in case a distro adds it, see Bug 1418398
2392         if compiler_is_gccish and optimize and not asan:
2393             flags.append("-U_FORTIFY_SOURCE")
2394             flags.append("-D_FORTIFY_SOURCE=2")
2396         # fstack-protector ------------------------------------
2397         # Enable only if hardening is not disabled and ASAN is
2398         # not on as ASAN will catch the crashes for us
2399         if compiler_is_gccish and not asan:
2400             flags.append("-fstack-protector-strong")
2401             ldflags.append("-fstack-protector-strong")
2403             if (
2404                 c_compiler.type == "clang"
2405                 and c_compiler.version >= "11.0.1"
2406                 and target.os not in ("WINNT", "OSX", "OpenBSD")
2407                 and target.cpu in ("x86", "x86_64", "ppc64", "s390x")
2408             ):
2409                 flags.append("-fstack-clash-protection")
2410                 ldflags.append("-fstack-clash-protection")
2412         # ftrivial-auto-var-init ------------------------------
2413         # Initialize local variables with a 0xAA pattern in clang builds.
2414         # Linux32 fails some xpcshell tests with -ftrivial-auto-var-init
2415         linux32 = target.kernel == "Linux" and target.cpu == "x86"
2416         if (
2417             (c_compiler.type == "clang" or c_compiler.type == "clang-cl")
2418             and c_compiler.version >= "8"
2419             and not linux32
2420         ):
2421             if c_compiler.type == "clang-cl":
2422                 trivial_auto_var_init.append("-Xclang")
2423             trivial_auto_var_init.append("-ftrivial-auto-var-init=pattern")
2424             # Always enable on debug builds.
2425             if debug:
2426                 flags.extend(trivial_auto_var_init)
2428         # ASLR ------------------------------------------------
2429         # ASLR (dynamicbase) is enabled by default in clang-cl; but the
2430         # mingw-clang build requires it to be explicitly enabled
2431         if mingw_clang:
2432             ldflags.append("-Wl,--dynamicbase")
2434         # Control Flow Guard (CFG) ----------------------------
2435         if (
2436             c_compiler.type == "clang-cl"
2437             and c_compiler.version >= "8"
2438             and (target.cpu != "aarch64" or c_compiler.version >= "8.0.1")
2439         ):
2440             if target.cpu == "aarch64" and c_compiler.version >= "10.0.0":
2441                 # The added checks in clang 10 make arm64 builds crash. (Bug 1639318)
2442                 flags.append("-guard:cf,nochecks")
2443             else:
2444                 flags.append("-guard:cf")
2445             # nolongjmp is needed because clang doesn't emit the CFG tables of
2446             # setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057
2447             ldflags.append("-guard:cf,nolongjmp")
2449     # ----------------------------------------------------------
2450     # If ASAN _is_ on, disable FORTIFY_SOURCE just to be safe
2451     if asan:
2452         flags.append("-D_FORTIFY_SOURCE=0")
2454     # fno-common -----------------------------------------
2455     # Do not merge variables for ASAN; can detect some subtle bugs
2456     if asan:
2457         # clang-cl does not recognize the flag, it must be passed down to clang
2458         if c_compiler.type == "clang-cl":
2459             flags.append("-Xclang")
2460         flags.append("-fno-common")
2462     return namespace(
2463         flags=flags,
2464         ldflags=ldflags,
2465         trivial_auto_var_init=trivial_auto_var_init,
2466     )
2469 set_config("MOZ_HARDENING_CFLAGS", security_hardening_cflags.flags)
2470 set_config("MOZ_HARDENING_LDFLAGS", security_hardening_cflags.ldflags)
2471 set_config(
2472     "MOZ_TRIVIAL_AUTO_VAR_INIT",
2473     security_hardening_cflags.trivial_auto_var_init,
2477 # Intel Control-flow Enforcement Technology
2478 # ==============================================================
2479 # We keep this separate from the hardening flags above, because we want to be
2480 # able to easily remove the flags in the build files for certain executables.
2481 @depends(c_compiler, target)
2482 def cet_ldflags(c_compiler, target):
2483     ldflags = []
2484     if (
2485         c_compiler.type == "clang-cl"
2486         and c_compiler.version >= "11"
2487         and target.cpu == "x86_64"
2488     ):
2489         ldflags.append("-CETCOMPAT")
2490     return ldflags
2493 set_config("MOZ_CETCOMPAT_LDFLAGS", cet_ldflags)
2496 # Frame pointers
2497 # ==============================================================
2498 @depends(c_compiler)
2499 def frame_pointer_flags(compiler):
2500     if compiler.type == "clang-cl":
2501         return namespace(
2502             enable=["-Oy-"],
2503             disable=["-Oy"],
2504         )
2505     return namespace(
2506         enable=["-fno-omit-frame-pointer", "-funwind-tables"],
2507         disable=["-fomit-frame-pointer", "-funwind-tables"],
2508     )
2511 @depends(
2512     moz_optimize.optimize,
2513     moz_debug,
2514     target,
2515     "--enable-memory-sanitizer",
2516     "--enable-address-sanitizer",
2517     "--enable-undefined-sanitizer",
2519 def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
2520     return bool(
2521         not optimize
2522         or debug
2523         or msan
2524         or asan
2525         or ubsan
2526         or (target.os == "WINNT" and target.cpu in ("x86", "aarch64"))
2527         or target.os == "OSX"
2528     )
2531 option(
2532     "--enable-frame-pointers",
2533     default=frame_pointer_default,
2534     help="{Enable|Disable} frame pointers",
2538 @depends("--enable-frame-pointers", frame_pointer_flags)
2539 def frame_pointer_flags(enable, flags):
2540     if enable:
2541         return flags.enable
2542     return flags.disable
2545 set_config("MOZ_FRAMEPTR_FLAGS", frame_pointer_flags)
2548 # Stack unwinding without frame pointers
2549 # ==============================================================
2552 have_unwind = check_symbol(
2553     "_Unwind_Backtrace", when=check_header("unwind.h", when=target_is_unix)
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)
2791 # Libstdc++ compatibility hacks
2792 # ==============================================================
2794 @depends(target, host)
2795 def target_or_host_is_linux(target, host):
2796     return any(t.os == "GNU" and t.kernel == "Linux" for t in (target, host))
2799 option(
2800     "--enable-stdcxx-compat",
2801     env="MOZ_STDCXX_COMPAT",
2802     help="Enable compatibility with older libstdc++",
2803     when=target_or_host_is_linux,
2807 @depends("--enable-stdcxx-compat", when=target_or_host_is_linux)
2808 def stdcxx_compat(value):
2809     if value:
2810         return True
2813 set_config("MOZ_STDCXX_COMPAT", True, when=stdcxx_compat)
2814 add_flag(
2815     "-D_GLIBCXX_USE_CXX11_ABI=0",
2816     cxx_compiler,
2817     when=stdcxx_compat,
2819 add_flag(
2820     "-D_GLIBCXX_USE_CXX11_ABI=0",
2821     host_cxx_compiler,
2822     when=stdcxx_compat,
2826 # Support various fuzzing options
2827 # ==============================================================
2828 option("--enable-fuzzing", help="Enable fuzzing support")
2831 @depends(build_project)
2832 def js_build(build_project):
2833     return build_project == "js"
2836 option(
2837     "--enable-js-fuzzilli",
2838     when=js_build,
2839     help="Enable fuzzilli support for the JS engine",
2843 option(
2844     "--enable-snapshot-fuzzing",
2845     help="Enable experimental snapshot fuzzing support",
2849 imply_option("--enable-fuzzing", True, when="--enable-snapshot-fuzzing")
2852 @depends("--enable-snapshot-fuzzing")
2853 def enable_snapshot_fuzzing(value):
2854     if value:
2855         return True
2858 @depends("--enable-fuzzing", enable_snapshot_fuzzing)
2859 def enable_fuzzing(value, snapshot_fuzzing):
2860     if value or snapshot_fuzzing:
2861         return True
2864 @depends("--enable-js-fuzzilli", when=js_build)
2865 def enable_js_fuzzilli(value):
2866     if value:
2867         return True
2870 @depends(enable_fuzzing, enable_snapshot_fuzzing)
2871 def check_aflfuzzer(fuzzing, snapshot_fuzzing):
2872     if fuzzing and not snapshot_fuzzing:
2873         return True
2876 @depends(
2877     try_compile(
2878         body="__AFL_COMPILER;", check_msg="for AFL compiler", when=check_aflfuzzer
2879     )
2881 def enable_aflfuzzer(afl):
2882     if afl:
2883         return True
2886 @depends(enable_fuzzing, enable_aflfuzzer, enable_snapshot_fuzzing, c_compiler, target)
2887 def enable_libfuzzer(fuzzing, afl, snapshot_fuzzing, c_compiler, target):
2888     if (
2889         fuzzing
2890         and not afl
2891         and not snapshot_fuzzing
2892         and c_compiler.type == "clang"
2893         and target.os != "Android"
2894     ):
2895         return True
2898 @depends(enable_fuzzing, enable_aflfuzzer, enable_libfuzzer, enable_js_fuzzilli)
2899 def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer, enable_js_fuzzilli):
2900     if fuzzing and (afl or libfuzzer) and not enable_js_fuzzilli:
2901         return True
2904 set_config("FUZZING", enable_fuzzing)
2905 set_define("FUZZING", enable_fuzzing)
2907 set_config("LIBFUZZER", enable_libfuzzer)
2908 set_define("LIBFUZZER", enable_libfuzzer)
2909 add_old_configure_assignment("LIBFUZZER", enable_libfuzzer)
2911 set_config("AFLFUZZ", enable_aflfuzzer)
2912 set_define("AFLFUZZ", enable_aflfuzzer)
2914 set_config("FUZZING_INTERFACES", enable_fuzzing_interfaces)
2915 set_define("FUZZING_INTERFACES", enable_fuzzing_interfaces)
2916 add_old_configure_assignment("FUZZING_INTERFACES", enable_fuzzing_interfaces)
2918 set_config("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
2919 set_define("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
2921 set_config("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
2922 set_define("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
2925 @depends(
2926     c_compiler.try_compile(
2927         flags=["-fsanitize=fuzzer-no-link"],
2928         when=enable_fuzzing,
2929         check_msg="whether the C compiler supports -fsanitize=fuzzer-no-link",
2930     ),
2931     tsan,
2932     enable_js_fuzzilli,
2934 def libfuzzer_flags(value, tsan, enable_js_fuzzilli):
2935     if tsan:
2936         # With ThreadSanitizer, we should not use any libFuzzer instrumentation because
2937         # it is incompatible (e.g. there are races on global sanitizer coverage counters).
2938         # Instead we use an empty set of flags here but still build the fuzzing targets.
2939         # With this setup, we can still run files through these targets in TSan builds,
2940         # e.g. those obtained from regular fuzzing.
2941         # This code can be removed once libFuzzer has been made compatible with TSan.
2942         #
2943         # Also, this code needs to be kept in sync with certain gyp files, currently:
2944         #   - dom/media/webrtc/transport/third_party/nICEr/nicer.gyp
2945         return namespace(no_link_flag_supported=False, use_flags=[])
2947     if enable_js_fuzzilli:
2948         # Fuzzilli comes with its own trace-pc interceptors and flag requirements.
2949         no_link_flag_supported = False
2950         use_flags = ["-fsanitize-coverage=trace-pc-guard", "-g"]
2951     elif value:
2952         no_link_flag_supported = True
2953         # recommended for (and only supported by) clang >= 6
2954         use_flags = ["-fsanitize=fuzzer-no-link"]
2955     else:
2956         no_link_flag_supported = False
2957         use_flags = ["-fsanitize-coverage=trace-pc-guard,trace-cmp"]
2959     return namespace(
2960         no_link_flag_supported=no_link_flag_supported,
2961         use_flags=use_flags,
2962     )
2965 set_config("HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK", libfuzzer_flags.no_link_flag_supported)
2966 set_config("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
2967 add_old_configure_assignment("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
2969 # Shared library building
2970 # ==============================================================
2973 # XXX: The use of makefile constructs in these variables is awful.
2974 @depends(target, c_compiler)
2975 def make_shared_library(target, compiler):
2976     if target.os == "WINNT":
2977         if compiler.type == "gcc":
2978             return namespace(
2979                 mkshlib=["$(CXX)", "$(DSO_LDOPTS)", "-o", "$@"],
2980                 mkcshlib=["$(CC)", "$(DSO_LDOPTS)", "-o", "$@"],
2981             )
2982         elif compiler.type == "clang":
2983             return namespace(
2984                 mkshlib=[
2985                     "$(CXX)",
2986                     "$(DSO_LDOPTS)",
2987                     "-Wl,-pdb,$(LINK_PDBFILE)",
2988                     "-o",
2989                     "$@",
2990                 ],
2991                 mkcshlib=[
2992                     "$(CC)",
2993                     "$(DSO_LDOPTS)",
2994                     "-Wl,-pdb,$(LINK_PDBFILE)",
2995                     "-o",
2996                     "$@",
2997                 ],
2998             )
2999         else:
3000             linker = [
3001                 "$(LINKER)",
3002                 "-NOLOGO",
3003                 "-DLL",
3004                 "-OUT:$@",
3005                 "-PDB:$(LINK_PDBFILE)",
3006                 "$(DSO_LDOPTS)",
3007             ]
3008             return namespace(
3009                 mkshlib=linker,
3010                 mkcshlib=linker,
3011             )
3013     cc = ["$(CC)", "$(COMPUTED_C_LDFLAGS)"]
3014     cxx = ["$(CXX)", "$(COMPUTED_CXX_LDFLAGS)"]
3015     flags = ["$(DSO_LDOPTS)"]
3016     output = ["-o", "$@"]
3018     if target.kernel == "Darwin":
3019         soname = []
3020     elif target.os == "NetBSD":
3021         soname = ["-Wl,-soname,$(DSO_SONAME)"]
3022     else:
3023         assert compiler.type in ("gcc", "clang")
3025         soname = ["-Wl,-h,$(DSO_SONAME)"]
3027     return namespace(
3028         mkshlib=cxx + flags + soname + output,
3029         mkcshlib=cc + flags + soname + output,
3030     )
3033 set_config("MKSHLIB", make_shared_library.mkshlib)
3034 set_config("MKCSHLIB", make_shared_library.mkcshlib)
3037 @depends(c_compiler, toolchain_prefix, when=target_is_windows)
3038 def rc_names(c_compiler, toolchain_prefix):
3039     if c_compiler.type in ("gcc", "clang"):
3040         return tuple("%s%s" % (p, "windres") for p in ("",) + (toolchain_prefix or ()))
3041     return ("llvm-rc",)
3044 check_prog("RC", rc_names, paths=clang_search_path, when=target_is_windows)
3047 @template
3048 def ar_config(c_compiler, toolchain_prefix=None):
3049     if not toolchain_prefix:
3050         toolchain_prefix = dependable(None)
3052     @depends(toolchain_prefix, c_compiler)
3053     def ar_config(toolchain_prefix, c_compiler):
3054         if c_compiler.type == "clang-cl":
3055             return namespace(
3056                 names=("llvm-lib",),
3057                 flags=("-llvmlibthin", "-out:$@"),
3058             )
3060         names = tuple("%s%s" % (p, "ar") for p in (toolchain_prefix or ()) + ("",))
3061         if c_compiler.type == "clang":
3062             # Get the llvm-ar path as per the output from clang --print-prog-name=llvm-ar
3063             # so that we directly get the one under the clang directory, rather than one
3064             # that might be in /usr/bin and that might point to one from a different version
3065             # of clang.
3066             out = check_cmd_output(
3067                 c_compiler.compiler, "--print-prog-name=llvm-ar", onerror=lambda: None
3068             )
3069             llvm_ar = out.rstrip() if out else "llvm-ar"
3070             names = (llvm_ar,) + names
3072         return namespace(
3073             names=names,
3074             flags=("crs", "$@"),
3075         )
3077     return ar_config
3080 target_ar_config = ar_config(c_compiler, toolchain_prefix)
3082 target_ar = check_prog("AR", target_ar_config.names, paths=clang_search_path)
3084 set_config("AR_FLAGS", target_ar_config.flags)
3087 @depends(c_compiler, extra_toolchain_flags, target_ar, target_ar_config)
3088 @checking("whether ar supports response files")
3089 @imports("os")
3090 @imports(_from="tempfile", _import="mkstemp")
3091 @imports(_from="__builtin__", _import="FileNotFoundError")
3092 @imports(_from="mozbuild.configure.util", _import="LineIO")
3093 def ar_supports_response_files(c_compiler, extra_toolchain_flags, ar, ar_config):
3094     lib_path = list_path = None
3095     try:
3096         fd, obj_path = mkstemp(prefix="conftest.", suffix=".o")
3097         os.close(fd)
3098         if (
3099             try_invoke_compiler(
3100                 # No configure_cache because it would not create the
3101                 # expected output file.
3102                 None,
3103                 [c_compiler.compiler] + c_compiler.flags,
3104                 c_compiler.language,
3105                 "void foo() {}",
3106                 ["-c", "-o", obj_path] + (extra_toolchain_flags or []),
3107                 wrapper=c_compiler.wrapper,
3108                 onerror=lambda: None,
3109             )
3110             is not None
3111         ):
3112             fd, list_path = mkstemp(prefix="conftest.", suffix=".list")
3113             with os.fdopen(fd, "w") as list:
3114                 list.write(obj_path)
3115                 log.debug("Creating `%s` with content:", list_path)
3116                 log.debug("| %s", obj_path)
3117             fd, lib_path = mkstemp(prefix="conftest.", suffix=".a")
3118             os.close(fd)
3119             os.remove(lib_path)
3120             ar_command = (
3121                 [ar]
3122                 + [x.replace("$@", lib_path) for x in ar_config.flags]
3123                 + ["@" + list_path]
3124             )
3125             result = check_cmd_output(*ar_command, onerror=lambda: None)
3126             return result is not None
3127     finally:
3128         for cleanup_path in (obj_path, list_path, lib_path):
3129             if cleanup_path:
3130                 try:
3131                     os.remove(cleanup_path)
3132                 except FileNotFoundError:
3133                     pass
3136 set_config("AR_SUPPORTS_RESPONSE_FILE", True, when=ar_supports_response_files)
3138 host_ar_config = ar_config(host_c_compiler)
3140 check_prog("HOST_AR", host_ar_config.names, paths=clang_search_path)
3143 @depends(toolchain_prefix, c_compiler)
3144 def nm_names(toolchain_prefix, c_compiler):
3145     names = tuple("%s%s" % (p, "nm") for p in (toolchain_prefix or ()) + ("",))
3146     if c_compiler.type == "clang":
3147         # Get the llvm-nm path as per the output from clang --print-prog-name=llvm-nm
3148         # so that we directly get the one under the clang directory, rather than one
3149         # that might be in /usr/bin and that might point to one from a different version
3150         # of clang.
3151         out = check_cmd_output(
3152             c_compiler.compiler, "--print-prog-name=llvm-nm", onerror=lambda: None
3153         )
3154         llvm_nm = out.rstrip() if out else "llvm-nm"
3155         names = (llvm_nm,) + names
3157     return names
3160 check_prog("NM", nm_names, paths=clang_search_path, when=target_has_linux_kernel)
3163 option("--enable-cpp-rtti", help="Enable C++ RTTI")
3165 add_old_configure_assignment("_MOZ_USE_RTTI", "1", when="--enable-cpp-rtti")
3168 option(
3169     "--enable-path-remapping",
3170     nargs="*",
3171     choices=("c", "rust"),
3172     help="Enable remapping source and object paths in compiled outputs.",
3176 @depends("--enable-path-remapping")
3177 def path_remapping(value):
3178     if len(value):
3179         return value
3180     if bool(value):
3181         return ["c", "rust"]
3182     return []
3185 @depends(
3186     target,
3187     build_environment,
3188     target_sysroot.path,
3189     valid_windows_sdk_dir,
3190     vc_path,
3191     when="--enable-path-remapping",
3193 def path_remappings(target, build_env, sysroot_path, windows_sdk_dir, vc_path):
3194     win = target.kernel == "WINNT"
3196     # The prefix maps are processed in the order they're specified on the
3197     # command line.  Therefore, to accommodate object directories in the source
3198     # directory, it's important that we map the topobjdir before the topsrcdir,
3199     # 'cuz we might have /src/obj/=/o/ and /src/=/s/.  The various other
3200     # directories might be subdirectories of topsrcdir as well, so they come
3201     # earlier still.
3203     path_remappings = []
3205     # We will have only one sysroot or SDK, so all can have the same mnemonic: K
3206     # for "kit" (since S is taken for "source").  See
3207     # https://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html
3208     # for how to use the Windows `subst` command to map these in debuggers and
3209     # IDEs.
3210     if sysroot_path:
3211         path_remappings.append((sysroot_path, "k:/" if win else "/sysroot/"))
3212     if windows_sdk_dir:
3213         path_remappings.append(
3214             (windows_sdk_dir.path, "k:/" if win else "/windows_sdk/")
3215         )
3216     if vc_path:
3217         path_remappings.append((vc_path, "v:/" if win else "/vc/"))
3219     path_remappings += [
3220         (build_env.topobjdir, "o:/" if win else "/topobjdir/"),
3221         (build_env.topsrcdir, "s:/" if win else "/topsrcdir/"),
3222     ]
3224     path_remappings = [
3225         (normsep(old).rstrip("/") + "/", new) for old, new in path_remappings
3226     ]
3228     # It is tempting to sort these, but we want the order to be the same across
3229     # machines so that we can share cache hits.  Therefore we reject bad
3230     # configurations rather than trying to make the configuration good.
3231     for i in range(len(path_remappings) - 1):
3232         p = path_remappings[i][0]
3233         for q, _ in path_remappings[i + 1 :]:
3234             if q.startswith(p):
3235                 die(f"Cannot remap paths because {p} is an ancestor of {q}")
3237     return path_remappings
3240 set_config("MMX_FLAGS", ["-mmmx"])
3241 set_config("SSE_FLAGS", ["-msse"])
3242 set_config("SSE2_FLAGS", ["-msse2"])
3243 set_config("SSSE3_FLAGS", ["-mssse3"])
3244 set_config("SSE4_2_FLAGS", ["-msse4.2"])
3245 set_config("FMA_FLAGS", ["-mfma"])
3246 set_config("AVX2_FLAGS", ["-mavx2"])