init.configure (44894B)
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 include("util.configure") 8 include("checks.configure") 9 10 # Make `toolkit` available when toolkit/moz.configure is not included. 11 toolkit = dependable(None) 12 # Likewise with `bindgen_config_paths` when 13 # build/moz.configure/bindgen.configure is not included. 14 bindgen_config_paths = dependable(None) 15 16 17 @depends("--help") 18 def build_environment(_): 19 topobjdir = os.path.realpath(".") 20 topsrcdir = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) 21 dist = os.path.join(topobjdir, "dist") 22 23 return namespace( 24 topsrcdir=topsrcdir, 25 topobjdir=topobjdir, 26 dist=dist, 27 ) 28 29 30 @depends(build_environment) 31 @imports(_from="mozfile", _import="json") 32 @imports(_from="pathlib", _import="Path") 33 def configure_cache(build_environment): 34 """ 35 This cache is used to cache the results of compiler invocations 36 between different runs of configure and save them to the disk, which 37 results in a significant speed up of subsequent configure runs (15%-50%). 38 39 It is not currently thread safe because it is just a simple dictionary 40 wrapped in a dummy class (ConfigureCache). It could be improved to be 41 thread safe in the future, if we ever make configure parallelized, but 42 for now there is no advantage to doing so. 43 """ 44 45 class ConfigureCache(dict): 46 pass 47 48 cache_file = Path(build_environment.topobjdir) / "configure.cache" 49 if cache_file.exists(): 50 with cache_file.open() as f: 51 cache_data = json.load(f) 52 else: 53 cache_data = {} 54 55 cache = ConfigureCache(cache_data) 56 cache.version_checked_compilers = set() 57 58 return cache 59 60 61 set_config("TOPSRCDIR", build_environment.topsrcdir) 62 set_config("TOPOBJDIR", build_environment.topobjdir) 63 set_config("DIST", build_environment.dist) 64 65 option(env="MOZ_AUTOMATION", help="Enable options for automated builds") 66 67 68 @depends("MOZ_AUTOMATION") 69 def moz_automation(value): 70 return bool(value) 71 72 73 set_config("MOZ_AUTOMATION", moz_automation) 74 75 option(env="MOZCONFIG", nargs=1, help="Mozconfig location") 76 77 # Read user mozconfig 78 # ============================================================== 79 # Note: the dependency on --help is only there to always read the mozconfig, 80 # even when --help is passed. Without this dependency, the function wouldn't 81 # be called when --help is passed, and the mozconfig wouldn't be read. 82 83 84 @depends("MOZCONFIG", build_environment, "--help") 85 @imports(_from="mozbuild.mozconfig", _import="MozconfigLoader") 86 @imports(_from="mozboot.mozconfig", _import="find_mozconfig") 87 @imports("os") 88 def mozconfig(mozconfig, build_env, help): 89 # Don't read the mozconfig for the js configure (yay backwards 90 # compatibility) 91 # While the long term goal is that js and top-level use the same configure 92 # and the same overall setup, including the possibility to use mozconfigs, 93 # figuring out what we want to do wrt mozconfig vs. command line and 94 # environment variable is not a clear-cut case, and it's more important to 95 # fix the immediate problem mozconfig causes to js developers by 96 # "temporarily" returning to the previous behavior of not loading the 97 # mozconfig for the js configure. 98 # Separately to the immediate problem for js developers, there is also the 99 # need to not load a mozconfig when running js configure as a subconfigure. 100 is_js_configure = "MOZ_JS_CONFIGURE" in os.environ 101 if is_js_configure or (mozconfig and mozconfig[0] == os.devnull): 102 return {"path": None} 103 104 topsrcdir = build_env.topsrcdir 105 loader = MozconfigLoader(topsrcdir) 106 mozconfig = mozconfig[0] if mozconfig else None 107 mozconfig = find_mozconfig(topsrcdir, env={"MOZCONFIG": mozconfig}) 108 mozconfig = loader.read_mozconfig(mozconfig) 109 110 return mozconfig 111 112 113 set_config("MOZCONFIG", depends(mozconfig)(lambda m: m["path"])) 114 115 116 # Mozilla-Build 117 # ============================================================== 118 option(env="MOZILLABUILD", nargs=1, help="Path to Mozilla Build (Windows-only)") 119 120 option(env="CONFIG_SHELL", nargs=1, help="Path to a POSIX shell") 121 122 # It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py, 123 # but the end goal being that the configure script would go away... 124 125 126 @depends("CONFIG_SHELL", "MOZILLABUILD") 127 @checking("for a shell") 128 @imports("sys") 129 @imports(_from="pathlib", _import="Path") 130 def shell(value, mozillabuild): 131 if value: 132 return find_program(value[0]) 133 shell = "sh" 134 if mozillabuild: 135 if (Path(mozillabuild[0]) / "msys2").exists(): 136 shell = mozillabuild[0] + "/msys2/usr/bin/sh" 137 else: 138 shell = mozillabuild[0] + "/msys/bin/sh" 139 if sys.platform == "win32": 140 shell = shell + ".exe" 141 return find_program(shell) 142 143 144 # This defines a reasonable shell for when running with --help. 145 # If one was passed in the environment, though, fall back to that. 146 @depends("--help", "CONFIG_SHELL") 147 def help_shell(help, shell): 148 if help and not shell: 149 return "sh" 150 151 152 shell = help_shell | shell 153 set_config("SHELL", shell) 154 155 156 # Python 3 157 # ======== 158 @dependable 159 @checking("for Python 3", callback=lambda x: "%s (%s)" % (x.path, x.str_version)) 160 @imports("sys") 161 @imports(_from="mach.site", _import="PythonVirtualenv") 162 @imports(_from="os.path", _import="realpath") 163 def virtualenv_python3(): 164 return namespace( 165 # sys.executable is currently not updated for in-process activations. However, 166 # sys.prefix is, so we can calculate the python executable's path from there. 167 path=normalize_path(PythonVirtualenv(realpath(sys.prefix)).python_path), 168 str_version=".".join(str(i) for i in sys.version_info[0:3]), 169 ) 170 171 172 set_config("PYTHON3", virtualenv_python3.path) 173 174 175 # Inject mozconfig options 176 # ============================================================== 177 # All options defined above this point can't be injected in mozconfig_options 178 # below, so collect them. 179 180 181 @template 182 def early_options(): 183 @depends("--help") 184 @imports("__sandbox__") 185 def early_options(_): 186 return set(option.env for option in __sandbox__._options.values() if option.env) 187 188 return early_options 189 190 191 early_options = early_options() 192 193 194 @depends(mozconfig, early_options, moz_automation, "--help") 195 # This gives access to the sandbox. Don't copy this blindly. 196 @imports("__sandbox__") 197 @imports("os") 198 def mozconfig_options(mozconfig, early_options, automation, help): 199 if mozconfig["path"]: 200 if "MOZ_AUTOMATION_MOZCONFIG" in mozconfig["env"]["added"]: 201 if not automation: 202 log.error( 203 "%s directly or indirectly includes an in-tree " "mozconfig.", 204 mozconfig["path"], 205 ) 206 log.error( 207 "In-tree mozconfigs make strong assumptions about " 208 "and are only meant to be used by Mozilla " 209 "automation." 210 ) 211 die("Please don't use them.") 212 helper = __sandbox__._helper 213 log.info("Adding configure options from %s" % mozconfig["path"]) 214 for arg in mozconfig["configure_args"]: 215 log.info(" %s" % arg) 216 # We could be using imply_option() here, but it has other 217 # contraints that don't really apply to the command-line 218 # emulation that mozconfig provides. 219 helper.add(arg, origin="mozconfig", args=helper._args) 220 221 def add(key, value): 222 if key.isupper(): 223 arg = "%s=%s" % (key, value) 224 log.info(" %s" % arg) 225 if key not in early_options: 226 helper.add(arg, origin="mozconfig", args=helper._args) 227 228 for key, value in mozconfig["env"]["added"].items(): 229 add(key, value) 230 os.environ[key] = value 231 for key, (_, value) in mozconfig["env"]["modified"].items(): 232 add(key, value) 233 os.environ[key] = value 234 for key, value in mozconfig["vars"]["added"].items(): 235 add(key, value) 236 for key, (_, value) in mozconfig["vars"]["modified"].items(): 237 add(key, value) 238 239 240 @depends(build_environment, "--help") 241 @imports(_from="os.path", _import="exists") 242 def js_package(build_env, help): 243 return not exists(os.path.join(build_env.topsrcdir, "browser")) 244 245 246 # Source checkout and version control integration. 247 # ================================================ 248 249 250 @depends(build_environment, moz_automation, js_package, "--help") 251 @checking("for vcs source checkout") 252 @imports("os") 253 def vcs_checkout_type(build_env, automation, js_package, help): 254 if os.path.exists(os.path.join(build_env.topsrcdir, ".hg")): 255 return "hg" 256 elif os.path.exists(os.path.join(build_env.topsrcdir, ".jj")): 257 return "jj" 258 elif os.path.exists(os.path.join(build_env.topsrcdir, ".git")): 259 return "git" 260 elif automation and not js_package and not help: 261 raise FatalCheckError( 262 "unable to resolve VCS type; must run " 263 "from a source checkout when MOZ_AUTOMATION " 264 "is set" 265 ) 266 267 268 # Resolve VCS binary for detected repository type. 269 270 271 # TODO remove hg.exe once bug 1382940 addresses ambiguous executables case. 272 hg = check_prog( 273 "HG", 274 ( 275 "hg.exe", 276 "hg", 277 ), 278 allow_missing=True, 279 when=depends(vcs_checkout_type)(lambda x: x == "hg"), 280 ) 281 git = check_prog( 282 "GIT", 283 ("git",), 284 allow_missing=True, 285 when=depends(vcs_checkout_type)(lambda x: x in ("git", "jj")), 286 ) 287 jj = check_prog( 288 "JJ", 289 ("jj",), 290 allow_missing=True, 291 when=depends(vcs_checkout_type)(lambda x: x == "jj"), 292 ) 293 294 295 @depends_if(hg) 296 @checking("for Mercurial version") 297 @imports("os") 298 @imports("re") 299 def hg_version(hg): 300 # HGPLAIN in Mercurial 1.5+ forces stable output, regardless of set 301 # locale or encoding. 302 env = dict(os.environ) 303 env["HGPLAIN"] = "1" 304 305 out = check_cmd_output(hg, "--version", env=env) 306 307 match = re.search(r"Mercurial Distributed SCM \(version ([^\)]+)", out) 308 309 if not match: 310 raise FatalCheckError("unable to determine Mercurial version: %s" % out) 311 312 # The version string may be "unknown" for Mercurial run out of its own 313 # source checkout or for bad builds. But LooseVersion handles it. 314 315 return Version(match.group(1)) 316 317 318 # Resolve Mercurial config items so other checks have easy access. 319 # Do NOT set this in the config because it may contain sensitive data 320 # like API keys. 321 @depends_all(build_environment, hg, hg_version) 322 @imports("os") 323 def hg_config(build_env, hg, version): 324 env = dict(os.environ) 325 env["HGPLAIN"] = "1" 326 327 # Warnings may get sent to stderr. But check_cmd_output() ignores 328 # stderr if exit code is 0. And the command should always succeed if 329 # `hg version` worked. 330 out = check_cmd_output(hg, "config", env=env, cwd=build_env.topsrcdir) 331 332 config = {} 333 334 for line in out.strip().splitlines(): 335 key, value = [s.strip() for s in line.split("=", 1)] 336 config[key] = value 337 338 return config 339 340 341 @depends_if(git) 342 @checking("for Git version") 343 @imports("re") 344 def git_version(git): 345 out = check_cmd_output(git, "--version").rstrip() 346 347 match = re.search("git version (.*)$", out) 348 349 if not match: 350 raise FatalCheckError("unable to determine Git version: %s" % out) 351 352 return Version(match.group(1)) 353 354 355 # Only set VCS_CHECKOUT_TYPE if we resolved the VCS binary. 356 # Require resolved VCS info when running in automation so automation's 357 # environment is more well-defined. 358 @depends(vcs_checkout_type, hg_version, git_version, jj, moz_automation) 359 def exposed_vcs_checkout_type(vcs_checkout_type, hg, git, jj, automation): 360 if vcs_checkout_type == "hg": 361 if hg: 362 return "hg" 363 364 if automation: 365 raise FatalCheckError("could not resolve Mercurial binary info") 366 367 elif vcs_checkout_type == "git": 368 if git: 369 return "git" 370 371 if automation: 372 raise FatalCheckError("could not resolve Git binary info") 373 374 elif vcs_checkout_type == "jj": 375 # mozversioncontrol wants both jj and git 376 if jj and git: 377 return "jj" 378 379 if automation: 380 raise FatalCheckError("could not resolve Jujutsu binary info") 381 elif vcs_checkout_type: 382 raise FatalCheckError("unhandled VCS type: %s" % vcs_checkout_type) 383 384 385 set_config("VCS_CHECKOUT_TYPE", exposed_vcs_checkout_type) 386 387 # The application/project to build 388 # ============================================================== 389 option( 390 "--enable-application", 391 nargs=1, 392 env="MOZ_BUILD_APP", 393 help="Application to build. Same as --enable-project", 394 ) 395 396 397 @depends("--enable-application") 398 def application(app): 399 if app: 400 return app 401 402 403 imply_option("--enable-project", application) 404 405 406 @depends(build_environment, js_package) 407 def default_project(build_env, js_package): 408 if js_package or build_env.topobjdir.endswith("/js/src"): 409 return "js" 410 return "browser" 411 412 413 option("--enable-project", nargs=1, default=default_project, help="Project to build") 414 415 416 # Artifact builds 417 # ============================================================== 418 419 option( 420 "--enable-artifact-builds", 421 env="MOZ_ARTIFACT_BUILDS", 422 help="Download and use prebuilt binary artifacts", 423 ) 424 425 426 @depends("--enable-artifact-builds") 427 def artifact_builds(value): 428 if value: 429 return True 430 431 432 set_config("MOZ_ARTIFACT_BUILDS", artifact_builds) 433 434 # Host and target systems 435 # ============================================================== 436 option("--host", nargs=1, help="Define the system type performing the build") 437 438 option( 439 "--target", 440 nargs=1, 441 help="Define the system type where the resulting executables will be " "used", 442 ) 443 444 445 @imports(_from="mozbuild.configure.constants", _import="Abi") 446 @imports(_from="mozbuild.configure.constants", _import="CPU") 447 @imports(_from="mozbuild.configure.constants", _import="CPU_bitness") 448 @imports(_from="mozbuild.configure.constants", _import="Endianness") 449 @imports(_from="mozbuild.configure.constants", _import="Kernel") 450 @imports(_from="mozbuild.configure.constants", _import="OS") 451 @imports(_from="__builtin__", _import="ValueError") 452 def split_triplet(triplet, allow_wasi=False): 453 # The standard triplet is defined as 454 # CPU_TYPE-VENDOR-OPERATING_SYSTEM 455 # There is also a quartet form: 456 # CPU_TYPE-VENDOR-KERNEL-OPERATING_SYSTEM 457 # But we can consider the "KERNEL-OPERATING_SYSTEM" as one. 458 # Additionally, some may omit "unknown" when the vendor 459 # is not specified and emit 460 # CPU_TYPE-OPERATING_SYSTEM 461 vendor = "unknown" 462 parts = triplet.split("-", 2) 463 if len(parts) == 3: 464 cpu, vendor, os = parts 465 elif len(parts) == 2: 466 cpu, os = parts 467 else: 468 raise ValueError("Unexpected triplet string: %s" % triplet) 469 470 # Autoconf uses config.sub to validate and canonicalize those triplets, 471 # but the granularity of its results has never been satisfying to our 472 # use, so we've had our own, different, canonicalization. We've also 473 # historically not been very consistent with how we use the canonicalized 474 # values. Hopefully, this will help us make things better. 475 # The tests are inherited from our decades-old autoconf-based configure, 476 # which can probably be improved/cleaned up because they are based on a 477 # mix of uname and config.guess output, while we now only use the latter, 478 # which presumably has a cleaner and leaner output. Let's refine later. 479 raw_os = os = os.replace("/", "_") 480 abi = None 481 sub_configure_alias = triplet 482 if "android" in os: 483 canonical_os = "Android" 484 canonical_kernel = "Linux" 485 elif os.startswith("linux"): 486 canonical_os = "GNU" 487 canonical_kernel = "Linux" 488 elif os.startswith("kfreebsd") and os.endswith("-gnu"): 489 canonical_os = "GNU" 490 canonical_kernel = "kFreeBSD" 491 elif os.startswith("gnu"): 492 canonical_os = canonical_kernel = "GNU" 493 elif os.startswith("mingw") or os in ( 494 "windows-msvc", 495 "windows-gnu", 496 "windows-gnullvm", 497 ): 498 canonical_os = canonical_kernel = "WINNT" 499 if not os.startswith("mingw"): 500 if os == "windows-msvc": 501 abi = "msvc" 502 elif os == "windows-gnu" or os == "windows-gnullvm": 503 abi = "mingw" 504 # Many things down the line are looking for the string "mingw32" 505 # until they are all fixed, we pretend that's the raw os we had 506 # in the first place, even when we didn't. 507 sub_configure_alias = sub_configure_alias[: -len(os)] + "mingw32" 508 raw_os = "mingw32" 509 elif os.startswith("darwin"): 510 canonical_kernel = "Darwin" 511 canonical_os = "OSX" 512 elif os.startswith("ios"): 513 canonical_kernel = "Darwin" 514 canonical_os = "iOS" 515 # old-configure does plenty of tests against $target and $target_os 516 # and expects darwin for iOS, so make it happy. 517 sub_configure_alias = sub_configure_alias[: -len(os)] + "darwin" 518 # rust knows ios-sim, clang knows ios-simulator. We only take the 519 # former as --target, but we need to make clang happy. 520 if os == "ios-sim": 521 os = "ios-simulator" 522 elif os.startswith("dragonfly"): 523 canonical_os = canonical_kernel = "DragonFly" 524 elif os.startswith("freebsd"): 525 canonical_os = canonical_kernel = "FreeBSD" 526 elif os.startswith("netbsd"): 527 canonical_os = canonical_kernel = "NetBSD" 528 elif os.startswith("openbsd"): 529 canonical_os = canonical_kernel = "OpenBSD" 530 elif os.startswith("solaris"): 531 canonical_os = canonical_kernel = "SunOS" 532 elif os.startswith("wasi") and allow_wasi: 533 canonical_os = canonical_kernel = "WASI" 534 else: 535 raise ValueError("Unknown OS: %s" % os) 536 537 # The CPU granularity is probably not enough. Moving more things from 538 # old-configure will tell us if we need more 539 if cpu.endswith("86") or (cpu.startswith("i") and "86" in cpu): 540 canonical_cpu = "x86" 541 endianness = "little" 542 elif cpu in ("x86_64", "ia64"): 543 canonical_cpu = cpu 544 endianness = "little" 545 elif cpu in ("s390", "s390x"): 546 canonical_cpu = cpu 547 endianness = "big" 548 elif cpu in ("powerpc64", "ppc64", "powerpc64le", "ppc64le"): 549 canonical_cpu = "ppc64" 550 endianness = "little" if "le" in cpu else "big" 551 elif cpu in ("powerpc", "ppc", "rs6000") or cpu.startswith("powerpc"): 552 canonical_cpu = "ppc" 553 endianness = "big" 554 elif cpu in ("Alpha", "alpha", "ALPHA"): 555 canonical_cpu = "Alpha" 556 endianness = "little" 557 elif cpu.startswith("hppa") or cpu == "parisc": 558 canonical_cpu = "hppa" 559 endianness = "big" 560 elif cpu.startswith("sparc64") or cpu.startswith("sparcv9"): 561 canonical_cpu = "sparc64" 562 endianness = "big" 563 elif cpu.startswith("sparc") or cpu == "sun4u": 564 canonical_cpu = "sparc" 565 endianness = "big" 566 elif cpu.startswith("arm"): 567 canonical_cpu = "arm" 568 endianness = "big" if cpu.startswith(("armeb", "armbe")) else "little" 569 elif cpu in ("m68k"): 570 canonical_cpu = "m68k" 571 endianness = "big" 572 elif cpu in ("mips", "mipsel"): 573 canonical_cpu = "mips32" 574 endianness = "little" if "el" in cpu else "big" 575 elif cpu in ("mips64", "mips64el"): 576 canonical_cpu = "mips64" 577 endianness = "little" if "el" in cpu else "big" 578 elif cpu.startswith("aarch64"): 579 canonical_cpu = "aarch64" 580 endianness = "little" 581 elif cpu in ("riscv64", "riscv64gc"): 582 canonical_cpu = "riscv64" 583 endianness = "little" 584 elif cpu.startswith("loongarch64"): 585 canonical_cpu = "loongarch64" 586 endianness = "little" 587 elif cpu == "sh4": 588 canonical_cpu = "sh4" 589 endianness = "little" 590 elif cpu == "wasm32" and allow_wasi: 591 canonical_cpu = "wasm32" 592 endianness = "little" 593 else: 594 raise ValueError("Unknown CPU type: %s" % cpu) 595 596 # Toolchains, most notably for cross compilation may use cpu-os 597 # prefixes. We need to be more specific about the LLVM target on Mac 598 # so cross-language LTO will work correctly. 599 600 if os.startswith(("darwin", "ios")): 601 toolchain = "%s-apple-%s" % (cpu, os) 602 else: 603 toolchain = "%s-%s" % (cpu, os) 604 605 # In tor-browser-build we use slightly different terminology for 606 # the supported platforms. Let's prepare that OS string here. 607 # 608 # Not all possible platforms listed here are supported in tbb, 609 # so this value will be empty sometimes. 610 tor_browser_build_alias = None 611 if canonical_os == "Android" and canonical_kernel == "Linux": 612 tor_browser_build_alias = f"android" 613 elif canonical_os == "GNU" and canonical_kernel == "Linux": 614 tor_browser_build_alias = f"linux" 615 elif canonical_os == "OSX" and canonical_kernel == "Darwin": 616 tor_browser_build_alias = f"macos" 617 elif canonical_os == "WINNT" and canonical_kernel == "WINNT": 618 tor_browser_build_alias = f"windows" 619 620 return namespace( 621 alias=triplet, 622 cpu=CPU(canonical_cpu), 623 bitness=CPU_bitness[canonical_cpu], 624 kernel=Kernel(canonical_kernel), 625 os=OS(canonical_os), 626 endianness=Endianness(endianness), 627 # For now, only report the Windows ABI. 628 abi=abi and Abi(abi), 629 raw_cpu=cpu, 630 raw_os=raw_os, 631 toolchain=toolchain, 632 vendor=vendor, 633 sub_configure_alias=sub_configure_alias, 634 tor_browser_build_alias=tor_browser_build_alias, 635 ) 636 637 638 # This defines a fake target/host namespace for when running with --help 639 # If either --host or --target is passed on the command line, then fall 640 # back to the real deal. 641 @depends("--help", "--host", "--target") 642 def help_host_target(help, host, target): 643 if help and not host and not target: 644 return namespace( 645 alias="unknown-unknown-unknown", 646 cpu="unknown", 647 bitness="unknown", 648 kernel="unknown", 649 os="unknown", 650 endianness="unknown", 651 abi="unknown", 652 raw_cpu="unknown", 653 raw_os="unknown", 654 toolchain="unknown-unknown", 655 ) 656 657 658 def config_sub(shell, triplet): 659 config_sub = os.path.join(os.path.dirname(__file__), "..", "autoconf", "config.sub") 660 # Config.sub doesn't like the *-windows-msvc/*-windows-gnu/*-ios-sim triplets, so 661 # munge those before and after calling config.sub. 662 suffix = None 663 munging = { 664 "-windows-msvc": "-mingw32", 665 "-windows-gnu": "-mingw32", 666 "-ios-sim": "-ios", 667 } 668 for check_suffix, replacement in munging.items(): 669 if triplet.endswith(check_suffix): 670 suffix = check_suffix 671 triplet = triplet[: -len(suffix)] + replacement 672 break 673 result = check_cmd_output(shell, config_sub, triplet).strip() 674 if suffix: 675 assert result.endswith(replacement) 676 result = result[: -len(replacement)] + suffix 677 return result 678 679 680 @depends("--host", shell) 681 @checking("for host system type", lambda h: h.alias) 682 @imports("os") 683 @imports("sys") 684 @imports(_from="__builtin__", _import="ValueError") 685 def real_host(value, shell): 686 if not value and sys.platform == "win32": 687 arch = os.environ.get("PROCESSOR_ARCHITEW6432") or os.environ.get( 688 "PROCESSOR_ARCHITECTURE" 689 ) 690 if arch == "AMD64": 691 return split_triplet("x86_64-pc-windows-msvc") 692 elif arch == "x86": 693 return split_triplet("i686-pc-windows-msvc") 694 elif arch == "ARM64": 695 return split_triplet("aarch64-pc-windows-msvc") 696 697 if not value: 698 config_guess = os.path.join( 699 os.path.dirname(__file__), "..", "autoconf", "config.guess" 700 ) 701 702 # Ensure that config.guess is determining the host triplet, not the target 703 # triplet 704 env = os.environ.copy() 705 env.pop("CC_FOR_BUILD", None) 706 env.pop("HOST_CC", None) 707 env.pop("CC", None) 708 709 host = check_cmd_output(shell, config_guess, env=env).strip() 710 try: 711 return split_triplet(host) 712 except ValueError: 713 pass 714 else: 715 host = value[0] 716 717 host = config_sub(shell, host) 718 719 try: 720 return split_triplet(host) 721 except ValueError as e: 722 die(e) 723 724 725 host = help_host_target | real_host 726 727 728 @depends("--target", real_host, shell, "--enable-project", "--enable-application") 729 @checking("for target system type", lambda t: t.alias) 730 @imports(_from="__builtin__", _import="ValueError") 731 def real_target(value, host, shell, project, application): 732 # Because --enable-project is implied by --enable-application, and 733 # implied options are not currently handled during --help, which is 734 # used get the build target in mozbuild.base, we manually check 735 # whether --enable-application was given, and fall back to 736 # --enable-project if not. Both can't be given contradictory values 737 # under normal circumstances, so it's fine. 738 if application: 739 project = application[0] 740 elif project: 741 project = project[0] 742 if not value: 743 if project == "mobile/android": 744 target_cpu, target_system = { 745 "aarch64": ("aarch64", "android"), 746 "x86_64": ("x86_64", "android"), 747 }.get(host.cpu, ("arm", "androideabi")) 748 return split_triplet(f"{target_cpu}-unknown-linux-{target_system}") 749 if project == "mobile/ios": 750 if (host.cpu, host.os) == ("x86_64", "OSX"): 751 return split_triplet(f"x86_64-apple-ios") 752 return split_triplet(f"aarch64-apple-ios-sim") 753 return host 754 # If --target was only given a cpu arch, expand it with the 755 # non-cpu part of the host. For mobile/android, expand it with 756 # unknown-linux-android. 757 target = value[0] 758 if "-" not in target: 759 if project == "mobile/android": 760 rest = "unknown-linux-android" 761 if target.startswith("arm"): 762 rest += "eabi" 763 elif project == "mobile/ios": 764 sim_suffix = "" if target == "x86_64" else "-sim" 765 rest = f"apple-ios{sim_suffix}" 766 else: 767 cpu, rest = host.alias.split("-", 1) 768 target = "-".join((target, rest)) 769 try: 770 return split_triplet(target) 771 except ValueError: 772 pass 773 774 try: 775 return split_triplet(config_sub(shell, target), allow_wasi=(project == "js")) 776 except ValueError as e: 777 die(e) 778 779 780 target = help_host_target | real_target 781 782 783 @depends(host, target) 784 @checking("whether cross compiling") 785 def cross_compiling(host, target): 786 return host != target 787 788 789 set_config("CROSS_COMPILE", cross_compiling) 790 set_define("CROSS_COMPILE", cross_compiling) 791 792 793 @depends(target) 794 def have_64_bit(target): 795 if target.bitness == 64: 796 return True 797 798 799 set_config("HAVE_64BIT_BUILD", have_64_bit) 800 set_define("HAVE_64BIT_BUILD", have_64_bit) 801 802 # Some third-party code bases depend on this being set for big-endians. 803 set_define( 804 "WORDS_BIGENDIAN", True, when=depends(target.endianness)(lambda e: e == "big") 805 ) 806 807 set_config("host", host.sub_configure_alias) 808 set_config("target", target.sub_configure_alias) 809 810 811 # These variables are for compatibility with the current moz.builds and 812 # old-configure. Eventually, we'll want to canonicalize better. 813 @depends(target) 814 def target_variables(target): 815 if target.kernel == "kFreeBSD": 816 os_target = "GNU/kFreeBSD" 817 os_arch = "GNU_kFreeBSD" 818 elif target.kernel == "Darwin" or (target.kernel == "Linux" and target.os == "GNU"): 819 os_target = target.kernel 820 os_arch = target.kernel 821 else: 822 os_target = target.os 823 os_arch = target.kernel 824 825 return namespace( 826 OS_TARGET=str(os_target), 827 OS_ARCH=str(os_arch), 828 INTEL_ARCHITECTURE=target.cpu in ("x86", "x86_64") or None, 829 ) 830 831 832 set_config("OS_TARGET", target_variables.OS_TARGET) 833 set_config("OS_ARCH", target_variables.OS_ARCH) 834 obsolete_config("CPU_ARCH", replacement="TARGET_CPU") 835 set_config("INTEL_ARCHITECTURE", target_variables.INTEL_ARCHITECTURE) 836 set_config("TARGET_CPU", target.cpu) 837 set_config("TARGET_RAW_CPU", target.raw_cpu) 838 set_config("TARGET_OS", target.os) 839 set_config("TARGET_RAW_OS", target.raw_os) 840 set_config("TARGET_KERNEL", target.kernel) 841 set_config("TARGET_ENDIANNESS", target.endianness) 842 843 844 @depends(host) 845 def host_variables(host): 846 if host.kernel == "kFreeBSD": 847 os_arch = "GNU_kFreeBSD" 848 else: 849 os_arch = host.kernel 850 return namespace( 851 HOST_OS_ARCH=os_arch, 852 ) 853 854 855 set_config("HOST_CPU_ARCH", host.cpu) 856 set_config("HOST_OS_ARCH", host_variables.HOST_OS_ARCH) 857 set_config("HOST_ALIAS", host.alias) 858 859 860 @depends(target) 861 def target_is_windows(target): 862 if target.kernel == "WINNT": 863 return True 864 865 866 @depends(host) 867 def host_is_windows(host): 868 if host.kernel == "WINNT": 869 return True 870 871 872 set_define("_WINDOWS", target_is_windows) 873 set_define("WIN32", target_is_windows) 874 set_define("XP_WIN", target_is_windows) 875 876 877 @depends(target) 878 def target_is_unix(target): 879 if target.kernel != "WINNT": 880 return True 881 882 883 set_define("XP_UNIX", target_is_unix) 884 885 886 @depends(target) 887 def target_is_darwin(target): 888 if target.kernel == "Darwin": 889 return True 890 891 892 set_define("XP_DARWIN", target_is_darwin) 893 894 895 @depends(target) 896 def target_is_osx(target): 897 if target.kernel == "Darwin" and target.os == "OSX": 898 return True 899 900 901 @depends(host) 902 def host_is_osx(host): 903 if host.os == "OSX": 904 return True 905 906 907 set_define("XP_MACOSX", target_is_osx) 908 909 910 @depends(target) 911 def target_is_ios(target): 912 if target.kernel == "Darwin" and target.os == "iOS": 913 return True 914 915 916 set_define("XP_IOS", target_is_ios) 917 918 919 @depends(target) 920 def target_has_linux_kernel(target): 921 if target.kernel == "Linux": 922 return True 923 924 925 set_define("XP_LINUX", target_has_linux_kernel) 926 927 928 @depends(target) 929 def target_is_linux_or_wasi(target): 930 if (target.kernel == "Linux" and target.os == "GNU") or target.kernel == "WASI": 931 return True 932 933 934 @depends(target) 935 def target_is_android(target): 936 if target.os == "Android": 937 return True 938 939 940 set_define("ANDROID", target_is_android) 941 942 943 @depends(target) 944 def target_is_openbsd(target): 945 if target.kernel == "OpenBSD": 946 return True 947 948 949 set_define("XP_OPENBSD", target_is_openbsd) 950 951 952 @depends(target) 953 def target_is_netbsd(target): 954 if target.kernel == "NetBSD": 955 return True 956 957 958 set_define("XP_NETBSD", target_is_netbsd) 959 960 961 @depends(target) 962 def target_is_freebsd(target): 963 if target.kernel == "FreeBSD": 964 return True 965 966 967 set_define("XP_FREEBSD", target_is_freebsd) 968 969 970 @depends(target) 971 def target_is_solaris(target): 972 if target.kernel == "SunOS": 973 return True 974 975 976 set_define("XP_SOLARIS", target_is_solaris) 977 978 979 @depends(target) 980 def target_is_sparc(target): 981 if target.cpu == "sparc64": 982 return True 983 984 985 set_define("SPARC64", target_is_sparc) 986 987 988 @depends(target, when=target_is_android) 989 def android_cpu_arch(target): 990 d = { 991 "aarch64": "arm64-v8a", 992 "arm": "armeabi-v7a", 993 "x86": "x86", 994 "x86_64": "x86_64", 995 } 996 if target.cpu not in d: 997 die(f"Cannot determine android_cpu_arch: unknown target.cpu: {target.cpu}") 998 return d[target.cpu] 999 1000 1001 set_config("ANDROID_CPU_ARCH", android_cpu_arch) 1002 1003 1004 @depends("--enable-project", build_environment, "--help") 1005 @imports(_from="os.path", _import="exists") 1006 def include_project_configure(project, build_env, help): 1007 if not project: 1008 die("--enable-project is required.") 1009 1010 base_dir = build_env.topsrcdir 1011 path = os.path.join(base_dir, project[0], "moz.configure") 1012 if not exists(path): 1013 die("Cannot find project %s", project[0]) 1014 return path 1015 1016 1017 @depends("--enable-project") 1018 def build_project(project): 1019 return project[0] 1020 1021 1022 set_config("MOZ_BUILD_APP", build_project) 1023 set_define("MOZ_BUILD_APP", build_project) 1024 1025 1026 option(env="MOZILLA_OFFICIAL", help="Build an official release") 1027 1028 1029 @depends("MOZILLA_OFFICIAL") 1030 def mozilla_official(official): 1031 if official: 1032 return True 1033 1034 1035 set_config("MOZILLA_OFFICIAL", mozilla_official) 1036 set_define("MOZILLA_OFFICIAL", mozilla_official) 1037 1038 # Allow specifying custom paths to the version files used by the milestone() function below. 1039 option( 1040 "--with-version-file-path", 1041 nargs=1, 1042 help="Specify a custom path to app version files instead of auto-detecting", 1043 default=None, 1044 ) 1045 1046 1047 @depends("--with-version-file-path") 1048 def version_path(path): 1049 return path 1050 1051 1052 # Allow to easily build nightly with a release / beta configuration so that we 1053 # can have the same options we'd have on a release version. 1054 # This is useful for performance profiling, as there are things that we don't 1055 # enable in release (like the background hang monitor) that can affect 1056 # performance. 1057 option( 1058 "--as-milestone", 1059 help="Build with another milestone configuration (e.g., as release)", 1060 choices=("early-beta", "late-beta", "release"), 1061 default=None, 1062 ) 1063 1064 1065 # Firefox looks for "a" or "a1" in the milestone to detect whether nightly 1066 # features should be enabled. We do not want them, because we want our nightly 1067 # builds to be as close as possible to actual releases. 1068 # So we set always is_release_or_beta to True. 1069 @depends( 1070 build_environment, 1071 build_project, 1072 version_path, 1073 "--as-milestone", 1074 "--help", 1075 ) 1076 @imports(_from="__builtin__", _import="open") 1077 @imports("os") 1078 @imports("re") 1079 def milestone(build_env, build_project, version_path, as_milestone, _): 1080 versions = [] 1081 paths = ["config/milestone.txt"] 1082 if build_project == "js": 1083 paths = paths * 3 1084 else: 1085 paths += [ 1086 "browser/config/version.txt", 1087 "browser/config/version_display.txt", 1088 ] 1089 if version_path: 1090 version_path = version_path[0] 1091 else: 1092 version_path = os.path.join(build_project, "config") 1093 for f in ("version.txt", "version_display.txt"): 1094 f = os.path.join(version_path, f) 1095 if not os.path.exists(os.path.join(build_env.topsrcdir, f)): 1096 break 1097 paths.append(f) 1098 1099 for p in paths: 1100 with open(os.path.join(build_env.topsrcdir, p), "r") as fh: 1101 content = fh.read().splitlines() 1102 if not content: 1103 die("Could not find a version number in {}".format(p)) 1104 versions.append(content[-1]) 1105 1106 is_early_beta_or_earlier = None 1107 if as_milestone: 1108 if "a1" not in versions[0]: 1109 # We could make this work with some effort 1110 die("--as-milestone only works on nightly builds") 1111 as_milestone = as_milestone[0] 1112 as_milestone_flag = "" if as_milestone == "release" else "b1" 1113 versions = [v.replace("a1", as_milestone_flag) for v in versions] 1114 1115 milestone, firefox_version, firefox_version_display = versions[:3] 1116 1117 # version.txt content from the project directory if there is one, otherwise 1118 # the firefox version. 1119 app_version = versions[3] if len(versions) > 3 else firefox_version 1120 # version_display.txt content from the project directory if there is one, 1121 # otherwise version.txt content from the project directory, otherwise the 1122 # firefox version for display. 1123 app_version_display = versions[-1] if len(versions) > 3 else firefox_version_display 1124 1125 is_nightly = is_release_or_beta = None 1126 1127 # Do not enable extra nightly features 1128 is_release_or_beta = True 1129 1130 major_version = milestone.split(".")[0] 1131 m = re.search(r"([ab]\d+)", milestone) 1132 ab_patch = m.group(1) if m else "" 1133 1134 if False: 1135 defines = os.path.join(build_env.topsrcdir, "build", "defines.sh") 1136 with open(defines, "r") as fh: 1137 for line in fh.read().splitlines(): 1138 line = line.strip() 1139 if not line or line.startswith("#"): 1140 continue 1141 name, _, value = line.partition("=") 1142 name = name.strip() 1143 value = value.strip() 1144 if name != "EARLY_BETA_OR_EARLIER": 1145 die( 1146 "Only the EARLY_BETA_OR_EARLIER variable can be set in build/defines.sh" 1147 ) 1148 if value and any(x in app_version_display for x in "ab"): 1149 is_early_beta_or_earlier = True 1150 1151 # Only expose the major version milestone in the UA string and hide the 1152 # patch leve (bugs 572659 and 870868). 1153 # 1154 # Only expose major milestone and alpha version in the symbolversion 1155 # string; as the name suggests, we use it for symbol versioning on Linux. 1156 return namespace( 1157 version=milestone, 1158 uaversion="%s.0" % major_version, 1159 symbolversion="%s%s" % (major_version, ab_patch), 1160 is_nightly=is_nightly, 1161 is_release_or_beta=is_release_or_beta, 1162 is_early_beta_or_earlier=is_early_beta_or_earlier, 1163 is_esr=app_version_display.endswith("esr") or None, 1164 app_version=app_version, 1165 app_version_display=app_version_display, 1166 ) 1167 1168 1169 # For extensions and langpacks, we require a max version that is compatible 1170 # across security releases. MOZ_APP_MAXVERSION is our method for doing that. 1171 # 24.0a1 and 24.0a2 aren't affected 1172 # 24.0 becomes 24.* 1173 # 24.1.1 becomes 24.* 1174 @depends(milestone.app_version, build_project) 1175 def moz_app_maxversion(app_version, build_project): 1176 if "a" not in app_version: 1177 version = Version(app_version) 1178 if build_project == "suite": 1179 return f"{version.major}.{version.minor}.*" 1180 else: 1181 return f"{version.major}.*" 1182 else: 1183 return app_version 1184 1185 1186 set_config("GRE_MILESTONE", milestone.version) 1187 set_config("NIGHTLY_BUILD", milestone.is_nightly) 1188 set_define("NIGHTLY_BUILD", milestone.is_nightly) 1189 set_config("RELEASE_OR_BETA", milestone.is_release_or_beta) 1190 set_define("RELEASE_OR_BETA", milestone.is_release_or_beta) 1191 set_config("MOZ_ESR", milestone.is_esr) 1192 set_define("MOZ_ESR", milestone.is_esr) 1193 set_config("EARLY_BETA_OR_EARLIER", milestone.is_early_beta_or_earlier) 1194 set_define("EARLY_BETA_OR_EARLIER", milestone.is_early_beta_or_earlier) 1195 set_define("MOZILLA_VERSION", depends(milestone)(lambda m: '"%s"' % m.version)) 1196 set_config("MOZILLA_VERSION", milestone.version) 1197 set_define("MOZILLA_UAVERSION", depends(milestone)(lambda m: '"%s"' % m.uaversion)) 1198 1199 set_config("MOZ_APP_VERSION", milestone.app_version) 1200 set_config("MOZ_APP_VERSION_DISPLAY", milestone.app_version_display) 1201 set_config("MOZ_APP_MAXVERSION", moz_app_maxversion) 1202 1203 1204 # The app update channel is 'default' when not supplied. The value is used in 1205 # the application's confvars.sh (and is made available to a project specific 1206 # moz.configure). 1207 option( 1208 "--enable-update-channel", 1209 nargs=1, 1210 help="Select application update channel", 1211 default="default", 1212 ) 1213 1214 1215 @depends("--enable-update-channel") 1216 def update_channel(channel): 1217 if not channel or channel[0] == "": 1218 return "default" 1219 return channel[0].lower() 1220 1221 1222 set_config("MOZ_UPDATE_CHANNEL", update_channel) 1223 set_define("MOZ_UPDATE_CHANNEL", update_channel) 1224 1225 1226 # Add a TOR_BROWSER_NIGHTLY_BUILD flag to use when MOZ_UPDATE_CHANNEL cannot be 1227 # used. For example, for the C++ preprocessor. See tor-browser#41340 1228 @depends("--enable-update-channel") 1229 def tor_browser_nightly_build(channel): 1230 if channel and channel[0] in ["default", "nightly"]: 1231 return True 1232 1233 1234 set_define("TOR_BROWSER_NIGHTLY_BUILD", tor_browser_nightly_build) 1235 1236 1237 option( 1238 env="MOZBUILD_STATE_PATH", 1239 nargs=1, 1240 help="Path to a persistent state directory for the build system " 1241 "and related tools", 1242 ) 1243 1244 1245 @depends("MOZBUILD_STATE_PATH", "--help") 1246 @imports("os") 1247 def mozbuild_state_path(path, _): 1248 if path: 1249 return normalize_path(path[0]) 1250 return normalize_path(os.path.expanduser(os.path.join("~", ".mozbuild"))) 1251 1252 1253 @depends("MOZILLABUILD", shell, host_is_windows) 1254 @imports(_from="pathlib", _import="Path") 1255 def mozillabuild_bin_paths(mozillabuild, shell, host_is_windows): 1256 paths = [] 1257 if not mozillabuild or not host_is_windows: 1258 return paths 1259 paths.append(os.path.dirname(shell)) 1260 paths.append(str(Path(mozillabuild[0]) / "bin")) 1261 return paths 1262 1263 1264 @depends(mozillabuild_bin_paths) 1265 @imports("os") 1266 def prefer_mozillabuild_path(mozillabuild_bin_paths): 1267 return mozillabuild_bin_paths + os.environ["PATH"].split(os.pathsep) 1268 1269 1270 # milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set. 1271 1272 1273 @depends(milestone) 1274 def enabled_in_nightly(milestone): 1275 return milestone.is_nightly 1276 1277 1278 # Check if we need the 32-bit Linux SSE2 error dialog 1279 # =================================================== 1280 option( 1281 env="MOZ_LINUX_32_SSE2_STARTUP_ERROR", 1282 help="Add code to perform startup checks to warn if SSE2 is not available", 1283 default=moz_automation, 1284 when=target_is_unix & depends(target)(lambda target: target.cpu == "x86"), 1285 ) 1286 set_config( 1287 "MOZ_LINUX_32_SSE2_STARTUP_ERROR", True, when="MOZ_LINUX_32_SSE2_STARTUP_ERROR" 1288 ) 1289 1290 1291 # Branding 1292 # ============================================================== 1293 option( 1294 "--with-app-basename", 1295 env="MOZ_APP_BASENAME", 1296 nargs=1, 1297 help="Typically stays consistent for multiple branded versions of a " 1298 'given application (e.g. Aurora and Firefox both use "Firefox"), but ' 1299 "may vary for full rebrandings (e.g. Iceweasel). Used for " 1300 'application.ini\'s "Name" field, which controls profile location in ' 1301 'the absence of a "Profile" field (see below), and various system ' 1302 "integration hooks (Unix remoting, Windows MessageWindow name, etc", 1303 ) 1304 1305 1306 @depends("--with-app-basename", target_is_android) 1307 def moz_app_basename(value, target_is_android): 1308 if value: 1309 return value[0] 1310 if target_is_android: 1311 return "Fennec" 1312 return "Firefox" 1313 1314 1315 set_config( 1316 "MOZ_APP_BASENAME", 1317 moz_app_basename, 1318 when=depends(build_project)(lambda p: p != "js"), 1319 ) 1320 1321 1322 @depends("MOZILLABUILD", when=host_is_windows) 1323 @checking("for MozillaBuild directory") 1324 @imports("os") 1325 def mozillabuild_dir(mozillabuild): 1326 if mozillabuild and os.path.exists(mozillabuild[0]): 1327 return mozillabuild[0] 1328 1329 1330 @depends_if(mozillabuild_dir) 1331 @checking("for MozillaBuild version") 1332 @imports(_from="__builtin__", _import="Exception") 1333 @imports(_from="__builtin__", _import="open") 1334 def mozillabuild_version(mozillabuild_dir): 1335 try: 1336 with open(os.path.join(mozillabuild_dir, "VERSION"), "r") as fh: 1337 return Version(fh.readline().strip()) 1338 except Exception as e: 1339 die(e) 1340 1341 1342 @depends_if(mozillabuild_version) 1343 def valid_mozillabuild_version(mozillabuild_version): 1344 if mozillabuild_version < Version("4.0"): 1345 die( 1346 f"Please upgrade MozillaBuild. You can find a recent version at: https://wiki.mozilla.org/MozillaBuild" 1347 ) 1348 1349 1350 # old-school options for controlling install step 1351 # ============================================================== 1352 option( 1353 "--prefix", 1354 nargs=1, 1355 metavar="PREFIX", 1356 default="/usr/local", 1357 help="Install files using PREFIX as root directory", 1358 ) 1359 set_config("prefix", depends("--prefix")(lambda prefix: prefix[0])) 1360 1361 # Unlike autoconf, we don't offer these as a customisation point. 1362 set_config("exec_prefix", depends("--prefix")(lambda prefix: prefix[0])) 1363 set_config("datadir", depends("--prefix")(lambda prefix: f"{prefix[0]}/share")) 1364 set_config("bindir", depends("--prefix")(lambda prefix: f"{prefix[0]}/bin")) 1365 set_config("sbindir", depends("--prefix")(lambda prefix: f"{prefix[0]}/sbin")) 1366 set_config("infodir", depends("--prefix")(lambda prefix: f"{prefix[0]}/info")) 1367 set_config("libexec", depends("--prefix")(lambda prefix: f"{prefix[0]}/libexec")) 1368 set_config("localstatedir", depends("--prefix")(lambda prefix: f"{prefix[0]}/var")) 1369 set_config("sharedstatedir", depends("--prefix")(lambda prefix: f"{prefix[0]}/com")) 1370 set_config("sysconfdir", depends("--prefix")(lambda prefix: f"{prefix[0]}/etc")) 1371 set_config("mandir", depends("--prefix")(lambda prefix: f"{prefix[0]}/man")) 1372 set_config("oldincludedir", depends("--prefix")(lambda prefix: f"{prefix[0]}/include")) 1373 set_config("top_srcdir", build_environment.topsrcdir) 1374 set_config("program_transform_name", "s,x,x,") 1375 1376 option( 1377 "--includedir", 1378 nargs=1, 1379 metavar="DIR", 1380 default=depends("--prefix")(lambda prefix: f"{prefix[0]}/include"), 1381 help="C header files in DIR", 1382 ) 1383 set_config("includedir", depends("--includedir")(lambda idir: idir[0])) 1384 1385 option( 1386 "--libdir", 1387 nargs=1, 1388 metavar="DIR", 1389 default=depends("--prefix")(lambda prefix: f"{prefix[0]}/lib"), 1390 help="Object code libraries in DIR", 1391 ) 1392 set_config("libdir", depends("--libdir")(lambda ldir: ldir[0]))