tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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]))