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):
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)
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.
52 "--enable-macos-target",
53 env="MACOSX_DEPLOYMENT_TARGET",
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{|}",
66 @depends_if("--enable-macos-target", developer_options)
67 def macos_target(value, _):
71 with only_when(host_is_osx | target_is_osx):
78 help="Location of platform SDK to use",
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)
89 "Error parsing SDKSettings.plist in the SDK directory: %s" % sdk
91 if "Version" not in obj:
93 "Error finding Version information in SDKSettings.plist from the SDK: %s"
96 return Version(obj["Version"])
98 def sdk_min_version():
105 "MacOSX{}.sdk".format(sdk_min_version()),
106 when=depends("--with-macos-sdk")(lambda x: not x),
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):
119 version = get_sdk_version(sdk)
120 except Exception as e:
122 elif host.os == "OSX":
123 sdk = check_cmd_output(
124 "xcrun", "--show-sdk-path", onerror=lambda: ""
128 "Could not find the macOS SDK. Please use --with-macos-sdk to give "
129 "the path to a macOS SDK."
131 # Scan the parent directory xcrun returns for the most recent SDK.
132 sdk_dir = os.path.dirname(sdk)
134 for d in listdir(sdk_dir):
135 if d.lower().startswith("macos"):
137 sdk = os.path.join(sdk_dir, d)
138 versions.append((get_sdk_version(sdk), sdk))
141 version, sdk = max(versions)
144 "Need a macOS SDK when targeting macOS. Please use --with-macos-sdk "
145 "to give the path to a macOS SDK."
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."
155 if version < Version(sdk_min_version()):
157 'SDK version "%s" is too old. Please upgrade to at least %s. Try '
158 "updating your system Xcode." % (version, sdk_min_version())
162 set_config("MACOS_SDK_DIR", macos_sdk)
165 with only_when(target_is_osx):
166 with only_when(cross_compiling):
168 "--with-macos-private-frameworks",
169 env="MACOS_PRIVATE_FRAMEWORKS_DIR",
171 help="Location of private frameworks to use",
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]):
179 "PrivateFrameworks not found not found in %s. When using "
180 "--with-macos-private-frameworks, you must specify a valid "
186 @depends(macos_private_frameworks, macos_sdk)
187 def macos_private_frameworks(value, sdk):
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):
206 set_config("MOZ_HAZARD", hazard_analysis)
209 # Cross-compilation related things.
210 # ==============================================================
212 "--with-toolchain-prefix",
213 env="TOOLCHAIN_PREFIX",
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):
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
236 return toolchain_prefix[0]
239 set_config("TOOLCHAIN_PREFIX", first_toolchain_prefix)
240 add_old_configure_assignment("TOOLCHAIN_PREFIX", first_toolchain_prefix)
244 # ==============================================================
245 include("compilers-util.configure")
249 configure_cache, compiler, language, source, onerror=None, wrapper=[]
251 return try_invoke_compiler(
252 configure_cache, compiler, language, source, ["-E"], onerror, wrapper
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
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.
278 #if defined(_MSC_VER) && defined(__clang__) && defined(_MT)
280 %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
281 #elif defined(__clang__)
283 %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
284 # ifdef __apple_build_version__
287 #elif defined(__GNUC__) && !defined(__MINGW32__)
289 %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
293 %cplusplus __cplusplus
294 #elif __STDC_VERSION__
295 %STDC_VERSION __STDC_VERSION__
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),
308 for n, (value, condition) in enumerate(preprocessor_checks.items()):
311 #%(if)s %(condition)s
312 %%%(name)s "%(value)s"
315 "if": "elif" if n else "if",
316 "condition": condition,
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.
328 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
330 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
336 result = try_preprocess(configure_cache, compiler, language, check)
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.
344 for line in result.splitlines():
345 if line.startswith("%"):
346 k, _, v = line.partition(" ")
348 data[k] = v.replace(" ", "").lstrip('"').rstrip('"')
349 log.debug("%s = %s", k, data[k])
352 type = CompilerType(data["COMPILER"])
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")
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
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")
389 version = Version("14.0.0.or.more")
395 kernel=data.get("KERNEL"),
396 endianness=data.get("ENDIANNESS"),
398 language="C++" if cplusplus else "C",
399 language_version=cplusplus if cplusplus else stdc_version,
400 xcode=bool(data.get("XCODE")),
404 def same_arch_different_bits():
408 ("sparc", "sparc64"),
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)
419 # Check language standards
420 # --------------------------------------------------------------------
421 if language != info.language:
422 raise FatalCheckError(
423 "`%s` is not a %s compiler." % (quote(*compiler), language)
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
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")
444 flags.append("-std=gnu++17")
446 # Check compiler target
447 # --------------------------------------------------------------------
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)
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
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")
470 or info.kernel != target.kernel
471 or not info.endianness
472 or info.endianness != target.endianness
474 flags.append("--target=%s" % toolchain)
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
483 flags.append("--target=%s" % toolchain)
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:
490 elif (info.cpu, target.cpu) in same_arch:
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)
499 version=info.version,
501 target_kernel=info.kernel,
502 target_endianness=info.endianness,
508 @imports(_from="__builtin__", _import="open")
511 def get_vc_paths(topsrcdir):
513 program_files = os.environ.get("PROGRAMFILES(X86)") or os.environ.get(
516 if not program_files:
518 vswhere = os.path.join(
519 program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe"
521 if not os.path.exists(vswhere):
523 return json.loads(check_cmd_output(vswhere, "-format", "json", *args))
525 for install in vswhere(
530 "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
533 path = install["installationPath"]
537 path, r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"
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.
559 help='Path to a Windows "sysroot" (directory containing MSVC, SDKs)',
567 when=depends("WINSYSROOT", when=is_windows)(lambda x: not x),
571 def winsysroot(winsysroot, bootstrapped):
582 help="Path to the Microsoft Visual C/C++ compiler",
594 @imports(_from="operator", _import="itemgetter")
595 def vc_compiler_paths_for_version(host, env, vc_path, winsysroot):
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)))]
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.
609 all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
612 # Choose the newest version.
613 path = all_versions[-1][1]
619 path = os.path.join(path, "bin", host_dir)
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")],
628 @depends(target, vc_compiler_paths_for_version, when=is_windows)
629 def vc_compiler_path(target, paths):
638 return paths.get(vc_target)
641 @depends(vc_compiler_path, original_path)
643 @imports(_from="os", _import="environ")
644 def vc_toolchain_search_path(vc_compiler_path, original_path):
645 result = list(original_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])
659 @depends_if(vc_compiler_path, when=is_windows)
660 def vc_compiler_version(vc_compiler_path):
663 os.path.dirname(os.path.dirname(os.path.dirname(vc_compiler_path[0])))
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"):
685 if vc_compiler_version >= Version("19.20"):
687 if vc_compiler_version >= Version("19.10"):
693 set_config("MSVS_VERSION", msvs_version)
696 clang_search_path = bootstrap_search_path("clang/bin")
700 bootstrap_search_path("rustc/bin", when="MOZ_AUTOMATION"),
701 bootstrap_search_path_order,
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", "")
711 cargo_home = os.path.abspath(cargo_home)
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)
718 result.append(rustup_path)
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.
726 mozillabuild_bin_paths, clang_search_path, rust_search_path, target, original_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
740 # cargo needs the rust search path to find cargo-$subcommand.
741 path += rust_search_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)
752 # ==============================================================
754 "--with-compiler-wrapper",
755 env="COMPILER_WRAPPER",
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")
767 # If --with-ccache was given without an explicit value, we default to
776 paths=bootstrap_search_path(
777 "sccache", when=depends("CCACHE")(lambda c: len(c) and c[0] == "sccache")
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.
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")
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:
821 sccache %s or later is required. sccache in use at %s has
824 Please upgrade or acquire a new version with |./mach bootstrap|.
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"
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):
859 raw_wrapper = wrapper[0]
860 wrapper = shell_split(raw_wrapper)
861 wrapper_program = find_program(wrapper[0])
862 if not wrapper_program:
864 "Cannot find `%s` from the given compiler wrapper `%s`",
868 wrapper[0] = wrapper_program
872 return tuple([ccache] + wrapper)
876 return tuple(wrapper)
879 @depends_if(compiler_wrapper)
880 def using_compiler_wrapper(compiler_wrapper):
884 set_config("MOZ_USING_COMPILER_WRAPPER", using_compiler_wrapper)
889 return split_triplet("wasm32-wasi", allow_wasi=True)
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
896 `host_or_target` is either `host` or `target` (the @depends functions
898 `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
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
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",)
915 supported = types = ("clang-cl", "clang")
916 elif host_or_target.kernel == "Darwin":
918 supported = ("clang", "gcc")
919 elif host_or_target.kernel == "WASI":
920 supported = types = ("clang",)
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()
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
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.
945 types = [prioritized] + [t for t in types if t != info.type]
948 if toolchain_prefix and host_or_target is target:
949 gcc = tuple("%sgcc" % p for p in toolchain_prefix) + gcc
960 return default_c_compilers
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
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.
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)
985 @depends(c_compiler, *other_compilers)
986 def default_cxx_compilers(c_compiler, *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
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.
1012 @depends_if(env_var, when=when)
1013 @imports(_from="itertools", _import="takewhile")
1014 @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split")
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))
1025 wrapper=without_flags[:-1],
1026 program=without_flags[-1],
1027 flags=cmd[len(without_flags) :],
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)
1050 assert host_or_target is target
1051 host_or_target_str = "target"
1052 opt = "--with-sysroot"
1054 when = target_is_linux_or_wasi
1055 bootstrap_when = bootstrap_target_when
1062 help="Use the given sysroot directory for %s build" % host_or_target_str,
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
1070 and input.origin == "default"
1078 depends(host_or_target)(lambda t: "sysroot-{}".format(t.toolchain)),
1079 when=bootstrap_sysroot,
1083 def sysroot(sysroot_input, host_or_target, macos_sdk, path):
1086 path = sysroot_input[0]
1087 elif host_or_target.kernel == "Darwin" and macos_sdk:
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)
1098 bootstrapped=bool(path and not sysroot_input),
1099 stdcxx_version=version,
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.
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:
1119 "%s is not supported with bootstrapped sysroot. "
1120 "Drop the option, or use --without-sysroot or --disable-bootstrap",
1125 host_sysroot = sysroot(host, target_sysroot)
1129 def multiarch_dir(host_or_target):
1132 target: target_sysroot,
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")
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`.
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 = {
1187 target: target_sysroot,
1188 wasm: dependable(lambda: namespace(path=None)),
1192 host: host_multiarch_dir,
1193 target: target_multiarch_dir,
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
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(
1228 progs=default_compilers,
1229 input=provided_compiler.program,
1230 paths=clang_search_path,
1242 vc_compiler_version,
1247 @checking("whether %s can be used" % what, lambda x: bool(x))
1248 @imports(_from="mozbuild.shellutil", _import="quote")
1259 vc_compiler_version,
1264 wrapper = list(compiler_wrapper or ())
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))
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(
1280 wrapper + [compiler] + flags,
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
1310 info = check_compiler(
1312 wrapper + [compiler] + flags,
1317 except FatalCheckError:
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)"
1324 host_or_target_str.capitalize(),
1326 info.target_cpu or "unknown",
1328 host_or_target.raw_cpu,
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)"
1336 host_or_target_str.capitalize(),
1338 info.target_kernel or "unknown",
1340 host_or_target.kernel,
1344 if not info.target_endianness or (
1345 info.target_endianness != host_or_target.endianness
1347 raise FatalCheckError(
1348 "%s %s compiler target endianness (%s) does not match --%s "
1351 host_or_target_str.capitalize(),
1353 info.target_endianness or "unknown",
1355 host_or_target.endianness,
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."
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)
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
1383 "usr/include/c++/{}".format(version),
1384 "usr/include/{}/c++/{}".format(multiarch_dir, version),
1385 "usr/include/{}".format(multiarch_dir),
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)"
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"
1407 [compiler] + flags + overlay_flags,
1410 onerror=lambda: False,
1414 flags.extend(overlay_flags)
1416 if (info.type, host_or_target.abi) in (
1418 ("clang-cl", "mingw"),
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)."
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
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)
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)
1450 raise FatalCheckError("Unknown compiler or compiler not supported.")
1457 version=info.version,
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:
1472 "The %s C compiler is %s, while the %s C++ compiler is "
1473 "%s. Need to use the same compiler suite.",
1480 if compiler.version != c_compiler.version:
1482 "The %s C compiler is version %s, while the %s C++ "
1483 "compiler is version %s. Need to use the same 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
1495 add_old_configure_assignment(
1497 depends_if(valid_compiler)(
1498 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)
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)
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"),
1515 gcc_like = depends(valid_compiler.type)(
1516 lambda x: "yes" if x in ("gcc", "clang") else "no"
1518 add_old_configure_assignment("ac_cv_prog_%s_g" % var.lower(), gcc_like)
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.
1527 set_config("%s_TYPE" % var, valid_compiler.type)
1528 add_old_configure_assignment("%s_TYPE" % var, valid_compiler.type)
1530 "%s_VERSION" % var, depends(valid_compiler.version)(lambda v: str(v))
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)
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:
1554 preprocessor = depends_if(valid_compiler)(
1555 lambda x: list(x.wrapper) + [x.compiler, "-E"] + list(x.flags)
1558 set_config(pp_var, preprocessor)
1559 add_old_configure_assignment(pp_var, preprocessor)
1565 }.get(host_or_target)
1569 @deprecated_option(env=linker_var, nargs=1)
1575 def unused_linker(linker):
1578 "The value of %s is not used by this build system." % linker_var
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(
1590 c_compiler=host_c_compiler,
1591 other_compiler=cxx_compiler,
1592 other_c_compiler=c_compiler,
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":
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)
1627 def cxx_is_icecream(info, ccache_prefix):
1629 os.path.islink(info.compiler)
1630 and os.path.basename(os.readlink(info.compiler)) == "icecc"
1633 if ccache_prefix and os.path.basename(ccache_prefix) == "icecc":
1637 set_config("CXX_IS_ICECREAM", cxx_is_icecream)
1641 # ==============================================================
1642 # The policy is as follows:
1644 # - the linker is picked via the LINKER environment variable per windows.configure,
1645 # but ought to be lld-link in any case.
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
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.
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"):
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):
1673 die("--enable-gold is deprecated, use --enable-linker=gold instead")
1675 die("--disable-gold is deprecated, use --enable-linker=something_else instead")
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):
1694 def select_linker_tmpl(host_or_target):
1695 if host_or_target is target:
1700 extra_toolchain_flags,
1702 when=is_linker_option_enabled,
1704 host_or_target_str = "target"
1712 when=is_not_winnt_or_sunos(host_or_target),
1714 host_or_target_str = "host"
1717 @checking(f"for {host_or_target_str} linker", lambda x: x.KIND)
1720 def select_linker(linker, c_compiler, developer_options, toolchain_flags, target):
1726 def is_valid_linker(linker):
1727 if target.kernel == "Darwin":
1728 valid_linkers = ("ld64", "lld")
1730 valid_linkers = ("bfd", "gold", "lld", "mold")
1731 if linker in valid_linkers:
1733 if "lld" in valid_linkers and linker.startswith("lld-"):
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"]
1750 linker_flag = ["-fuse-ld=" + linker]
1753 cmd = cmd_base + linker_flag + version_check
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.
1768 retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
1769 if retcode == 1 and "Logging ld64 options" in stderr:
1775 elif "mold" in stdout:
1778 elif "GNU ld" in stdout:
1779 # We are using the normal linker
1782 elif "GNU gold" in stdout:
1785 elif "LLD" in stdout:
1791 if kind == "unknown" or is_valid_linker(kind):
1794 LINKER_FLAG=linker_flag,
1799 result = try_linker(linker)
1801 die("Could not use {} as linker".format(linker))
1805 and c_compiler.type == "clang"
1808 target.kernel != "Darwin"
1811 or host_or_target_str == "host"
1812 or c_compiler.version >= "15.0"
1816 target.kernel == "Darwin"
1818 (developer_options and c_compiler.version >= "13.0")
1819 or c_compiler.version >= "15.0"
1824 result = try_linker("lld")
1826 if result is None and developer_options:
1827 result = try_linker("gold")
1830 result = try_linker(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))
1841 return select_linker
1844 select_linker = select_linker_tmpl(target)
1845 set_config("LINKER_KIND", select_linker.KIND)
1849 def linker_ldflags_tmpl(host_or_target):
1850 if host_or_target is target:
1855 target_multiarch_dir,
1863 select_linker_tmpl(host),
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)
1898 c_compiler.type == "gcc"
1899 and sysroot.bootstrapped
1900 and sysroot.stdcxx_version
1903 "-L{}/usr/lib/gcc/{}/{}".format(
1904 sysroot.path, multiarch_dir, sysroot.stdcxx_version
1908 # BFD/gold linkers need a manual --rpath-link for indirect
1911 "-Wl,--rpath-link={}/usr/lib/{}".format(
1912 android_sysroot, target.toolchain
1914 "-Wl,--rpath-link={}/usr/lib/{}/{}".format(
1915 android_sysroot, target.toolchain, android_version
1921 and linker.KIND == "lld"
1922 and target.kernel != "WINNT"
1924 flags.append("-Wl,-O0")
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":
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"))
1960 body='static_assert(sizeof(void *) == 8, "")', check_msg="for 64-bit OS"
1963 def check_have_64_bit(have_64_bit, compiler_have_64_bit):
1964 if have_64_bit != compiler_have_64_bit:
1966 "The target compiler does not agree with configure "
1967 "about the target bitness."
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":
1977 if target.kernel not in ("Linux", "WINNT"):
1980 if target.os == "Android":
1986 def die_on_old_libstdcxx():
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."
1995 includes=["cstddef"],
1998 # _GLIBCXX_RELEASE showed up in libstdc++ 7.
1999 "#if defined(__GLIBCXX__) && !defined(_GLIBCXX_RELEASE)",
2000 "# error libstdc++ not new enough",
2002 "#if defined(_GLIBCXX_RELEASE)",
2003 "# if _GLIBCXX_RELEASE < %d" % minimum_gcc_version().major,
2004 "# error libstdc++ not new enough",
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":
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.
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))
2035 "--disable-debug-symbols",
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]
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)
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
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"
2080 return "-fcolor-diagnostics"
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):
2093 # // Capture the definition of FUNC.
2094 # inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
2096 # // Use a real inline definition.
2097 # inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
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
2135 @depends(c_compiler, target)
2136 def libcxx_override_visibility(c_compiler, target):
2137 if c_compiler.type == "clang" and target.os == "Android":
2140 hide_from_abi="__attribute__((__exclude_from_explicit_instantiation__))",
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")
2156 "-I%s/system_wrappers" % os.path.join(env.dist),
2158 "%s/config/gcc_hidden.h" % env.topsrcdir,
2162 @depends(target, visibility_flags)
2163 def wrap_system_includes(target, visibility_flags):
2164 if visibility_flags and target.kernel != "Darwin":
2169 "HAVE_VISIBILITY_HIDDEN_ATTRIBUTE",
2170 depends(visibility_flags)(lambda v: bool(v) or None),
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)
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"]
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.
2197 "$(MDDEPDIR)/$(@F).pp",
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"):
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.
2233 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2239 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2244 ["-mvsx", "-mcpu=power9"],
2245 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")),
2249 # ==============================================================
2251 option("--enable-address-sanitizer", help="Enable Address Sanitizer")
2254 @depends(when="--enable-address-sanitizer")
2259 add_old_configure_assignment("MOZ_ASAN", asan)
2262 # ==============================================================
2264 option("--enable-memory-sanitizer", help="Enable Memory Sanitizer")
2267 @depends(when="--enable-memory-sanitizer")
2272 add_old_configure_assignment("MOZ_MSAN", msan)
2275 # ==============================================================
2277 option("--enable-thread-sanitizer", help="Enable Thread Sanitizer")
2280 @depends(when="--enable-thread-sanitizer")
2285 add_old_configure_assignment("MOZ_TSAN", tsan)
2288 # ==============================================================
2291 "--enable-undefined-sanitizer", nargs="*", help="Enable UndefinedBehavior Sanitizer"
2295 @depends_if("--enable-undefined-sanitizer")
2302 "integer-divide-by-zero",
2309 checks = options if len(options) else default_checks
2311 return ",".join(checks)
2314 add_old_configure_assignment("MOZ_UBSAN_CHECKS", ubsan)
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():
2328 add_old_configure_assignment("MOZ_SIGNED_OVERFLOW_SANITIZE", ub_signed_overflow_san)
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():
2342 add_old_configure_assignment("MOZ_UNSIGNED_OVERFLOW_SANITIZE", ub_unsigned_overflow_san)
2345 # Security Hardening
2346 # ==============================================================
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.)
2365 "--enable-hardening",
2366 "--enable-address-sanitizer",
2368 "--enable-optimize",
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"
2380 trivial_auto_var_init = []
2382 # WASI compiler doesn't support security hardening cflags
2383 if target.os == "WASI":
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")
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")
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"
2417 (c_compiler.type == "clang" or c_compiler.type == "clang-cl")
2418 and c_compiler.version >= "8"
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.
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
2432 ldflags.append("-Wl,--dynamicbase")
2434 # Control Flow Guard (CFG) ----------------------------
2436 c_compiler.type == "clang-cl"
2437 and c_compiler.version >= "8"
2438 and (target.cpu != "aarch64" or c_compiler.version >= "8.0.1")
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")
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
2452 flags.append("-D_FORTIFY_SOURCE=0")
2454 # fno-common -----------------------------------------
2455 # Do not merge variables for ASAN; can detect some subtle bugs
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")
2465 trivial_auto_var_init=trivial_auto_var_init,
2469 set_config("MOZ_HARDENING_CFLAGS", security_hardening_cflags.flags)
2470 set_config("MOZ_HARDENING_LDFLAGS", security_hardening_cflags.ldflags)
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):
2485 c_compiler.type == "clang-cl"
2486 and c_compiler.version >= "11"
2487 and target.cpu == "x86_64"
2489 ldflags.append("-CETCOMPAT")
2493 set_config("MOZ_CETCOMPAT_LDFLAGS", cet_ldflags)
2497 # ==============================================================
2498 @depends(c_compiler)
2499 def frame_pointer_flags(compiler):
2500 if compiler.type == "clang-cl":
2506 enable=["-fno-omit-frame-pointer", "-funwind-tables"],
2507 disable=["-fomit-frame-pointer", "-funwind-tables"],
2512 moz_optimize.optimize,
2515 "--enable-memory-sanitizer",
2516 "--enable-address-sanitizer",
2517 "--enable-undefined-sanitizer",
2519 def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
2526 or (target.os == "WINNT" and target.cpu in ("x86", "aarch64"))
2527 or target.os == "OSX"
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):
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)
2558 # ==============================================================
2560 option("--enable-coverage", env="MOZ_CODE_COVERAGE", help="Enable code coverage")
2563 @depends("--enable-coverage")
2564 def code_coverage(value):
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)
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":
2584 "-coverage-no-function-names-in-data",
2588 if target.os == "WINNT" and c_compiler.type == "clang-cl":
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/.*$")
2600 exclude.append("^/usr/.*$")
2603 exclude = ";".join(exclude)
2605 f"-fprofile-exclude-files={exclude}",
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":
2629 "x86_64": "ml64.exe",
2630 "aarch64": "armasm64.exe",
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(
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":
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":
2678 if c_compiler.type == "gcc":
2679 cmd = [assembler] + c_compiler.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: ""):
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":
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":
2715 set_config("ASOUTOPTION", as_outoption)
2717 # clang plugin handling
2718 # ==============================================================
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):
2734 for compiler in (host_c_compiler, c_compiler):
2735 if compiler and compiler.type == "clang":
2736 clang = compiler.compiler
2738 elif compiler and compiler.type == "clang-cl":
2739 clang = os.path.join(os.path.dirname(compiler.compiler), "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)
2747 llvm_config = out.rstrip()
2748 return (llvm_config,)
2751 llvm_config = check_prog(
2755 when="--enable-clang-plugin",
2756 paths=clang_search_path,
2759 add_old_configure_assignment("LLVM_CONFIG", llvm_config)
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:
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)
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))
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):
2813 set_config("MOZ_STDCXX_COMPAT", True, when=stdcxx_compat)
2815 "-D_GLIBCXX_USE_CXX11_ABI=0",
2820 "-D_GLIBCXX_USE_CXX11_ABI=0",
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"
2837 "--enable-js-fuzzilli",
2839 help="Enable fuzzilli support for the JS engine",
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):
2858 @depends("--enable-fuzzing", enable_snapshot_fuzzing)
2859 def enable_fuzzing(value, snapshot_fuzzing):
2860 if value or snapshot_fuzzing:
2864 @depends("--enable-js-fuzzilli", when=js_build)
2865 def enable_js_fuzzilli(value):
2870 @depends(enable_fuzzing, enable_snapshot_fuzzing)
2871 def check_aflfuzzer(fuzzing, snapshot_fuzzing):
2872 if fuzzing and not snapshot_fuzzing:
2878 body="__AFL_COMPILER;", check_msg="for AFL compiler", when=check_aflfuzzer
2881 def enable_aflfuzzer(afl):
2886 @depends(enable_fuzzing, enable_aflfuzzer, enable_snapshot_fuzzing, c_compiler, target)
2887 def enable_libfuzzer(fuzzing, afl, snapshot_fuzzing, c_compiler, target):
2891 and not snapshot_fuzzing
2892 and c_compiler.type == "clang"
2893 and target.os != "Android"
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:
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)
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",
2934 def libfuzzer_flags(value, tsan, enable_js_fuzzilli):
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.
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"]
2952 no_link_flag_supported = True
2953 # recommended for (and only supported by) clang >= 6
2954 use_flags = ["-fsanitize=fuzzer-no-link"]
2956 no_link_flag_supported = False
2957 use_flags = ["-fsanitize-coverage=trace-pc-guard,trace-cmp"]
2960 no_link_flag_supported=no_link_flag_supported,
2961 use_flags=use_flags,
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":
2979 mkshlib=["$(CXX)", "$(DSO_LDOPTS)", "-o", "$@"],
2980 mkcshlib=["$(CC)", "$(DSO_LDOPTS)", "-o", "$@"],
2982 elif compiler.type == "clang":
2987 "-Wl,-pdb,$(LINK_PDBFILE)",
2994 "-Wl,-pdb,$(LINK_PDBFILE)",
3005 "-PDB:$(LINK_PDBFILE)",
3013 cc = ["$(CC)", "$(COMPUTED_C_LDFLAGS)"]
3014 cxx = ["$(CXX)", "$(COMPUTED_CXX_LDFLAGS)"]
3015 flags = ["$(DSO_LDOPTS)"]
3016 output = ["-o", "$@"]
3018 if target.kernel == "Darwin":
3020 elif target.os == "NetBSD":
3021 soname = ["-Wl,-soname,$(DSO_SONAME)"]
3023 assert compiler.type in ("gcc", "clang")
3025 soname = ["-Wl,-h,$(DSO_SONAME)"]
3028 mkshlib=cxx + flags + soname + output,
3029 mkcshlib=cc + flags + soname + output,
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 ()))
3044 check_prog("RC", rc_names, paths=clang_search_path, when=target_is_windows)
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":
3056 names=("llvm-lib",),
3057 flags=("-llvmlibthin", "-out:$@"),
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
3066 out = check_cmd_output(
3067 c_compiler.compiler, "--print-prog-name=llvm-ar", onerror=lambda: None
3069 llvm_ar = out.rstrip() if out else "llvm-ar"
3070 names = (llvm_ar,) + names
3074 flags=("crs", "$@"),
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")
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
3096 fd, obj_path = mkstemp(prefix="conftest.", suffix=".o")
3099 try_invoke_compiler(
3100 # No configure_cache because it would not create the
3101 # expected output file.
3103 [c_compiler.compiler] + c_compiler.flags,
3104 c_compiler.language,
3106 ["-c", "-o", obj_path] + (extra_toolchain_flags or []),
3107 wrapper=c_compiler.wrapper,
3108 onerror=lambda: None,
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")
3122 + [x.replace("$@", lib_path) for x in ar_config.flags]
3125 result = check_cmd_output(*ar_command, onerror=lambda: None)
3126 return result is not None
3128 for cleanup_path in (obj_path, list_path, lib_path):
3131 os.remove(cleanup_path)
3132 except FileNotFoundError:
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
3151 out = check_cmd_output(
3152 c_compiler.compiler, "--print-prog-name=llvm-nm", onerror=lambda: None
3154 llvm_nm = out.rstrip() if out else "llvm-nm"
3155 names = (llvm_nm,) + 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")
3169 "--enable-path-remapping",
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):
3181 return ["c", "rust"]
3188 target_sysroot.path,
3189 valid_windows_sdk_dir,
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
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
3211 path_remappings.append((sysroot_path, "k:/" if win else "/sysroot/"))
3213 path_remappings.append(
3214 (windows_sdk_dir.path, "k:/" if win else "/windows_sdk/")
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/"),
3225 (normsep(old).rstrip("/") + "/", new) for old, new in path_remappings
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 :]:
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"])