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/. 6 7# Code optimization 8# ============================================================== 9 10option("--disable-optimize", nargs="?", help="Disable optimizations via compiler flags") 11 12 13@depends("--enable-optimize") 14def moz_optimize(option): 15 flags = None 16 17 if len(option): 18 val = "2" 19 flags = option[0] 20 elif option: 21 val = "1" 22 else: 23 val = None 24 25 return namespace( 26 optimize=val, 27 flags=flags, 28 ) 29 30 31set_config("MOZ_OPTIMIZE", moz_optimize.optimize) 32add_old_configure_assignment("MOZ_OPTIMIZE", moz_optimize.optimize) 33add_old_configure_assignment("MOZ_CONFIGURE_OPTIMIZE_FLAGS", moz_optimize.flags) 34 35# Android NDK 36# ============================================================== 37 38 39@depends("--disable-compile-environment", target) 40def compiling_android(compile_env, target): 41 return compile_env and target.os == "Android" 42 43 44include("android-ndk.configure", when=compiling_android) 45 46with only_when(target_is_osx): 47 # MacOS deployment target version 48 # ============================================================== 49 # This needs to happen before any compilation test is done. 50 51 option( 52 "--enable-macos-target", 53 env="MACOSX_DEPLOYMENT_TARGET", 54 nargs=1, 55 default=depends(target)(lambda t: "11.0" if t.cpu == "aarch64" else "10.12"), 56 help="Set the minimum MacOS version needed at runtime{|}", 57 ) 58 59 @depends("--enable-macos-target") 60 @imports(_from="os", _import="environ") 61 def macos_target(value): 62 if value: 63 # Ensure every compiler process we spawn uses this value. 64 environ["MACOSX_DEPLOYMENT_TARGET"] = value[0] 65 return value[0] 66 67 set_config("MACOSX_DEPLOYMENT_TARGET", macos_target) 68 add_old_configure_assignment("MACOSX_DEPLOYMENT_TARGET", macos_target) 69 70 71@depends(host) 72def host_is_osx(host): 73 if host.os == "OSX": 74 return True 75 76 77with only_when(host_is_osx | target_is_osx): 78 # MacOS SDK 79 # ========= 80 option( 81 "--with-macos-sdk", 82 env="MACOS_SDK_DIR", 83 nargs=1, 84 help="Location of platform SDK to use", 85 ) 86 87 @depends("--with-macos-sdk", host) 88 @imports(_from="__builtin__", _import="open") 89 @imports(_from="os.path", _import="isdir") 90 @imports("plistlib") 91 def macos_sdk(sdk, host): 92 # When we change the SDK we build with, please update the manual SDK 93 # installation docs: 94 # https://firefox-source-docs.mozilla.org/setup/macos_build.html#macos-sdk-is-unsupported 95 sdk_min_version = Version("10.12") 96 97 if sdk: 98 sdk = sdk[0] 99 elif host.os == "OSX": 100 sdk = check_cmd_output( 101 "xcrun", "--show-sdk-path", onerror=lambda: "" 102 ).rstrip() 103 if not sdk: 104 die( 105 "Could not find the macOS SDK. Please use --with-macos-sdk to give " 106 "the path to a macOS SDK." 107 ) 108 else: 109 die( 110 "Need a macOS SDK when targeting macOS. Please use --with-macos-sdk " 111 "to give the path to a macOS SDK." 112 ) 113 114 if not isdir(sdk): 115 die( 116 "SDK not found in %s. When using --with-macos-sdk, you must specify a " 117 "valid SDK. SDKs are installed when the optional cross-development " 118 "tools are selected during the Xcode/Developer Tools installation." 119 % sdk 120 ) 121 with open(os.path.join(sdk, "SDKSettings.plist"), "rb") as plist: 122 obj = plistlib.load(plist) 123 if not obj: 124 die("Error parsing SDKSettings.plist in the SDK directory: %s" % sdk) 125 if "Version" not in obj: 126 die( 127 "Error finding Version information in SDKSettings.plist from the SDK: %s" 128 % sdk 129 ) 130 version = Version(obj["Version"]) 131 sdk_installation_docs_url = "https://firefox-source-docs.mozilla.org/setup/macos_build.html#macos-sdk-is-unsupported" 132 if version < sdk_min_version: 133 die( 134 'SDK version "%s" is too old. Please upgrade to at least %s. Try ' 135 "updating your system Xcode. If that's not sufficient, see the manual " 136 "SDK installation docs: %s" 137 % (version, sdk_min_version, sdk_installation_docs_url) 138 ) 139 return sdk 140 141 set_config("MACOS_SDK_DIR", macos_sdk) 142 143 144with only_when(target_is_osx): 145 with only_when(cross_compiling): 146 option( 147 "--with-macos-private-frameworks", 148 env="MACOS_PRIVATE_FRAMEWORKS_DIR", 149 nargs=1, 150 help="Location of private frameworks to use", 151 ) 152 153 @depends_if("--with-macos-private-frameworks") 154 @imports(_from="os.path", _import="isdir") 155 def macos_private_frameworks(value): 156 if value and not isdir(value[0]): 157 die( 158 "PrivateFrameworks not found not found in %s. When using " 159 "--with-macos-private-frameworks, you must specify a valid " 160 "directory", 161 value[0], 162 ) 163 return value[0] 164 165 @depends(macos_private_frameworks, macos_sdk) 166 def macos_private_frameworks(value, sdk): 167 if value: 168 return value 169 return os.path.join(sdk or "/", "System/Library/PrivateFrameworks") 170 171 set_config("MACOS_PRIVATE_FRAMEWORKS_DIR", macos_private_frameworks) 172 173 174# GC rooting and hazard analysis. 175# ============================================================== 176option(env="MOZ_HAZARD", help="Build for the GC rooting hazard analysis") 177 178 179@depends("MOZ_HAZARD") 180def hazard_analysis(value): 181 if value: 182 return True 183 184 185set_config("MOZ_HAZARD", hazard_analysis) 186 187 188# Cross-compilation related things. 189# ============================================================== 190option( 191 "--with-toolchain-prefix", 192 env="TOOLCHAIN_PREFIX", 193 nargs=1, 194 help="Prefix for the target toolchain", 195) 196 197 198@depends("--with-toolchain-prefix", host, target, cross_compiling) 199def toolchain_prefix(value, host, target, cross_compiling): 200 if value: 201 return tuple(value) 202 # We don't want a toolchain prefix by default when building on mac for mac. 203 if cross_compiling and not (target.os == "OSX" and host.os == "OSX"): 204 return ("%s-" % target.toolchain, "%s-" % target.alias) 205 206 207@depends(toolchain_prefix, target) 208def first_toolchain_prefix(toolchain_prefix, target): 209 # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the 210 # command line/environment (in which case there's only one value in the tuple), 211 # or when cross-compiling for Android or OSX. 212 if toolchain_prefix and ( 213 target.os in ("Android", "OSX") or len(toolchain_prefix) == 1 214 ): 215 return toolchain_prefix[0] 216 217 218set_config("TOOLCHAIN_PREFIX", first_toolchain_prefix) 219add_old_configure_assignment("TOOLCHAIN_PREFIX", first_toolchain_prefix) 220 221 222# Compilers 223# ============================================================== 224include("compilers-util.configure") 225 226 227def try_preprocess(compiler, language, source, onerror=None): 228 return try_invoke_compiler(compiler, language, source, ["-E"], onerror) 229 230 231@imports(_from="mozbuild.configure.constants", _import="CompilerType") 232@imports(_from="mozbuild.configure.constants", _import="CPU_preprocessor_checks") 233@imports(_from="mozbuild.configure.constants", _import="kernel_preprocessor_checks") 234@imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks") 235@imports(_from="six", _import="iteritems") 236@imports(_from="textwrap", _import="dedent") 237@imports(_from="__builtin__", _import="Exception") 238def get_compiler_info(compiler, language): 239 """Returns information about the given `compiler` (command line in the 240 form of a list or tuple), in the given `language`. 241 242 The returned information includes: 243 - the compiler type (clang-cl, clang or gcc) 244 - the compiler version 245 - the compiler supported language 246 - the compiler supported language version 247 """ 248 # Xcode clang versions are different from the underlying llvm version (they 249 # instead are aligned with the Xcode version). Fortunately, we can tell 250 # apart plain clang from Xcode clang, and convert the Xcode clang version 251 # into the more or less corresponding plain clang version. 252 check = dedent( 253 """\ 254 #if defined(_MSC_VER) && defined(__clang__) && defined(_MT) 255 %COMPILER "clang-cl" 256 %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__ 257 #elif defined(__clang__) 258 %COMPILER "clang" 259 %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__ 260 # ifdef __apple_build_version__ 261 %XCODE 1 262 # endif 263 #elif defined(__GNUC__) && !defined(__MINGW32__) 264 %COMPILER "gcc" 265 %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__ 266 #endif 267 268 #if __cplusplus 269 %cplusplus __cplusplus 270 #elif __STDC_VERSION__ 271 %STDC_VERSION __STDC_VERSION__ 272 #endif 273 """ 274 ) 275 276 # While we're doing some preprocessing, we might as well do some more 277 # preprocessor-based tests at the same time, to check the toolchain 278 # matches what we want. 279 for name, preprocessor_checks in ( 280 ("CPU", CPU_preprocessor_checks), 281 ("KERNEL", kernel_preprocessor_checks), 282 ("OS", OS_preprocessor_checks), 283 ): 284 for n, (value, condition) in enumerate(iteritems(preprocessor_checks)): 285 check += dedent( 286 """\ 287 #%(if)s %(condition)s 288 %%%(name)s "%(value)s" 289 """ 290 % { 291 "if": "elif" if n else "if", 292 "condition": condition, 293 "name": name, 294 "value": value, 295 } 296 ) 297 check += "#endif\n" 298 299 # Also check for endianness. The advantage of living in modern times is 300 # that all the modern compilers we support now have __BYTE_ORDER__ defined 301 # by the preprocessor. 302 check += dedent( 303 """\ 304 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 305 %ENDIANNESS "little" 306 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 307 %ENDIANNESS "big" 308 #endif 309 """ 310 ) 311 312 result = try_preprocess(compiler, language, check) 313 314 if not result: 315 raise FatalCheckError("Unknown compiler or compiler not supported.") 316 317 # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may 318 # have non-ASCII characters. Treat the output as bytearray. 319 data = {} 320 for line in result.splitlines(): 321 if line.startswith("%"): 322 k, _, v = line.partition(" ") 323 k = k.lstrip("%") 324 data[k] = v.replace(" ", "").lstrip('"').rstrip('"') 325 log.debug("%s = %s", k, data[k]) 326 327 try: 328 type = CompilerType(data["COMPILER"]) 329 except Exception: 330 raise FatalCheckError("Unknown compiler or compiler not supported.") 331 332 cplusplus = int(data.get("cplusplus", "0L").rstrip("L")) 333 stdc_version = int(data.get("STDC_VERSION", "0L").rstrip("L")) 334 335 version = data.get("VERSION") 336 if version: 337 version = Version(version) 338 if data.get("XCODE"): 339 # Derived from https://en.wikipedia.org/wiki/Xcode#Toolchain_versions 340 # with enough granularity for major.minor version checks further 341 # down the line 342 if version < "9.1": 343 version = Version("4.0.0.or.less") 344 elif version < "10.0": 345 version = Version("5.0.2") 346 elif version < "10.0.1": 347 version = Version("6.0.1") 348 elif version < "11.0": 349 version = Version("7.0.0") 350 elif version < "11.0.3": 351 version = Version("8.0.0") 352 elif version < "12.0": 353 version = Version("9.0.0") 354 elif version < "12.0.1": 355 version = Version("10.0.0") 356 else: 357 version = Version("10.0.0.or.more") 358 359 return namespace( 360 type=type, 361 version=version, 362 cpu=data.get("CPU"), 363 kernel=data.get("KERNEL"), 364 endianness=data.get("ENDIANNESS"), 365 os=data.get("OS"), 366 language="C++" if cplusplus else "C", 367 language_version=cplusplus if cplusplus else stdc_version, 368 xcode=bool(data.get("XCODE")), 369 ) 370 371 372def same_arch_different_bits(): 373 return ( 374 ("x86", "x86_64"), 375 ("ppc", "ppc64"), 376 ("sparc", "sparc64"), 377 ) 378 379 380@imports(_from="mozbuild.shellutil", _import="quote") 381@imports(_from="mozbuild.configure.constants", _import="OS_preprocessor_checks") 382def check_compiler(compiler, language, target): 383 info = get_compiler_info(compiler, language) 384 385 flags = [] 386 387 # Check language standards 388 # -------------------------------------------------------------------- 389 if language != info.language: 390 raise FatalCheckError( 391 "`%s` is not a %s compiler." % (quote(*compiler), language) 392 ) 393 394 # Note: We do a strict version check because there sometimes are backwards 395 # incompatible changes in the standard, and not all code that compiles as 396 # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for 397 # example) 398 if info.language == "C" and info.language_version != 199901: 399 if info.type == "clang-cl": 400 flags.append("-Xclang") 401 flags.append("-std=gnu99") 402 403 cxx17_version = 201703 404 if info.language == "C++": 405 if info.language_version != cxx17_version: 406 # MSVC headers include C++17 features, but don't guard them 407 # with appropriate checks. 408 if info.type == "clang-cl": 409 flags.append("-Xclang") 410 flags.append("-std=c++17") 411 else: 412 flags.append("-std=gnu++17") 413 414 # Check compiler target 415 # -------------------------------------------------------------------- 416 has_target = False 417 if info.type == "clang": 418 # Add the target explicitly when the target is aarch64 macosx, because 419 # the Xcode clang target is named differently, and we need to work around 420 # https://github.com/rust-lang/rust-bindgen/issues/1871 and 421 # https://github.com/alexcrichton/cc-rs/issues/542 so we always want 422 # the target on the command line, even if the compiler would default to 423 # that. 424 if info.xcode and target.os == "OSX" and target.cpu == "aarch64": 425 if "--target=arm64-apple-darwin" not in compiler: 426 flags.append("--target=arm64-apple-darwin") 427 has_target = True 428 429 elif ( 430 not info.kernel 431 or info.kernel != target.kernel 432 or not info.endianness 433 or info.endianness != target.endianness 434 ): 435 flags.append("--target=%s" % target.toolchain) 436 has_target = True 437 438 # Add target flag when there is an OS mismatch (e.g. building for Android on 439 # Linux). However, only do this if the target OS is in our whitelist, to 440 # keep things the same on other platforms. 441 elif target.os in OS_preprocessor_checks and ( 442 not info.os or info.os != target.os 443 ): 444 flags.append("--target=%s" % target.toolchain) 445 has_target = True 446 447 if not has_target and (not info.cpu or info.cpu != target.cpu): 448 same_arch = same_arch_different_bits() 449 if (target.cpu, info.cpu) in same_arch: 450 flags.append("-m32") 451 elif (info.cpu, target.cpu) in same_arch: 452 flags.append("-m64") 453 elif info.type == "clang-cl" and target.cpu == "aarch64": 454 flags.append("--target=%s" % target.toolchain) 455 elif info.type == "clang": 456 flags.append("--target=%s" % target.toolchain) 457 458 return namespace( 459 type=info.type, 460 version=info.version, 461 target_cpu=info.cpu, 462 target_kernel=info.kernel, 463 target_endianness=info.endianness, 464 target_os=info.os, 465 flags=flags, 466 ) 467 468 469@imports(_from="__builtin__", _import="open") 470@imports("json") 471@imports("os") 472def get_vc_paths(topsrcdir): 473 def vswhere(args): 474 program_files = os.environ.get("PROGRAMFILES(X86)") or os.environ.get( 475 "PROGRAMFILES" 476 ) 477 if not program_files: 478 return [] 479 vswhere = os.path.join( 480 program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe" 481 ) 482 if not os.path.exists(vswhere): 483 return [] 484 return json.loads(check_cmd_output(vswhere, "-format", "json", *args)) 485 486 for install in vswhere( 487 [ 488 "-products", 489 "*", 490 "-requires", 491 "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", 492 ] 493 ): 494 path = install["installationPath"] 495 tools_version = ( 496 open( 497 os.path.join( 498 path, r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt" 499 ), 500 "r", 501 ) 502 .read() 503 .strip() 504 ) 505 tools_path = os.path.join(path, r"VC\Tools\MSVC", tools_version) 506 yield (Version(install["installationVersion"]), tools_path) 507 508 509@depends(host) 510def host_is_windows(host): 511 if host.kernel == "WINNT": 512 return True 513 514 515option( 516 "--with-visual-studio-version", 517 nargs=1, 518 choices=("2017",), 519 when=host_is_windows, 520 help="Select a specific Visual Studio version to use", 521) 522 523 524@depends("--with-visual-studio-version", when=host_is_windows) 525def vs_major_version(value): 526 if value: 527 return {"2017": 15}[value[0]] 528 529 530option( 531 env="VC_PATH", 532 nargs=1, 533 when=host_is_windows, 534 help="Path to the Microsoft Visual C/C++ compiler", 535) 536 537 538@depends( 539 host, 540 vs_major_version, 541 check_build_environment, 542 "VC_PATH", 543 "--with-visual-studio-version", 544 when=host_is_windows, 545) 546@imports(_from="operator", _import="itemgetter") 547def vc_compiler_paths_for_version( 548 host, vs_major_version, env, vc_path, vs_release_name 549): 550 if vc_path and vs_release_name: 551 die("VC_PATH and --with-visual-studio-version cannot be used together.") 552 if vc_path: 553 # Use an arbitrary version, it doesn't matter. 554 all_versions = [(Version("15"), vc_path[0])] 555 else: 556 all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0)) 557 if not all_versions: 558 return 559 if vs_major_version: 560 versions = [d for (v, d) in all_versions if v.major == vs_major_version] 561 if not versions: 562 die("Visual Studio %s could not be found!" % vs_release_name) 563 path = versions[0] 564 else: 565 # Choose the newest version. 566 path = all_versions[-1][1] 567 host_dir = { 568 "x86_64": "HostX64", 569 "x86": "HostX86", 570 }.get(host.cpu) 571 if host_dir: 572 path = os.path.join(path, "bin", host_dir) 573 return { 574 "x64": [os.path.join(path, "x64")], 575 # The cross toolchains require DLLs from the native x64 toolchain. 576 "x86": [os.path.join(path, "x86"), os.path.join(path, "x64")], 577 "arm64": [os.path.join(path, "arm64"), os.path.join(path, "x64")], 578 } 579 580 581@depends(target, vc_compiler_paths_for_version, when=host_is_windows) 582def vc_compiler_path(target, paths): 583 vc_target = { 584 "x86": "x86", 585 "x86_64": "x64", 586 "arm": "arm", 587 "aarch64": "arm64", 588 }.get(target.cpu) 589 if not paths: 590 return 591 return paths.get(vc_target) 592 593 594@depends(vc_compiler_path, original_path) 595@imports("os") 596@imports(_from="os", _import="environ") 597def vc_toolchain_search_path(vc_compiler_path, original_path): 598 result = list(original_path) 599 600 if vc_compiler_path: 601 # The second item, if there is one, is necessary to have in $PATH for 602 # Windows to load the required DLLs from there. 603 if len(vc_compiler_path) > 1: 604 environ["PATH"] = os.pathsep.join(result + vc_compiler_path[1:]) 605 606 # The first item is where the programs are going to be 607 result.append(vc_compiler_path[0]) 608 609 return result 610 611 612clang_search_path = bootstrap_search_path("clang/bin") 613 614 615@depends( 616 bootstrap_search_path("rustc/bin", when="MOZ_AUTOMATION"), 617 bootstrap_search_path_order, 618 original_path, 619) 620@imports("os") 621@imports(_from="os", _import="environ") 622def rust_search_path(rust_path, search_order, original_path): 623 result = list(rust_path or original_path) 624 # Also add the rustup install directory for cargo/rustc. 625 cargo_home = environ.get("CARGO_HOME", "") 626 if cargo_home: 627 cargo_home = os.path.abspath(cargo_home) 628 else: 629 cargo_home = os.path.expanduser(os.path.join("~", ".cargo")) 630 rustup_path = os.path.join(cargo_home, "bin") 631 if search_order == "prepend": 632 result.insert(0, rustup_path) 633 else: 634 result.append(rustup_path) 635 return result 636 637 638# As a workaround until bug 1516228 and bug 1516253 are fixed, set the PATH 639# variable for the build to contain the toolchain search path. 640@depends(vc_toolchain_search_path) 641@imports("os") 642@imports(_from="os", _import="environ") 643def altered_path(vc_toolchain_search_path): 644 path = environ["PATH"].split(os.pathsep) 645 altered_path = list(vc_toolchain_search_path) 646 for p in path: 647 if p not in altered_path: 648 altered_path.append(p) 649 return os.pathsep.join(altered_path) 650 651 652set_config("PATH", altered_path) 653 654 655# Compiler wrappers 656# ============================================================== 657option( 658 "--with-compiler-wrapper", 659 env="COMPILER_WRAPPER", 660 nargs=1, 661 help="Enable compiling with wrappers such as distcc and ccache", 662) 663 664option("--with-ccache", env="CCACHE", nargs="?", help="Enable compiling with ccache") 665 666 667@depends_if("--with-ccache") 668def ccache(value): 669 if len(value): 670 return value 671 # If --with-ccache was given without an explicit value, we default to 672 # 'ccache'. 673 return "ccache" 674 675 676ccache = check_prog( 677 "CCACHE", 678 progs=(), 679 input=ccache, 680 paths=bootstrap_search_path( 681 "sccache", when=depends("CCACHE")(lambda c: len(c) and c[0] == "sccache") 682 ), 683 allow_missing=True, 684) 685 686option(env="CCACHE_PREFIX", nargs=1, help="Compiler prefix to use when using ccache") 687 688ccache_prefix = depends_if("CCACHE_PREFIX")(lambda prefix: prefix[0]) 689set_config("CCACHE_PREFIX", ccache_prefix) 690 691# Distinguish ccache from sccache. 692 693 694@depends_if(ccache) 695def ccache_is_sccache(ccache): 696 return check_cmd_output(ccache, "--version").startswith("sccache") 697 698 699@depends(ccache, ccache_is_sccache) 700def using_ccache(ccache, ccache_is_sccache): 701 return ccache and not ccache_is_sccache 702 703 704@depends_if(ccache, ccache_is_sccache) 705def using_sccache(ccache, ccache_is_sccache): 706 return ccache and ccache_is_sccache 707 708 709option(env="RUSTC_WRAPPER", nargs=1, help="Wrap rust compilation with given tool") 710 711 712@depends(ccache, ccache_is_sccache, "RUSTC_WRAPPER") 713@imports(_from="textwrap", _import="dedent") 714@imports("os") 715def check_sccache_version(ccache, ccache_is_sccache, rustc_wrapper): 716 sccache_min_version = Version("0.2.13") 717 718 def check_version(path): 719 out = check_cmd_output(path, "--version") 720 version = Version(out.rstrip().split()[-1]) 721 if version < sccache_min_version: 722 die( 723 dedent( 724 """\ 725 sccache %s or later is required. sccache in use at %s has 726 version %s. 727 728 Please upgrade or acquire a new version with |./mach bootstrap|. 729 """ 730 ), 731 sccache_min_version, 732 path, 733 version, 734 ) 735 736 if ccache and ccache_is_sccache: 737 check_version(ccache) 738 739 if rustc_wrapper and ( 740 os.path.splitext(os.path.basename(rustc_wrapper[0]))[0].lower() == "sccache" 741 ): 742 check_version(rustc_wrapper[0]) 743 744 745set_config("MOZ_USING_CCACHE", using_ccache) 746set_config("MOZ_USING_SCCACHE", using_sccache) 747 748option(env="SCCACHE_VERBOSE_STATS", help="Print verbose sccache stats after build") 749 750 751@depends(using_sccache, "SCCACHE_VERBOSE_STATS") 752def sccache_verbose_stats(using_sccache, verbose_stats): 753 return using_sccache and bool(verbose_stats) 754 755 756set_config("SCCACHE_VERBOSE_STATS", sccache_verbose_stats) 757 758 759@depends("--with-compiler-wrapper", ccache) 760@imports(_from="mozbuild.shellutil", _import="split", _as="shell_split") 761def compiler_wrapper(wrapper, ccache): 762 if wrapper: 763 raw_wrapper = wrapper[0] 764 wrapper = shell_split(raw_wrapper) 765 wrapper_program = find_program(wrapper[0]) 766 if not wrapper_program: 767 die( 768 "Cannot find `%s` from the given compiler wrapper `%s`", 769 wrapper[0], 770 raw_wrapper, 771 ) 772 wrapper[0] = wrapper_program 773 774 if ccache: 775 if wrapper: 776 return tuple([ccache] + wrapper) 777 else: 778 return (ccache,) 779 elif wrapper: 780 return tuple(wrapper) 781 782 783@depends_if(compiler_wrapper) 784def using_compiler_wrapper(compiler_wrapper): 785 return True 786 787 788set_config("MOZ_USING_COMPILER_WRAPPER", using_compiler_wrapper) 789 790 791@template 792def default_c_compilers(host_or_target, other_c_compiler=None): 793 """Template defining the set of default C compilers for the host and 794 target platforms. 795 `host_or_target` is either `host` or `target` (the @depends functions 796 from init.configure. 797 `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`. 798 """ 799 assert host_or_target in {host, target} 800 801 other_c_compiler = () if other_c_compiler is None else (other_c_compiler,) 802 803 @depends(host_or_target, target, toolchain_prefix, *other_c_compiler) 804 def default_c_compilers( 805 host_or_target, target, toolchain_prefix, *other_c_compiler 806 ): 807 if host_or_target.kernel == "WINNT": 808 supported = types = ("clang-cl", "clang") 809 elif host_or_target.kernel == "Darwin": 810 types = ("clang",) 811 supported = ("clang", "gcc") 812 else: 813 supported = types = ("clang", "gcc") 814 815 info = other_c_compiler[0] if other_c_compiler else None 816 if info and info.type in supported: 817 # When getting default C compilers for the host, we prioritize the 818 # same compiler as the target C compiler. 819 prioritized = info.compiler 820 if info.type == "gcc": 821 same_arch = same_arch_different_bits() 822 if ( 823 target.cpu != host_or_target.cpu 824 and (target.cpu, host_or_target.cpu) not in same_arch 825 and (host_or_target.cpu, target.cpu) not in same_arch 826 ): 827 # If the target C compiler is GCC, and it can't be used with 828 # -m32/-m64 for the host, it's probably toolchain-prefixed, 829 # so we prioritize a raw 'gcc' instead. 830 prioritized = info.type 831 832 types = [prioritized] + [t for t in types if t != info.type] 833 834 gcc = ("gcc",) 835 if toolchain_prefix and host_or_target is target: 836 gcc = tuple("%sgcc" % p for p in toolchain_prefix) + gcc 837 838 result = [] 839 for type in types: 840 if type == "gcc": 841 result.extend(gcc) 842 else: 843 result.append(type) 844 845 return tuple(result) 846 847 return default_c_compilers 848 849 850@template 851def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None): 852 """Template defining the set of default C++ compilers for the host and 853 target platforms. 854 `c_compiler` is the @depends function returning a Compiler instance for 855 the desired platform. 856 857 Because the build system expects the C and C++ compilers to be from the 858 same compiler suite, we derive the default C++ compilers from the C 859 compiler that was found if none was provided. 860 861 We also factor in the target C++ compiler when getting the default host 862 C++ compiler, using the target C++ compiler if the host and target C 863 compilers are the same. 864 """ 865 866 assert (other_c_compiler is None) == (other_cxx_compiler is None) 867 if other_c_compiler is not None: 868 other_compilers = (other_c_compiler, other_cxx_compiler) 869 else: 870 other_compilers = () 871 872 @depends(c_compiler, *other_compilers) 873 def default_cxx_compilers(c_compiler, *other_compilers): 874 if other_compilers: 875 other_c_compiler, other_cxx_compiler = other_compilers 876 if other_c_compiler.compiler == c_compiler.compiler: 877 return (other_cxx_compiler.compiler,) 878 879 dir = os.path.dirname(c_compiler.compiler) 880 file = os.path.basename(c_compiler.compiler) 881 882 if c_compiler.type == "gcc": 883 return (os.path.join(dir, file.replace("gcc", "g++")),) 884 885 if c_compiler.type == "clang": 886 return (os.path.join(dir, file.replace("clang", "clang++")),) 887 888 return (c_compiler.compiler,) 889 890 return default_cxx_compilers 891 892 893@template 894def provided_program(env_var, when=None): 895 """Template handling cases where a program can be specified either as a 896 path or as a path with applicable arguments. 897 """ 898 899 @depends_if(env_var, when=when) 900 @imports(_from="itertools", _import="takewhile") 901 @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split") 902 def provided(cmd): 903 # Assume the first dash-prefixed item (and any subsequent items) are 904 # command-line options, the item before the dash-prefixed item is 905 # the program we're looking for, and anything before that is a wrapper 906 # of some kind (e.g. sccache). 907 cmd = shell_split(cmd[0]) 908 909 without_flags = list(takewhile(lambda x: not x.startswith("-"), cmd)) 910 911 return namespace( 912 wrapper=without_flags[:-1], 913 program=without_flags[-1], 914 flags=cmd[len(without_flags) :], 915 ) 916 917 return provided 918 919 920option( 921 "--with-sysroot", 922 env="SYSROOT", 923 nargs=1, 924 when=target_is_linux_or_wasi, 925 help="Build using the given sysroot directory", 926) 927 928sysroot_input = depends("--with-sysroot", when=target_is_linux_or_wasi)(lambda x: x) 929bootstrap_sysroot = depends(target_is_linux, sysroot_input)( 930 lambda linux, input: linux and not input and input.origin == "default" 931) 932 933# We'll re-evaluate later, but for now, don't use the sysroot automatically 934# even if it exists, unless --enable-bootstrap was passed explicitly, or when 935# building on automation. 936@depends( 937 sysroot_input, 938 bootstrap_path("sysroot", context=target, when=bootstrap_sysroot), 939 "--enable-bootstrap", 940 "MOZ_AUTOMATION", 941) 942def sysroot_path(sysroot_input, path, bootstrap, automation): 943 if sysroot_input: 944 path = sysroot_input[0] 945 elif sysroot_input is not None and sysroot_input.origin != "default": 946 return 947 if sysroot_input or (bootstrap and bootstrap.origin != "default") or automation: 948 if path: 949 log.info("Using sysroot in %s", path) 950 return path 951 952 953@template 954def sysroot_flags(host_or_target): 955 @depends( 956 host_or_target, macos_sdk, sysroot_path if host_or_target is target else never 957 ) 958 def sysroot_flags(host_or_target, macos_sdk, sysroot_path): 959 if macos_sdk and host_or_target.os == "OSX": 960 return ["-isysroot", macos_sdk] 961 if sysroot_path: 962 return ["--sysroot", sysroot_path] 963 return [] 964 965 return sysroot_flags 966 967 968host_sysroot_flags = sysroot_flags(host) 969target_sysroot_flags = sysroot_flags(target) 970 971 972@depends(target, when=sysroot_path) 973def multiarch_dir(target): 974 if target.cpu == "x86": 975 # Turn e.g. i686-linux-gnu into i386-linux-gnu 976 return target.toolchain.replace(target.raw_cpu, "i386") 977 return target.toolchain 978 979 980def minimum_gcc_version(): 981 return Version("7.1.0") 982 983 984@template 985def compiler( 986 language, 987 host_or_target, 988 c_compiler=None, 989 other_compiler=None, 990 other_c_compiler=None, 991): 992 """Template handling the generic base checks for the compiler for the 993 given `language` on the given platform (`host_or_target`). 994 `host_or_target` is either `host` or `target` (the @depends functions 995 from init.configure. 996 When the language is 'C++', `c_compiler` is the result of the `compiler` 997 template for the language 'C' for the same `host_or_target`. 998 When `host_or_target` is `host`, `other_compiler` is the result of the 999 `compiler` template for the same `language` for `target`. 1000 When `host_or_target` is `host` and the language is 'C++', 1001 `other_c_compiler` is the result of the `compiler` template for the 1002 language 'C' for `target`. 1003 """ 1004 assert host_or_target in {host, target} 1005 assert language in ("C", "C++") 1006 assert language == "C" or c_compiler is not None 1007 assert host_or_target is target or other_compiler is not None 1008 assert language == "C" or host_or_target is target or other_c_compiler is not None 1009 1010 host_or_target_str = { 1011 host: "host", 1012 target: "target", 1013 }[host_or_target] 1014 1015 sysroot_flags = { 1016 host: host_sysroot_flags, 1017 target: target_sysroot_flags, 1018 }[host_or_target] 1019 1020 var = { 1021 ("C", target): "CC", 1022 ("C++", target): "CXX", 1023 ("C", host): "HOST_CC", 1024 ("C++", host): "HOST_CXX", 1025 }[language, host_or_target] 1026 1027 default_compilers = { 1028 "C": lambda: default_c_compilers(host_or_target, other_compiler), 1029 "C++": lambda: default_cxx_compilers( 1030 c_compiler, other_c_compiler, other_compiler 1031 ), 1032 }[language]() 1033 1034 what = "the %s %s compiler" % (host_or_target_str, language) 1035 1036 option(env=var, nargs=1, help="Path to %s" % what) 1037 1038 # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/ 1039 # HOST_CXX variables. 1040 provided_compiler = provided_program(var) 1041 1042 # Normally, we'd use `var` instead of `_var`, but the interaction with 1043 # old-configure complicates things, and for now, we a) can't take the plain 1044 # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let 1045 # old-configure AC_SUBST it (because it's autoconf doing it, not us) 1046 compiler = check_prog( 1047 "_%s" % var, 1048 what=what, 1049 progs=default_compilers, 1050 input=provided_compiler.program, 1051 paths=clang_search_path, 1052 ) 1053 1054 @depends( 1055 compiler, provided_compiler, compiler_wrapper, host_or_target, sysroot_flags 1056 ) 1057 @checking("whether %s can be used" % what, lambda x: bool(x)) 1058 @imports(_from="mozbuild.shellutil", _import="quote") 1059 def valid_compiler( 1060 compiler, provided_compiler, compiler_wrapper, host_or_target, sysroot_flags 1061 ): 1062 wrapper = list(compiler_wrapper or ()) 1063 flags = list(sysroot_flags or ()) 1064 if provided_compiler: 1065 provided_wrapper = list(provided_compiler.wrapper) 1066 # When doing a subconfigure, the compiler is set by old-configure 1067 # and it contains the wrappers from --with-compiler-wrapper and 1068 # --with-ccache. 1069 if provided_wrapper[: len(wrapper)] == wrapper: 1070 provided_wrapper = provided_wrapper[len(wrapper) :] 1071 wrapper.extend(provided_wrapper) 1072 flags.extend(provided_compiler.flags) 1073 1074 info = check_compiler(wrapper + [compiler] + flags, language, host_or_target) 1075 1076 # Check that the additional flags we got are enough to not require any 1077 # more flags. If we get an exception, just ignore it; it's liable to be 1078 # invalid command-line flags, which means the compiler we're checking 1079 # doesn't support those command-line flags and will fail one or more of 1080 # the checks below. 1081 try: 1082 if info.flags: 1083 flags += info.flags 1084 info = check_compiler( 1085 wrapper + [compiler] + flags, language, host_or_target 1086 ) 1087 except FatalCheckError: 1088 pass 1089 1090 if not info.target_cpu or info.target_cpu != host_or_target.cpu: 1091 raise FatalCheckError( 1092 "%s %s compiler target CPU (%s) does not match --%s CPU (%s)" 1093 % ( 1094 host_or_target_str.capitalize(), 1095 language, 1096 info.target_cpu or "unknown", 1097 host_or_target_str, 1098 host_or_target.raw_cpu, 1099 ) 1100 ) 1101 1102 if not info.target_kernel or (info.target_kernel != host_or_target.kernel): 1103 raise FatalCheckError( 1104 "%s %s compiler target kernel (%s) does not match --%s kernel (%s)" 1105 % ( 1106 host_or_target_str.capitalize(), 1107 language, 1108 info.target_kernel or "unknown", 1109 host_or_target_str, 1110 host_or_target.kernel, 1111 ) 1112 ) 1113 1114 if not info.target_endianness or ( 1115 info.target_endianness != host_or_target.endianness 1116 ): 1117 raise FatalCheckError( 1118 "%s %s compiler target endianness (%s) does not match --%s " 1119 "endianness (%s)" 1120 % ( 1121 host_or_target_str.capitalize(), 1122 language, 1123 info.target_endianness or "unknown", 1124 host_or_target_str, 1125 host_or_target.endianness, 1126 ) 1127 ) 1128 1129 # Compiler version checks 1130 # =================================================== 1131 # Check the compiler version here instead of in `compiler_version` so 1132 # that the `checking` message doesn't pretend the compiler can be used 1133 # to then bail out one line later. 1134 if info.type == "gcc": 1135 if host_or_target.os == "Android": 1136 raise FatalCheckError( 1137 "GCC is not supported on Android.\n" 1138 "Please use clang from the Android NDK instead." 1139 ) 1140 gcc_version = minimum_gcc_version() 1141 if info.version < gcc_version: 1142 raise FatalCheckError( 1143 "Only GCC %d.%d or newer is supported (found version %s)." 1144 % (gcc_version.major, gcc_version.minor, info.version) 1145 ) 1146 1147 if info.type == "clang-cl": 1148 if info.version < "8.0.0": 1149 raise FatalCheckError( 1150 "Only clang-cl 8.0 or newer is supported (found version %s)" 1151 % info.version 1152 ) 1153 1154 # If you want to bump the version check here ensure the version 1155 # is known for Xcode in get_compiler_info. 1156 if info.type == "clang" and info.version < "5.0": 1157 raise FatalCheckError( 1158 "Only clang/llvm 5.0 or newer is supported (found version %s)." 1159 % info.version 1160 ) 1161 1162 if info.flags: 1163 raise FatalCheckError("Unknown compiler or compiler not supported.") 1164 1165 return namespace( 1166 wrapper=wrapper, 1167 compiler=compiler, 1168 flags=flags, 1169 type=info.type, 1170 version=info.version, 1171 language=language, 1172 ) 1173 1174 @depends(valid_compiler) 1175 @checking("%s version" % what) 1176 def compiler_version(compiler): 1177 return compiler.version 1178 1179 if language == "C++": 1180 1181 @depends(valid_compiler, c_compiler) 1182 def valid_compiler(compiler, c_compiler): 1183 if compiler.type != c_compiler.type: 1184 die( 1185 "The %s C compiler is %s, while the %s C++ compiler is " 1186 "%s. Need to use the same compiler suite.", 1187 host_or_target_str, 1188 c_compiler.type, 1189 host_or_target_str, 1190 compiler.type, 1191 ) 1192 1193 if compiler.version != c_compiler.version: 1194 die( 1195 "The %s C compiler is version %s, while the %s C++ " 1196 "compiler is version %s. Need to use the same compiler " 1197 "version.", 1198 host_or_target_str, 1199 c_compiler.version, 1200 host_or_target_str, 1201 compiler.version, 1202 ) 1203 return compiler 1204 1205 # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper 1206 # and the flags that were part of the user input for those variables to 1207 # be provided. 1208 add_old_configure_assignment( 1209 var, 1210 depends_if(valid_compiler)( 1211 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags) 1212 ), 1213 ) 1214 1215 if host_or_target is target: 1216 add_old_configure_assignment( 1217 "ac_cv_prog_%s" % var, 1218 depends_if(valid_compiler)( 1219 lambda x: list(x.wrapper) + [x.compiler] + list(x.flags) 1220 ), 1221 ) 1222 # We check that it works in python configure already. 1223 add_old_configure_assignment("ac_cv_prog_%s_works" % var.lower(), "yes") 1224 add_old_configure_assignment( 1225 "ac_cv_prog_%s_cross" % var.lower(), 1226 depends(cross_compiling)(lambda x: "yes" if x else "no"), 1227 ) 1228 gcc_like = depends(valid_compiler.type)( 1229 lambda x: "yes" if x in ("gcc", "clang") else "no" 1230 ) 1231 add_old_configure_assignment("ac_cv_prog_%s_g" % var.lower(), gcc_like) 1232 if language == "C": 1233 add_old_configure_assignment("ac_cv_prog_gcc", gcc_like) 1234 if language == "C++": 1235 add_old_configure_assignment("ac_cv_prog_gxx", gcc_like) 1236 1237 # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow 1238 # old-configure to do some of its still existing checks. 1239 if language == "C": 1240 set_config("%s_TYPE" % var, valid_compiler.type) 1241 add_old_configure_assignment("%s_TYPE" % var, valid_compiler.type) 1242 set_config( 1243 "%s_VERSION" % var, depends(valid_compiler.version)(lambda v: str(v)) 1244 ) 1245 1246 valid_compiler = compiler_class(valid_compiler, host_or_target) 1247 1248 def compiler_error(): 1249 raise FatalCheckError( 1250 "Failed compiling a simple %s source with %s" % (language, what) 1251 ) 1252 1253 valid_compiler.try_compile(check_msg="%s works" % what, onerror=compiler_error) 1254 1255 set_config("%s_BASE_FLAGS" % var, valid_compiler.flags) 1256 1257 # Set CPP/CXXCPP for both the build system and old-configure. We don't 1258 # need to check this works for preprocessing, because we already relied 1259 # on $CC -E/$CXX -E doing preprocessing work to validate the compiler 1260 # in the first place. 1261 if host_or_target is target: 1262 pp_var = { 1263 "C": "CPP", 1264 "C++": "CXXCPP", 1265 }[language] 1266 1267 preprocessor = depends_if(valid_compiler)( 1268 lambda x: list(x.wrapper) + [x.compiler, "-E"] + list(x.flags) 1269 ) 1270 1271 set_config(pp_var, preprocessor) 1272 add_old_configure_assignment(pp_var, preprocessor) 1273 1274 if language == "C": 1275 linker_var = { 1276 target: "LD", 1277 host: "HOST_LD", 1278 }[host_or_target] 1279 1280 @deprecated_option(env=linker_var, nargs=1) 1281 def linker(value): 1282 if value: 1283 return value[0] 1284 1285 @depends(linker) 1286 def unused_linker(linker): 1287 if linker: 1288 log.warning( 1289 "The value of %s is not used by this build system." % linker_var 1290 ) 1291 1292 return valid_compiler 1293 1294 1295c_compiler = compiler("C", target) 1296cxx_compiler = compiler("C++", target, c_compiler=c_compiler) 1297host_c_compiler = compiler("C", host, other_compiler=c_compiler) 1298host_cxx_compiler = compiler( 1299 "C++", 1300 host, 1301 c_compiler=host_c_compiler, 1302 other_compiler=cxx_compiler, 1303 other_c_compiler=c_compiler, 1304) 1305 1306# Generic compiler-based conditions. 1307building_with_gcc = depends(c_compiler)(lambda info: info.type == "gcc") 1308 1309 1310@depends(cxx_compiler, ccache_prefix) 1311@imports("os") 1312def cxx_is_icecream(info, ccache_prefix): 1313 if ( 1314 os.path.islink(info.compiler) 1315 and os.path.basename(os.readlink(info.compiler)) == "icecc" 1316 ): 1317 return True 1318 if ccache_prefix and os.path.basename(ccache_prefix) == "icecc": 1319 return True 1320 1321 1322set_config("CXX_IS_ICECREAM", cxx_is_icecream) 1323 1324 1325@depends(c_compiler) 1326def msvs_version(info): 1327 # clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to 1328 # be set for GYP on Windows. 1329 if info.type == "clang-cl": 1330 return "2017" 1331 1332 return "" 1333 1334 1335set_config("MSVS_VERSION", msvs_version) 1336 1337include("compile-checks.configure") 1338include("arm.configure", when=depends(target.cpu)(lambda cpu: cpu == "arm")) 1339 1340 1341@depends(host, host_os_kernel_major_version, target) 1342def needs_macos_sdk_headers_check(host, version, target): 1343 # Only an issue on Mac OS X 10.14 (and probably above). 1344 if host.kernel != "Darwin" or target.kernel != "Darwin" or version < "18": 1345 return 1346 1347 return True 1348 1349 1350@depends( 1351 cxx_compiler.try_run( 1352 header="#include_next <inttypes.h>", 1353 check_msg="for macOS SDK headers", 1354 when=needs_macos_sdk_headers_check, 1355 ), 1356 when=needs_macos_sdk_headers_check, 1357) 1358def check_have_mac_10_14_sdk(value): 1359 if value: 1360 return 1361 1362 die( 1363 "System inttypes.h not found. Please try running " 1364 "`open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg` " 1365 "and following the instructions to install the necessary headers" 1366 ) 1367 1368 1369@depends( 1370 have_64_bit, 1371 try_compile( 1372 body='static_assert(sizeof(void *) == 8, "")', check_msg="for 64-bit OS" 1373 ), 1374) 1375def check_have_64_bit(have_64_bit, compiler_have_64_bit): 1376 if have_64_bit != compiler_have_64_bit: 1377 configure_error( 1378 "The target compiler does not agree with configure " 1379 "about the target bitness." 1380 ) 1381 1382 1383@depends(cxx_compiler, target) 1384def needs_libstdcxx_newness_check(cxx_compiler, target): 1385 # We only have to care about this on Linux and MinGW. 1386 if cxx_compiler.type == "clang-cl": 1387 return 1388 1389 if target.kernel not in ("Linux", "WINNT"): 1390 return 1391 1392 if target.os == "Android": 1393 return 1394 1395 return True 1396 1397 1398def die_on_old_libstdcxx(): 1399 die( 1400 "The libstdc++ in use is not new enough. Please run " 1401 "./mach bootstrap to update your compiler, or update your system " 1402 "libstdc++ installation." 1403 ) 1404 1405 1406try_compile( 1407 includes=["cstddef"], 1408 body="\n".join( 1409 [ 1410 # _GLIBCXX_RELEASE showed up in libstdc++ 7. 1411 "#if defined(__GLIBCXX__) && !defined(_GLIBCXX_RELEASE)", 1412 "# error libstdc++ not new enough", 1413 "#endif", 1414 "#if defined(_GLIBCXX_RELEASE)", 1415 "# if _GLIBCXX_RELEASE < %d" % minimum_gcc_version().major, 1416 "# error libstdc++ not new enough", 1417 "# else", 1418 " (void) 0", 1419 "# endif", 1420 "#endif", 1421 ] 1422 ), 1423 check_msg="for new enough STL headers from libstdc++", 1424 when=needs_libstdcxx_newness_check, 1425 onerror=die_on_old_libstdcxx, 1426) 1427 1428 1429@depends(c_compiler, target) 1430def default_debug_flags(compiler_info, target): 1431 # Debug info is ON by default. 1432 if compiler_info.type == "clang-cl": 1433 return "-Z7" 1434 elif target.kernel == "WINNT" and compiler_info.type == "clang": 1435 return "-g -gcodeview" 1436 return "-g" 1437 1438 1439option(env="MOZ_DEBUG_FLAGS", nargs=1, help="Debug compiler flags") 1440 1441imply_option("--enable-debug-symbols", depends_if("--enable-debug")(lambda v: v)) 1442 1443option( 1444 "--disable-debug-symbols", 1445 nargs="?", 1446 help="Disable debug symbols using the given compiler flags", 1447) 1448 1449set_config("MOZ_DEBUG_SYMBOLS", depends_if("--enable-debug-symbols")(lambda _: True)) 1450 1451 1452@depends("MOZ_DEBUG_FLAGS", "--enable-debug-symbols", default_debug_flags) 1453def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags): 1454 # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value, 1455 # --enable-debug-symbols takes precedence. Note, the value of 1456 # --enable-debug-symbols may be implied by --enable-debug. 1457 if len(enable_debug_flags): 1458 return enable_debug_flags[0] 1459 if env_debug_flags: 1460 return env_debug_flags[0] 1461 return default_debug_flags 1462 1463 1464set_config("MOZ_DEBUG_FLAGS", debug_flags) 1465add_old_configure_assignment("MOZ_DEBUG_FLAGS", debug_flags) 1466 1467 1468@depends(c_compiler) 1469def color_cflags(info): 1470 # We could test compiling with flags. By why incur the overhead when 1471 # color support should always be present in a specific toolchain 1472 # version? 1473 1474 # Code for auto-adding this flag to compiler invocations needs to 1475 # determine if an existing flag isn't already present. That is likely 1476 # using exact string matching on the returned value. So if the return 1477 # value changes to e.g. "<x>=always", exact string match may fail and 1478 # multiple color flags could be added. So examine downstream consumers 1479 # before adding flags to return values. 1480 if info.type == "gcc": 1481 return "-fdiagnostics-color" 1482 elif info.type == "clang": 1483 return "-fcolor-diagnostics" 1484 else: 1485 return "" 1486 1487 1488set_config("COLOR_CFLAGS", color_cflags) 1489 1490# Some standard library headers (notably bionic on Android) declare standard 1491# functions (e.g. getchar()) and also #define macros for those standard 1492# functions. libc++ deals with this by doing something like the following 1493# (explanatory comments added): 1494# 1495# #ifdef FUNC 1496# // Capture the definition of FUNC. 1497# inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); } 1498# #undef FUNC 1499# // Use a real inline definition. 1500# inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); } 1501# #endif 1502# 1503# _LIBCPP_INLINE_VISIBILITY is typically defined as: 1504# 1505# __attribute__((__visibility__("hidden"), __always_inline__)) 1506# 1507# Unfortunately, this interacts badly with our system header wrappers, as the: 1508# 1509# #pragma GCC visibility push(default) 1510# 1511# that they do prior to including the actual system header is treated by the 1512# compiler as an explicit declaration of visibility on every function declared 1513# in the header. Therefore, when the libc++ code above is encountered, it is 1514# as though the compiler has effectively seen: 1515# 1516# int FUNC(...) __attribute__((__visibility__("default"))); 1517# int FUNC(...) __attribute__((__visibility__("hidden"))); 1518# 1519# and the compiler complains about the mismatched visibility declarations. 1520# 1521# However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no 1522# existing definition. We can therefore define it to the empty string (since 1523# we are properly managing visibility ourselves) and avoid this whole mess. 1524# Note that we don't need to do this with gcc, as libc++ detects gcc and 1525# effectively does the same thing we are doing here. 1526# 1527# _LIBCPP_ALWAYS_INLINE needs a similar workarounds, since it too declares 1528# hidden visibility. 1529# 1530# _LIBCPP_HIDE_FROM_ABI is a macro in libc++ versions in NDKs >=r19. It too 1531# declares hidden visibility, but it also declares functions as excluded from 1532# explicit instantiation (roughly: the function can be unused in the current 1533# compilation, but does not then trigger an actual definition of the function; 1534# it is assumed the real definition comes from elsewhere). We need to replicate 1535# this setup. 1536 1537 1538@depends(c_compiler, target) 1539def libcxx_override_visibility(c_compiler, target): 1540 if c_compiler.type == "clang" and target.os == "Android": 1541 return namespace( 1542 empty="", 1543 hide_from_abi="__attribute__((__exclude_from_explicit_instantiation__))", 1544 ) 1545 1546 1547set_define("_LIBCPP_INLINE_VISIBILITY", libcxx_override_visibility.empty) 1548set_define("_LIBCPP_ALWAYS_INLINE", libcxx_override_visibility.empty) 1549 1550set_define("_LIBCPP_HIDE_FROM_ABI", libcxx_override_visibility.hide_from_abi) 1551 1552 1553@depends(target, check_build_environment) 1554def visibility_flags(target, env): 1555 if target.os != "WINNT": 1556 if target.kernel == "Darwin": 1557 return ("-fvisibility=hidden", "-fvisibility-inlines-hidden") 1558 return ( 1559 "-I%s/system_wrappers" % os.path.join(env.dist), 1560 "-include", 1561 "%s/config/gcc_hidden.h" % env.topsrcdir, 1562 ) 1563 1564 1565@depends(target, visibility_flags) 1566def wrap_system_includes(target, visibility_flags): 1567 if visibility_flags and target.kernel != "Darwin": 1568 return True 1569 1570 1571set_define( 1572 "HAVE_VISIBILITY_HIDDEN_ATTRIBUTE", 1573 depends(visibility_flags)(lambda v: bool(v) or None), 1574) 1575set_define( 1576 "HAVE_VISIBILITY_ATTRIBUTE", depends(visibility_flags)(lambda v: bool(v) or None) 1577) 1578set_config("WRAP_SYSTEM_INCLUDES", wrap_system_includes) 1579set_config("VISIBILITY_FLAGS", visibility_flags) 1580 1581 1582@template 1583def depend_cflags(host_or_target_c_compiler): 1584 @depends(host_or_target_c_compiler) 1585 def depend_cflags(host_or_target_c_compiler): 1586 if host_or_target_c_compiler.type != "clang-cl": 1587 return ["-MD", "-MP", "-MF $(MDDEPDIR)/$(@F).pp"] 1588 else: 1589 # clang-cl doesn't accept the normal -MD -MP -MF options that clang 1590 # does, but the underlying cc1 binary understands how to generate 1591 # dependency files. These options are based on analyzing what the 1592 # normal clang driver sends to cc1 when given the "correct" 1593 # dependency options. 1594 return [ 1595 "-Xclang", 1596 "-MP", 1597 "-Xclang", 1598 "-dependency-file", 1599 "-Xclang", 1600 "$(MDDEPDIR)/$(@F).pp", 1601 "-Xclang", 1602 "-MT", 1603 "-Xclang", 1604 "$@", 1605 ] 1606 1607 return depend_cflags 1608 1609 1610set_config("_DEPEND_CFLAGS", depend_cflags(c_compiler)) 1611set_config("_HOST_DEPEND_CFLAGS", depend_cflags(host_c_compiler)) 1612 1613 1614@depends(c_compiler) 1615def preprocess_option(compiler): 1616 # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi. 1617 if compiler.type in ("gcc", "clang"): 1618 return "-E -o " 1619 else: 1620 return "-P -Fi" 1621 1622 1623set_config("PREPROCESS_OPTION", preprocess_option) 1624 1625 1626# We only want to include windows.configure when we are compiling on 1627# Windows, or for Windows. 1628 1629 1630@depends(target, host) 1631def is_windows(target, host): 1632 return host.kernel == "WINNT" or target.kernel == "WINNT" 1633 1634 1635include("windows.configure", when=is_windows) 1636 1637 1638# On Power ISA, determine compiler flags for VMX, VSX and VSX-3. 1639 1640set_config( 1641 "PPC_VMX_FLAGS", 1642 ["-maltivec"], 1643 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")), 1644) 1645 1646set_config( 1647 "PPC_VSX_FLAGS", 1648 ["-mvsx"], 1649 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")), 1650) 1651 1652set_config( 1653 "PPC_VSX3_FLAGS", 1654 ["-mvsx", "-mcpu=power9"], 1655 when=depends(target.cpu)(lambda cpu: cpu.startswith("ppc")), 1656) 1657 1658# ASAN 1659# ============================================================== 1660 1661option("--enable-address-sanitizer", help="Enable Address Sanitizer") 1662 1663 1664@depends(when="--enable-address-sanitizer") 1665def asan(): 1666 return True 1667 1668 1669add_old_configure_assignment("MOZ_ASAN", asan) 1670 1671# MSAN 1672# ============================================================== 1673 1674option("--enable-memory-sanitizer", help="Enable Memory Sanitizer") 1675 1676 1677@depends(when="--enable-memory-sanitizer") 1678def msan(): 1679 return True 1680 1681 1682add_old_configure_assignment("MOZ_MSAN", msan) 1683 1684# TSAN 1685# ============================================================== 1686 1687option("--enable-thread-sanitizer", help="Enable Thread Sanitizer") 1688 1689 1690@depends(when="--enable-thread-sanitizer") 1691def tsan(): 1692 return True 1693 1694 1695add_old_configure_assignment("MOZ_TSAN", tsan) 1696 1697# UBSAN 1698# ============================================================== 1699 1700option( 1701 "--enable-undefined-sanitizer", nargs="*", help="Enable UndefinedBehavior Sanitizer" 1702) 1703 1704 1705@depends_if("--enable-undefined-sanitizer") 1706def ubsan(options): 1707 default_checks = [ 1708 "bool", 1709 "bounds", 1710 "enum", 1711 "integer-divide-by-zero", 1712 "object-size", 1713 "pointer-overflow", 1714 "return", 1715 "vla-bound", 1716 ] 1717 1718 checks = options if len(options) else default_checks 1719 1720 return ",".join(checks) 1721 1722 1723add_old_configure_assignment("MOZ_UBSAN_CHECKS", ubsan) 1724 1725 1726option( 1727 "--enable-signed-overflow-sanitizer", 1728 help="Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)", 1729) 1730 1731 1732@depends(when="--enable-signed-overflow-sanitizer") 1733def ub_signed_overflow_san(): 1734 return True 1735 1736 1737add_old_configure_assignment("MOZ_SIGNED_OVERFLOW_SANITIZE", ub_signed_overflow_san) 1738 1739 1740option( 1741 "--enable-unsigned-overflow-sanitizer", 1742 help="Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)", 1743) 1744 1745 1746@depends(when="--enable-unsigned-overflow-sanitizer") 1747def ub_unsigned_overflow_san(): 1748 return True 1749 1750 1751add_old_configure_assignment("MOZ_UNSIGNED_OVERFLOW_SANITIZE", ub_unsigned_overflow_san) 1752 1753# Security Hardening 1754# ============================================================== 1755 1756option( 1757 "--enable-hardening", 1758 env="MOZ_SECURITY_HARDENING", 1759 help="Enables security hardening compiler options", 1760) 1761 1762 1763# This function is a bit confusing. It adds or removes hardening flags in 1764# three stuations: if --enable-hardening is passed; if --disable-hardening 1765# is passed, and if no flag is passed. 1766# 1767# At time of this comment writing, all flags are actually added in the 1768# default no-flag case; making --enable-hardening the same as omitting the 1769# flag. --disable-hardening will omit the security flags. (However, not all 1770# possible security flags will be omitted by --disable-hardening, as many are 1771# compiler-default options we do not explicitly enable.) 1772@depends( 1773 "--enable-hardening", 1774 "--enable-address-sanitizer", 1775 "--enable-debug", 1776 "--enable-optimize", 1777 c_compiler, 1778 target, 1779) 1780def security_hardening_cflags( 1781 hardening_flag, asan, debug, optimize, c_compiler, target 1782): 1783 compiler_is_gccish = c_compiler.type in ("gcc", "clang") 1784 mingw_clang = c_compiler.type == "clang" and target.os == "WINNT" 1785 1786 flags = [] 1787 ldflags = [] 1788 js_flags = [] 1789 js_ldflags = [] 1790 1791 # WASI compiler doesn't support security hardening cflags 1792 if target.os == "WASI": 1793 return 1794 1795 # ---------------------------------------------------------- 1796 # If hardening is explicitly enabled, or not explicitly disabled 1797 if hardening_flag.origin == "default" or hardening_flag: 1798 # FORTIFY_SOURCE ------------------------------------ 1799 # Require optimization for FORTIFY_SOURCE. See Bug 1417452 1800 # Also, undefine it before defining it just in case a distro adds it, see Bug 1418398 1801 if compiler_is_gccish and optimize and not asan: 1802 # Don't enable FORTIFY_SOURCE on Android on the top-level, but do enable in js/ 1803 if target.os != "Android": 1804 flags.append("-U_FORTIFY_SOURCE") 1805 flags.append("-D_FORTIFY_SOURCE=2") 1806 js_flags.append("-U_FORTIFY_SOURCE") 1807 js_flags.append("-D_FORTIFY_SOURCE=2") 1808 if mingw_clang: 1809 # mingw-clang needs to link in ssp which is not done by default 1810 ldflags.append("-lssp") 1811 js_ldflags.append("-lssp") 1812 1813 # fstack-protector ------------------------------------ 1814 # Enable only if hardening is not disabled and ASAN is 1815 # not on as ASAN will catch the crashes for us 1816 if compiler_is_gccish and not asan: 1817 flags.append("-fstack-protector-strong") 1818 ldflags.append("-fstack-protector-strong") 1819 js_flags.append("-fstack-protector-strong") 1820 js_ldflags.append("-fstack-protector-strong") 1821 1822 if ( 1823 c_compiler.type == "clang" 1824 and c_compiler.version >= "11.0.1" 1825 and target.os not in ("WINNT", "OSX") 1826 and target.cpu in ("x86", "x86_64", "ppc64", "s390x") 1827 ): 1828 flags.append("-fstack-clash-protection") 1829 ldflags.append("-fstack-clash-protection") 1830 js_flags.append("-fstack-clash-protection") 1831 js_ldflags.append("-fstack-clash-protection") 1832 1833 # ftrivial-auto-var-init ------------------------------ 1834 # Initialize local variables with a 0xAA pattern in clang debug builds. 1835 # Linux32 fails some xpcshell tests with -ftrivial-auto-var-init 1836 linux32 = target.kernel == "Linux" and target.cpu == "x86" 1837 if ( 1838 (c_compiler.type == "clang" or c_compiler.type == "clang-cl") 1839 and c_compiler.version >= "8" 1840 and debug 1841 and not linux32 1842 ): 1843 if c_compiler.type == "clang-cl": 1844 flags.append("-Xclang") 1845 js_flags.append("-Xclang") 1846 flags.append("-ftrivial-auto-var-init=pattern") 1847 js_flags.append("-ftrivial-auto-var-init=pattern") 1848 1849 # ASLR ------------------------------------------------ 1850 # ASLR (dynamicbase) is enabled by default in clang-cl; but the 1851 # mingw-clang build requires it to be explicitly enabled 1852 if mingw_clang: 1853 ldflags.append("-Wl,--dynamicbase") 1854 js_ldflags.append("-Wl,--dynamicbase") 1855 1856 # Control Flow Guard (CFG) ---------------------------- 1857 if ( 1858 c_compiler.type == "clang-cl" 1859 and c_compiler.version >= "8" 1860 and (target.cpu != "aarch64" or c_compiler.version >= "8.0.1") 1861 ): 1862 if target.cpu == "aarch64" and c_compiler.version >= "10.0.0": 1863 # The added checks in clang 10 make arm64 builds crash. (Bug 1639318) 1864 flags.append("-guard:cf,nochecks") 1865 js_flags.append("-guard:cf,nochecks") 1866 else: 1867 flags.append("-guard:cf") 1868 js_flags.append("-guard:cf") 1869 # nolongjmp is needed because clang doesn't emit the CFG tables of 1870 # setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057 1871 ldflags.append("-guard:cf,nolongjmp") 1872 js_ldflags.append("-guard:cf,nolongjmp") 1873 1874 # ---------------------------------------------------------- 1875 # If ASAN _is_ on, undefine FORTIFY_SOURCE just to be safe 1876 if asan: 1877 flags.append("-U_FORTIFY_SOURCE") 1878 js_flags.append("-U_FORTIFY_SOURCE") 1879 1880 # fno-common ----------------------------------------- 1881 # Do not merge variables for ASAN; can detect some subtle bugs 1882 if asan: 1883 # clang-cl does not recognize the flag, it must be passed down to clang 1884 if c_compiler.type == "clang-cl": 1885 flags.append("-Xclang") 1886 flags.append("-fno-common") 1887 1888 return namespace( 1889 flags=flags, 1890 ldflags=ldflags, 1891 js_flags=js_flags, 1892 js_ldflags=js_ldflags, 1893 ) 1894 1895 1896set_config("MOZ_HARDENING_CFLAGS", security_hardening_cflags.flags) 1897set_config("MOZ_HARDENING_LDFLAGS", security_hardening_cflags.ldflags) 1898set_config("MOZ_HARDENING_CFLAGS_JS", security_hardening_cflags.js_flags) 1899set_config("MOZ_HARDENING_LDFLAGS_JS", security_hardening_cflags.js_ldflags) 1900 1901 1902# Frame pointers 1903# ============================================================== 1904@depends(c_compiler) 1905def frame_pointer_flags(compiler): 1906 if compiler.type == "clang-cl": 1907 return namespace( 1908 enable=["-Oy-"], 1909 disable=["-Oy"], 1910 ) 1911 return namespace( 1912 enable=["-fno-omit-frame-pointer", "-funwind-tables"], 1913 disable=["-fomit-frame-pointer", "-funwind-tables"], 1914 ) 1915 1916 1917@depends( 1918 moz_optimize.optimize, 1919 moz_debug, 1920 target, 1921 "--enable-memory-sanitizer", 1922 "--enable-address-sanitizer", 1923 "--enable-undefined-sanitizer", 1924) 1925def frame_pointer_default(optimize, debug, target, msan, asan, ubsan): 1926 return bool( 1927 not optimize 1928 or debug 1929 or msan 1930 or asan 1931 or ubsan 1932 or (target.os == "WINNT" and target.cpu in ("x86", "aarch64")) 1933 ) 1934 1935 1936option( 1937 "--enable-frame-pointers", 1938 default=frame_pointer_default, 1939 help="{Enable|Disable} frame pointers", 1940) 1941 1942 1943@depends("--enable-frame-pointers", frame_pointer_flags) 1944def frame_pointer_flags(enable, flags): 1945 if enable: 1946 return flags.enable 1947 return flags.disable 1948 1949 1950set_config("MOZ_FRAMEPTR_FLAGS", frame_pointer_flags) 1951 1952 1953# Code Coverage 1954# ============================================================== 1955 1956option("--enable-coverage", env="MOZ_CODE_COVERAGE", help="Enable code coverage") 1957 1958 1959@depends("--enable-coverage") 1960def code_coverage(value): 1961 if value: 1962 return True 1963 1964 1965set_config("MOZ_CODE_COVERAGE", code_coverage) 1966set_define("MOZ_CODE_COVERAGE", code_coverage) 1967 1968 1969@depends(target, c_compiler, check_build_environment, when=code_coverage) 1970@imports("os") 1971@imports("re") 1972@imports(_from="__builtin__", _import="open") 1973def coverage_cflags(target, c_compiler, build_env): 1974 cflags = ["--coverage"] 1975 1976 # clang 11 no longer accepts this flag (its behavior became the default) 1977 if c_compiler.type in ("clang", "clang-cl") and c_compiler.version < "11.0.0": 1978 cflags += [ 1979 "-Xclang", 1980 "-coverage-no-function-names-in-data", 1981 ] 1982 1983 exclude = [] 1984 if target.os == "WINNT" and c_compiler.type == "clang-cl": 1985 # VS files 1986 exclude.append("^.*[vV][sS]20[0-9]{2}.*$") 1987 # Files in fetches directory. 1988 exclude.append("^.*[\\\\/]fetches[\\\\/].*$") 1989 elif target.os == "OSX": 1990 # Files in fetches directory. 1991 exclude.append("^.*/fetches/.*$") 1992 elif target.os == "GNU": 1993 # Files in fetches directory. 1994 exclude.append("^.*/fetches/.*$") 1995 # Files in /usr/ 1996 exclude.append("^/usr/.*$") 1997 1998 if exclude: 1999 exclude = ";".join(exclude) 2000 cflags += [ 2001 f"-fprofile-exclude-files={exclude}", 2002 ] 2003 2004 response_file_path = os.path.join(build_env.topobjdir, "code_coverage_cflags") 2005 2006 with open(response_file_path, "w") as f: 2007 f.write(" ".join(cflags)) 2008 2009 return ["@{}".format(response_file_path)] 2010 2011 2012set_config("COVERAGE_CFLAGS", coverage_cflags) 2013 2014# Linker detection 2015# ============================================================== 2016# The policy is as follows: 2017# For Windows: 2018# - the linker is picked via the LINKER environment variable per windows.configure, 2019# but ought to be llvm-lld in any case. 2020# For macOS: 2021# - the linker is ld64, either from XCode on macOS, or from cctools-ports when 2022# cross-compiling. lld can be enabled manually, but as of writing, mach-o support 2023# for lld is incomplete. 2024# For other OSes: 2025# - on local developer builds: lld is used if present. Otherwise gold is used if present 2026# otherwise, BFD ld is used. 2027# - on release/official builds: whatever "ld" resolves to is used, except on Android x86/x86_64 2028# where BFD ld is used. Usually, "ld" resolves to BFD ld, except with the Android NDK, 2029# where it resolves to gold. lld is not used by default on Linux and Android because 2030# it introduces layout changes that prevent elfhack from working. See e.g. 2031# https://bugzilla.mozilla.org/show_bug.cgi?id=1563654#c2. 2032@depends(target) 2033def is_linker_option_enabled(target): 2034 if target.kernel not in ("WINNT", "SunOS"): 2035 return True 2036 2037 2038option( 2039 "--enable-gold", 2040 env="MOZ_FORCE_GOLD", 2041 help="Enable GNU Gold Linker when it is not already the default", 2042 when=is_linker_option_enabled, 2043) 2044 2045imply_option("--enable-linker", "gold", when="--enable-gold") 2046 2047 2048@depends(target, developer_options) 2049def enable_linker_default(target, developer_options): 2050 # x86-64 gold has bugs in how it lays out .note.* sections. See bug 1573820. 2051 # x86-32 gold has a bug when assembly files are built. See bug 1651699. 2052 # lld is faster, so prefer that for developer builds. 2053 if target.os == "Android" and target.cpu in ("x86", "x86_64"): 2054 return "lld" if developer_options else "bfd" 2055 2056 2057option( 2058 "--enable-linker", 2059 nargs=1, 2060 help="Select the linker {bfd, gold, ld64, lld, lld-*}{|}", 2061 default=enable_linker_default, 2062 when=is_linker_option_enabled, 2063) 2064 2065 2066# No-op to enable depending on --enable-linker from default_elfhack in 2067# toolkit/moz.configure. 2068@depends("--enable-linker", when=is_linker_option_enabled) 2069def enable_linker(linker): 2070 return linker 2071 2072 2073@depends( 2074 "--enable-linker", 2075 c_compiler, 2076 developer_options, 2077 "--enable-gold", 2078 extra_toolchain_flags, 2079 target, 2080 when=is_linker_option_enabled, 2081) 2082@checking("for linker", lambda x: x.KIND) 2083@imports("os") 2084@imports("shutil") 2085def select_linker( 2086 linker, c_compiler, developer_options, enable_gold, toolchain_flags, target 2087): 2088 2089 if linker: 2090 linker = linker[0] 2091 else: 2092 linker = None 2093 2094 def is_valid_linker(linker): 2095 if target.kernel == "Darwin": 2096 valid_linkers = ("ld64", "lld") 2097 else: 2098 valid_linkers = ("bfd", "gold", "lld") 2099 if linker in valid_linkers: 2100 return True 2101 if "lld" in valid_linkers and linker.startswith("lld-"): 2102 return True 2103 return False 2104 2105 if linker and not is_valid_linker(linker): 2106 # Check that we are trying to use a supported linker 2107 die("Unsupported linker " + linker) 2108 2109 # Check the kind of linker 2110 version_check = ["-Wl,--version"] 2111 cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags 2112 2113 def try_linker(linker): 2114 # Generate the compiler flag 2115 if linker == "ld64": 2116 linker_flag = ["-fuse-ld=ld"] 2117 elif linker: 2118 linker_flag = ["-fuse-ld=" + linker] 2119 else: 2120 linker_flag = [] 2121 cmd = cmd_base + linker_flag + version_check 2122 if toolchain_flags: 2123 cmd += toolchain_flags 2124 2125 # ld64 doesn't have anything to print out a version. It does print out 2126 # "ld64: For information on command line options please use 'man ld'." 2127 # but that would require doing two attempts, one with --version, that 2128 # would fail, and another with --help. 2129 # Instead, abuse its LD_PRINT_OPTIONS feature to detect a message 2130 # specific to it on stderr when it fails to process --version. 2131 env = dict(os.environ) 2132 env["LD_PRINT_OPTIONS"] = "1" 2133 # Some locales might not print out the strings we are looking for, so 2134 # ensure consistent output. 2135 env["LC_ALL"] = "C" 2136 retcode, stdout, stderr = get_cmd_output(*cmd, env=env) 2137 if retcode == 1 and "Logging ld64 options" in stderr: 2138 kind = "ld64" 2139 2140 elif retcode != 0: 2141 return None 2142 2143 elif "GNU ld" in stdout: 2144 # We are using the normal linker 2145 kind = "bfd" 2146 2147 elif "GNU gold" in stdout: 2148 kind = "gold" 2149 2150 elif "LLD" in stdout: 2151 kind = "lld" 2152 2153 else: 2154 kind = "unknown" 2155 2156 return namespace( 2157 KIND=kind, 2158 LINKER_FLAG=linker_flag, 2159 ) 2160 2161 result = try_linker(linker) 2162 if result is None and linker: 2163 die("Could not use {} as linker".format(linker)) 2164 2165 if ( 2166 linker is None 2167 and enable_gold.origin == "default" 2168 and developer_options 2169 and (result is None or result.KIND in ("bfd", "gold")) 2170 ): 2171 # try and use lld if available. 2172 tried = try_linker("lld") 2173 if (result is None or result.KIND != "gold") and ( 2174 tried is None or tried.KIND != "lld" 2175 ): 2176 tried = try_linker("gold") 2177 if tried is None or tried.KIND != "gold": 2178 tried = None 2179 if tried: 2180 result = tried 2181 2182 if result is None: 2183 die("Failed to find a linker") 2184 2185 # If an explicit linker was given, error out if what we found is different. 2186 if linker and not linker.startswith(result.KIND): 2187 die("Could not use {} as linker".format(linker)) 2188 2189 return result 2190 2191 2192set_config("LINKER_KIND", select_linker.KIND) 2193 2194 2195@depends_if(select_linker, target, macos_sdk, sysroot_path, multiarch_dir) 2196@imports("os") 2197def linker_ldflags(linker, target, macos_sdk, sysroot_path, multiarch_dir): 2198 flags = list((linker and linker.LINKER_FLAG) or []) 2199 if target.kernel == "Darwin": 2200 if linker and linker.KIND == "ld64": 2201 flags.append("-Wl,-syslibroot,%s" % macos_sdk) 2202 else: 2203 flags.append("-Wl,--sysroot=%s" % macos_sdk) 2204 2205 # rpath-link is irrelevant to wasm, see for more info https://github.com/emscripten-core/emscripten/issues/11076. 2206 if sysroot_path and multiarch_dir and target.os != "WASI": 2207 for d in ("lib", "usr/lib"): 2208 multiarch_lib_dir = os.path.join(sysroot_path, d, multiarch_dir) 2209 if os.path.exists(multiarch_lib_dir): 2210 # Non-Debian-patched binutils linkers (both BFD and gold) don't lookup 2211 # in multi-arch directories. 2212 flags.append("-Wl,-rpath-link,%s" % multiarch_lib_dir) 2213 return flags 2214 2215 2216add_old_configure_assignment("LINKER_LDFLAGS", linker_ldflags) 2217 2218 2219# There's a wrinkle with MinGW: linker configuration is not enabled, so 2220# `select_linker` is never invoked. Hard-code around it. 2221@depends(select_linker, target, c_compiler) 2222def gcc_use_gnu_ld(select_linker, target, c_compiler): 2223 if select_linker is not None: 2224 return select_linker.KIND in ("bfd", "gold", "lld") 2225 if target.kernel == "WINNT" and c_compiler.type == "clang": 2226 return True 2227 return None 2228 2229 2230# GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld. 2231set_config("GCC_USE_GNU_LD", gcc_use_gnu_ld) 2232add_old_configure_assignment("GCC_USE_GNU_LD", gcc_use_gnu_ld) 2233 2234# Assembler detection 2235# ============================================================== 2236 2237option(env="AS", nargs=1, help="Path to the assembler") 2238 2239 2240@depends(target, c_compiler) 2241def as_info(target, c_compiler): 2242 if c_compiler.type == "clang-cl": 2243 ml = { 2244 "x86": "ml.exe", 2245 "x86_64": "ml64.exe", 2246 "aarch64": "armasm64.exe", 2247 }.get(target.cpu) 2248 return namespace(type="masm", names=(ml,)) 2249 # When building with anything but clang-cl, we just use the C compiler as the assembler. 2250 return namespace(type="gcc", names=(c_compiler.compiler,)) 2251 2252 2253# One would expect the assembler to be specified merely as a program. But in 2254# cases where the assembler is passed down into js/, it can be specified in 2255# the same way as CC: a program + a list of argument flags. We might as well 2256# permit the same behavior in general, even though it seems somewhat unusual. 2257# So we have to do the same sort of dance as we did above with 2258# `provided_compiler`. 2259provided_assembler = provided_program("AS") 2260assembler = check_prog( 2261 "_AS", 2262 input=provided_assembler.program, 2263 what="the assembler", 2264 progs=as_info.names, 2265 paths=vc_toolchain_search_path, 2266) 2267 2268 2269@depends(as_info, assembler, provided_assembler, c_compiler) 2270def as_with_flags(as_info, assembler, provided_assembler, c_compiler): 2271 if provided_assembler: 2272 return provided_assembler.wrapper + [assembler] + provided_assembler.flags 2273 2274 if as_info.type == "masm": 2275 return assembler 2276 2277 assert as_info.type == "gcc" 2278 2279 # Need to add compiler wrappers and flags as appropriate. 2280 return c_compiler.wrapper + [assembler] + c_compiler.flags 2281 2282 2283add_old_configure_assignment("AS", as_with_flags) 2284add_old_configure_assignment("ac_cv_prog_AS", as_with_flags) 2285 2286 2287@depends(assembler, c_compiler, extra_toolchain_flags) 2288@imports("subprocess") 2289@imports(_from="os", _import="devnull") 2290def gnu_as(assembler, c_compiler, toolchain_flags): 2291 # clang uses a compatible GNU assembler. 2292 if c_compiler.type == "clang": 2293 return True 2294 2295 if c_compiler.type == "gcc": 2296 cmd = [assembler] + c_compiler.flags 2297 if toolchain_flags: 2298 cmd += toolchain_flags 2299 cmd += ["-Wa,--version", "-c", "-o", devnull, "-x", "assembler", "-"] 2300 # We don't actually have to provide any input on stdin, `Popen.communicate` will 2301 # close the stdin pipe. 2302 # clang will error if it uses its integrated assembler for this target, 2303 # so handle failures gracefully. 2304 if "GNU" in check_cmd_output(*cmd, stdin=subprocess.PIPE, onerror=lambda: ""): 2305 return True 2306 2307 2308set_config("GNU_AS", gnu_as) 2309add_old_configure_assignment("GNU_AS", gnu_as) 2310 2311 2312@depends(as_info, target) 2313def as_dash_c_flag(as_info, target): 2314 # armasm64 doesn't understand -c. 2315 if as_info.type == "masm" and target.cpu == "aarch64": 2316 return "" 2317 else: 2318 return "-c" 2319 2320 2321set_config("AS_DASH_C_FLAG", as_dash_c_flag) 2322 2323 2324@depends(as_info, target) 2325def as_outoption(as_info, target): 2326 # The uses of ASOUTOPTION depend on the spacing for -o/-Fo. 2327 if as_info.type == "masm" and target.cpu != "aarch64": 2328 return "-Fo" 2329 2330 return "-o " 2331 2332 2333set_config("ASOUTOPTION", as_outoption) 2334 2335# clang plugin handling 2336# ============================================================== 2337 2338option( 2339 "--enable-clang-plugin", 2340 env="ENABLE_CLANG_PLUGIN", 2341 help="Enable building with the Clang plugin (gecko specific static analyzers)", 2342) 2343 2344add_old_configure_assignment( 2345 "ENABLE_CLANG_PLUGIN", depends_if("--enable-clang-plugin")(lambda _: True) 2346) 2347 2348 2349@depends(host_c_compiler, c_compiler, when="--enable-clang-plugin") 2350def llvm_config(host_c_compiler, c_compiler): 2351 clang = None 2352 for compiler in (host_c_compiler, c_compiler): 2353 if compiler and compiler.type == "clang": 2354 clang = compiler.compiler 2355 break 2356 elif compiler and compiler.type == "clang-cl": 2357 clang = os.path.join(os.path.dirname(compiler.compiler), "clang") 2358 break 2359 2360 if not clang: 2361 die("Cannot --enable-clang-plugin when not building with clang") 2362 llvm_config = "llvm-config" 2363 out = check_cmd_output(clang, "--print-prog-name=llvm-config", onerror=lambda: None) 2364 if out: 2365 llvm_config = out.rstrip() 2366 return (llvm_config,) 2367 2368 2369llvm_config = check_prog( 2370 "LLVM_CONFIG", 2371 llvm_config, 2372 what="llvm-config", 2373 when="--enable-clang-plugin", 2374 paths=clang_search_path, 2375) 2376 2377add_old_configure_assignment("LLVM_CONFIG", llvm_config) 2378 2379 2380option( 2381 "--enable-clang-plugin-alpha", 2382 env="ENABLE_CLANG_PLUGIN_ALPHA", 2383 help="Enable static analysis with clang-plugin alpha checks.", 2384) 2385 2386 2387@depends("--enable-clang-plugin", "--enable-clang-plugin-alpha") 2388def check_clang_plugin_alpha(enable_clang_plugin, enable_clang_plugin_alpha): 2389 if enable_clang_plugin_alpha: 2390 if enable_clang_plugin: 2391 return True 2392 die("Cannot enable clang-plugin alpha checkers without --enable-clang-plugin.") 2393 2394 2395add_old_configure_assignment("ENABLE_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha) 2396set_define("MOZ_CLANG_PLUGIN_ALPHA", check_clang_plugin_alpha) 2397 2398option( 2399 "--enable-mozsearch-plugin", 2400 env="ENABLE_MOZSEARCH_PLUGIN", 2401 help="Enable building with the mozsearch indexer plugin", 2402) 2403 2404add_old_configure_assignment( 2405 "ENABLE_MOZSEARCH_PLUGIN", depends_if("--enable-mozsearch-plugin")(lambda _: True) 2406) 2407 2408# Libstdc++ compatibility hacks 2409# ============================================================== 2410# 2411option( 2412 "--enable-stdcxx-compat", 2413 env="MOZ_STDCXX_COMPAT", 2414 help="Enable compatibility with older libstdc++", 2415) 2416 2417 2418@template 2419def libstdcxx_version(var, compiler): 2420 @depends(compiler, when="--enable-stdcxx-compat") 2421 @checking(var, lambda v: v and "GLIBCXX_%s" % v.dotted) 2422 @imports(_from="mozbuild.configure.libstdcxx", _import="find_version") 2423 @imports(_from="__builtin__", _import="Exception") 2424 def version(compiler): 2425 try: 2426 result = find_version( 2427 compiler.wrapper + [compiler.compiler] + compiler.flags 2428 ) 2429 except Exception: 2430 die("Couldn't determine libstdc++ version") 2431 if result: 2432 return namespace( 2433 dotted=result[0], 2434 encoded=str(result[1]), 2435 ) 2436 2437 set_config(var, version.encoded) 2438 return version 2439 2440 2441add_gcc_flag( 2442 "-D_GLIBCXX_USE_CXX11_ABI=0", 2443 cxx_compiler, 2444 when=libstdcxx_version("MOZ_LIBSTDCXX_TARGET_VERSION", cxx_compiler), 2445) 2446add_gcc_flag( 2447 "-D_GLIBCXX_USE_CXX11_ABI=0", 2448 host_cxx_compiler, 2449 when=libstdcxx_version("MOZ_LIBSTDCXX_HOST_VERSION", host_cxx_compiler), 2450) 2451 2452 2453# Support various fuzzing options 2454# ============================================================== 2455option("--enable-fuzzing", help="Enable fuzzing support") 2456 2457 2458@depends(build_project) 2459def js_build(build_project): 2460 return build_project == "js" 2461 2462 2463option( 2464 "--enable-js-fuzzilli", 2465 when=js_build, 2466 help="Enable fuzzilli support for the JS engine", 2467) 2468 2469 2470@depends("--enable-fuzzing") 2471def enable_fuzzing(value): 2472 if value: 2473 return True 2474 2475 2476@depends("--enable-js-fuzzilli", when=js_build) 2477def enable_js_fuzzilli(value): 2478 if value: 2479 return True 2480 2481 2482@depends( 2483 try_compile( 2484 body="__AFL_COMPILER;", check_msg="for AFL compiler", when="--enable-fuzzing" 2485 ) 2486) 2487def enable_aflfuzzer(afl): 2488 if afl: 2489 return True 2490 2491 2492@depends(enable_fuzzing, enable_aflfuzzer, c_compiler, target) 2493def enable_libfuzzer(fuzzing, afl, c_compiler, target): 2494 if fuzzing and not afl and c_compiler.type == "clang" and target.os != "Android": 2495 return True 2496 2497 2498@depends(enable_fuzzing, enable_aflfuzzer, enable_libfuzzer, enable_js_fuzzilli) 2499def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer, enable_js_fuzzilli): 2500 if fuzzing and (afl or libfuzzer) and not enable_js_fuzzilli: 2501 return True 2502 2503 2504set_config("FUZZING", enable_fuzzing) 2505set_define("FUZZING", enable_fuzzing) 2506 2507set_config("LIBFUZZER", enable_libfuzzer) 2508set_define("LIBFUZZER", enable_libfuzzer) 2509add_old_configure_assignment("LIBFUZZER", enable_libfuzzer) 2510 2511set_config("FUZZING_INTERFACES", enable_fuzzing_interfaces) 2512set_define("FUZZING_INTERFACES", enable_fuzzing_interfaces) 2513add_old_configure_assignment("FUZZING_INTERFACES", enable_fuzzing_interfaces) 2514 2515set_config("FUZZING_JS_FUZZILLI", enable_js_fuzzilli) 2516set_define("FUZZING_JS_FUZZILLI", enable_js_fuzzilli) 2517 2518 2519@depends( 2520 c_compiler.try_compile( 2521 flags=["-fsanitize=fuzzer-no-link"], 2522 when=enable_fuzzing, 2523 check_msg="whether the C compiler supports -fsanitize=fuzzer-no-link", 2524 ), 2525 tsan, 2526 enable_js_fuzzilli, 2527) 2528def libfuzzer_flags(value, tsan, enable_js_fuzzilli): 2529 if tsan: 2530 # With ThreadSanitizer, we should not use any libFuzzer instrumentation because 2531 # it is incompatible (e.g. there are races on global sanitizer coverage counters). 2532 # Instead we use an empty set of flags here but still build the fuzzing targets. 2533 # With this setup, we can still run files through these targets in TSan builds, 2534 # e.g. those obtained from regular fuzzing. 2535 # This code can be removed once libFuzzer has been made compatible with TSan. 2536 # 2537 # Also, this code needs to be kept in sync with certain gyp files, currently: 2538 # - dom/media/webrtc/transport/third_party/nICEr/nicer.gyp 2539 return namespace(no_link_flag_supported=False, use_flags=[]) 2540 2541 if enable_js_fuzzilli: 2542 # Fuzzilli comes with its own trace-pc interceptors and flag requirements. 2543 no_link_flag_supported = False 2544 use_flags = ["-fsanitize-coverage=trace-pc-guard", "-g"] 2545 elif value: 2546 no_link_flag_supported = True 2547 # recommended for (and only supported by) clang >= 6 2548 use_flags = ["-fsanitize=fuzzer-no-link"] 2549 else: 2550 no_link_flag_supported = False 2551 use_flags = ["-fsanitize-coverage=trace-pc-guard,trace-cmp"] 2552 2553 return namespace( 2554 no_link_flag_supported=no_link_flag_supported, 2555 use_flags=use_flags, 2556 ) 2557 2558 2559set_config("HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK", libfuzzer_flags.no_link_flag_supported) 2560set_config("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags) 2561add_old_configure_assignment("LIBFUZZER_FLAGS", libfuzzer_flags.use_flags) 2562 2563# Shared library building 2564# ============================================================== 2565 2566# XXX: The use of makefile constructs in these variables is awful. 2567@depends(target, c_compiler) 2568def make_shared_library(target, compiler): 2569 if target.os == "WINNT": 2570 if compiler.type == "gcc": 2571 return namespace( 2572 mkshlib=["$(CXX)", "$(DSO_LDOPTS)", "-o", "$@"], 2573 mkcshlib=["$(CC)", "$(DSO_LDOPTS)", "-o", "$@"], 2574 ) 2575 elif compiler.type == "clang": 2576 return namespace( 2577 mkshlib=[ 2578 "$(CXX)", 2579 "$(DSO_LDOPTS)", 2580 "-Wl,-pdb,$(LINK_PDBFILE)", 2581 "-o", 2582 "$@", 2583 ], 2584 mkcshlib=[ 2585 "$(CC)", 2586 "$(DSO_LDOPTS)", 2587 "-Wl,-pdb,$(LINK_PDBFILE)", 2588 "-o", 2589 "$@", 2590 ], 2591 ) 2592 else: 2593 linker = [ 2594 "$(LINKER)", 2595 "-NOLOGO", 2596 "-DLL", 2597 "-OUT:$@", 2598 "-PDB:$(LINK_PDBFILE)", 2599 "$(DSO_LDOPTS)", 2600 ] 2601 return namespace( 2602 mkshlib=linker, 2603 mkcshlib=linker, 2604 ) 2605 2606 cc = ["$(CC)", "$(COMPUTED_C_LDFLAGS)"] 2607 cxx = ["$(CXX)", "$(COMPUTED_CXX_LDFLAGS)"] 2608 flags = ["$(PGO_CFLAGS)", "$(DSO_LDOPTS)"] 2609 output = ["-o", "$@"] 2610 2611 if target.kernel == "Darwin": 2612 soname = [] 2613 elif target.os == "NetBSD": 2614 soname = ["-Wl,-soname,$(DSO_SONAME)"] 2615 else: 2616 assert compiler.type in ("gcc", "clang") 2617 2618 soname = ["-Wl,-h,$(DSO_SONAME)"] 2619 2620 return namespace( 2621 mkshlib=cxx + flags + soname + output, 2622 mkcshlib=cc + flags + soname + output, 2623 ) 2624 2625 2626set_config("MKSHLIB", make_shared_library.mkshlib) 2627set_config("MKCSHLIB", make_shared_library.mkcshlib) 2628 2629 2630@depends(c_compiler, toolchain_prefix, when=target_is_windows) 2631def rc_names(c_compiler, toolchain_prefix): 2632 if c_compiler.type in ("gcc", "clang"): 2633 return tuple("%s%s" % (p, "windres") for p in ("",) + (toolchain_prefix or ())) 2634 return ("llvm-rc",) 2635 2636 2637check_prog("RC", rc_names, paths=clang_search_path, when=target_is_windows) 2638 2639 2640@depends(toolchain_prefix, c_compiler) 2641def ar_config(toolchain_prefix, c_compiler): 2642 if c_compiler.type == "clang-cl": 2643 return namespace( 2644 names=("llvm-lib",), 2645 flags=("-llvmlibthin", "-out:$@"), 2646 ) 2647 2648 names = tuple("%s%s" % (p, "ar") for p in (toolchain_prefix or ()) + ("",)) 2649 if c_compiler.type == "clang": 2650 # Get the llvm-ar path as per the output from clang --print-prog-name=llvm-ar 2651 # so that we directly get the one under the clang directory, rather than one 2652 # that might be in /usr/bin and that might point to one from a different version 2653 # of clang. 2654 out = check_cmd_output( 2655 c_compiler.compiler, "--print-prog-name=llvm-ar", onerror=lambda: None 2656 ) 2657 llvm_ar = out.rstrip() if out else "llvm-ar" 2658 names = (llvm_ar,) + names 2659 2660 return namespace( 2661 names=names, 2662 flags=("crs", "$@"), 2663 ) 2664 2665 2666ar = check_prog("AR", ar_config.names, paths=clang_search_path) 2667 2668add_old_configure_assignment("AR", ar) 2669 2670set_config("AR_FLAGS", ar_config.flags) 2671 2672 2673@depends(toolchain_prefix, c_compiler) 2674def nm_names(toolchain_prefix, c_compiler): 2675 names = tuple("%s%s" % (p, "nm") for p in (toolchain_prefix or ()) + ("",)) 2676 if c_compiler.type == "clang": 2677 # Get the llvm-nm path as per the output from clang --print-prog-name=llvm-nm 2678 # so that we directly get the one under the clang directory, rather than one 2679 # that might be in /usr/bin and that might point to one from a different version 2680 # of clang. 2681 out = check_cmd_output( 2682 c_compiler.compiler, "--print-prog-name=llvm-nm", onerror=lambda: None 2683 ) 2684 llvm_nm = out.rstrip() if out else "llvm-nm" 2685 names = (llvm_nm,) + names 2686 2687 return names 2688 2689 2690check_prog("NM", nm_names, paths=clang_search_path, when=target_is_linux) 2691 2692 2693option("--enable-cpp-rtti", help="Enable C++ RTTI") 2694 2695add_old_configure_assignment("_MOZ_USE_RTTI", "1", when="--enable-cpp-rtti") 2696 2697 2698option( 2699 "--enable-path-remapping", 2700 nargs="*", 2701 choices=("c", "rust"), 2702 help="Enable remapping source and object paths in compiled outputs.", 2703) 2704 2705 2706@depends("--enable-path-remapping") 2707def path_remapping(value): 2708 if len(value): 2709 return value 2710 if bool(value): 2711 return ["c", "rust"] 2712 return [] 2713 2714 2715@depends( 2716 target, 2717 check_build_environment, 2718 sysroot_path, 2719 macos_sdk, 2720 windows_sdk_dir, 2721 vc_path, 2722 when="--enable-path-remapping", 2723) 2724def path_remappings( 2725 target, build_env, sysroot_path, macos_sdk, windows_sdk_dir, vc_path 2726): 2727 win = target.kernel == "WINNT" 2728 2729 # The prefix maps are processed in the order they're specified on the 2730 # command line. Therefore, to accommodate object directories in the source 2731 # directory, it's important that we map the topobjdir before the topsrcdir, 2732 # 'cuz we might have /src/obj/=/o/ and /src/=/s/. The various other 2733 # directories might be subdirectories of topsrcdir as well, so they come 2734 # earlier still. 2735 2736 path_remappings = [] 2737 2738 # We will have only one sysroot or SDK, so all can have the same mnemonic: K 2739 # for "kit" (since S is taken for "source"). See 2740 # https://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html 2741 # for how to use the Windows `subst` command to map these in debuggers and 2742 # IDEs. 2743 if sysroot_path: 2744 path_remappings.append((sysroot_path, "k:/" if win else "/sysroot/")) 2745 if macos_sdk: 2746 path_remappings.append((macos_sdk, "k:/" if win else "/sysroot/")) 2747 if windows_sdk_dir: 2748 path_remappings.append((windows_sdk_dir, "k:/" if win else "/windows_sdk/")) 2749 if vc_path: 2750 path_remappings.append((vc_path, "v:/" if win else "/vc/")) 2751 2752 path_remappings += [ 2753 (build_env.topobjdir, "o:/" if win else "/topobjdir/"), 2754 (build_env.topsrcdir, "s:/" if win else "/topsrcdir/"), 2755 ] 2756 2757 path_remappings = [ 2758 (normsep(old).rstrip("/") + "/", new) for old, new in path_remappings 2759 ] 2760 2761 # It is tempting to sort these, but we want the order to be the same across 2762 # machines so that we can share cache hits. Therefore we reject bad 2763 # configurations rather than trying to make the configuration good. 2764 for i in range(len(path_remappings) - 1): 2765 p = path_remappings[i][0] 2766 for q, _ in path_remappings[i + 1 :]: 2767 if q.startswith(p): 2768 die(f"Cannot remap paths because {p} is an ancestor of {q}") 2769 2770 return path_remappings 2771