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/.
8 # ==============================================================
10 option("--disable-optimize", nargs="?", help="Disable optimizations via compiler flags")
13 @depends("--enable-optimize")
14 def moz_optimize(option):
34 set_config("MOZ_OPTIMIZE", moz_optimize.optimize)
35 add_old_configure_assignment("MOZ_OPTIMIZE", moz_optimize.optimize)
36 add_old_configure_assignment("MOZ_CONFIGURE_OPTIMIZE_FLAGS", moz_optimize.flags)
39 # ==============================================================
42 @depends("--disable-compile-environment", target)
43 def compiling_android(compile_env, target):
44 return compile_env and target.os == "Android"
47 include("android-ndk.configure", when=compiling_android)
49 with only_when(target_is_osx):
50 # MacOS deployment target version
51 # ==============================================================
52 # This needs to happen before any compilation test is done.
55 "--enable-macos-target",
56 env="MACOSX_DEPLOYMENT_TARGET",
58 default=depends(target, developer_options)
59 # We continue to target 10.15 on Intel, but can target 11.0 for
60 # aarch64 since the earliest hardware was released alongside 11.0.
61 # For local builds, we want to target 10.15 regardless of the
62 # underlying platform to catch any errors or warnings that wouldn't
63 # show up when targeting 11.0, since these would later show up on
64 # CI for Intel builds.
65 (lambda t, d: "11.0" if (t.cpu == "aarch64" and not d) else "10.15"),
66 help="Set the minimum MacOS version needed at runtime{|}",
69 @depends_if("--enable-macos-target", developer_options)
70 def macos_target(value, _):
75 @imports(_from="__builtin__", _import="open")
76 @imports(_from="__builtin__", _import="Exception")
77 def get_sdk_version(sdk):
78 with open(os.path.join(sdk, "SDKSettings.plist"), "rb") as plist:
79 obj = plistlib.load(plist)
82 "Error parsing SDKSettings.plist in the SDK directory: %s" % sdk
84 if "Version" not in obj:
86 "Error finding Version information in SDKSettings.plist from the SDK: %s"
89 return Version(obj["Version"])
92 with only_when(host_is_osx | target_is_osx):
99 help="Location of platform SDK to use",
102 def mac_sdk_min_version():
109 "MacOSX{}.sdk".format(mac_sdk_min_version()),
110 when=depends("--with-macos-sdk")(lambda x: not x),
114 @imports(_from="__builtin__", _import="Exception")
115 @imports(_from="os.path", _import="isdir")
116 @imports(_from="os", _import="listdir")
117 def macos_sdk(sdk, host, bootstrapped):
123 version = get_sdk_version(sdk)
124 except Exception as e:
126 elif host.os == "OSX":
127 sdk = check_cmd_output(
128 "xcrun", "--show-sdk-path", onerror=lambda: ""
132 "Could not find the macOS SDK. Please use --with-macos-sdk to give "
133 "the path to a macOS SDK."
135 # Scan the parent directory xcrun returns for the most recent SDK.
136 sdk_dir = os.path.dirname(sdk)
138 for d in listdir(sdk_dir):
139 if d.lower().startswith("macos"):
141 sdk = os.path.join(sdk_dir, d)
142 versions.append((get_sdk_version(sdk), sdk))
145 version, sdk = max(versions)
148 "Need a macOS SDK when targeting macOS. Please use --with-macos-sdk "
149 "to give the path to a macOS SDK."
154 "SDK not found in %s. When using --with-macos-sdk, you must specify a "
155 "valid SDK. SDKs are installed when the optional cross-development "
156 "tools are selected during the Xcode/Developer Tools installation."
159 if version < Version(mac_sdk_min_version()):
161 'SDK version "%s" is too old. Please upgrade to at least %s. Try '
162 "updating your system Xcode." % (version, mac_sdk_min_version())
166 set_config("MACOS_SDK_DIR", macos_sdk)
169 with only_when(target_is_osx):
170 with only_when(cross_compiling):
172 "--with-macos-private-frameworks",
173 env="MACOS_PRIVATE_FRAMEWORKS_DIR",
175 help="Location of private frameworks to use",
178 @depends_if("--with-macos-private-frameworks")
179 @imports(_from="os.path", _import="isdir")
180 def macos_private_frameworks(value):
181 if value and not isdir(value[0]):
183 "PrivateFrameworks not found not found in %s. When using "
184 "--with-macos-private-frameworks, you must specify a valid "
190 @depends(macos_private_frameworks, macos_sdk)
191 def macos_private_frameworks(value, sdk):
194 return os.path.join(sdk or "/", "System/Library/PrivateFrameworks")
196 set_config("MACOS_PRIVATE_FRAMEWORKS_DIR", macos_private_frameworks)
199 with only_when(target_is_ios):
200 # iOS deployment target version
201 # ==============================================================
202 # This needs to happen before any compilation test is done.
205 "--enable-ios-target",
206 env="IPHONEOS_DEPLOYMENT_TARGET",
209 help="Set the minimum iOS version needed at runtime",
212 @depends_if("--enable-ios-target")
213 def ios_target(value):
217 with only_when(target_is_ios):
222 env="IPHONEOS_SDK_DIR",
224 help="Location of platform SDK to use",
227 def ios_sdk_min_version():
231 def ios_sdk_name(target):
232 return "iPhone{}{}.sdk".format(
233 "Simulator" if target.raw_os == "ios-sim" else "OS",
234 ios_sdk_min_version(),
241 bootstrap_path(ios_sdk_name, when=depends("--with-ios-sdk")(lambda x: not x)),
243 @imports(_from="__builtin__", _import="Exception")
244 @imports(_from="os.path", _import="isdir")
245 @imports(_from="os", _import="listdir")
246 def ios_sdk(sdk, host, target, bootstrapped):
252 version = get_sdk_version(sdk)
253 except Exception as e:
255 elif host.os == "OSX":
256 sdk_name = "iphonesimulator" if target.raw_os == "ios-sim" else "iphoneos"
257 sdk = check_cmd_output(
258 "xcrun", "--show-sdk-path", "--sdk", sdk_name, onerror=lambda: ""
262 "Could not find the iOS SDK. Please use --with-ios-sdk to give "
263 "the path to a iOS SDK."
265 # Scan the parent directory xcrun returns for the most recent SDK.
266 sdk_dir = os.path.dirname(sdk)
268 for d in listdir(sdk_dir):
269 if d.lower().startswith(sdk_name):
271 sdk = os.path.join(sdk_dir, d)
272 versions.append((get_sdk_version(sdk), sdk))
275 version, sdk = max(versions)
278 "Need an iOS SDK when targeting iOS. Please use --with-ios-sdk "
279 "to give the path to a iOS SDK."
284 "SDK not found in %s. When using --with-ios-sdk, you must specify a "
285 "valid SDK. SDKs are installed when the optional cross-development "
286 "tools are selected during the Xcode installation." % sdk
288 if version < Version(ios_sdk_min_version()):
290 'SDK version "%s" is too old. Please upgrade to at least %s. Try '
291 "updating your system Xcode." % (version, ios_sdk_min_version())
295 set_config("IPHONEOS_SDK_DIR", ios_sdk)
298 # GC rooting and hazard analysis.
299 # ==============================================================
300 option(env="MOZ_HAZARD", help="Build for the GC rooting hazard analysis")
303 @depends("MOZ_HAZARD")
304 def hazard_analysis(value):
309 set_config("MOZ_HAZARD", hazard_analysis)
312 # Cross-compilation related things.
313 # ==============================================================
315 "--with-toolchain-prefix",
316 env="TOOLCHAIN_PREFIX",
318 help="Prefix for the target toolchain",
322 @depends("--with-toolchain-prefix", host, target, cross_compiling)
323 def toolchain_prefix(value, host, target, cross_compiling):
326 # We don't want a toolchain prefix by default when building on mac for mac.
327 if cross_compiling and not (target.os == "OSX" and host.os == "OSX"):
328 return ("%s-" % target.toolchain, "%s-" % target.alias)
331 @depends(toolchain_prefix, target)
332 def first_toolchain_prefix(toolchain_prefix, target):
333 # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
334 # command line/environment (in which case there's only one value in the tuple),
335 # or when cross-compiling for Android or OSX.
336 if toolchain_prefix and (
337 target.os in ("Android", "OSX") or len(toolchain_prefix) == 1
339 return toolchain_prefix[0]
342 set_config("TOOLCHAIN_PREFIX", first_toolchain_prefix)
343 add_old_configure_assignment("TOOLCHAIN_PREFIX", first_toolchain_prefix)
347 # ==============================================================
348 include("compilers-util.configure")
352 configure_cache, compiler, language, source, onerror=None, wrapper=[]
354 return try_invoke_compiler(
355 configure_cache, compiler, language, source, ["-E"], onerror, wrapper
359 @imports(_from="mozbuild.configure.constants", _import="CompilerType")
360 @imports(_from="mozbuild.configure.constants", _import="CPU_preprocessor_checks")
361 @imports(_from="mozbuild.configure.constants", _import="kernel_preprocessor_checks")
362 @imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks")
363 @imports(_from="textwrap", _import="dedent")
364 @imports(_from="__builtin__", _import="Exception")
365 def get_compiler_info(configure_cache, compiler, language):
366 """Returns information about the given `compiler` (command line in the
367 form of a list or tuple), in the given `language`.
369 The returned information includes:
370 - the compiler type (clang-cl, clang or gcc)
371 - the compiler version
372 - the compiler supported language
373 - the compiler supported language version
375 # Xcode clang versions are different from the underlying llvm version (they
376 # instead are aligned with the Xcode version). Fortunately, we can tell
377 # apart plain clang from Xcode clang, and convert the Xcode clang version
378 # into the more or less corresponding plain clang version.
381 #if defined(_MSC_VER) && defined(__clang__) && defined(_MT)
383 %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
384 #elif defined(__clang__)
386 %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
387 # ifdef __apple_build_version__
390 #elif defined(__GNUC__) && !defined(__MINGW32__)
392 %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
396 %cplusplus __cplusplus
397 #elif __STDC_VERSION__
398 %STDC_VERSION __STDC_VERSION__
403 # While we're doing some preprocessing, we might as well do some more
404 # preprocessor-based tests at the same time, to check the toolchain
405 # matches what we want.
406 for name, preprocessor_checks in (
407 ("CPU", CPU_preprocessor_checks),
408 ("KERNEL", kernel_preprocessor_checks),
409 ("OS", OS_preprocessor_checks),
411 for n, (value, condition) in enumerate(preprocessor_checks.items()):
414 #%(if)s %(condition)s
415 %%%(name)s "%(value)s"
418 "if": "elif" if n else "if",
419 "condition": condition,
426 # Also check for endianness. The advantage of living in modern times is
427 # that all the modern compilers we support now have __BYTE_ORDER__ defined
428 # by the preprocessor.
431 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
433 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
439 result = try_preprocess(configure_cache, compiler, language, check)
442 raise FatalCheckError("Unknown compiler or compiler not supported.")
444 # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
445 # have non-ASCII characters. Treat the output as bytearray.
447 for line in result.splitlines():
448 if line.startswith("%"):
449 k, _, v = line.partition(" ")
451 data[k] = v.replace(" ", "").lstrip('"').rstrip('"')
452 log.debug("%s = %s", k, data[k])
455 type = CompilerType(data["COMPILER"])
457 raise FatalCheckError("Unknown compiler or compiler not supported.")
459 cplusplus = int(data.get("cplusplus", "0L").rstrip("L"))
460 stdc_version = int(data.get("STDC_VERSION", "0L").rstrip("L"))
462 version = data.get("VERSION")
464 version = Version(version)
465 if data.get("XCODE"):
466 # Derived from https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
467 # with enough granularity for major.minor version checks further
470 version = Version("4.0.0.or.less")
471 elif version < "10.0":
472 version = Version("5.0.2")
473 elif version < "10.0.1":
474 version = Version("6.0.1")
475 elif version < "11.0":
476 version = Version("7.0.0")
477 elif version < "11.0.3":
478 version = Version("8.0.0")
479 elif version < "12.0":
480 version = Version("9.0.0")
481 elif version < "12.0.5":
482 version = Version("10.0.0")
483 elif version < "13.0":
484 version = Version("11.1.0")
485 elif version < "13.0.1":
486 version = Version("12.0.0")
487 elif version < "14.0":
488 version = Version("13.0.0")
489 elif version < "15.0":
490 version = Version("14.0.0")
492 version = Version("14.0.0.or.more")
498 kernel=data.get("KERNEL"),
499 endianness=data.get("ENDIANNESS"),
501 language="C++" if cplusplus else "C",
502 language_version=cplusplus if cplusplus else stdc_version,
503 xcode=bool(data.get("XCODE")),
507 def same_arch_different_bits():
511 ("sparc", "sparc64"),
515 @imports(_from="mozbuild.shellutil", _import="quote")
516 @imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks")
517 def check_compiler(configure_cache, compiler, language, target, android_version):
518 info = get_compiler_info(configure_cache, compiler, language)
522 # Check language standards
523 # --------------------------------------------------------------------
524 if language != info.language:
525 raise FatalCheckError(
526 "`%s` is not a %s compiler." % (quote(*compiler), language)
529 # Note: We do a strict version check because there sometimes are backwards
530 # incompatible changes in the standard, and not all code that compiles as
531 # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
533 if info.language == "C" and info.language_version != 199901:
534 if info.type == "clang-cl":
535 flags.append("-Xclang")
536 flags.append("-std=gnu99")
538 cxx17_version = 201703
539 if info.language == "C++":
540 if info.language_version != cxx17_version:
541 # MSVC headers include C++17 features, but don't guard them
542 # with appropriate checks.
543 if info.type == "clang-cl":
544 flags.append("-Xclang")
545 flags.append("-std=c++17")
547 flags.append("-std=gnu++17")
549 # Check compiler target
550 # --------------------------------------------------------------------
552 if target.os == "Android" and android_version:
553 # This makes clang define __ANDROID_API__ and use versioned library
554 # directories from the NDK.
555 toolchain = "%s%d" % (target.toolchain, android_version)
557 toolchain = target.toolchain
559 if info.type == "clang":
560 # Add the target explicitly when the target is aarch64 macosx, because
561 # the Xcode clang target is named differently, and we need to work around
562 # https://github.com/rust-lang/rust-bindgen/issues/1871 and
563 # https://github.com/alexcrichton/cc-rs/issues/542 so we always want
564 # the target on the command line, even if the compiler would default to
566 if info.xcode and target.os == "OSX" and target.cpu == "aarch64":
567 if "--target=arm64-apple-darwin" not in compiler:
568 flags.append("--target=arm64-apple-darwin")
570 elif target.os == "iOS":
571 target_flag = "--target=%s" % toolchain
572 if target_flag not in compiler:
573 flags.append(target_flag)
577 or info.kernel != target.kernel
578 or not info.endianness
579 or info.endianness != target.endianness
581 flags.append("--target=%s" % toolchain)
584 # Add target flag when there is an OS mismatch (e.g. building for Android on
585 # Linux). However, only do this if the target OS is in our whitelist, to
586 # keep things the same on other platforms.
587 elif target.os in OS_preprocessor_checks and (
588 not info.os or info.os != target.os
590 flags.append("--target=%s" % toolchain)
593 if not has_target and (not info.cpu or info.cpu != target.cpu):
594 same_arch = same_arch_different_bits()
595 if (target.cpu, info.cpu) in same_arch:
597 elif (info.cpu, target.cpu) in same_arch:
599 elif info.type == "clang-cl" and target.cpu == "aarch64":
600 flags.append("--target=%s" % toolchain)
601 elif info.type == "clang":
602 flags.append("--target=%s" % toolchain)
606 version=info.version,
608 target_kernel=info.kernel,
609 target_endianness=info.endianness,
615 @imports(_from="__builtin__", _import="open")
618 def get_vc_paths(topsrcdir):
620 program_files = os.environ.get("PROGRAMFILES(X86)") or os.environ.get(
623 if not program_files:
625 vswhere = os.path.join(
626 program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe"
628 if not os.path.exists(vswhere):
630 return json.loads(check_cmd_output(vswhere, "-format", "json", *args))
632 for install in vswhere(
637 "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
640 path = install["installationPath"]
644 path, r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"
651 tools_path = os.path.join(path, r"VC\Tools\MSVC", tools_version)
652 yield (Version(install["installationVersion"]), tools_path)
655 @depends(target, host)
656 def is_windows(target, host):
657 return host.kernel == "WINNT" or target.kernel == "WINNT"
660 # Calling this a sysroot is a little weird, but it's the terminology clang went
661 # with with its -winsysroot flag.
666 help='Path to a Windows "sysroot" (directory containing MSVC, SDKs)',
674 when=depends("WINSYSROOT", when=is_windows)(lambda x: not x),
678 def winsysroot(winsysroot, bootstrapped):
689 help="Path to the Microsoft Visual C/C++ compiler",
701 @imports(_from="operator", _import="itemgetter")
702 def vc_compiler_paths_for_version(host, env, vc_path, winsysroot):
705 die("WINSYSROOT and VC_PATH cannot be set together.")
706 base_vc_path = os.path.join(winsysroot, "VC", "Tools", "MSVC")
707 versions = os.listdir(base_vc_path)
708 vc_path = [os.path.join(base_vc_path, str(max(Version(v) for v in versions)))]
710 # Use an arbitrary version, it doesn't matter.
711 all_versions = [(Version("15"), vc_path[0])]
712 elif host.kernel != "WINNT":
713 # Don't try to do anything when VC_PATH is not set on cross-compiles.
716 all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
719 # Choose the newest version.
720 path = all_versions[-1][1]
726 path = os.path.join(path, "bin", host_dir)
728 "x64": [os.path.join(path, "x64")],
729 # The cross toolchains require DLLs from the native x64 toolchain.
730 "x86": [os.path.join(path, "x86"), os.path.join(path, "x64")],
731 "arm64": [os.path.join(path, "arm64"), os.path.join(path, "x64")],
735 @depends(target, host, vc_compiler_paths_for_version, when=is_windows)
736 def vc_compiler_path(target, host, paths):
737 cpu = target.cpu if target.os == "WINNT" else host.cpu
746 return paths.get(vc_target)
749 @depends(vc_compiler_path, original_path)
751 @imports(_from="os", _import="environ")
752 def vc_toolchain_search_path(vc_compiler_path, original_path):
753 result = list(original_path)
756 # The second item, if there is one, is necessary to have in $PATH for
757 # Windows to load the required DLLs from there.
758 if len(vc_compiler_path) > 1:
759 environ["PATH"] = os.pathsep.join(result + vc_compiler_path[1:])
761 # The first item is where the programs are going to be
762 result.append(vc_compiler_path[0])
767 @depends_if(vc_compiler_path, when=is_windows)
768 def vc_compiler_version(vc_compiler_path):
771 os.path.dirname(os.path.dirname(os.path.dirname(vc_compiler_path[0])))
774 # MSVC path with version 14.x is actually version 19.x
775 if version.major == 14:
776 return Version(f"19.{version.minor}")
779 @depends_if(vc_compiler_version)
780 def msvs_version(vc_compiler_version):
781 # clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to
782 # be set for GYP on Windows.
783 if vc_compiler_version >= Version("19.30"):
785 configure_error("Only Visual Studio 2022 or newer are supported")
790 set_config("MSVS_VERSION", msvs_version)
793 clang_search_path = bootstrap_search_path("clang/bin")
797 bootstrap_search_path("rustc/bin", when="MOZ_AUTOMATION"),
801 @imports(_from="os", _import="environ")
802 def rust_search_path(rust_path, original_path):
803 result = list(rust_path or original_path)
804 # Also add the rustup install directory for cargo/rustc.
805 cargo_home = environ.get("CARGO_HOME", "")
807 cargo_home = os.path.abspath(cargo_home)
809 cargo_home = os.path.expanduser(os.path.join("~", ".cargo"))
810 rustup_path = os.path.join(cargo_home, "bin")
811 result.insert(0, rustup_path)
815 # Prepend the mozilla-build msys2 path, since otherwise we can get mismatched
816 # cygwin dll errors during configure if we get called from another msys2
817 # environment, see bug 1801826.
819 mozillabuild_bin_paths, clang_search_path, rust_search_path, target, original_path
823 mozillabuild_bin_paths, clang_search_path, rust_search_path, target, original_path
825 altered_path = mozillabuild_bin_paths
826 if target.kernel == "Darwin":
827 # The rust compiler wants to execute dsymutil, but it does so in a
828 # non-configurable way (https://github.com/rust-lang/rust/issues/52728)
829 # so we add the clang path.
830 path = clang_search_path
833 # cargo needs the rust search path to find cargo-$subcommand.
834 path += rust_search_path
836 if p not in altered_path:
837 altered_path.append(p)
838 return os.pathsep.join(altered_path)
841 set_config("PATH", altered_path)
845 # ==============================================================
847 "--with-compiler-wrapper",
848 env="COMPILER_WRAPPER",
850 help="Enable compiling with wrappers such as distcc and ccache",
853 option("--with-ccache", env="CCACHE", nargs="?", help="Enable compiling with ccache")
856 @depends_if("--with-ccache")
860 # If --with-ccache was given without an explicit value, we default to
869 paths=bootstrap_search_path(
870 "sccache", when=depends("CCACHE")(lambda c: len(c) and c[0] == "sccache")
875 option(env="CCACHE_PREFIX", nargs=1, help="Compiler prefix to use when using ccache")
877 ccache_prefix = depends_if("CCACHE_PREFIX")(lambda prefix: prefix[0])
878 set_config("CCACHE_PREFIX", ccache_prefix)
880 # Distinguish ccache from sccache.
884 def ccache_is_sccache(ccache):
885 return check_cmd_output(ccache, "--version").startswith("sccache")
888 @depends(ccache, ccache_is_sccache)
889 def using_ccache(ccache, ccache_is_sccache):
890 return ccache and not ccache_is_sccache
893 @depends_if(ccache, ccache_is_sccache)
894 def using_sccache(ccache, ccache_is_sccache):
895 return ccache and ccache_is_sccache
898 option(env="RUSTC_WRAPPER", nargs=1, help="Wrap rust compilation with given tool")
901 @depends(ccache, ccache_is_sccache, "RUSTC_WRAPPER")
902 @imports(_from="textwrap", _import="dedent")
904 def check_sccache_version(ccache, ccache_is_sccache, rustc_wrapper):
905 sccache_min_version = Version("0.2.13")
907 def check_version(path):
908 out = check_cmd_output(path, "--version")
909 version = Version(out.rstrip().split()[-1])
910 if version < sccache_min_version:
914 sccache %s or later is required. sccache in use at %s has
917 Please upgrade or acquire a new version with |./mach bootstrap|.
925 if ccache and ccache_is_sccache:
926 check_version(ccache)
928 if rustc_wrapper and (
929 os.path.splitext(os.path.basename(rustc_wrapper[0]))[0].lower() == "sccache"
931 check_version(rustc_wrapper[0])
934 set_config("MOZ_USING_CCACHE", using_ccache)
935 set_config("MOZ_USING_SCCACHE", using_sccache)
937 option(env="SCCACHE_VERBOSE_STATS", help="Print verbose sccache stats after build")
940 @depends(using_sccache, "SCCACHE_VERBOSE_STATS")
941 def sccache_verbose_stats(using_sccache, verbose_stats):
942 return using_sccache and bool(verbose_stats)
945 set_config("SCCACHE_VERBOSE_STATS", sccache_verbose_stats)
948 @depends("--with-compiler-wrapper", ccache)
949 @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
950 def compiler_wrapper(wrapper, ccache):
952 raw_wrapper = wrapper[0]
953 wrapper = shell_split(raw_wrapper)
954 wrapper_program = find_program(wrapper[0])
955 if not wrapper_program:
957 "Cannot find `%s` from the given compiler wrapper `%s`",
961 wrapper[0] = wrapper_program
965 return tuple([ccache] + wrapper)
969 return tuple(wrapper)
972 @depends_if(compiler_wrapper)
973 def using_compiler_wrapper(compiler_wrapper):
977 set_config("MOZ_USING_COMPILER_WRAPPER", using_compiler_wrapper)
982 return split_triplet("wasm32-wasi", allow_wasi=True)
986 def default_c_compilers(host_or_target, other_c_compiler=None):
987 """Template defining the set of default C compilers for the host and
989 `host_or_target` is either `host` or `target` (the @depends functions
991 `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
993 assert host_or_target in {host, target, wasm}
995 other_c_compiler = () if other_c_compiler is None else (other_c_compiler,)
997 @depends(host_or_target, target, toolchain_prefix, *other_c_compiler)
998 def default_c_compilers(
999 host_or_target, target, toolchain_prefix, *other_c_compiler
1001 if host_or_target.kernel == "WINNT":
1002 if host_or_target.abi:
1003 if host_or_target.abi == "msvc":
1004 supported = types = ("clang-cl",)
1005 elif host_or_target.abi == "mingw":
1006 supported = types = ("clang",)
1008 supported = types = ("clang-cl", "clang")
1009 elif host_or_target.kernel == "Darwin":
1011 supported = ("clang", "gcc")
1012 elif host_or_target.kernel == "WASI":
1013 supported = types = ("clang",)
1015 supported = types = ("clang", "gcc")
1017 info = other_c_compiler[0] if other_c_compiler else None
1018 if info and info.type in supported:
1019 # When getting default C compilers for the host, we prioritize the
1020 # same compiler as the target C compiler.
1021 prioritized = info.compiler
1022 if info.type == "gcc":
1023 same_arch = same_arch_different_bits()
1025 target.cpu != host_or_target.cpu
1026 and (target.cpu, host_or_target.cpu) not in same_arch
1027 and (host_or_target.cpu, target.cpu) not in same_arch
1029 # If the target C compiler is GCC, and it can't be used with
1030 # -m32/-m64 for the host, it's probably toolchain-prefixed,
1031 # so we prioritize a raw 'gcc' instead.
1032 prioritized = info.type
1033 if target.os != "WINNT" and host_or_target.os == "WINNT":
1034 # When cross-compiling on Windows, don't prioritize. We'll fallback
1035 # to checking for clang-cl first.
1038 types = [prioritized] + [t for t in types if t != info.type]
1041 if toolchain_prefix and host_or_target is target:
1042 gcc = tuple("%sgcc" % p for p in toolchain_prefix) + gcc
1051 return tuple(result)
1053 return default_c_compilers
1057 def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None):
1058 """Template defining the set of default C++ compilers for the host and
1060 `c_compiler` is the @depends function returning a Compiler instance for
1061 the desired platform.
1063 Because the build system expects the C and C++ compilers to be from the
1064 same compiler suite, we derive the default C++ compilers from the C
1065 compiler that was found if none was provided.
1067 We also factor in the target C++ compiler when getting the default host
1068 C++ compiler, using the target C++ compiler if the host and target C
1069 compilers are the same.
1072 assert (other_c_compiler is None) == (other_cxx_compiler is None)
1073 if other_c_compiler is not None:
1074 other_compilers = (other_c_compiler, other_cxx_compiler)
1076 other_compilers = ()
1078 @depends(c_compiler, *other_compilers)
1079 def default_cxx_compilers(c_compiler, *other_compilers):
1081 other_c_compiler, other_cxx_compiler = other_compilers
1082 if other_c_compiler.compiler == c_compiler.compiler:
1083 return (other_cxx_compiler.compiler,)
1085 dir = os.path.dirname(c_compiler.compiler)
1086 file = os.path.basename(c_compiler.compiler)
1088 if c_compiler.type == "gcc":
1089 return (os.path.join(dir, file.replace("gcc", "g++")),)
1091 if c_compiler.type == "clang":
1092 return (os.path.join(dir, file.replace("clang", "clang++")),)
1094 return (c_compiler.compiler,)
1096 return default_cxx_compilers
1100 def provided_program(env_var, when=None):
1101 """Template handling cases where a program can be specified either as a
1102 path or as a path with applicable arguments.
1105 @depends_if(env_var, when=when)
1106 @imports(_from="itertools", _import="takewhile")
1107 @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
1109 # Assume the first dash-prefixed item (and any subsequent items) are
1110 # command-line options, the item before the dash-prefixed item is
1111 # the program we're looking for, and anything before that is a wrapper
1112 # of some kind (e.g. sccache).
1113 cmd = shell_split(cmd[0])
1115 without_flags = list(takewhile(lambda x: not x.startswith("-"), cmd))
1118 wrapper=without_flags[:-1],
1119 program=without_flags[-1],
1120 flags=cmd[len(without_flags) :],
1127 def sysroot(host_or_target, target_sysroot=None):
1128 assert target_sysroot or host_or_target is target
1129 bootstrap_target_when = target_is_linux_or_wasi
1130 if host_or_target is host:
1131 host_or_target_str = "host"
1132 opt = "--with-host-sysroot"
1133 env = "HOST_SYSROOT"
1134 when = depends(host)(lambda h: h.kernel == "Linux")
1136 # Only bootstrap a host sysroot when using a bootstrapped target sysroot
1137 # or when the target doesn't use a bootstrapped sysroot in the first place.
1138 @depends(when, bootstrap_target_when, target_sysroot.bootstrapped)
1139 def bootstrap_when(when, bootstrap_target_when, bootstrapped):
1140 return when and (bootstrapped or not bootstrap_target_when)
1143 assert host_or_target is target
1144 host_or_target_str = "target"
1145 opt = "--with-sysroot"
1147 when = target_is_linux_or_wasi
1148 bootstrap_when = bootstrap_target_when
1155 help="Use the given sysroot directory for %s build" % host_or_target_str,
1158 sysroot_input = depends(opt, when=when)(lambda x: x)
1159 bootstrap_sysroot = depends(bootstrap_when, sysroot_input)(
1160 # Only bootstrap when no flag was explicitly given (either --with or --without)
1161 lambda bootstrap, input: bootstrap
1163 and input.origin == "default"
1172 depends(host_or_target)(lambda t: "sysroot-{}".format(t.toolchain)),
1173 when=bootstrap_sysroot,
1177 def sysroot(sysroot_input, host_or_target, macos_sdk, ios_sdk, path):
1180 path = sysroot_input[0]
1181 elif host_or_target.os == "OSX" and macos_sdk:
1183 elif host_or_target.os == "iOS" and ios_sdk:
1186 # Find the version of libstdc++ headears in the sysroot
1187 include = os.path.join(path, "usr/include/c++")
1188 if os.path.isdir(include):
1189 with os.scandir(include) as d:
1190 version = max(Version(e.name) for e in d if e.is_dir())
1191 log.info("Using %s sysroot in %s", host_or_target_str, path)
1194 bootstrapped=bool(path and not sysroot_input),
1195 stdcxx_version=version,
1201 target_sysroot = sysroot(target)
1204 # Use `system_lib_option` instead of `option` for options that enable building
1205 # with a system library for which the development headers are not available in
1206 # the bootstrapped sysroots.
1208 def system_lib_option(name, *args, **kwargs):
1209 option(name, *args, **kwargs)
1211 @depends(name, target_sysroot.bootstrapped)
1212 def no_system_lib_in_sysroot(value, bootstrapped):
1213 if bootstrapped and value:
1215 "%s is not supported with bootstrapped sysroot. "
1216 "Drop the option, or use --without-sysroot or --disable-bootstrap",
1221 host_sysroot = sysroot(host, target_sysroot)
1225 def multiarch_dir(host_or_target):
1228 target: target_sysroot,
1231 @depends(host_or_target, when=sysroot.path)
1232 def multiarch_dir(target):
1233 if target.cpu == "x86":
1234 # Turn e.g. i686-linux-gnu into i386-linux-gnu
1235 return target.toolchain.replace(target.raw_cpu, "i386")
1236 return target.toolchain
1238 return multiarch_dir
1241 target_multiarch_dir = multiarch_dir(target)
1242 host_multiarch_dir = multiarch_dir(host)
1245 def minimum_gcc_version():
1246 return Version("8.1.0")
1254 other_compiler=None,
1255 other_c_compiler=None,
1257 """Template handling the generic base checks for the compiler for the
1258 given `language` on the given platform (`host_or_target`).
1259 `host_or_target` is either `host` or `target` (the @depends functions
1260 from init.configure.
1261 When the language is 'C++', `c_compiler` is the result of the `compiler`
1262 template for the language 'C' for the same `host_or_target`.
1263 When `host_or_target` is `host`, `other_compiler` is the result of the
1264 `compiler` template for the same `language` for `target`.
1265 When `host_or_target` is `host` and the language is 'C++',
1266 `other_c_compiler` is the result of the `compiler` template for the
1267 language 'C' for `target`.
1269 assert host_or_target in {host, target, wasm}
1270 assert language in ("C", "C++")
1271 assert language == "C" or c_compiler is not None
1272 assert host_or_target is target or other_compiler is not None
1273 assert language == "C" or host_or_target is target or other_c_compiler is not None
1275 host_or_target_str = {
1283 target: target_sysroot,
1284 wasm: dependable(lambda: namespace(path=None)),
1288 host: host_multiarch_dir,
1289 target: target_multiarch_dir,
1294 ("C", target): "CC",
1295 ("C++", target): "CXX",
1296 ("C", host): "HOST_CC",
1297 ("C++", host): "HOST_CXX",
1298 ("C", wasm): "WASM_CC",
1299 ("C++", wasm): "WASM_CXX",
1300 }[language, host_or_target]
1302 default_compilers = {
1303 "C": lambda: default_c_compilers(host_or_target, other_compiler),
1304 "C++": lambda: default_cxx_compilers(
1305 c_compiler, other_c_compiler, other_compiler
1309 what = "the %s %s compiler" % (host_or_target_str, language)
1311 option(env=var, nargs=1, help="Path to %s" % what)
1313 # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
1314 # HOST_CXX variables.
1315 provided_compiler = provided_program(var)
1317 # Normally, we'd use `var` instead of `_var`, but the interaction with
1318 # old-configure complicates things, and for now, we a) can't take the plain
1319 # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
1320 # old-configure AC_SUBST it (because it's autoconf doing it, not us)
1321 compiler = check_prog(
1324 progs=default_compilers,
1325 input=provided_compiler.program,
1326 paths=clang_search_path,
1339 vc_compiler_version,
1344 @checking("whether %s can be used" % what, lambda x: bool(x))
1345 @imports(_from="mozbuild.shellutil", _import="quote")
1357 vc_compiler_version,
1362 wrapper = list(compiler_wrapper or ())
1365 if host_or_target.kernel == "Darwin":
1366 # While --sysroot and -isysroot are roughly equivalent, when not using
1367 # -isysroot on mac, clang takes the SDKROOT environment variable into
1368 # consideration, which may be set by python and break things.
1369 flags.extend(("-isysroot", sysroot.path))
1371 flags.extend(("--sysroot", sysroot.path))
1372 if provided_compiler:
1373 wrapper.extend(provided_compiler.wrapper)
1374 flags.extend(provided_compiler.flags)
1376 info = check_compiler(
1378 wrapper + [compiler] + flags,
1384 if host_or_target.os == "OSX" and macos_target:
1385 flags.append("-mmacosx-version-min=%s" % macos_target)
1386 if host_or_target.os == "iOS" and ios_target:
1387 flags.append("-mios-version-min=%s" % ios_target)
1389 # When not given an explicit compatibility version, clang-cl tries
1390 # to get one from MSVC, which might not even be the one used by the
1391 # build. And when it can't find one, its default might also not match
1392 # what the build is using. So if we were able to figure out the version
1393 # we're building with, explicitly use that.
1394 # This also means that, as a side effect, clang-cl will not try to find
1395 # MSVC, which saves a little overhead.
1396 if info.type == "clang-cl" and vc_compiler_version:
1397 flags.append(f"-fms-compatibility-version={vc_compiler_version}")
1399 if info.type == "clang" and language == "C++" and host_or_target.os == "OSX":
1400 flags.append("-stdlib=libc++")
1402 # Check that the additional flags we got are enough to not require any
1403 # more flags. If we get an exception, just ignore it; it's liable to be
1404 # invalid command-line flags, which means the compiler we're checking
1405 # doesn't support those command-line flags and will fail one or more of
1410 info = check_compiler(
1412 wrapper + [compiler] + flags,
1417 except FatalCheckError:
1420 if not info.target_cpu or info.target_cpu != host_or_target.cpu:
1421 raise FatalCheckError(
1422 "%s %s compiler target CPU (%s) does not match --%s CPU (%s)"
1424 host_or_target_str.capitalize(),
1426 info.target_cpu or "unknown",
1428 host_or_target.raw_cpu,
1432 if not info.target_kernel or (info.target_kernel != host_or_target.kernel):
1433 raise FatalCheckError(
1434 "%s %s compiler target kernel (%s) does not match --%s kernel (%s)"
1436 host_or_target_str.capitalize(),
1438 info.target_kernel or "unknown",
1440 host_or_target.kernel,
1444 if not info.target_endianness or (
1445 info.target_endianness != host_or_target.endianness
1447 raise FatalCheckError(
1448 "%s %s compiler target endianness (%s) does not match --%s "
1451 host_or_target_str.capitalize(),
1453 info.target_endianness or "unknown",
1455 host_or_target.endianness,
1459 # Compiler version checks
1460 # ===================================================
1461 # Check the compiler version here instead of in `compiler_version` so
1462 # that the `checking` message doesn't pretend the compiler can be used
1463 # to then bail out one line later.
1464 if info.type == "gcc":
1465 if host_or_target.os == "Android":
1466 raise FatalCheckError(
1467 "GCC is not supported on Android.\n"
1468 "Please use clang from the Android NDK instead."
1470 gcc_version = minimum_gcc_version()
1471 if info.version < gcc_version:
1472 raise FatalCheckError(
1473 "Only GCC %d.%d or newer is supported (found version %s)."
1474 % (gcc_version.major, gcc_version.minor, info.version)
1477 # Force GCC to use the C++ headers from the sysroot, and to prefer the
1478 # sysroot system headers to /usr/include.
1479 # Non-Debian GCC also doesn't look at headers in multiarch directory.
1480 if sysroot.bootstrapped and sysroot.stdcxx_version:
1481 version = sysroot.stdcxx_version
1483 "usr/include/c++/{}".format(version),
1484 "usr/include/{}/c++/{}".format(multiarch_dir, version),
1485 "usr/include/{}".format(multiarch_dir),
1488 flags.extend(("-isystem", os.path.join(sysroot.path, path)))
1490 if info.type == "clang-cl":
1491 if info.version < "9.0.0":
1492 raise FatalCheckError(
1493 "Only clang-cl 9.0 or newer is supported (found version %s)"
1496 if winsysroot and host.os != "WINNT":
1497 overlay = os.path.join(winsysroot, "overlay.yaml")
1498 if os.path.exists(overlay):
1499 overlay_flags = ["-Xclang", "-ivfsoverlay", "-Xclang", overlay]
1500 if info.version >= "16.0" or (
1501 # clang-cl 15 normally doesn't support the root-relative
1502 # overlay we use, but the bootstrapped clang-cl 15 is patched
1503 # to support it, so check we're using a patched version.
1504 info.version >= "15.0"
1507 [compiler] + flags + overlay_flags,
1510 onerror=lambda: False,
1514 flags.extend(overlay_flags)
1516 if (info.type, host_or_target.abi) in (
1518 ("clang-cl", "mingw"),
1520 raise FatalCheckError("Unknown compiler or compiler not supported.")
1522 # If you want to bump the version check here ensure the version
1523 # is known for Xcode in get_compiler_info.
1524 if info.type == "clang" and info.version < "8.0":
1525 raise FatalCheckError(
1526 "Only clang/llvm 8.0 or newer is supported (found version %s)."
1530 if host_or_target.kernel == "WASI":
1531 if info.type != "clang":
1532 raise FatalCheckError(
1533 "Only clang is supported for %s" % host_or_target.alias
1535 if info.version < "8.0":
1536 raise FatalCheckError(
1537 "Only clang/llvm 8.0 or newer is supported for %s (found version %s)."
1538 % (host_or_target.alias, info.version)
1541 if host_or_target.os == "Android":
1542 # Need at least clang 13 for compiler-rt/libunwind being the default.
1543 if info.type == "clang" and info.version < "13.0":
1544 raise FatalCheckError(
1545 "Only clang/llvm 13.0 or newer is supported for %s (found version %s)."
1546 % (host_or_target.alias, info.version)
1550 raise FatalCheckError("Unknown compiler or compiler not supported.")
1557 version=info.version,
1561 @depends(valid_compiler)
1562 @checking("%s version" % what)
1563 def compiler_version(compiler):
1564 return compiler.version
1566 if language == "C++":
1568 @depends(valid_compiler, c_compiler)
1569 def valid_compiler(compiler, c_compiler):
1570 if compiler.type != c_compiler.type:
1572 "The %s C compiler is %s, while the %s C++ compiler is "
1573 "%s. Need to use the same compiler suite.",
1580 if compiler.version != c_compiler.version:
1582 "The %s C compiler is version %s, while the %s C++ "
1583 "compiler is version %s. Need to use the same compiler "
1592 # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
1593 # and the flags that were part of the user input for those variables to
1595 add_old_configure_assignment(
1597 depends_if(valid_compiler)(
1598 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1602 if host_or_target is target:
1603 add_old_configure_assignment(
1604 "ac_cv_prog_%s" % var,
1605 depends_if(valid_compiler)(
1606 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
1609 # We check that it works in python configure already.
1610 add_old_configure_assignment("ac_cv_prog_%s_works" % var.lower(), "yes")
1611 add_old_configure_assignment(
1612 "ac_cv_prog_%s_cross" % var.lower(),
1613 depends(cross_compiling)(lambda x: "yes" if x else "no"),
1615 gcc_like = depends(valid_compiler.type)(
1616 lambda x: "yes" if x in ("gcc", "clang") else "no"
1618 add_old_configure_assignment("ac_cv_prog_%s_g" % var.lower(), gcc_like)
1620 add_old_configure_assignment("ac_cv_prog_gcc", gcc_like)
1621 if language == "C++":
1622 add_old_configure_assignment("ac_cv_prog_gxx", gcc_like)
1624 # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
1625 # old-configure to do some of its still existing checks.
1627 set_config("%s_TYPE" % var, valid_compiler.type)
1628 add_old_configure_assignment("%s_TYPE" % var, valid_compiler.type)
1630 "%s_VERSION" % var, depends(valid_compiler.version)(lambda v: str(v))
1633 valid_compiler = compiler_class(valid_compiler, host_or_target)
1635 def compiler_error():
1636 raise FatalCheckError(
1637 "Failed compiling a simple %s source with %s" % (language, what)
1640 valid_compiler.try_compile(check_msg="%s works" % what, onerror=compiler_error)
1642 set_config("%s_BASE_FLAGS" % var, valid_compiler.flags)
1644 # Set CPP/CXXCPP for both the build system and old-configure. We don't
1645 # need to check this works for preprocessing, because we already relied
1646 # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
1647 # in the first place.
1648 if host_or_target is target:
1654 preprocessor = depends_if(valid_compiler)(
1655 lambda x: list(x.wrapper) + [x.compiler, "-E"] + list(x.flags)
1658 set_config(pp_var, preprocessor)
1659 add_old_configure_assignment(pp_var, preprocessor)
1665 }.get(host_or_target)
1669 @deprecated_option(env=linker_var, nargs=1)
1675 def unused_linker(linker):
1678 "The value of %s is not used by this build system." % linker_var
1681 return valid_compiler
1684 c_compiler = compiler("C", target)
1685 cxx_compiler = compiler("C++", target, c_compiler=c_compiler)
1686 host_c_compiler = compiler("C", host, other_compiler=c_compiler)
1687 host_cxx_compiler = compiler(
1690 c_compiler=host_c_compiler,
1691 other_compiler=cxx_compiler,
1692 other_c_compiler=c_compiler,
1697 def windows_abi(host_or_target, c_compiler):
1698 @depends(host_or_target)
1699 def windows_abi(host_or_target):
1700 if host_or_target.os == "WINNT":
1701 return host_or_target.abi
1703 @depends(host_or_target, windows_abi)
1704 def need_windows_abi_from_compiler(host_or_target, windows_abi):
1705 return host_or_target.os == "WINNT" and windows_abi is None
1707 @depends(host_or_target, c_compiler, when=need_windows_abi_from_compiler)
1708 def windows_abi_from_compiler(host_or_target, c_compiler):
1709 if host_or_target.os == "WINNT":
1710 if c_compiler.type == "clang-cl":
1714 return windows_abi | windows_abi_from_compiler
1717 target_windows_abi = windows_abi(target, c_compiler)
1718 host_windows_abi = windows_abi(host, host_c_compiler)
1721 # Generic compiler-based conditions.
1722 building_with_gcc = depends(c_compiler)(lambda info: info.type == "gcc")
1723 building_with_gnu_cc = depends(c_compiler)(lambda info: info.type != "clang-cl")
1726 @depends(cxx_compiler, ccache_prefix)
1728 def cxx_is_icecream(info, ccache_prefix):
1730 os.path.islink(info.compiler)
1731 and os.path.basename(os.readlink(info.compiler)) == "icecc"
1734 if ccache_prefix and os.path.basename(ccache_prefix) == "icecc":
1738 set_config("CXX_IS_ICECREAM", cxx_is_icecream)
1741 # Libstdc++ compatibility hacks
1742 # ==============================================================
1744 @depends(target, host)
1745 def target_or_host_is_linux(target, host):
1746 return any(t.os == "GNU" and t.kernel == "Linux" for t in (target, host))
1750 "--enable-stdcxx-compat",
1751 env="MOZ_STDCXX_COMPAT",
1752 help="Enable compatibility with older libstdc++",
1753 when=target_or_host_is_linux,
1757 @depends("--enable-stdcxx-compat", when=target_or_host_is_linux)
1758 def stdcxx_compat(value):
1763 set_config("MOZ_STDCXX_COMPAT", True, when=stdcxx_compat)
1767 # ==============================================================
1768 # The policy is as follows:
1770 # - the linker is picked via the LINKER environment variable per windows.configure,
1771 # but ought to be lld-link in any case.
1773 # - the linker is lld if the clang used is >= 15 (per LLVM version, not Xcode version).
1774 # - the linker is also lld on local developer builds if the clang used is >= 13 (per LLVM
1775 # version, not Xcode version)
1776 # - otherwise the linker is ld64, either from XCode on macOS, or from cctools-ports when
1779 # - on local developer builds: lld if present and the compiler is clang. Otherwise gold
1780 # is used if present otherwise, whatever the compiler uses by default.
1781 # - on release/official builds: whatever the compiler uses by default, except when the
1782 # compiler is clang, in which case lld is preferred when it's new enough.
1784 def is_not_winnt_or_sunos(host_or_target):
1785 @depends(host_or_target)
1786 def is_not_winnt_or_sunos(host_or_target):
1787 if host_or_target.kernel not in ("WINNT", "SunOS"):
1790 return is_not_winnt_or_sunos
1793 is_linker_option_enabled = is_not_winnt_or_sunos(target)
1796 @deprecated_option("--enable-gold", env="MOZ_FORCE_GOLD", when=is_linker_option_enabled)
1797 def enable_gold(value):
1799 die("--enable-gold is deprecated, use --enable-linker=gold instead")
1801 die("--disable-gold is deprecated, use --enable-linker=something_else instead")
1807 help="Select the linker {bfd, gold, ld64, lld, lld-*, mold}",
1808 when=is_linker_option_enabled,
1812 # No-op to enable depending on --enable-linker from default_elfhack in
1813 # toolkit/moz.configure.
1814 @depends("--enable-linker", when=is_linker_option_enabled)
1815 def enable_linker(linker):
1820 def select_linker_tmpl(host_or_target):
1821 if host_or_target is target:
1826 extra_toolchain_flags,
1829 when=is_linker_option_enabled,
1831 host_or_target_str = "target"
1840 when=is_not_winnt_or_sunos(host_or_target),
1842 host_or_target_str = "host"
1845 @checking(f"for {host_or_target_str} linker", lambda x: x.KIND)
1849 linker, c_compiler, developer_options, toolchain_flags, target, stdcxx_compat
1856 def is_valid_linker(linker):
1857 if target.kernel == "Darwin":
1858 valid_linkers = ("ld64", "lld")
1860 valid_linkers = ("bfd", "gold", "lld", "mold")
1861 if linker in valid_linkers:
1863 if "lld" in valid_linkers and linker.startswith("lld-"):
1867 if linker and not is_valid_linker(linker):
1868 # Check that we are trying to use a supported linker
1869 die("Unsupported linker " + linker)
1871 # Check the kind of linker
1872 version_check = ["-Wl,--version"]
1873 cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
1875 def try_linker(linker):
1876 # Generate the compiler flag
1877 if linker == "ld64":
1878 linker_flag = ["-fuse-ld=ld"]
1880 linker_flag = ["-fuse-ld=" + linker]
1883 cmd = cmd_base + linker_flag + version_check
1885 cmd += toolchain_flags
1887 # ld64 doesn't have anything to print out a version. It does print out
1888 # "ld64: For information on command line options please use 'man ld'."
1889 # but that would require doing two attempts, one with --version, that
1890 # would fail, and another with --help.
1891 # Instead, abuse its LD_PRINT_OPTIONS feature to detect a message
1892 # specific to it on stderr when it fails to process --version.
1893 env = dict(os.environ)
1894 env["LD_PRINT_OPTIONS"] = "1"
1895 # Some locales might not print out the strings we are looking for, so
1896 # ensure consistent output.
1898 retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
1899 if retcode == 1 and "Logging ld64 options" in stderr:
1905 elif "mold" in stdout:
1908 elif "GNU ld" in stdout:
1909 # We are using the normal linker
1912 elif "GNU gold" in stdout:
1915 elif "LLD" in stdout:
1921 if kind == "unknown" or is_valid_linker(kind):
1924 LINKER_FLAG=linker_flag,
1929 result = try_linker(linker)
1931 die("Could not use {} as linker".format(linker))
1935 and c_compiler.type == "clang"
1938 target.kernel != "Darwin"
1941 or host_or_target_str == "host"
1942 or c_compiler.version >= "15.0"
1946 target.kernel == "Darwin"
1948 (developer_options and c_compiler.version >= "13.0")
1949 or c_compiler.version >= "15.0"
1954 result = try_linker("lld")
1956 if result is None and developer_options and not stdcxx_compat:
1957 result = try_linker("gold")
1960 result = try_linker(None)
1963 die("Failed to find an adequate linker")
1965 if stdcxx_compat and result.KIND == "gold":
1966 die("--enable-stdcxx-compat is not compatible with the gold linker")
1968 # If an explicit linker was given, error out if what we found is different.
1969 if linker and not linker.startswith(result.KIND):
1970 die("Could not use {} as linker".format(linker))
1974 return select_linker
1977 select_linker = select_linker_tmpl(target)
1978 set_config("LINKER_KIND", select_linker.KIND)
1982 def linker_ldflags_tmpl(host_or_target):
1983 if host_or_target is target:
1988 target_multiarch_dir,
1996 select_linker_tmpl(host),
2018 flags = list((linker and linker.LINKER_FLAG) or [])
2019 # rpath-link is irrelevant to wasm, see for more info https://github.com/emscripten-core/emscripten/issues/11076.
2020 if sysroot.path and multiarch_dir and target.os != "WASI":
2021 for d in ("lib", "usr/lib"):
2022 multiarch_lib_dir = os.path.join(sysroot.path, d, multiarch_dir)
2023 if os.path.exists(multiarch_lib_dir):
2024 # Non-Debian-patched binutils linkers (both BFD and gold) don't lookup
2025 # in multi-arch directories.
2026 flags.append("-Wl,-rpath-link,%s" % multiarch_lib_dir)
2027 # GCC also needs -L.
2028 if c_compiler.type == "gcc":
2029 flags.append("-L%s" % multiarch_lib_dir)
2031 c_compiler.type == "gcc"
2032 and sysroot.bootstrapped
2033 and sysroot.stdcxx_version
2036 "-L{}/usr/lib/gcc/{}/{}".format(
2037 sysroot.path, multiarch_dir, sysroot.stdcxx_version
2041 # BFD/gold linkers need a manual --rpath-link for indirect
2044 "-Wl,--rpath-link={}/usr/lib/{}".format(
2045 android_sysroot, target.toolchain
2047 "-Wl,--rpath-link={}/usr/lib/{}/{}".format(
2048 android_sysroot, target.toolchain, android_version
2054 and linker.KIND == "lld"
2055 and target.kernel != "WINNT"
2057 flags.append("-Wl,-O0")
2060 return linker_ldflags
2063 linker_ldflags = linker_ldflags_tmpl(target)
2064 add_old_configure_assignment("LINKER_LDFLAGS", linker_ldflags)
2066 host_linker_ldflags = linker_ldflags_tmpl(host)
2067 add_old_configure_assignment("HOST_LINKER_LDFLAGS", host_linker_ldflags)
2070 # There's a wrinkle with MinGW: linker configuration is not enabled, so
2071 # `select_linker` is never invoked. Hard-code around it.
2072 @depends(select_linker, target, c_compiler)
2073 def gcc_use_gnu_ld(select_linker, target, c_compiler):
2074 if select_linker is not None and target.kernel != "Darwin":
2075 return select_linker.KIND in ("bfd", "gold", "lld", "mold")
2076 if target.kernel == "WINNT" and c_compiler.type == "clang":
2081 # GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld.
2082 set_config("GCC_USE_GNU_LD", gcc_use_gnu_ld)
2083 add_old_configure_assignment("GCC_USE_GNU_LD", gcc_use_gnu_ld)
2086 include("compile-checks.configure")
2087 include("arm.configure", when=depends(target.cpu)(lambda cpu: cpu == "arm"))
2093 body='static_assert(sizeof(void *) == 8, "")', check_msg="for 64-bit OS"
2096 def check_have_64_bit(have_64_bit, compiler_have_64_bit):
2097 if have_64_bit != compiler_have_64_bit:
2099 "The target compiler does not agree with configure "
2100 "about the target bitness."
2104 @depends(cxx_compiler, target)
2105 def needs_libstdcxx_newness_check(cxx_compiler, target):
2106 # We only have to care about this on Linux and MinGW.
2107 if cxx_compiler.type == "clang-cl":
2110 if target.kernel not in ("Linux", "WINNT"):
2113 if target.os == "Android":
2119 def die_on_old_libstdcxx():
2121 "The libstdc++ in use is not new enough. Please run "
2122 "./mach bootstrap to update your compiler, or update your system "
2123 "libstdc++ installation."
2128 includes=["cstddef"],
2131 # _GLIBCXX_RELEASE showed up in libstdc++ 7.
2132 "#if defined(__GLIBCXX__) && !defined(_GLIBCXX_RELEASE)",
2133 "# error libstdc++ not new enough",
2135 "#if defined(_GLIBCXX_RELEASE)",
2136 "# if _GLIBCXX_RELEASE < %d" % minimum_gcc_version().major,
2137 "# error libstdc++ not new enough",
2144 check_msg="for new enough STL headers from libstdc++",
2145 when=needs_libstdcxx_newness_check,
2146 onerror=die_on_old_libstdcxx,
2150 @depends(c_compiler, target)
2151 def default_debug_flags(compiler_info, target):
2152 # Debug info is ON by default.
2153 if compiler_info.type == "clang-cl":
2155 elif target.kernel == "WINNT" and compiler_info.type == "clang":
2156 return "-g -gcodeview"
2157 # The oldest versions of supported compilers default to DWARF-4, but
2158 # newer versions may default to DWARF-5 or newer (e.g. clang 14), which
2159 # Valgrind doesn't support. Force-use DWARF-4.
2163 option(env="MOZ_DEBUG_FLAGS", nargs=1, help="Debug compiler flags")
2165 imply_option("--enable-debug-symbols", depends_if("--enable-debug")(lambda v: v))
2168 "--disable-debug-symbols",
2170 help="Disable debug symbols using the given compiler flags",
2173 set_config("MOZ_DEBUG_SYMBOLS", depends_if("--enable-debug-symbols")(lambda _: True))
2176 @depends("MOZ_DEBUG_FLAGS", "--enable-debug-symbols", default_debug_flags)
2177 def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
2178 # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
2179 # --enable-debug-symbols takes precedence. Note, the value of
2180 # --enable-debug-symbols may be implied by --enable-debug.
2181 if len(enable_debug_flags):
2182 return enable_debug_flags[0]
2184 return env_debug_flags[0]
2185 return default_debug_flags
2188 set_config("MOZ_DEBUG_FLAGS", debug_flags)
2189 add_old_configure_assignment("MOZ_DEBUG_FLAGS", debug_flags)
2192 @depends(c_compiler, host)
2194 _from="mach.logging", _import="enable_blessed", _as="_enable_ansi_escape_codes"
2196 def color_cflags(info, host):
2197 # We could test compiling with flags. By why incur the overhead when
2198 # color support should always be present in a specific toolchain
2201 # Code for auto-adding this flag to compiler invocations needs to
2202 # determine if an existing flag isn't already present. That is likely
2203 # using exact string matching on the returned value. So if the return
2204 # value changes to e.g. "<x>=always", exact string match may fail and
2205 # multiple color flags could be added. So examine downstream consumers
2206 # before adding flags to return values.
2207 if info.type == "gcc":
2208 return "-fdiagnostics-color"
2209 elif info.type in ["clang", "clang-cl"]:
2210 if host.os == "WINNT" and _enable_ansi_escape_codes():
2211 return "-fcolor-diagnostics -fansi-escape-codes"
2213 return "-fcolor-diagnostics"
2218 set_config("COLOR_CFLAGS", color_cflags)
2220 # Some standard library headers (notably bionic on Android) declare standard
2221 # functions (e.g. getchar()) and also #define macros for those standard
2222 # functions. libc++ deals with this by doing something like the following
2223 # (explanatory comments added):
2226 # // Capture the definition of FUNC.
2227 # inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
2229 # // Use a real inline definition.
2230 # inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
2233 # _LIBCPP_INLINE_VISIBILITY is typically defined as:
2235 # __attribute__((__visibility__("hidden"), __always_inline__))
2237 # Unfortunately, this interacts badly with our system header wrappers, as the:
2239 # #pragma GCC visibility push(default)
2241 # that they do prior to including the actual system header is treated by the
2242 # compiler as an explicit declaration of visibility on every function declared
2243 # in the header. Therefore, when the libc++ code above is encountered, it is
2244 # as though the compiler has effectively seen:
2246 # int FUNC(...) __attribute__((__visibility__("default")));
2247 # int FUNC(...) __attribute__((__visibility__("hidden")));
2249 # and the compiler complains about the mismatched visibility declarations.
2251 # However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
2252 # existing definition. We can therefore define it to the empty string (since
2253 # we are properly managing visibility ourselves) and avoid this whole mess.
2254 # Note that we don't need to do this with gcc, as libc++ detects gcc and
2255 # effectively does the same thing we are doing here.
2257 # _LIBCPP_ALWAYS_INLINE needs a similar workarounds, since it too declares
2258 # hidden visibility.
2260 # _LIBCPP_HIDE_FROM_ABI is a macro in libc++ versions in NDKs >=r19. It too
2261 # declares hidden visibility, but it also declares functions as excluded from
2262 # explicit instantiation (roughly: the function can be unused in the current
2263 # compilation, but does not then trigger an actual definition of the function;
2264 # it is assumed the real definition comes from elsewhere). We need to replicate
2268 @depends(c_compiler, target)
2269 def libcxx_override_visibility(c_compiler, target):
2270 if c_compiler.type == "clang" and target.os == "Android":
2273 hide_from_abi="__attribute__((__exclude_from_explicit_instantiation__))",
2277 set_define("_LIBCPP_INLINE_VISIBILITY", libcxx_override_visibility.empty)
2278 set_define("_LIBCPP_ALWAYS_INLINE", libcxx_override_visibility.empty)
2280 set_define("_LIBCPP_HIDE_FROM_ABI", libcxx_override_visibility.hide_from_abi)
2283 @depends(target, build_environment)
2284 def visibility_flags(target, env):
2285 if target.os != "WINNT":
2286 if target.kernel == "Darwin":
2287 return ("-fvisibility=hidden", "-fvisibility-inlines-hidden")
2289 "-I%s/system_wrappers" % os.path.join(env.dist),
2291 "%s/config/gcc_hidden.h" % env.topsrcdir,
2295 @depends(target, visibility_flags)
2296 def wrap_system_includes(target, visibility_flags):
2297 if visibility_flags and target.kernel != "Darwin":
2302 "HAVE_VISIBILITY_HIDDEN_ATTRIBUTE",
2303 depends(visibility_flags)(lambda v: bool(v) or None),
2306 "HAVE_VISIBILITY_ATTRIBUTE", depends(visibility_flags)(lambda v: bool(v) or None)
2308 set_config("WRAP_SYSTEM_INCLUDES", wrap_system_includes)
2309 set_config("VISIBILITY_FLAGS", visibility_flags)
2312 # try harder, when checking for __thread support, see bug 521750 comment #33 and below
2313 # We pass linker_optimize_flags to the linker because if dead_strip is
2314 # enabled, the linker in xcode 4.1 will crash. Without this it would crash when
2318 @depends(target, c_compiler)
2319 def check_thread(target, c_compiler):
2320 if target.cpu in ("mips32", "mips64"):
2321 # mips builds fail with TLS variables because of a binutils bug.
2324 if target.os == "Android":
2325 # The custom dynamic linker doesn't support TLS variables
2327 if target.kernel == "OpenBSD":
2328 # OpenBSD doesn't have TLS support, and the test succeeds with clang++
2330 return c_compiler.type != "clang-cl"
2334 "HAVE_THREAD_TLS_KEYWORD",
2336 body="static __thread bool tlsIsMainThread = false; return tlsIsMainThread;",
2337 flags=linker_optimize_flags.ldflags,
2338 check_msg="for __thread keyword for TLS variables",
2345 def depend_cflags(host_or_target_c_compiler):
2346 @depends(host_or_target_c_compiler)
2347 def depend_cflags(host_or_target_c_compiler):
2348 if host_or_target_c_compiler.type != "clang-cl":
2349 return ["-MD", "-MP", "-MF $(MDDEPDIR)/$(@F).pp"]
2351 # clang-cl doesn't accept the normal -MD -MP -MF options that clang
2352 # does, but the underlying cc1 binary understands how to generate
2353 # dependency files. These options are based on analyzing what the
2354 # normal clang driver sends to cc1 when given the "correct"
2355 # dependency options.
2362 "$(MDDEPDIR)/$(@F).pp",
2369 return depend_cflags
2372 set_config("_DEPEND_CFLAGS", depend_cflags(c_compiler))
2373 set_config("_HOST_DEPEND_CFLAGS", depend_cflags(host_c_compiler))
2376 @depends(c_compiler)
2377 def preprocess_option(compiler):
2378 # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
2379 if compiler.type in ("gcc", "clang"):
2385 set_config("PREPROCESS_OPTION", preprocess_option)
2388 # We only want to include windows.configure when we are compiling on
2389 # Windows, or for Windows.
2390 include("windows.configure", when=is_windows)
2393 # On Power ISA, determine compiler flags for VMX, VSX and VSX-3.
2398 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2404 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2409 ["-mvsx", "-mcpu=power9"],
2410 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2414 # ==============================================================
2416 option("--enable-address-sanitizer", help="Enable Address Sanitizer")
2419 @depends(when="--enable-address-sanitizer")
2424 add_old_configure_assignment("MOZ_ASAN", asan)
2427 # ==============================================================
2429 option("--enable-memory-sanitizer", help="Enable Memory Sanitizer")
2432 @depends(when="--enable-memory-sanitizer")
2437 add_old_configure_assignment("MOZ_MSAN", msan)
2440 # ==============================================================
2442 option("--enable-thread-sanitizer", help="Enable Thread Sanitizer")
2445 @depends(when="--enable-thread-sanitizer")
2450 add_old_configure_assignment("MOZ_TSAN", tsan)
2453 # ==============================================================
2456 "--enable-undefined-sanitizer", nargs="*", help="Enable UndefinedBehavior Sanitizer"
2460 @depends("--enable-undefined-sanitizer", moz_optimize.optimize)
2461 def ubsan(options, optimize):
2470 "integer-divide-by-zero",
2476 # adding object-size generates a warning if -O0 is set
2478 default_checks.append("object-size")
2480 checks = options if len(options) else default_checks
2482 return ",".join(checks)
2485 add_old_configure_assignment("MOZ_UBSAN_CHECKS", ubsan)
2489 "--enable-signed-overflow-sanitizer",
2490 help="Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)",
2494 @depends(when="--enable-signed-overflow-sanitizer")
2495 def ub_signed_overflow_san():
2499 add_old_configure_assignment("MOZ_SIGNED_OVERFLOW_SANITIZE", ub_signed_overflow_san)
2503 "--enable-unsigned-overflow-sanitizer",
2504 help="Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)",
2508 @depends(when="--enable-unsigned-overflow-sanitizer")
2509 def ub_unsigned_overflow_san():
2513 add_old_configure_assignment("MOZ_UNSIGNED_OVERFLOW_SANITIZE", ub_unsigned_overflow_san)
2516 # Security Hardening
2517 # ==============================================================
2520 "--enable-hardening",
2521 env="MOZ_SECURITY_HARDENING",
2522 help="Enables security hardening compiler options",
2526 # This function is a bit confusing. It adds or removes hardening flags in
2527 # three stuations: if --enable-hardening is passed; if --disable-hardening
2528 # is passed, and if no flag is passed.
2530 # At time of this comment writing, all flags are actually added in the
2531 # default no-flag case; making --enable-hardening the same as omitting the
2532 # flag. --disable-hardening will omit the security flags. (However, not all
2533 # possible security flags will be omitted by --disable-hardening, as many are
2534 # compiler-default options we do not explicitly enable.)
2536 "--enable-hardening",
2537 "--enable-address-sanitizer",
2539 "--enable-optimize",
2543 def security_hardening_cflags(
2544 hardening_flag, asan, debug, optimize, c_compiler, target
2546 compiler_is_gccish = c_compiler.type in ("gcc", "clang")
2547 mingw_clang = c_compiler.type == "clang" and target.os == "WINNT"
2551 trivial_auto_var_init = []
2553 # WASI compiler doesn't support security hardening cflags
2554 if target.os == "WASI":
2557 # ----------------------------------------------------------
2558 # If hardening is explicitly enabled, or not explicitly disabled
2559 if hardening_flag.origin == "default" or hardening_flag:
2560 # FORTIFY_SOURCE ------------------------------------
2561 # Require optimization for FORTIFY_SOURCE. See Bug 1417452
2562 # Also, undefine it before defining it just in case a distro adds it, see Bug 1418398
2563 if compiler_is_gccish and optimize and not asan:
2564 flags.append("-U_FORTIFY_SOURCE")
2565 flags.append("-D_FORTIFY_SOURCE=2")
2567 # fstack-protector ------------------------------------
2568 # Enable only if hardening is not disabled and ASAN is
2569 # not on as ASAN will catch the crashes for us
2570 if compiler_is_gccish and not asan:
2571 flags.append("-fstack-protector-strong")
2572 ldflags.append("-fstack-protector-strong")
2575 c_compiler.type == "clang"
2576 and c_compiler.version >= "11.0.1"
2577 and target.os not in ("WINNT", "OSX", "OpenBSD")
2578 and target.cpu in ("x86", "x86_64", "ppc64", "s390x")
2580 flags.append("-fstack-clash-protection")
2581 ldflags.append("-fstack-clash-protection")
2583 # ftrivial-auto-var-init ------------------------------
2584 # Initialize local variables with a 0xAA pattern in clang builds.
2585 # Linux32 fails some xpcshell tests with -ftrivial-auto-var-init
2586 linux32 = target.kernel == "Linux" and target.cpu == "x86"
2588 (c_compiler.type == "clang" or c_compiler.type == "clang-cl")
2589 and c_compiler.version >= "8"
2592 if c_compiler.type == "clang-cl":
2593 trivial_auto_var_init.append("-Xclang")
2594 trivial_auto_var_init.append("-ftrivial-auto-var-init=pattern")
2595 # Always enable on debug builds.
2597 flags.extend(trivial_auto_var_init)
2599 # ASLR ------------------------------------------------
2600 # ASLR (dynamicbase) is enabled by default in clang-cl; but the
2601 # mingw-clang build requires it to be explicitly enabled
2603 ldflags.append("-Wl,--dynamicbase")
2605 # Control Flow Guard (CFG) ----------------------------
2607 c_compiler.type == "clang-cl"
2608 and c_compiler.version >= "8"
2609 and (target.cpu != "aarch64" or c_compiler.version >= "8.0.1")
2611 if target.cpu == "aarch64" and c_compiler.version >= "10.0.0":
2612 # The added checks in clang 10 make arm64 builds crash. (Bug 1639318)
2613 flags.append("-guard:cf,nochecks")
2615 flags.append("-guard:cf")
2616 # nolongjmp is needed because clang doesn't emit the CFG tables of
2617 # setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057
2618 ldflags.append("-guard:cf,nolongjmp")
2620 # ----------------------------------------------------------
2621 # If ASAN _is_ on, disable FORTIFY_SOURCE just to be safe
2623 flags.append("-D_FORTIFY_SOURCE=0")
2625 # fno-common -----------------------------------------
2626 # Do not merge variables for ASAN; can detect some subtle bugs
2628 # clang-cl does not recognize the flag, it must be passed down to clang
2629 if c_compiler.type == "clang-cl":
2630 flags.append("-Xclang")
2631 flags.append("-fno-common")
2636 trivial_auto_var_init=trivial_auto_var_init,
2640 set_config("MOZ_HARDENING_CFLAGS", security_hardening_cflags.flags)
2641 set_config("MOZ_HARDENING_LDFLAGS", security_hardening_cflags.ldflags)
2643 "MOZ_TRIVIAL_AUTO_VAR_INIT",
2644 security_hardening_cflags.trivial_auto_var_init,
2648 # Intel Control-flow Enforcement Technology
2649 # ==============================================================
2650 # We keep this separate from the hardening flags above, because we want to be
2651 # able to easily remove the flags in the build files for certain executables.
2652 @depends(c_compiler, target)
2653 def cet_ldflags(c_compiler, target):
2656 c_compiler.type == "clang-cl"
2657 and c_compiler.version >= "11"
2658 and target.cpu == "x86_64"
2660 ldflags.append("-CETCOMPAT")
2664 set_config("MOZ_CETCOMPAT_LDFLAGS", cet_ldflags)
2668 # ==============================================================
2669 @depends(c_compiler)
2670 def frame_pointer_flags(compiler):
2671 if compiler.type == "clang-cl":
2677 enable=["-fno-omit-frame-pointer", "-funwind-tables"],
2678 disable=["-fomit-frame-pointer", "-funwind-tables"],
2683 moz_optimize.optimize,
2686 "--enable-memory-sanitizer",
2687 "--enable-address-sanitizer",
2688 "--enable-undefined-sanitizer",
2690 def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
2697 or (target.os == "WINNT" and target.cpu in ("x86", "aarch64"))
2698 or target.os == "OSX"
2703 "--enable-frame-pointers",
2704 default=frame_pointer_default,
2705 help="{Enable|Disable} frame pointers",
2709 @depends("--enable-frame-pointers", frame_pointer_flags)
2710 def frame_pointer_flags(enable, flags):
2713 return flags.disable
2716 set_config("MOZ_FRAMEPTR_FLAGS", frame_pointer_flags)
2719 # Stack unwinding without frame pointers
2720 # ==============================================================
2723 have_unwind = check_symbol(
2724 "_Unwind_Backtrace", when=check_header("unwind.h", when=target_is_unix)
2729 # ==============================================================
2731 option("--enable-coverage", env="MOZ_CODE_COVERAGE", help="Enable code coverage")
2734 @depends("--enable-coverage")
2735 def code_coverage(value):
2740 set_config("MOZ_CODE_COVERAGE", code_coverage)
2741 set_define("MOZ_CODE_COVERAGE", code_coverage)
2744 @depends(target, c_compiler, build_environment, when=code_coverage)
2747 @imports(_from="__builtin__", _import="open")
2748 def coverage_cflags(target, c_compiler, build_env):
2749 cflags = ["--coverage"]
2751 # clang 11 no longer accepts this flag (its behavior became the default)
2752 if c_compiler.type in ("clang", "clang-cl") and c_compiler.version < "11.0.0":
2755 "-coverage-no-function-names-in-data",
2759 if target.os == "WINNT" and c_compiler.type == "clang-cl":
2761 exclude.append("^.*[vV][sS]20[0-9]{2}.*$")
2762 # Files in fetches directory.
2763 exclude.append("^.*[\\\\/]fetches[\\\\/].*$")
2764 elif target.os == "OSX":
2765 # Files in fetches directory.
2766 exclude.append("^.*/fetches/.*$")
2767 elif target.os == "GNU":
2768 # Files in fetches directory.
2769 exclude.append("^.*/fetches/.*$")
2771 exclude.append("^/usr/.*$")
2774 exclude = ";".join(exclude)
2776 f"-fprofile-exclude-files={exclude}",
2779 response_file_path = os.path.join(build_env.topobjdir, "code_coverage_cflags")
2781 with open(response_file_path, "w") as f:
2782 f.write(" ".join(cflags))
2784 return ["@{}".format(response_file_path)]
2787 set_config("COVERAGE_CFLAGS", coverage_cflags)
2789 # Assembler detection
2790 # ==============================================================
2792 option(env="AS", nargs=1, help="Path to the assembler")
2795 @depends(target, c_compiler)
2796 def as_info(target, c_compiler):
2797 if c_compiler.type == "clang-cl":
2800 "x86_64": "ml64.exe",
2801 "aarch64": "armasm64.exe",
2803 return namespace(type="masm", names=(ml,))
2804 # When building with anything but clang-cl, we just use the C compiler as the assembler.
2805 return namespace(type="gcc", names=(c_compiler.compiler,))
2808 # One would expect the assembler to be specified merely as a program. But in
2809 # cases where the assembler is passed down into js/, it can be specified in
2810 # the same way as CC: a program + a list of argument flags. We might as well
2811 # permit the same behavior in general, even though it seems somewhat unusual.
2812 # So we have to do the same sort of dance as we did above with
2813 # `provided_compiler`.
2814 provided_assembler = provided_program("AS")
2815 assembler = check_prog(
2817 input=provided_assembler.program,
2818 what="the assembler",
2819 progs=as_info.names,
2820 paths=vc_toolchain_search_path,
2824 @depends(as_info, assembler, provided_assembler, c_compiler)
2825 def as_with_flags(as_info, assembler, provided_assembler, c_compiler):
2826 if provided_assembler:
2827 return provided_assembler.wrapper + [assembler] + provided_assembler.flags
2829 if as_info.type == "masm":
2832 assert as_info.type == "gcc"
2834 # Need to add compiler wrappers and flags as appropriate.
2835 return c_compiler.wrapper + [assembler] + c_compiler.flags
2838 set_config("AS", as_with_flags)
2841 @depends(assembler, c_compiler, extra_toolchain_flags)
2842 @imports("subprocess")
2843 @imports(_from="os", _import="devnull")
2844 def gnu_as(assembler, c_compiler, toolchain_flags):
2845 # clang uses a compatible GNU assembler.
2846 if c_compiler.type == "clang":
2849 if c_compiler.type == "gcc":
2850 cmd = [assembler] + c_compiler.flags
2852 cmd += toolchain_flags
2853 cmd += ["-Wa,--version", "-c", "-o", devnull, "-x", "assembler", "-"]
2854 # We don't actually have to provide any input on stdin, `Popen.communicate` will
2855 # close the stdin pipe.
2856 # clang will error if it uses its integrated assembler for this target,
2857 # so handle failures gracefully.
2858 if "GNU" in check_cmd_output(*cmd, stdin=subprocess.PIPE, onerror=lambda: ""):
2862 set_config("GNU_AS", gnu_as)
2865 @depends(as_info, target)
2866 def as_dash_c_flag(as_info, target):
2867 # armasm64 doesn't understand -c.
2868 if as_info.type == "masm" and target.cpu == "aarch64":
2874 set_config("AS_DASH_C_FLAG", as_dash_c_flag)
2877 @depends(as_info, target)
2878 def as_outoption(as_info, target):
2879 # The uses of ASOUTOPTION depend on the spacing for -o/-Fo.
2880 if as_info.type == "masm" and target.cpu != "aarch64":
2886 set_config("ASOUTOPTION", as_outoption)
2888 # clang plugin handling
2889 # ==============================================================
2892 "--enable-clang-plugin",
2893 env="ENABLE_CLANG_PLUGIN",
2894 help="Enable building with the Clang plugin (gecko specific static analyzers)",
2897 add_old_configure_assignment(
2898 "ENABLE_CLANG_PLUGIN", depends_if("--enable-clang-plugin")(lambda _: True)
2902 @depends(host_c_compiler, c_compiler, when="--enable-clang-plugin")
2903 def llvm_config(host_c_compiler, c_compiler):
2905 for compiler in (host_c_compiler, c_compiler):
2906 if compiler and compiler.type == "clang":
2907 clang = compiler.compiler
2909 elif compiler and compiler.type == "clang-cl":
2910 clang = os.path.join(os.path.dirname(compiler.compiler), "clang")
2914 die("Cannot --enable-clang-plugin when not building with clang")
2915 llvm_config = "llvm-config"
2916 out = check_cmd_output(clang, "--print-prog-name=llvm-config", onerror=lambda: None)
2918 llvm_config = out.rstrip()
2919 return (llvm_config,)
2922 llvm_config = check_prog(
2926 when="--enable-clang-plugin",
2927 paths=clang_search_path,
2930 add_old_configure_assignment("LLVM_CONFIG", llvm_config)
2934 "--enable-clang-plugin-alpha",
2935 env="ENABLE_CLANG_PLUGIN_ALPHA",
2936 help="Enable static analysis with clang-plugin alpha checks.",
2940 @depends("--enable-clang-plugin", "--enable-clang-plugin-alpha")
2941 def check_clang_plugin_alpha(enable_clang_plugin, enable_clang_plugin_alpha):
2942 if enable_clang_plugin_alpha:
2943 if enable_clang_plugin:
2945 die("Cannot enable clang-plugin alpha checkers without --enable-clang-plugin.")
2948 add_old_configure_assignment("ENABLE_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha)
2949 set_define("MOZ_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha)
2952 "--enable-mozsearch-plugin",
2953 env="ENABLE_MOZSEARCH_PLUGIN",
2954 help="Enable building with the mozsearch indexer plugin",
2957 add_old_configure_assignment(
2958 "ENABLE_MOZSEARCH_PLUGIN", depends_if("--enable-mozsearch-plugin")(lambda _: True)
2961 # Use the old libstdc++ ABI
2962 # ==============================================================
2964 "-D_GLIBCXX_USE_CXX11_ABI=0",
2969 "-D_GLIBCXX_USE_CXX11_ABI=0",
2975 # Support various fuzzing options
2976 # ==============================================================
2977 option("--enable-fuzzing", help="Enable fuzzing support")
2980 @depends(build_project)
2981 def js_build(build_project):
2982 return build_project == "js"
2986 "--enable-js-fuzzilli",
2988 help="Enable fuzzilli support for the JS engine",
2993 "--enable-snapshot-fuzzing",
2994 help="Enable experimental snapshot fuzzing support",
2998 imply_option("--enable-fuzzing", True, when="--enable-snapshot-fuzzing")
3001 @depends("--enable-snapshot-fuzzing")
3002 def enable_snapshot_fuzzing(value):
3007 @depends("--enable-fuzzing", enable_snapshot_fuzzing)
3008 def enable_fuzzing(value, snapshot_fuzzing):
3009 if value or snapshot_fuzzing:
3013 @depends("--enable-js-fuzzilli", when=js_build)
3014 def enable_js_fuzzilli(value):
3019 @depends(enable_fuzzing, enable_snapshot_fuzzing)
3020 def check_aflfuzzer(fuzzing, snapshot_fuzzing):
3021 if fuzzing and not snapshot_fuzzing:
3027 body="__AFL_COMPILER;", check_msg="for AFL compiler", when=check_aflfuzzer
3030 def enable_aflfuzzer(afl):
3035 @depends(enable_fuzzing, enable_aflfuzzer, enable_snapshot_fuzzing, c_compiler, target)
3036 def enable_libfuzzer(fuzzing, afl, snapshot_fuzzing, c_compiler, target):
3040 and not snapshot_fuzzing
3041 and c_compiler.type == "clang"
3042 and target.os != "Android"
3047 @depends(enable_fuzzing, enable_aflfuzzer, enable_libfuzzer, enable_js_fuzzilli)
3048 def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer, enable_js_fuzzilli):
3049 if fuzzing and (afl or libfuzzer) and not enable_js_fuzzilli:
3053 set_config("FUZZING", enable_fuzzing)
3054 set_define("FUZZING", enable_fuzzing)
3056 set_config("LIBFUZZER", enable_libfuzzer)
3057 set_define("LIBFUZZER", enable_libfuzzer)
3058 add_old_configure_assignment("LIBFUZZER", enable_libfuzzer)
3060 set_config("AFLFUZZ", enable_aflfuzzer)
3061 set_define("AFLFUZZ", enable_aflfuzzer)
3063 set_config("FUZZING_INTERFACES", enable_fuzzing_interfaces)
3064 set_define("FUZZING_INTERFACES", enable_fuzzing_interfaces)
3065 add_old_configure_assignment("FUZZING_INTERFACES", enable_fuzzing_interfaces)
3067 set_config("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
3068 set_define("FUZZING_JS_FUZZILLI", enable_js_fuzzilli)
3070 set_config("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
3071 set_define("FUZZING_SNAPSHOT", enable_snapshot_fuzzing)
3075 c_compiler.try_compile(
3076 flags=["-fsanitize=fuzzer-no-link"],
3077 when=enable_fuzzing,
3078 check_msg="whether the C compiler supports -fsanitize=fuzzer-no-link",
3083 def libfuzzer_flags(value, tsan, enable_js_fuzzilli):
3085 # With ThreadSanitizer, we should not use any libFuzzer instrumentation because
3086 # it is incompatible (e.g. there are races on global sanitizer coverage counters).
3087 # Instead we use an empty set of flags here but still build the fuzzing targets.
3088 # With this setup, we can still run files through these targets in TSan builds,
3089 # e.g. those obtained from regular fuzzing.
3090 # This code can be removed once libFuzzer has been made compatible with TSan.
3092 # Also, this code needs to be kept in sync with certain gyp files, currently:
3093 # - dom/media/webrtc/transport/third_party/nICEr/nicer.gyp
3094 return namespace(no_link_flag_supported=False, use_flags=[])
3096 if enable_js_fuzzilli:
3097 # Fuzzilli comes with its own trace-pc interceptors and flag requirements.
3098 no_link_flag_supported = False
3099 use_flags = ["-fsanitize-coverage=trace-pc-guard", "-g"]
3101 no_link_flag_supported = True
3102 # recommended for (and only supported by) clang >= 6
3103 use_flags = ["-fsanitize=fuzzer-no-link"]
3105 no_link_flag_supported = False
3106 use_flags = ["-fsanitize-coverage=trace-pc-guard,trace-cmp"]
3109 no_link_flag_supported=no_link_flag_supported,
3110 use_flags=use_flags,
3114 set_config("HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK", libfuzzer_flags.no_link_flag_supported)
3115 set_config("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
3116 add_old_configure_assignment("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags)
3118 # Shared library building
3119 # ==============================================================
3122 # XXX: The use of makefile constructs in these variables is awful.
3123 @depends(target, c_compiler)
3124 def make_shared_library(target, compiler):
3125 if target.os == "WINNT":
3126 if compiler.type == "gcc":
3128 mkshlib=["$(CXX)", "$(DSO_LDOPTS)", "-o", "$@"],
3129 mkcshlib=["$(CC)", "$(DSO_LDOPTS)", "-o", "$@"],
3131 elif compiler.type == "clang":
3136 "-Wl,-pdb,$(LINK_PDBFILE)",
3143 "-Wl,-pdb,$(LINK_PDBFILE)",
3154 "-PDB:$(LINK_PDBFILE)",
3162 cc = ["$(CC)", "$(COMPUTED_C_LDFLAGS)"]
3163 cxx = ["$(CXX)", "$(COMPUTED_CXX_LDFLAGS)"]
3164 flags = ["$(DSO_LDOPTS)"]
3165 output = ["-o", "$@"]
3167 if target.kernel == "Darwin":
3169 elif target.os == "NetBSD":
3170 soname = ["-Wl,-soname,$(DSO_SONAME)"]
3172 assert compiler.type in ("gcc", "clang")
3174 soname = ["-Wl,-h,$(DSO_SONAME)"]
3177 mkshlib=cxx + flags + soname + output,
3178 mkcshlib=cc + flags + soname + output,
3182 set_config("MKSHLIB", make_shared_library.mkshlib)
3183 set_config("MKCSHLIB", make_shared_library.mkcshlib)
3186 @depends(c_compiler, toolchain_prefix, when=target_is_windows)
3187 def rc_names(c_compiler, toolchain_prefix):
3188 if c_compiler.type in ("gcc", "clang"):
3189 return tuple("%s%s" % (p, "windres") for p in ("",) + (toolchain_prefix or ()))
3193 check_prog("RC", rc_names, paths=clang_search_path, when=target_is_windows)
3197 def ar_config(c_compiler, toolchain_prefix=None):
3198 if not toolchain_prefix:
3199 toolchain_prefix = dependable(None)
3201 @depends(toolchain_prefix, c_compiler)
3202 def ar_config(toolchain_prefix, c_compiler):
3203 if c_compiler.type == "clang-cl":
3205 names=("llvm-lib",),
3206 flags=("-llvmlibthin", "-out:$@"),
3209 names = tuple("%s%s" % (p, "ar") for p in (toolchain_prefix or ()) + ("",))
3210 if c_compiler.type == "clang":
3211 # Get the llvm-ar path as per the output from clang --print-prog-name=llvm-ar
3212 # so that we directly get the one under the clang directory, rather than one
3213 # that might be in /usr/bin and that might point to one from a different version
3215 out = check_cmd_output(
3216 c_compiler.compiler, "--print-prog-name=llvm-ar", onerror=lambda: None
3218 llvm_ar = out.rstrip() if out else "llvm-ar"
3219 names = (llvm_ar,) + names
3223 flags=("crs", "$@"),
3229 target_ar_config = ar_config(c_compiler, toolchain_prefix)
3231 target_ar = check_prog("AR", target_ar_config.names, paths=clang_search_path)
3233 set_config("AR_FLAGS", target_ar_config.flags)
3236 @depends(c_compiler, extra_toolchain_flags, target_ar, target_ar_config)
3237 @checking("whether ar supports response files")
3239 @imports(_from="tempfile", _import="mkstemp")
3240 @imports(_from="__builtin__", _import="FileNotFoundError")
3241 @imports(_from="mozbuild.configure.util", _import="LineIO")
3242 def ar_supports_response_files(c_compiler, extra_toolchain_flags, ar, ar_config):
3243 lib_path = list_path = None
3245 fd, obj_path = mkstemp(prefix="conftest.", suffix=".o")
3248 try_invoke_compiler(
3249 # No configure_cache because it would not create the
3250 # expected output file.
3252 [c_compiler.compiler] + c_compiler.flags,
3253 c_compiler.language,
3255 ["-c", "-o", obj_path] + (extra_toolchain_flags or []),
3256 wrapper=c_compiler.wrapper,
3257 onerror=lambda: None,
3261 fd, list_path = mkstemp(prefix="conftest.", suffix=".list")
3262 with os.fdopen(fd, "w") as list:
3263 list.write(obj_path)
3264 log.debug("Creating `%s` with content:", list_path)
3265 log.debug("| %s", obj_path)
3266 fd, lib_path = mkstemp(prefix="conftest.", suffix=".a")
3271 + [x.replace("$@", lib_path) for x in ar_config.flags]
3274 result = check_cmd_output(*ar_command, onerror=lambda: None)
3275 return result is not None
3277 for cleanup_path in (obj_path, list_path, lib_path):
3280 os.remove(cleanup_path)
3281 except FileNotFoundError:
3285 set_config("AR_SUPPORTS_RESPONSE_FILE", True, when=ar_supports_response_files)
3287 host_ar_config = ar_config(host_c_compiler)
3289 check_prog("HOST_AR", host_ar_config.names, paths=clang_search_path)
3292 @depends(toolchain_prefix, c_compiler)
3293 def nm_names(toolchain_prefix, c_compiler):
3294 names = tuple("%s%s" % (p, "nm") for p in (toolchain_prefix or ()) + ("",))
3295 if c_compiler.type == "clang":
3296 # Get the llvm-nm path as per the output from clang --print-prog-name=llvm-nm
3297 # so that we directly get the one under the clang directory, rather than one
3298 # that might be in /usr/bin and that might point to one from a different version
3300 out = check_cmd_output(
3301 c_compiler.compiler, "--print-prog-name=llvm-nm", onerror=lambda: None
3303 llvm_nm = out.rstrip() if out else "llvm-nm"
3304 names = (llvm_nm,) + names
3309 check_prog("NM", nm_names, paths=clang_search_path, when=target_has_linux_kernel)
3312 option("--enable-cpp-rtti", help="Enable C++ RTTI")
3314 add_old_configure_assignment("_MOZ_USE_RTTI", "1", when="--enable-cpp-rtti")
3318 "--enable-path-remapping",
3320 choices=("c", "rust"),
3321 help="Enable remapping source and object paths in compiled outputs.",
3325 @depends("--enable-path-remapping")
3326 def path_remapping(value):
3330 return ["c", "rust"]
3337 target_sysroot.path,
3338 valid_windows_sdk_dir,
3340 when="--enable-path-remapping",
3342 def path_remappings(target, build_env, sysroot_path, windows_sdk_dir, vc_path):
3343 win = target.kernel == "WINNT"
3345 # The prefix maps are processed in the order they're specified on the
3346 # command line. Therefore, to accommodate object directories in the source
3347 # directory, it's important that we map the topobjdir before the topsrcdir,
3348 # 'cuz we might have /src/obj/=/o/ and /src/=/s/. The various other
3349 # directories might be subdirectories of topsrcdir as well, so they come
3352 path_remappings = []
3354 # We will have only one sysroot or SDK, so all can have the same mnemonic: K
3355 # for "kit" (since S is taken for "source"). See
3356 # https://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html
3357 # for how to use the Windows `subst` command to map these in debuggers and
3360 path_remappings.append((sysroot_path, "k:/" if win else "/sysroot/"))
3362 path_remappings.append(
3363 (windows_sdk_dir.path, "k:/" if win else "/windows_sdk/")
3366 path_remappings.append((vc_path, "v:/" if win else "/vc/"))
3368 path_remappings += [
3369 (build_env.topobjdir, "o:/" if win else "/topobjdir/"),
3370 (build_env.topsrcdir, "s:/" if win else "/topsrcdir/"),
3374 (normsep(old).rstrip("/") + "/", new) for old, new in path_remappings
3377 # It is tempting to sort these, but we want the order to be the same across
3378 # machines so that we can share cache hits. Therefore we reject bad
3379 # configurations rather than trying to make the configuration good.
3380 for i in range(len(path_remappings) - 1):
3381 p = path_remappings[i][0]
3382 for q, _ in path_remappings[i + 1 :]:
3384 die(f"Cannot remap paths because {p} is an ancestor of {q}")
3386 return path_remappings
3390 def is_intel_target(target):
3391 return target.cpu in ("x86", "x86_64")
3395 def is_aarch64_target(target):
3396 return target.cpu == "aarch64"
3399 set_config("MMX_FLAGS", ["-mmmx"])
3400 set_config("SSE_FLAGS", ["-msse"])
3401 set_config("SSE2_FLAGS", ["-msse2"])
3402 set_config("SSSE3_FLAGS", ["-mssse3"])
3403 set_config("SSE4_2_FLAGS", ["-msse4.2"])
3404 set_config("FMA_FLAGS", ["-mfma"])
3405 set_config("AVX2_FLAGS", ["-mavx2"])
3410 check_msg="for -mavxvnni support", flags=["-mavxvnni"], when=is_intel_target
3415 ["-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3417 check_msg="for -mavx512bw support",
3418 flags=["-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3419 when=is_intel_target,
3423 # AVX512VNNI can be based on either avx512bw or avx512vbmi. We choose the
3427 ["-mavx512vnni", "-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3429 check_msg="for -mavx512vnni support",
3430 flags=["-mavx512vnni", "-mavx512bw", "-mavx512f", "-mavx512dq", "-mavx512cd"],
3431 when=is_intel_target,
3438 ["-march=armv8.2-a+i8mm"],
3440 check_msg="for i8mm target feature",
3441 flags=["-march=armv8.2-a+i8mm"],
3442 when=is_aarch64_target,
3448 option("--enable-dtrace", help="Build with dtrace support")
3450 dtrace = check_header(
3452 when="--enable-dtrace",
3453 onerror=lambda: die("dtrace enabled but sys/sdt.h not found"),
3456 set_config("HAVE_DTRACE", True, when=dtrace)
3457 set_define("INCLUDE_MOZILLA_DTRACE", True, when=dtrace)