diff options
Diffstat (limited to 'build/moz.configure/init.configure')
-rw-r--r-- | build/moz.configure/init.configure | 1307 |
1 files changed, 1307 insertions, 0 deletions
diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure new file mode 100644 index 0000000000..53bbc4203f --- /dev/null +++ b/build/moz.configure/init.configure @@ -0,0 +1,1307 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include("util.configure") +include("checks.configure") + +# Make `toolkit` available when toolkit/moz.configure is not included. +toolkit = dependable(None) +# Likewise with `bindgen_config_paths` when +# build/moz.configure/bindgen.configure is not included. +bindgen_config_paths = dependable(None) + + +@depends("--help") +def build_environment(_): + topobjdir = os.path.realpath(".") + topsrcdir = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) + dist = os.path.join(topobjdir, "dist") + + return namespace( + topsrcdir=topsrcdir, + topobjdir=topobjdir, + dist=dist, + ) + + +@depends(build_environment) +@imports(_import="json") +@imports(_from="pathlib", _import="Path") +def configure_cache(build_environment): + """ + This cache is used to cache the results of compiler invocations + between different runs of configure and save them to the disk, which + results in a significant speed up of subsequent configure runs (15%-50%). + + It is not currently thread safe because it is just a simple dictionary + wrapped in a dummy class (ConfigureCache). It could be improved to be + thread safe in the future, if we ever make configure parallelized, but + for now there is no advantage to doing so. + """ + + class ConfigureCache(dict): + pass + + cache_file = Path(build_environment.topobjdir) / "configure.cache" + if cache_file.exists(): + with cache_file.open() as f: + cache_data = json.load(f) + else: + cache_data = {} + + cache = ConfigureCache(cache_data) + cache.version_checked_compilers = set() + + return cache + + +set_config("TOPSRCDIR", build_environment.topsrcdir) +set_config("TOPOBJDIR", build_environment.topobjdir) +set_config("DIST", build_environment.dist) + +add_old_configure_assignment("_topsrcdir", build_environment.topsrcdir) +add_old_configure_assignment("_objdir", build_environment.topobjdir) +add_old_configure_assignment("DIST", build_environment.dist) + +option(env="MOZ_AUTOMATION", help="Enable options for automated builds") +set_config("MOZ_AUTOMATION", depends_if("MOZ_AUTOMATION")(lambda x: True)) + + +option(env="OLD_CONFIGURE", nargs=1, help="Path to the old configure script") + +option(env="MOZCONFIG", nargs=1, help="Mozconfig location") + + +# Read user mozconfig +# ============================================================== +# Note: the dependency on --help is only there to always read the mozconfig, +# even when --help is passed. Without this dependency, the function wouldn't +# be called when --help is passed, and the mozconfig wouldn't be read. + + +@depends("MOZCONFIG", "OLD_CONFIGURE", build_environment, "--help") +@imports(_from="mozbuild.mozconfig", _import="MozconfigLoader") +@imports(_from="mozboot.mozconfig", _import="find_mozconfig") +@imports("os") +def mozconfig(mozconfig, old_configure, build_env, help): + # Don't read the mozconfig for the js configure (yay backwards + # compatibility) + # While the long term goal is that js and top-level use the same configure + # and the same overall setup, including the possibility to use mozconfigs, + # figuring out what we want to do wrt mozconfig vs. command line and + # environment variable is not a clear-cut case, and it's more important to + # fix the immediate problem mozconfig causes to js developers by + # "temporarily" returning to the previous behavior of not loading the + # mozconfig for the js configure. + # Separately to the immediate problem for js developers, there is also the + # need to not load a mozconfig when running js configure as a subconfigure. + # Unfortunately, there is no direct way to tell whether the running + # configure is the js configure. The indirect way is to look at the + # OLD_CONFIGURE path, which points to js/src/old-configure. + # I expect we'll have figured things out for mozconfigs well before + # old-configure dies. + if ( + old_configure + and os.path.dirname(os.path.abspath(old_configure[0])).endswith("/js/src") + or (mozconfig and mozconfig[0] == os.devnull) + ): + return {"path": None} + + topsrcdir = build_env.topsrcdir + loader = MozconfigLoader(topsrcdir) + mozconfig = mozconfig[0] if mozconfig else None + mozconfig = find_mozconfig(topsrcdir, env={"MOZCONFIG": mozconfig}) + mozconfig = loader.read_mozconfig(mozconfig) + + return mozconfig + + +set_config("MOZCONFIG", depends(mozconfig)(lambda m: m["path"])) + + +# Mozilla-Build +# ============================================================== +option(env="MOZILLABUILD", nargs=1, help="Path to Mozilla Build (Windows-only)") + +option(env="CONFIG_SHELL", nargs=1, help="Path to a POSIX shell") + +# It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py, +# but the end goal being that the configure script would go away... + + +@depends("CONFIG_SHELL", "MOZILLABUILD") +@checking("for a shell") +@imports("sys") +@imports(_from="pathlib", _import="Path") +def shell(value, mozillabuild): + if value: + return find_program(value[0]) + shell = "sh" + if mozillabuild: + if (Path(mozillabuild[0]) / "msys2").exists(): + shell = mozillabuild[0] + "/msys2/usr/bin/sh" + else: + shell = mozillabuild[0] + "/msys/bin/sh" + if sys.platform == "win32": + shell = shell + ".exe" + return find_program(shell) + + +# This defines a reasonable shell for when running with --help. +# If one was passed in the environment, though, fall back to that. +@depends("--help", "CONFIG_SHELL") +def help_shell(help, shell): + if help and not shell: + return "sh" + + +shell = help_shell | shell + + +# Python 3 +# ======== +@dependable +@checking("for Python 3", callback=lambda x: "%s (%s)" % (x.path, x.str_version)) +@imports("sys") +@imports(_from="mach.site", _import="PythonVirtualenv") +@imports(_from="os.path", _import="realpath") +def virtualenv_python3(): + return namespace( + # sys.executable is currently not updated for in-process activations. However, + # sys.prefix is, so we can calculate the python executable's path from there. + path=normsep(PythonVirtualenv(realpath(sys.prefix)).python_path), + str_version=".".join(str(i) for i in sys.version_info[0:3]), + ) + + +set_config("PYTHON3", virtualenv_python3.path) +set_config("PYTHON3_VERSION", virtualenv_python3.str_version) +add_old_configure_assignment("PYTHON3", virtualenv_python3.path) + + +# Inject mozconfig options +# ============================================================== +# All options defined above this point can't be injected in mozconfig_options +# below, so collect them. + + +@template +def early_options(): + @depends("--help") + @imports("__sandbox__") + def early_options(_): + return set(option.env for option in __sandbox__._options.values() if option.env) + + return early_options + + +early_options = early_options() + + +@depends(mozconfig, early_options, "MOZ_AUTOMATION", "--help") +# This gives access to the sandbox. Don't copy this blindly. +@imports("__sandbox__") +@imports("os") +def mozconfig_options(mozconfig, early_options, automation, help): + if mozconfig["path"]: + if "MOZ_AUTOMATION_MOZCONFIG" in mozconfig["env"]["added"]: + if not automation: + log.error( + "%s directly or indirectly includes an in-tree " "mozconfig.", + mozconfig["path"], + ) + log.error( + "In-tree mozconfigs make strong assumptions about " + "and are only meant to be used by Mozilla " + "automation." + ) + die("Please don't use them.") + helper = __sandbox__._helper + log.info("Adding configure options from %s" % mozconfig["path"]) + for arg in mozconfig["configure_args"]: + log.info(" %s" % arg) + # We could be using imply_option() here, but it has other + # contraints that don't really apply to the command-line + # emulation that mozconfig provides. + helper.add(arg, origin="mozconfig", args=helper._args) + + def add(key, value): + if key.isupper(): + arg = "%s=%s" % (key, value) + log.info(" %s" % arg) + if key not in early_options: + helper.add(arg, origin="mozconfig", args=helper._args) + + for key, value in mozconfig["env"]["added"].items(): + add(key, value) + os.environ[key] = value + for key, (_, value) in mozconfig["env"]["modified"].items(): + add(key, value) + os.environ[key] = value + for key, value in mozconfig["vars"]["added"].items(): + add(key, value) + for key, (_, value) in mozconfig["vars"]["modified"].items(): + add(key, value) + + +@depends(build_environment, "--help") +@imports(_from="os.path", _import="exists") +def js_package(build_env, help): + return not exists(os.path.join(build_env.topsrcdir, "browser")) + + +# Source checkout and version control integration. +# ================================================ + + +@depends(build_environment, "MOZ_AUTOMATION", js_package, "--help") +@checking("for vcs source checkout") +@imports("os") +def vcs_checkout_type(build_env, automation, js_package, help): + if os.path.exists(os.path.join(build_env.topsrcdir, ".hg")): + return "hg" + elif os.path.exists(os.path.join(build_env.topsrcdir, ".git")): + return "git" + elif automation and not js_package and not help: + raise FatalCheckError( + "unable to resolve VCS type; must run " + "from a source checkout when MOZ_AUTOMATION " + "is set" + ) + + +# Resolve VCS binary for detected repository type. + + +# TODO remove hg.exe once bug 1382940 addresses ambiguous executables case. +hg = check_prog( + "HG", + ( + "hg.exe", + "hg", + ), + allow_missing=True, + when=depends(vcs_checkout_type)(lambda x: x == "hg"), +) +git = check_prog( + "GIT", + ("git",), + allow_missing=True, + when=depends(vcs_checkout_type)(lambda x: x == "git"), +) + + +@depends_if(hg) +@checking("for Mercurial version") +@imports("os") +@imports("re") +def hg_version(hg): + # HGPLAIN in Mercurial 1.5+ forces stable output, regardless of set + # locale or encoding. + env = dict(os.environ) + env["HGPLAIN"] = "1" + + out = check_cmd_output(hg, "--version", env=env) + + match = re.search(r"Mercurial Distributed SCM \(version ([^\)]+)", out) + + if not match: + raise FatalCheckError("unable to determine Mercurial version: %s" % out) + + # The version string may be "unknown" for Mercurial run out of its own + # source checkout or for bad builds. But LooseVersion handles it. + + return Version(match.group(1)) + + +# Resolve Mercurial config items so other checks have easy access. +# Do NOT set this in the config because it may contain sensitive data +# like API keys. + + +@depends_all(build_environment, hg, hg_version) +@imports("os") +def hg_config(build_env, hg, version): + env = dict(os.environ) + env["HGPLAIN"] = "1" + + # Warnings may get sent to stderr. But check_cmd_output() ignores + # stderr if exit code is 0. And the command should always succeed if + # `hg version` worked. + out = check_cmd_output(hg, "config", env=env, cwd=build_env.topsrcdir) + + config = {} + + for line in out.strip().splitlines(): + key, value = [s.strip() for s in line.split("=", 1)] + config[key] = value + + return config + + +@depends_if(git) +@checking("for Git version") +@imports("re") +def git_version(git): + out = check_cmd_output(git, "--version").rstrip() + + match = re.search("git version (.*)$", out) + + if not match: + raise FatalCheckError("unable to determine Git version: %s" % out) + + return Version(match.group(1)) + + +# Only set VCS_CHECKOUT_TYPE if we resolved the VCS binary. +# Require resolved VCS info when running in automation so automation's +# environment is more well-defined. + + +@depends(vcs_checkout_type, hg_version, git_version, "MOZ_AUTOMATION") +def exposed_vcs_checkout_type(vcs_checkout_type, hg, git, automation): + if vcs_checkout_type == "hg": + if hg: + return "hg" + + if automation: + raise FatalCheckError("could not resolve Mercurial binary info") + + elif vcs_checkout_type == "git": + if git: + return "git" + + if automation: + raise FatalCheckError("could not resolve Git binary info") + elif vcs_checkout_type: + raise FatalCheckError("unhandled VCS type: %s" % vcs_checkout_type) + + +set_config("VCS_CHECKOUT_TYPE", exposed_vcs_checkout_type) + +# Obtain a Repository interface for the current VCS repository. + + +@depends(build_environment, exposed_vcs_checkout_type, hg, git) +@imports(_from="mozversioncontrol", _import="get_repository_object") +def vcs_repository(build_env, vcs_checkout_type, hg, git): + if vcs_checkout_type == "hg": + return get_repository_object(build_env.topsrcdir, hg=hg) + elif vcs_checkout_type == "git": + return get_repository_object(build_env.topsrcdir, git=git) + elif vcs_checkout_type: + raise FatalCheckError("unhandled VCS type: %s" % vcs_checkout_type) + + +@depends_if(vcs_repository) +@checking("for sparse checkout") +def vcs_sparse_checkout(repo): + return repo.sparse_checkout_present() + + +set_config("VCS_SPARSE_CHECKOUT", vcs_sparse_checkout) + +# The application/project to build +# ============================================================== +option( + "--enable-application", + nargs=1, + env="MOZ_BUILD_APP", + help="Application to build. Same as --enable-project.", +) + + +@depends("--enable-application") +def application(app): + if app: + return app + + +imply_option("--enable-project", application) + + +@depends(build_environment, js_package) +def default_project(build_env, js_package): + if js_package or build_env.topobjdir.endswith("/js/src"): + return "js" + return "browser" + + +option("--enable-project", nargs=1, default=default_project, help="Project to build") + + +# Artifact builds +# ============================================================== + +option( + "--enable-artifact-builds", + env="MOZ_ARTIFACT_BUILDS", + help="Download and use prebuilt binary artifacts.", +) + + +@depends("--enable-artifact-builds") +def artifact_builds(value): + if value: + return True + + +set_config("MOZ_ARTIFACT_BUILDS", artifact_builds) + +# Host and target systems +# ============================================================== +option("--host", nargs=1, help="Define the system type performing the build") + +option( + "--target", + nargs=1, + help="Define the system type where the resulting executables will be " "used", +) + + +@imports(_from="mozbuild.configure.constants", _import="Abi") +@imports(_from="mozbuild.configure.constants", _import="CPU") +@imports(_from="mozbuild.configure.constants", _import="CPU_bitness") +@imports(_from="mozbuild.configure.constants", _import="Endianness") +@imports(_from="mozbuild.configure.constants", _import="Kernel") +@imports(_from="mozbuild.configure.constants", _import="OS") +@imports(_from="__builtin__", _import="ValueError") +def split_triplet(triplet, allow_wasi=False): + # The standard triplet is defined as + # CPU_TYPE-VENDOR-OPERATING_SYSTEM + # There is also a quartet form: + # CPU_TYPE-VENDOR-KERNEL-OPERATING_SYSTEM + # But we can consider the "KERNEL-OPERATING_SYSTEM" as one. + # Additionally, some may omit "unknown" when the vendor + # is not specified and emit + # CPU_TYPE-OPERATING_SYSTEM + vendor = "unknown" + parts = triplet.split("-", 2) + if len(parts) == 3: + cpu, vendor, os = parts + elif len(parts) == 2: + cpu, os = parts + else: + raise ValueError("Unexpected triplet string: %s" % triplet) + + # Autoconf uses config.sub to validate and canonicalize those triplets, + # but the granularity of its results has never been satisfying to our + # use, so we've had our own, different, canonicalization. We've also + # historically not been very consistent with how we use the canonicalized + # values. Hopefully, this will help us make things better. + # The tests are inherited from our decades-old autoconf-based configure, + # which can probably be improved/cleaned up because they are based on a + # mix of uname and config.guess output, while we now only use the latter, + # which presumably has a cleaner and leaner output. Let's refine later. + raw_os = os = os.replace("/", "_") + abi = None + sub_configure_alias = triplet + if "android" in os: + canonical_os = "Android" + canonical_kernel = "Linux" + elif os.startswith("linux"): + canonical_os = "GNU" + canonical_kernel = "Linux" + elif os.startswith("kfreebsd") and os.endswith("-gnu"): + canonical_os = "GNU" + canonical_kernel = "kFreeBSD" + elif os.startswith("gnu"): + canonical_os = canonical_kernel = "GNU" + elif os.startswith("mingw") or os in ("windows-msvc", "windows-gnu"): + canonical_os = canonical_kernel = "WINNT" + if not os.startswith("mingw"): + if os == "windows-msvc": + abi = "msvc" + elif os == "windows-gnu": + abi = "mingw" + # Many things down the line are looking for the string "mingw32" + # until they are all fixed, we pretend that's the raw os we had + # in the first place, even when we didn't. + sub_configure_alias = sub_configure_alias[: -len(os)] + "mingw32" + raw_os = "mingw32" + elif os.startswith("darwin"): + canonical_kernel = "Darwin" + canonical_os = "OSX" + elif os.startswith("dragonfly"): + canonical_os = canonical_kernel = "DragonFly" + elif os.startswith("freebsd"): + canonical_os = canonical_kernel = "FreeBSD" + elif os.startswith("netbsd"): + canonical_os = canonical_kernel = "NetBSD" + elif os.startswith("openbsd"): + canonical_os = canonical_kernel = "OpenBSD" + elif os.startswith("solaris"): + canonical_os = canonical_kernel = "SunOS" + elif os.startswith("wasi") and allow_wasi: + canonical_os = canonical_kernel = "WASI" + else: + raise ValueError("Unknown OS: %s" % os) + + # The CPU granularity is probably not enough. Moving more things from + # old-configure will tell us if we need more + if cpu.endswith("86") or (cpu.startswith("i") and "86" in cpu): + canonical_cpu = "x86" + endianness = "little" + elif cpu in ("x86_64", "ia64"): + canonical_cpu = cpu + endianness = "little" + elif cpu in ("s390", "s390x"): + canonical_cpu = cpu + endianness = "big" + elif cpu in ("powerpc64", "ppc64", "powerpc64le", "ppc64le"): + canonical_cpu = "ppc64" + endianness = "little" if "le" in cpu else "big" + elif cpu in ("powerpc", "ppc", "rs6000") or cpu.startswith("powerpc"): + canonical_cpu = "ppc" + endianness = "big" + elif cpu in ("Alpha", "alpha", "ALPHA"): + canonical_cpu = "Alpha" + endianness = "little" + elif cpu.startswith("hppa") or cpu == "parisc": + canonical_cpu = "hppa" + endianness = "big" + elif cpu.startswith("sparc64") or cpu.startswith("sparcv9"): + canonical_cpu = "sparc64" + endianness = "big" + elif cpu.startswith("sparc") or cpu == "sun4u": + canonical_cpu = "sparc" + endianness = "big" + elif cpu.startswith("arm"): + canonical_cpu = "arm" + endianness = "big" if cpu.startswith(("armeb", "armbe")) else "little" + elif cpu in ("m68k"): + canonical_cpu = "m68k" + endianness = "big" + elif cpu in ("mips", "mipsel"): + canonical_cpu = "mips32" + endianness = "little" if "el" in cpu else "big" + elif cpu in ("mips64", "mips64el"): + canonical_cpu = "mips64" + endianness = "little" if "el" in cpu else "big" + elif cpu.startswith("aarch64"): + canonical_cpu = "aarch64" + endianness = "little" + elif cpu in ("riscv64", "riscv64gc"): + canonical_cpu = "riscv64" + endianness = "little" + elif cpu.startswith("loongarch64"): + canonical_cpu = "loongarch64" + endianness = "little" + elif cpu == "sh4": + canonical_cpu = "sh4" + endianness = "little" + elif cpu == "wasm32" and allow_wasi: + canonical_cpu = "wasm32" + endianness = "little" + else: + raise ValueError("Unknown CPU type: %s" % cpu) + + # Toolchains, most notably for cross compilation may use cpu-os + # prefixes. We need to be more specific about the LLVM target on Mac + # so cross-language LTO will work correctly. + + if os.startswith("darwin"): + toolchain = "%s-apple-%s" % (cpu, os) + else: + toolchain = "%s-%s" % (cpu, os) + + return namespace( + alias=triplet, + cpu=CPU(canonical_cpu), + bitness=CPU_bitness[canonical_cpu], + kernel=Kernel(canonical_kernel), + os=OS(canonical_os), + endianness=Endianness(endianness), + # For now, only report the Windows ABI. + abi=abi and Abi(abi), + raw_cpu=cpu, + raw_os=raw_os, + toolchain=toolchain, + vendor=vendor, + sub_configure_alias=sub_configure_alias, + ) + + +# This defines a fake target/host namespace for when running with --help +# If either --host or --target is passed on the command line, then fall +# back to the real deal. +@depends("--help", "--host", "--target") +def help_host_target(help, host, target): + if help and not host and not target: + return namespace( + alias="unknown-unknown-unknown", + cpu="unknown", + bitness="unknown", + kernel="unknown", + os="unknown", + endianness="unknown", + abi="unknown", + raw_cpu="unknown", + raw_os="unknown", + toolchain="unknown-unknown", + ) + + +def config_sub(shell, triplet): + config_sub = os.path.join(os.path.dirname(__file__), "..", "autoconf", "config.sub") + # Config.sub doesn't like the *-windows-msvc/*-windows-gnu triplets, so + # munge those before and after calling config.sub. + suffix = None + munging = { + "-windows-msvc": "-mingw32", + "-windows-gnu": "-mingw32", + } + for check_suffix, replacement in munging.items(): + if triplet.endswith(check_suffix): + suffix = check_suffix + triplet = triplet[: -len(suffix)] + replacement + break + result = check_cmd_output(shell, config_sub, triplet).strip() + if suffix: + assert result.endswith(replacement) + result = result[: -len(replacement)] + suffix + return result + + +@depends("--host", shell) +@checking("for host system type", lambda h: h.alias) +@imports("os") +@imports("sys") +@imports(_from="__builtin__", _import="ValueError") +def real_host(value, shell): + if not value and sys.platform == "win32": + arch = os.environ.get("PROCESSOR_ARCHITEW6432") or os.environ.get( + "PROCESSOR_ARCHITECTURE" + ) + if arch == "AMD64": + return split_triplet("x86_64-pc-windows-msvc") + elif arch == "x86": + return split_triplet("i686-pc-windows-msvc") + + if not value: + config_guess = os.path.join( + os.path.dirname(__file__), "..", "autoconf", "config.guess" + ) + + # Ensure that config.guess is determining the host triplet, not the target + # triplet + env = os.environ.copy() + env.pop("CC_FOR_BUILD", None) + env.pop("HOST_CC", None) + env.pop("CC", None) + + host = check_cmd_output(shell, config_guess, env=env).strip() + try: + return split_triplet(host) + except ValueError: + pass + else: + host = value[0] + + host = config_sub(shell, host) + + try: + return split_triplet(host) + except ValueError as e: + die(e) + + +host = help_host_target | real_host + + +@depends("--target", real_host, shell, "--enable-project", "--enable-application") +@checking("for target system type", lambda t: t.alias) +@imports(_from="__builtin__", _import="ValueError") +def real_target(value, host, shell, project, application): + # Because --enable-project is implied by --enable-application, and + # implied options are not currently handled during --help, which is + # used get the build target in mozbuild.base, we manually check + # whether --enable-application was given, and fall back to + # --enable-project if not. Both can't be given contradictory values + # under normal circumstances, so it's fine. + if application: + project = application[0] + elif project: + project = project[0] + if not value: + if project == "mobile/android": + if host.raw_os == "mingw32": + log.warning( + "Building Firefox for Android on Windows is not fully " + "supported. See https://bugzilla.mozilla.org/show_bug.cgi?" + "id=1169873 for details." + ) + return split_triplet("arm-unknown-linux-androideabi") + return host + # If --target was only given a cpu arch, expand it with the + # non-cpu part of the host. For mobile/android, expand it with + # unknown-linux-android. + target = value[0] + if "-" not in target: + if project == "mobile/android": + rest = "unknown-linux-android" + if target.startswith("arm"): + rest += "eabi" + else: + cpu, rest = host.alias.split("-", 1) + target = "-".join((target, rest)) + try: + return split_triplet(target) + except ValueError: + pass + + try: + return split_triplet(config_sub(shell, target), allow_wasi=(project == "js")) + except ValueError as e: + die(e) + + +target = help_host_target | real_target + + +@depends(host, target) +@checking("whether cross compiling") +def cross_compiling(host, target): + return host != target + + +set_config("CROSS_COMPILE", cross_compiling) +set_define("CROSS_COMPILE", cross_compiling) + + +@depends(target) +def have_64_bit(target): + if target.bitness == 64: + return True + + +set_config("HAVE_64BIT_BUILD", have_64_bit) +set_define("HAVE_64BIT_BUILD", have_64_bit) +add_old_configure_assignment("HAVE_64BIT_BUILD", have_64_bit) + +# Some third-party code bases depend on this being set for big-endians. +set_define( + "WORDS_BIGENDIAN", True, when=depends(target.endianness)(lambda e: e == "big") +) + + +# Autoconf needs these set + + +@depends(host) +def host_for_sub_configure(host): + return "--host=%s" % host.sub_configure_alias + + +@depends(target) +def target_for_sub_configure(target): + return "--target=%s" % target.sub_configure_alias + + +# These variables are for compatibility with the current moz.builds and +# old-configure. Eventually, we'll want to canonicalize better. +@depends(target) +def target_variables(target): + if target.kernel == "kFreeBSD": + os_target = "GNU/kFreeBSD" + os_arch = "GNU_kFreeBSD" + elif target.kernel == "Darwin" or (target.kernel == "Linux" and target.os == "GNU"): + os_target = target.kernel + os_arch = target.kernel + else: + os_target = target.os + os_arch = target.kernel + + return namespace( + OS_TARGET=os_target, + OS_ARCH=os_arch, + INTEL_ARCHITECTURE=target.cpu in ("x86", "x86_64") or None, + ) + + +set_config("OS_TARGET", target_variables.OS_TARGET) +add_old_configure_assignment("OS_TARGET", target_variables.OS_TARGET) +set_config("OS_ARCH", target_variables.OS_ARCH) +add_old_configure_assignment("OS_ARCH", target_variables.OS_ARCH) +set_config("CPU_ARCH", target.cpu) +add_old_configure_assignment("CPU_ARCH", target.cpu) +set_config("INTEL_ARCHITECTURE", target_variables.INTEL_ARCHITECTURE) +set_config("TARGET_CPU", target.raw_cpu) +set_config("TARGET_OS", target.os) +set_config("TARGET_RAW_OS", target.raw_os) +set_config("TARGET_ENDIANNESS", target.endianness) + + +@depends(host) +def host_variables(host): + if host.kernel == "kFreeBSD": + os_arch = "GNU_kFreeBSD" + else: + os_arch = host.kernel + return namespace( + HOST_OS_ARCH=os_arch, + ) + + +set_config("HOST_CPU_ARCH", host.cpu) +set_config("HOST_OS_ARCH", host_variables.HOST_OS_ARCH) +add_old_configure_assignment("HOST_OS_ARCH", host_variables.HOST_OS_ARCH) +set_config("HOST_ALIAS", host.alias) + + +@depends(target) +def target_is_windows(target): + if target.kernel == "WINNT": + return True + + +@depends(host) +def host_is_windows(host): + if host.kernel == "WINNT": + return True + + +set_define("_WINDOWS", target_is_windows) +set_define("WIN32", target_is_windows) +set_define("XP_WIN", target_is_windows) + + +@depends(target) +def target_is_unix(target): + if target.kernel != "WINNT": + return True + + +set_define("XP_UNIX", target_is_unix) + + +@depends(target) +def target_is_darwin(target): + if target.kernel == "Darwin": + return True + + +set_define("XP_DARWIN", target_is_darwin) + + +@depends(target) +def target_is_osx(target): + if target.kernel == "Darwin" and target.os == "OSX": + return True + + +@depends(host) +def host_is_osx(host): + if host.os == "OSX": + return True + + +set_define("XP_MACOSX", target_is_osx) + + +@depends(target) +def target_has_linux_kernel(target): + if target.kernel == "Linux": + return True + + +set_define("XP_LINUX", target_has_linux_kernel) + + +@depends(target) +def target_is_linux_or_wasi(target): + if (target.kernel == "Linux" and target.os == "GNU") or target.kernel == "WASI": + return True + + +@depends(target) +def target_is_android(target): + if target.os == "Android": + return True + + +set_define("ANDROID", target_is_android) + + +@depends(target) +def target_is_openbsd(target): + if target.kernel == "OpenBSD": + return True + + +set_define("XP_OPENBSD", target_is_openbsd) + + +@depends(target) +def target_is_netbsd(target): + if target.kernel == "NetBSD": + return True + + +set_define("XP_NETBSD", target_is_netbsd) + + +@depends(target) +def target_is_freebsd(target): + if target.kernel == "FreeBSD": + return True + + +set_define("XP_FREEBSD", target_is_freebsd) + + +@depends(target) +def target_is_solaris(target): + if target.kernel == "SunOS": + return True + + +set_define("XP_SOLARIS", target_is_solaris) + + +@depends(target) +def target_is_sparc(target): + if target.cpu == "sparc64": + return True + + +set_define("SPARC64", target_is_sparc) + + +@depends(target, when=target_is_android) +def android_cpu_arch(target): + d = { + "aarch64": "arm64-v8a", + "arm": "armeabi-v7a", + "x86": "x86", + "x86_64": "x86_64", + } + if target.cpu not in d: + die(f"Cannot determine android_cpu_arch: unknown target.cpu: {target.cpu}") + return d[target.cpu] + + +set_config("ANDROID_CPU_ARCH", android_cpu_arch) + + +@depends("--enable-project", build_environment, "--help") +@imports(_from="os.path", _import="exists") +def include_project_configure(project, build_env, help): + if not project: + die("--enable-project is required.") + + base_dir = build_env.topsrcdir + path = os.path.join(base_dir, project[0], "moz.configure") + if not exists(path): + die("Cannot find project %s", project[0]) + return path + + +@depends("--enable-project") +def build_project(project): + return project[0] + + +set_config("MOZ_BUILD_APP", build_project) +set_define("MOZ_BUILD_APP", build_project) +add_old_configure_assignment("MOZ_BUILD_APP", build_project) + + +option(env="MOZILLA_OFFICIAL", help="Build an official release") + + +@depends("MOZILLA_OFFICIAL") +def mozilla_official(official): + if official: + return True + + +set_config("MOZILLA_OFFICIAL", mozilla_official) +set_define("MOZILLA_OFFICIAL", mozilla_official) +add_old_configure_assignment("MOZILLA_OFFICIAL", mozilla_official) + + +# Allow specifying custom paths to the version files used by the milestone() function below. +option( + "--with-version-file-path", + nargs=1, + help="Specify a custom path to app version files instead of auto-detecting", + default=None, +) + + +@depends("--with-version-file-path") +def version_path(path): + return path + + +# Allow to easily build nightly with a release / beta configuration so that we +# can have the same options we'd have on a release version. +# This is useful for performance profiling, as there are things that we don't +# enable in release (like the background hang monitor) that can affect +# performance. +option( + "--as-milestone", + help="Build with another milestone configuration (e.g., as release)", + choices=("early-beta", "late-beta", "release"), + default=None, +) + + +# set RELEASE_OR_BETA and NIGHTLY_BUILD variables depending on the cycle we're in +# The logic works like this: +# - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD) +# - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora +# - otherwise, we're building Release/Beta (define RELEASE_OR_BETA) +@depends( + build_environment, + build_project, + version_path, + "--as-milestone", + "--help", +) +@imports(_from="__builtin__", _import="open") +@imports("os") +@imports("re") +def milestone(build_env, build_project, version_path, as_milestone, _): + versions = [] + paths = ["config/milestone.txt"] + if build_project == "js": + paths = paths * 3 + else: + paths += [ + "browser/config/version.txt", + "browser/config/version_display.txt", + ] + if version_path: + version_path = version_path[0] + else: + version_path = os.path.join(build_project, "config") + for f in ("version.txt", "version_display.txt"): + f = os.path.join(version_path, f) + if not os.path.exists(os.path.join(build_env.topsrcdir, f)): + break + paths.append(f) + + for p in paths: + with open(os.path.join(build_env.topsrcdir, p), "r") as fh: + content = fh.read().splitlines() + if not content: + die("Could not find a version number in {}".format(p)) + versions.append(content[-1]) + + is_early_beta_or_earlier = None + if as_milestone: + if "a1" not in versions[0]: + # We could make this work with some effort + die("--as-milestone only works on nightly builds") + as_milestone = as_milestone[0] + as_milestone_flag = "" if as_milestone == "release" else "b1" + versions = [v.replace("a1", as_milestone_flag) for v in versions] + if as_milestone == "early-beta": + is_early_beta_or_earlier = True + + milestone, firefox_version, firefox_version_display = versions[:3] + + # version.txt content from the project directory if there is one, otherwise + # the firefox version. + app_version = versions[3] if len(versions) > 3 else firefox_version + # version_display.txt content from the project directory if there is one, + # otherwise version.txt content from the project directory, otherwise the + # firefox version for display. + app_version_display = versions[-1] if len(versions) > 3 else firefox_version_display + + is_nightly = is_release_or_beta = None + + if "a1" in milestone: + is_nightly = True + elif "a" not in milestone: + is_release_or_beta = True + + major_version = milestone.split(".")[0] + m = re.search(r"([ab]\d+)", milestone) + ab_patch = m.group(1) if m else "" + + if not as_milestone: + defines = os.path.join(build_env.topsrcdir, "build", "defines.sh") + with open(defines, "r") as fh: + for line in fh.read().splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + name, _, value = line.partition("=") + name = name.strip() + value = value.strip() + if name != "EARLY_BETA_OR_EARLIER": + die( + "Only the EARLY_BETA_OR_EARLIER variable can be set in build/defines.sh" + ) + if value and any(x in app_version_display for x in "ab"): + is_early_beta_or_earlier = True + + # Only expose the major version milestone in the UA string and hide the + # patch leve (bugs 572659 and 870868). + # + # Only expose major milestone and alpha version in the symbolversion + # string; as the name suggests, we use it for symbol versioning on Linux. + return namespace( + version=milestone, + uaversion="%s.0" % major_version, + symbolversion="%s%s" % (major_version, ab_patch), + is_nightly=is_nightly, + is_release_or_beta=is_release_or_beta, + is_early_beta_or_earlier=is_early_beta_or_earlier, + is_esr=app_version_display.endswith("esr") or None, + app_version=app_version, + app_version_display=app_version_display, + ) + + +set_config("GRE_MILESTONE", milestone.version) +set_config("NIGHTLY_BUILD", milestone.is_nightly) +set_define("NIGHTLY_BUILD", milestone.is_nightly) +set_config("RELEASE_OR_BETA", milestone.is_release_or_beta) +set_define("RELEASE_OR_BETA", milestone.is_release_or_beta) +set_config("MOZ_ESR", milestone.is_esr) +set_define("MOZ_ESR", milestone.is_esr) +set_config("EARLY_BETA_OR_EARLIER", milestone.is_early_beta_or_earlier) +set_define("EARLY_BETA_OR_EARLIER", milestone.is_early_beta_or_earlier) +set_define("MOZILLA_VERSION", depends(milestone)(lambda m: '"%s"' % m.version)) +set_config("MOZILLA_VERSION", milestone.version) +set_define("MOZILLA_VERSION_U", milestone.version) +set_define("MOZILLA_UAVERSION", depends(milestone)(lambda m: '"%s"' % m.uaversion)) +set_config("MOZILLA_SYMBOLVERSION", milestone.symbolversion) +# JS configure still want to look at this one. +add_old_configure_assignment("MOZILLA_SYMBOLVERSION", milestone.symbolversion) + +set_config("MOZ_APP_VERSION", milestone.app_version) +set_config("MOZ_APP_VERSION_DISPLAY", milestone.app_version_display) +add_old_configure_assignment("MOZ_APP_VERSION", milestone.app_version) + + +# The app update channel is 'default' when not supplied. The value is used in +# the application's confvars.sh (and is made available to a project specific +# moz.configure). +option( + "--enable-update-channel", + nargs=1, + help="Select application update channel", + default="default", +) + + +@depends("--enable-update-channel") +def update_channel(channel): + if not channel or channel[0] == "": + return "default" + return channel[0].lower() + + +set_config("MOZ_UPDATE_CHANNEL", update_channel) +set_define("MOZ_UPDATE_CHANNEL", update_channel) +add_old_configure_assignment("MOZ_UPDATE_CHANNEL", update_channel) + + +option( + env="MOZBUILD_STATE_PATH", + nargs=1, + help="Path to a persistent state directory for the build system " + "and related tools", +) + + +@depends("MOZBUILD_STATE_PATH", "--help") +@imports("os") +def mozbuild_state_path(path, _): + if path: + return normalize_path(path[0]) + return normalize_path(os.path.expanduser(os.path.join("~", ".mozbuild"))) + + +@depends("MOZILLABUILD", shell, host_is_windows) +@imports(_from="pathlib", _import="Path") +def mozillabuild_bin_paths(mozillabuild, shell, host_is_windows): + paths = [] + if not mozillabuild or not host_is_windows: + return paths + paths.append(os.path.dirname(shell)) + paths.append(str(Path(mozillabuild[0]) / "bin")) + return paths + + +@depends(mozillabuild_bin_paths) +@imports("os") +def prefer_mozillabuild_path(mozillabuild_bin_paths): + return mozillabuild_bin_paths + os.environ["PATH"].split(os.pathsep) + + +# A template providing a shorthand for setting a variable. The created +# option will only be settable with imply_option. +# It is expected that a project-specific moz.configure will call imply_option +# to set a value other than the default. +# If required, the set_as_define and set_for_old_configure arguments +# will additionally cause the variable to be set using set_define and +# add_old_configure_assignment. util.configure would be an appropriate place for +# this, but it uses add_old_configure_assignment, which is defined in this file. +@template +def project_flag(env=None, set_for_old_configure=False, set_as_define=False, **kwargs): + + if not env: + configure_error("A project_flag must be passed a variable name to set.") + + opt = option(env=env, possible_origins=("implied",), **kwargs) + + @depends(opt.option) + def option_implementation(value): + if value: + if len(value): + return value + return bool(value) + + set_config(env, option_implementation) + if set_as_define: + set_define(env, option_implementation) + if set_for_old_configure: + add_old_configure_assignment(env, option_implementation) + + +# milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set. + + +@depends(milestone) +def enabled_in_nightly(milestone): + return milestone.is_nightly + + +# Branding +# ============================================================== +option( + "--with-app-basename", + env="MOZ_APP_BASENAME", + nargs=1, + help="Typically stays consistent for multiple branded versions of a " + 'given application (e.g. Aurora and Firefox both use "Firefox"), but ' + "may vary for full rebrandings (e.g. Iceweasel). Used for " + 'application.ini\'s "Name" field, which controls profile location in ' + 'the absence of a "Profile" field (see below), and various system ' + "integration hooks (Unix remoting, Windows MessageWindow name, etc.", +) + + +@depends("--with-app-basename", target_is_android) +def moz_app_basename(value, target_is_android): + if value: + return value[0] + if target_is_android: + return "Fennec" + return "Firefox" + + +set_config( + "MOZ_APP_BASENAME", + moz_app_basename, + when=depends(build_project)(lambda p: p != "js"), +) |