Bug 1760185 [wpt PR 33176] - [FedCM] Add hint argument to revoke, a=testonly
[gecko.git] / build / moz.configure / init.configure
blob81f500a0b7527d5a0caa4ab47e9a1b84f52eb543
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/.
7 include("util.configure")
8 include("checks.configure")
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)
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")
23     return namespace(
24         topsrcdir=topsrcdir,
25         topobjdir=topobjdir,
26         dist=dist,
27     )
30 set_config("TOPSRCDIR", build_environment.topsrcdir)
31 set_config("TOPOBJDIR", build_environment.topobjdir)
32 set_config("DIST", build_environment.dist)
34 add_old_configure_assignment("_topsrcdir", build_environment.topsrcdir)
35 add_old_configure_assignment("_objdir", build_environment.topobjdir)
36 add_old_configure_assignment("DIST", build_environment.dist)
38 option(env="MOZ_AUTOMATION", help="Enable options for automated builds")
39 set_config("MOZ_AUTOMATION", depends_if("MOZ_AUTOMATION")(lambda x: True))
42 option(env="OLD_CONFIGURE", nargs=1, help="Path to the old configure script")
44 option(env="MOZCONFIG", nargs=1, help="Mozconfig location")
47 # Read user mozconfig
48 # ==============================================================
49 # Note: the dependency on --help is only there to always read the mozconfig,
50 # even when --help is passed. Without this dependency, the function wouldn't
51 # be called when --help is passed, and the mozconfig wouldn't be read.
54 @depends("MOZCONFIG", "OLD_CONFIGURE", build_environment, "--help")
55 @imports(_from="mozbuild.mozconfig", _import="MozconfigLoader")
56 @imports(_from="mozboot.mozconfig", _import="find_mozconfig")
57 @imports("os")
58 def mozconfig(mozconfig, old_configure, build_env, help):
59     # Don't read the mozconfig for the js configure (yay backwards
60     # compatibility)
61     # While the long term goal is that js and top-level use the same configure
62     # and the same overall setup, including the possibility to use mozconfigs,
63     # figuring out what we want to do wrt mozconfig vs. command line and
64     # environment variable is not a clear-cut case, and it's more important to
65     # fix the immediate problem mozconfig causes to js developers by
66     # "temporarily" returning to the previous behavior of not loading the
67     # mozconfig for the js configure.
68     # Separately to the immediate problem for js developers, there is also the
69     # need to not load a mozconfig when running js configure as a subconfigure.
70     # Unfortunately, there is no direct way to tell whether the running
71     # configure is the js configure. The indirect way is to look at the
72     # OLD_CONFIGURE path, which points to js/src/old-configure.
73     # I expect we'll have figured things out for mozconfigs well before
74     # old-configure dies.
75     if (
76         old_configure
77         and os.path.dirname(os.path.abspath(old_configure[0])).endswith("/js/src")
78         or (mozconfig and mozconfig[0] == os.devnull)
79     ):
80         return {"path": None}
82     topsrcdir = build_env.topsrcdir
83     loader = MozconfigLoader(topsrcdir)
84     mozconfig = mozconfig[0] if mozconfig else None
85     mozconfig = find_mozconfig(topsrcdir, env={"MOZCONFIG": mozconfig})
86     mozconfig = loader.read_mozconfig(mozconfig)
88     return mozconfig
91 set_config("MOZCONFIG", depends(mozconfig)(lambda m: m["path"]))
94 # Mozilla-Build
95 # ==============================================================
96 option(env="MOZILLABUILD", nargs=1, help="Path to Mozilla Build (Windows-only)")
98 option(env="CONFIG_SHELL", nargs=1, help="Path to a POSIX shell")
100 # It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py,
101 # but the end goal being that the configure script would go away...
104 @depends("CONFIG_SHELL", "MOZILLABUILD")
105 @checking("for a shell")
106 @imports("sys")
107 @imports(_from="pathlib", _import="Path")
108 def shell(value, mozillabuild):
109     if value:
110         return find_program(value[0])
111     shell = "sh"
112     if mozillabuild:
113         if (Path(mozillabuild[0]) / "msys2").exists():
114             shell = mozillabuild[0] + "/msys2/usr/bin/sh"
115         else:
116             shell = mozillabuild[0] + "/msys/bin/sh"
117     if sys.platform == "win32":
118         shell = shell + ".exe"
119     return find_program(shell)
122 # This defines a reasonable shell for when running with --help.
123 # If one was passed in the environment, though, fall back to that.
124 @depends("--help", "CONFIG_SHELL")
125 def help_shell(help, shell):
126     if help and not shell:
127         return "sh"
130 shell = help_shell | shell
133 # Python 3
134 # ========
135 @dependable
136 @checking("for Python 3", callback=lambda x: "%s (%s)" % (x.path, x.str_version))
137 @imports("sys")
138 @imports(_from="mach.site", _import="PythonVirtualenv")
139 @imports(_from="os.path", _import="realpath")
140 def virtualenv_python3():
141     return namespace(
142         # sys.executable is currently not updated for in-process activations. However,
143         # sys.prefix is, so we can calculate the python executable's path from there.
144         path=normsep(PythonVirtualenv(realpath(sys.prefix)).python_path),
145         str_version=".".join(str(i) for i in sys.version_info[0:3]),
146     )
149 set_config("PYTHON3", virtualenv_python3.path)
150 set_config("PYTHON3_VERSION", virtualenv_python3.str_version)
151 add_old_configure_assignment("PYTHON3", virtualenv_python3.path)
154 # Inject mozconfig options
155 # ==============================================================
156 # All options defined above this point can't be injected in mozconfig_options
157 # below, so collect them.
160 @template
161 def early_options():
162     @depends("--help")
163     @imports("__sandbox__")
164     def early_options(_):
165         return set(option.env for option in __sandbox__._options.values() if option.env)
167     return early_options
170 early_options = early_options()
173 @depends(mozconfig, early_options, "MOZ_AUTOMATION", "--help")
174 # This gives access to the sandbox. Don't copy this blindly.
175 @imports("__sandbox__")
176 @imports("os")
177 def mozconfig_options(mozconfig, early_options, automation, help):
178     if mozconfig["path"]:
179         if "MOZ_AUTOMATION_MOZCONFIG" in mozconfig["env"]["added"]:
180             if not automation:
181                 log.error(
182                     "%s directly or indirectly includes an in-tree " "mozconfig.",
183                     mozconfig["path"],
184                 )
185                 log.error(
186                     "In-tree mozconfigs make strong assumptions about "
187                     "and are only meant to be used by Mozilla "
188                     "automation."
189                 )
190                 die("Please don't use them.")
191         helper = __sandbox__._helper
192         log.info("Adding configure options from %s" % mozconfig["path"])
193         for arg in mozconfig["configure_args"]:
194             log.info("  %s" % arg)
195             # We could be using imply_option() here, but it has other
196             # contraints that don't really apply to the command-line
197             # emulation that mozconfig provides.
198             helper.add(arg, origin="mozconfig", args=helper._args)
200         def add(key, value):
201             if key.isupper():
202                 arg = "%s=%s" % (key, value)
203                 log.info("  %s" % arg)
204                 if key not in early_options:
205                     helper.add(arg, origin="mozconfig", args=helper._args)
207         for key, value in mozconfig["env"]["added"].items():
208             add(key, value)
209             os.environ[key] = value
210         for key, (_, value) in mozconfig["env"]["modified"].items():
211             add(key, value)
212             os.environ[key] = value
213         for key, value in mozconfig["vars"]["added"].items():
214             add(key, value)
215         for key, (_, value) in mozconfig["vars"]["modified"].items():
216             add(key, value)
219 @depends(build_environment, "--help")
220 @imports(_from="os.path", _import="exists")
221 def js_package(build_env, help):
222     return not exists(os.path.join(build_env.topsrcdir, "browser"))
225 # Source checkout and version control integration.
226 # ================================================
229 @depends(build_environment, "MOZ_AUTOMATION", js_package, "--help")
230 @checking("for vcs source checkout")
231 @imports("os")
232 def vcs_checkout_type(build_env, automation, js_package, help):
233     if os.path.exists(os.path.join(build_env.topsrcdir, ".hg")):
234         return "hg"
235     elif os.path.exists(os.path.join(build_env.topsrcdir, ".git")):
236         return "git"
237     elif automation and not js_package and not help:
238         raise FatalCheckError(
239             "unable to resolve VCS type; must run "
240             "from a source checkout when MOZ_AUTOMATION "
241             "is set"
242         )
245 # Resolve VCS binary for detected repository type.
248 # TODO remove hg.exe once bug 1382940 addresses ambiguous executables case.
249 hg = check_prog(
250     "HG",
251     (
252         "hg.exe",
253         "hg",
254     ),
255     allow_missing=True,
256     when=depends(vcs_checkout_type)(lambda x: x == "hg"),
258 git = check_prog(
259     "GIT",
260     ("git",),
261     allow_missing=True,
262     when=depends(vcs_checkout_type)(lambda x: x == "git"),
266 @depends_if(hg)
267 @checking("for Mercurial version")
268 @imports("os")
269 @imports("re")
270 def hg_version(hg):
271     # HGPLAIN in Mercurial 1.5+ forces stable output, regardless of set
272     # locale or encoding.
273     env = dict(os.environ)
274     env["HGPLAIN"] = "1"
276     out = check_cmd_output(hg, "--version", env=env)
278     match = re.search(r"Mercurial Distributed SCM \(version ([^\)]+)", out)
280     if not match:
281         raise FatalCheckError("unable to determine Mercurial version: %s" % out)
283     # The version string may be "unknown" for Mercurial run out of its own
284     # source checkout or for bad builds. But LooseVersion handles it.
286     return Version(match.group(1))
289 # Resolve Mercurial config items so other checks have easy access.
290 # Do NOT set this in the config because it may contain sensitive data
291 # like API keys.
294 @depends_all(build_environment, hg, hg_version)
295 @imports("os")
296 def hg_config(build_env, hg, version):
297     env = dict(os.environ)
298     env["HGPLAIN"] = "1"
300     # Warnings may get sent to stderr. But check_cmd_output() ignores
301     # stderr if exit code is 0. And the command should always succeed if
302     # `hg version` worked.
303     out = check_cmd_output(hg, "config", env=env, cwd=build_env.topsrcdir)
305     config = {}
307     for line in out.strip().splitlines():
308         key, value = [s.strip() for s in line.split("=", 1)]
309         config[key] = value
311     return config
314 @depends_if(git)
315 @checking("for Git version")
316 @imports("re")
317 def git_version(git):
318     out = check_cmd_output(git, "--version").rstrip()
320     match = re.search("git version (.*)$", out)
322     if not match:
323         raise FatalCheckError("unable to determine Git version: %s" % out)
325     return Version(match.group(1))
328 # Only set VCS_CHECKOUT_TYPE if we resolved the VCS binary.
329 # Require resolved VCS info when running in automation so automation's
330 # environment is more well-defined.
333 @depends(vcs_checkout_type, hg_version, git_version, "MOZ_AUTOMATION")
334 def exposed_vcs_checkout_type(vcs_checkout_type, hg, git, automation):
335     if vcs_checkout_type == "hg":
336         if hg:
337             return "hg"
339         if automation:
340             raise FatalCheckError("could not resolve Mercurial binary info")
342     elif vcs_checkout_type == "git":
343         if git:
344             return "git"
346         if automation:
347             raise FatalCheckError("could not resolve Git binary info")
348     elif vcs_checkout_type:
349         raise FatalCheckError("unhandled VCS type: %s" % vcs_checkout_type)
352 set_config("VCS_CHECKOUT_TYPE", exposed_vcs_checkout_type)
354 # Obtain a Repository interface for the current VCS repository.
357 @depends(build_environment, exposed_vcs_checkout_type, hg, git)
358 @imports(_from="mozversioncontrol", _import="get_repository_object")
359 def vcs_repository(build_env, vcs_checkout_type, hg, git):
360     if vcs_checkout_type == "hg":
361         return get_repository_object(build_env.topsrcdir, hg=hg)
362     elif vcs_checkout_type == "git":
363         return get_repository_object(build_env.topsrcdir, git=git)
364     elif vcs_checkout_type:
365         raise FatalCheckError("unhandled VCS type: %s" % vcs_checkout_type)
368 @depends_if(vcs_repository)
369 @checking("for sparse checkout")
370 def vcs_sparse_checkout(repo):
371     return repo.sparse_checkout_present()
374 set_config("VCS_SPARSE_CHECKOUT", vcs_sparse_checkout)
376 # The application/project to build
377 # ==============================================================
378 option(
379     "--enable-application",
380     nargs=1,
381     env="MOZ_BUILD_APP",
382     help="Application to build. Same as --enable-project.",
386 @depends("--enable-application")
387 def application(app):
388     if app:
389         return app
392 imply_option("--enable-project", application)
395 @depends(build_environment, js_package)
396 def default_project(build_env, js_package):
397     if js_package or build_env.topobjdir.endswith("/js/src"):
398         return "js"
399     return "browser"
402 option("--enable-project", nargs=1, default=default_project, help="Project to build")
405 # Host and target systems
406 # ==============================================================
407 option("--host", nargs=1, help="Define the system type performing the build")
409 option(
410     "--target",
411     nargs=1,
412     help="Define the system type where the resulting executables will be " "used",
416 @imports(_from="mozbuild.configure.constants", _import="CPU")
417 @imports(_from="mozbuild.configure.constants", _import="CPU_bitness")
418 @imports(_from="mozbuild.configure.constants", _import="Endianness")
419 @imports(_from="mozbuild.configure.constants", _import="Kernel")
420 @imports(_from="mozbuild.configure.constants", _import="OS")
421 @imports(_from="__builtin__", _import="ValueError")
422 def split_triplet(triplet, allow_msvc=False, allow_wasi=False):
423     # The standard triplet is defined as
424     #   CPU_TYPE-VENDOR-OPERATING_SYSTEM
425     # There is also a quartet form:
426     #   CPU_TYPE-VENDOR-KERNEL-OPERATING_SYSTEM
427     # But we can consider the "KERNEL-OPERATING_SYSTEM" as one.
428     # Additionally, some may omit "unknown" when the vendor
429     # is not specified and emit
430     #   CPU_TYPE-OPERATING_SYSTEM
431     vendor = "unknown"
432     parts = triplet.split("-", 2)
433     if len(parts) == 3:
434         cpu, vendor, os = parts
435     elif len(parts) == 2:
436         cpu, os = parts
437     else:
438         raise ValueError("Unexpected triplet string: %s" % triplet)
440     # Autoconf uses config.sub to validate and canonicalize those triplets,
441     # but the granularity of its results has never been satisfying to our
442     # use, so we've had our own, different, canonicalization. We've also
443     # historically not been very consistent with how we use the canonicalized
444     # values. Hopefully, this will help us make things better.
445     # The tests are inherited from our decades-old autoconf-based configure,
446     # which can probably be improved/cleaned up because they are based on a
447     # mix of uname and config.guess output, while we now only use the latter,
448     # which presumably has a cleaner and leaner output. Let's refine later.
449     os = os.replace("/", "_")
450     if "android" in os:
451         canonical_os = "Android"
452         canonical_kernel = "Linux"
453     elif os.startswith("linux"):
454         canonical_os = "GNU"
455         canonical_kernel = "Linux"
456     elif os.startswith("kfreebsd") and os.endswith("-gnu"):
457         canonical_os = "GNU"
458         canonical_kernel = "kFreeBSD"
459     elif os.startswith("gnu"):
460         canonical_os = canonical_kernel = "GNU"
461     elif os.startswith("mingw") or (allow_msvc and os == "windows-msvc"):
462         # windows-msvc is only opt-in for the caller of this function until
463         # full support in bug 1617793.
464         canonical_os = canonical_kernel = "WINNT"
465     elif os.startswith("darwin"):
466         canonical_kernel = "Darwin"
467         canonical_os = "OSX"
468     elif os.startswith("dragonfly"):
469         canonical_os = canonical_kernel = "DragonFly"
470     elif os.startswith("freebsd"):
471         canonical_os = canonical_kernel = "FreeBSD"
472     elif os.startswith("netbsd"):
473         canonical_os = canonical_kernel = "NetBSD"
474     elif os.startswith("openbsd"):
475         canonical_os = canonical_kernel = "OpenBSD"
476     elif os.startswith("solaris"):
477         canonical_os = canonical_kernel = "SunOS"
478     elif os.startswith("wasi") and allow_wasi:
479         canonical_os = canonical_kernel = "WASI"
480     else:
481         raise ValueError("Unknown OS: %s" % os)
483     # The CPU granularity is probably not enough. Moving more things from
484     # old-configure will tell us if we need more
485     if cpu.endswith("86") or (cpu.startswith("i") and "86" in cpu):
486         canonical_cpu = "x86"
487         endianness = "little"
488     elif cpu in ("x86_64", "ia64"):
489         canonical_cpu = cpu
490         endianness = "little"
491     elif cpu in ("s390", "s390x"):
492         canonical_cpu = cpu
493         endianness = "big"
494     elif cpu in ("powerpc64", "ppc64", "powerpc64le", "ppc64le"):
495         canonical_cpu = "ppc64"
496         endianness = "little" if "le" in cpu else "big"
497     elif cpu in ("powerpc", "ppc", "rs6000") or cpu.startswith("powerpc"):
498         canonical_cpu = "ppc"
499         endianness = "big"
500     elif cpu in ("Alpha", "alpha", "ALPHA"):
501         canonical_cpu = "Alpha"
502         endianness = "little"
503     elif cpu.startswith("hppa") or cpu == "parisc":
504         canonical_cpu = "hppa"
505         endianness = "big"
506     elif cpu.startswith("sparc64") or cpu.startswith("sparcv9"):
507         canonical_cpu = "sparc64"
508         endianness = "big"
509     elif cpu.startswith("sparc") or cpu == "sun4u":
510         canonical_cpu = "sparc"
511         endianness = "big"
512     elif cpu.startswith("arm"):
513         canonical_cpu = "arm"
514         endianness = "big" if cpu.startswith(("armeb", "armbe")) else "little"
515     elif cpu in ("m68k"):
516         canonical_cpu = "m68k"
517         endianness = "big"
518     elif cpu in ("mips", "mipsel"):
519         canonical_cpu = "mips32"
520         endianness = "little" if "el" in cpu else "big"
521     elif cpu in ("mips64", "mips64el"):
522         canonical_cpu = "mips64"
523         endianness = "little" if "el" in cpu else "big"
524     elif cpu.startswith("aarch64"):
525         canonical_cpu = "aarch64"
526         endianness = "little"
527     elif cpu in ("riscv64", "riscv64gc"):
528         canonical_cpu = "riscv64"
529         endianness = "little"
530     elif cpu.startswith("loongarch64"):
531         canonical_cpu = "loongarch64"
532         endianness = "little"
533     elif cpu == "sh4":
534         canonical_cpu = "sh4"
535         endianness = "little"
536     elif cpu == "wasm32" and allow_wasi:
537         canonical_cpu = "wasm32"
538         endianness = "little"
539     else:
540         raise ValueError("Unknown CPU type: %s" % cpu)
542     # Toolchains, most notably for cross compilation may use cpu-os
543     # prefixes. We need to be more specific about the LLVM target on Mac
544     # so cross-language LTO will work correctly.
546     if os.startswith("darwin"):
547         toolchain = "%s-apple-%s" % (cpu, os)
548     elif canonical_cpu == "aarch64" and canonical_os == "WINNT":
549         toolchain = "aarch64-windows-msvc"
550     else:
551         toolchain = "%s-%s" % (cpu, os)
553     return namespace(
554         alias=triplet,
555         cpu=CPU(canonical_cpu),
556         bitness=CPU_bitness[canonical_cpu],
557         kernel=Kernel(canonical_kernel),
558         os=OS(canonical_os),
559         endianness=Endianness(endianness),
560         raw_cpu=cpu,
561         raw_os=os,
562         toolchain=toolchain,
563         vendor=vendor,
564     )
567 # This defines a fake target/host namespace for when running with --help
568 # If either --host or --target is passed on the command line, then fall
569 # back to the real deal.
570 @depends("--help", "--host", "--target")
571 def help_host_target(help, host, target):
572     if help and not host and not target:
573         return namespace(
574             alias="unknown-unknown-unknown",
575             cpu="unknown",
576             bitness="unknown",
577             kernel="unknown",
578             os="unknown",
579             endianness="unknown",
580             raw_cpu="unknown",
581             raw_os="unknown",
582             toolchain="unknown-unknown",
583         )
586 def config_sub(shell, triplet):
587     config_sub = os.path.join(os.path.dirname(__file__), "..", "autoconf", "config.sub")
588     return check_cmd_output(shell, config_sub, triplet).strip()
591 @depends("--host", shell)
592 @checking("for host system type", lambda h: h.alias)
593 @imports("os")
594 @imports("sys")
595 @imports(_from="__builtin__", _import="ValueError")
596 def real_host(value, shell):
597     if not value and sys.platform == "win32":
598         arch = os.environ.get("PROCESSOR_ARCHITEW6432") or os.environ.get(
599             "PROCESSOR_ARCHITECTURE"
600         )
601         if arch == "AMD64":
602             return split_triplet("x86_64-pc-mingw32")
603         elif arch == "x86":
604             return split_triplet("i686-pc-mingw32")
606     if not value:
607         config_guess = os.path.join(
608             os.path.dirname(__file__), "..", "autoconf", "config.guess"
609         )
611         # Ensure that config.guess is determining the host triplet, not the target
612         # triplet
613         env = os.environ.copy()
614         env.pop("CC_FOR_BUILD", None)
615         env.pop("HOST_CC", None)
616         env.pop("CC", None)
618         host = check_cmd_output(shell, config_guess, env=env).strip()
619         try:
620             return split_triplet(host)
621         except ValueError:
622             pass
623     else:
624         host = value[0]
626     host = config_sub(shell, host)
628     try:
629         return split_triplet(host)
630     except ValueError as e:
631         die(e)
634 host = help_host_target | real_host
637 @depends("--target", real_host, shell, "--enable-project", "--enable-application")
638 @checking("for target system type", lambda t: t.alias)
639 @imports(_from="__builtin__", _import="ValueError")
640 def real_target(value, host, shell, project, application):
641     # Because --enable-project is implied by --enable-application, and
642     # implied options are not currently handled during --help, which is
643     # used get the build target in mozbuild.base, we manually check
644     # whether --enable-application was given, and fall back to
645     # --enable-project if not. Both can't be given contradictory values
646     # under normal circumstances, so it's fine.
647     if application:
648         project = application[0]
649     elif project:
650         project = project[0]
651     if not value:
652         if project == "mobile/android":
653             if host.raw_os == "mingw32":
654                 log.warning(
655                     "Building Firefox for Android on Windows is not fully "
656                     "supported. See https://bugzilla.mozilla.org/show_bug.cgi?"
657                     "id=1169873 for details."
658                 )
659             return split_triplet("arm-unknown-linux-androideabi")
660         return host
661     # If --target was only given a cpu arch, expand it with the
662     # non-cpu part of the host. For mobile/android, expand it with
663     # unknown-linux-android.
664     target = value[0]
665     if "-" not in target:
666         if project == "mobile/android":
667             rest = "unknown-linux-android"
668             if target.startswith("arm"):
669                 rest += "eabi"
670         else:
671             cpu, rest = host.alias.split("-", 1)
672         target = "-".join((target, rest))
673         try:
674             return split_triplet(target)
675         except ValueError:
676             pass
678     try:
679         return split_triplet(config_sub(shell, target), allow_wasi=(project == "js"))
680     except ValueError as e:
681         die(e)
684 target = help_host_target | real_target
687 @depends(host, target)
688 @checking("whether cross compiling")
689 def cross_compiling(host, target):
690     return host != target
693 set_config("CROSS_COMPILE", cross_compiling)
694 set_define("CROSS_COMPILE", cross_compiling)
695 add_old_configure_assignment("CROSS_COMPILE", cross_compiling)
698 @depends(target)
699 def have_64_bit(target):
700     if target.bitness == 64:
701         return True
704 set_config("HAVE_64BIT_BUILD", have_64_bit)
705 set_define("HAVE_64BIT_BUILD", have_64_bit)
706 add_old_configure_assignment("HAVE_64BIT_BUILD", have_64_bit)
709 @depends(host)
710 def host_os_kernel_major_version(host):
711     versions = host.raw_os.split(".")
712     version = "".join(x for x in versions[0] if x.isdigit())
713     return version
716 set_config("HOST_MAJOR_VERSION", host_os_kernel_major_version)
718 # Autoconf needs these set
721 @depends(host)
722 def host_for_sub_configure(host):
723     return "--host=%s" % host.alias
726 @depends(target)
727 def target_for_sub_configure(target):
728     target_alias = target.alias
729     return "--target=%s" % target_alias
732 # These variables are for compatibility with the current moz.builds and
733 # old-configure. Eventually, we'll want to canonicalize better.
734 @depends(target)
735 def target_variables(target):
736     if target.kernel == "kFreeBSD":
737         os_target = "GNU/kFreeBSD"
738         os_arch = "GNU_kFreeBSD"
739     elif target.kernel == "Darwin" or (target.kernel == "Linux" and target.os == "GNU"):
740         os_target = target.kernel
741         os_arch = target.kernel
742     else:
743         os_target = target.os
744         os_arch = target.kernel
746     return namespace(
747         OS_TARGET=os_target,
748         OS_ARCH=os_arch,
749         INTEL_ARCHITECTURE=target.cpu in ("x86", "x86_64") or None,
750     )
753 set_config("OS_TARGET", target_variables.OS_TARGET)
754 add_old_configure_assignment("OS_TARGET", target_variables.OS_TARGET)
755 set_config("OS_ARCH", target_variables.OS_ARCH)
756 add_old_configure_assignment("OS_ARCH", target_variables.OS_ARCH)
757 set_config("CPU_ARCH", target.cpu)
758 add_old_configure_assignment("CPU_ARCH", target.cpu)
759 set_config("INTEL_ARCHITECTURE", target_variables.INTEL_ARCHITECTURE)
760 set_config("TARGET_CPU", target.raw_cpu)
761 set_config("TARGET_OS", target.raw_os)
762 set_config("TARGET_ENDIANNESS", target.endianness)
765 @depends(host)
766 def host_variables(host):
767     if host.kernel == "kFreeBSD":
768         os_arch = "GNU_kFreeBSD"
769     else:
770         os_arch = host.kernel
771     return namespace(
772         HOST_OS_ARCH=os_arch,
773     )
776 set_config("HOST_CPU_ARCH", host.cpu)
777 set_config("HOST_OS_ARCH", host_variables.HOST_OS_ARCH)
778 add_old_configure_assignment("HOST_OS_ARCH", host_variables.HOST_OS_ARCH)
779 set_config("HOST_ALIAS", host.alias)
782 @depends(target)
783 def target_is_windows(target):
784     if target.kernel == "WINNT":
785         return True
788 set_define("_WINDOWS", target_is_windows)
789 set_define("WIN32", target_is_windows)
790 set_define("XP_WIN", target_is_windows)
793 @depends(target)
794 def target_is_unix(target):
795     if target.kernel != "WINNT":
796         return True
799 set_define("XP_UNIX", target_is_unix)
802 @depends(target)
803 def target_is_darwin(target):
804     if target.kernel == "Darwin":
805         return True
808 set_define("XP_DARWIN", target_is_darwin)
811 @depends(target)
812 def target_is_osx(target):
813     if target.kernel == "Darwin" and target.os == "OSX":
814         return True
817 set_define("XP_MACOSX", target_is_osx)
820 @depends(target)
821 def target_is_linux(target):
822     if target.kernel == "Linux":
823         return True
826 set_define("XP_LINUX", target_is_linux)
829 @depends(target)
830 def target_is_linux_or_wasi(target):
831     if target.kernel in ("Linux", "WASI"):
832         return True
835 @depends(target)
836 def target_is_android(target):
837     if target.os == "Android":
838         return True
841 set_define("ANDROID", target_is_android)
844 @depends(target)
845 def target_is_openbsd(target):
846     if target.kernel == "OpenBSD":
847         return True
850 set_define("XP_OPENBSD", target_is_openbsd)
853 @depends(target)
854 def target_is_netbsd(target):
855     if target.kernel == "NetBSD":
856         return True
859 set_define("XP_NETBSD", target_is_netbsd)
862 @depends(target)
863 def target_is_freebsd(target):
864     if target.kernel == "FreeBSD":
865         return True
868 set_define("XP_FREEBSD", target_is_freebsd)
871 @depends(target)
872 def target_is_solaris(target):
873     if target.kernel == "SunOS":
874         return True
877 set_define("XP_SOLARIS", target_is_solaris)
880 @depends(target)
881 def target_is_sparc(target):
882     if target.cpu == "sparc64":
883         return True
886 set_define("SPARC64", target_is_sparc)
889 @depends(target, when=target_is_android)
890 def android_cpu_arch(target):
891     d = {
892         "aarch64": "arm64-v8a",
893         "arm": "armeabi-v7a",
894         "x86": "x86",
895         "x86_64": "x86_64",
896     }
897     if target.cpu not in d:
898         die(f"Cannot determine android_cpu_arch: unknown target.cpu: {target.cpu}")
899     return d[target.cpu]
902 set_config("ANDROID_CPU_ARCH", android_cpu_arch)
903 add_old_configure_assignment("ANDROID_CPU_ARCH", android_cpu_arch)
906 @depends("--enable-project", build_environment, "--help")
907 @imports(_from="os.path", _import="exists")
908 def include_project_configure(project, build_env, help):
909     if not project:
910         die("--enable-project is required.")
912     base_dir = build_env.topsrcdir
913     path = os.path.join(base_dir, project[0], "moz.configure")
914     if not exists(path):
915         die("Cannot find project %s", project[0])
916     return path
919 @depends("--enable-project")
920 def build_project(project):
921     return project[0]
924 set_config("MOZ_BUILD_APP", build_project)
925 set_define("MOZ_BUILD_APP", build_project)
926 add_old_configure_assignment("MOZ_BUILD_APP", build_project)
929 option(env="MOZILLA_OFFICIAL", help="Build an official release")
932 @depends("MOZILLA_OFFICIAL")
933 def mozilla_official(official):
934     if official:
935         return True
938 set_config("MOZILLA_OFFICIAL", mozilla_official)
939 set_define("MOZILLA_OFFICIAL", mozilla_official)
940 add_old_configure_assignment("MOZILLA_OFFICIAL", mozilla_official)
943 # Allow specifying custom paths to the version files used by the milestone() function below.
944 option(
945     "--with-version-file-path",
946     nargs=1,
947     help="Specify a custom path to app version files instead of auto-detecting",
948     default=None,
952 @depends("--with-version-file-path")
953 def version_path(path):
954     return path
957 # set RELEASE_OR_BETA and NIGHTLY_BUILD variables depending on the cycle we're in
958 # The logic works like this:
959 # - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD)
960 # - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora
961 # - otherwise, we're building Release/Beta (define RELEASE_OR_BETA)
962 @depends(build_environment, build_project, version_path, "--help")
963 @imports(_from="__builtin__", _import="open")
964 @imports("os")
965 @imports("re")
966 def milestone(build_env, build_project, version_path, _):
967     versions = []
968     paths = ["config/milestone.txt"]
969     if build_project == "js":
970         paths = paths * 3
971     else:
972         paths += [
973             "browser/config/version.txt",
974             "browser/config/version_display.txt",
975         ]
976     if version_path:
977         version_path = version_path[0]
978     else:
979         version_path = os.path.join(build_project, "config")
980     for f in ("version.txt", "version_display.txt"):
981         f = os.path.join(version_path, f)
982         if not os.path.exists(os.path.join(build_env.topsrcdir, f)):
983             break
984         paths.append(f)
986     for p in paths:
987         with open(os.path.join(build_env.topsrcdir, p), "r") as fh:
988             content = fh.read().splitlines()
989             if not content:
990                 die("Could not find a version number in {}".format(p))
991             versions.append(content[-1])
993     milestone, firefox_version, firefox_version_display = versions[:3]
995     # version.txt content from the project directory if there is one, otherwise
996     # the firefox version.
997     app_version = versions[3] if len(versions) > 3 else firefox_version
998     # version_display.txt content from the project directory if there is one,
999     # otherwise version.txt content from the project directory, otherwise the
1000     # firefox version for display.
1001     app_version_display = versions[-1] if len(versions) > 3 else firefox_version_display
1003     is_nightly = is_release_or_beta = is_early_beta_or_earlier = None
1005     if "a1" in milestone:
1006         is_nightly = True
1007     elif "a" not in milestone:
1008         is_release_or_beta = True
1010     major_version = milestone.split(".")[0]
1011     m = re.search(r"([ab]\d+)", milestone)
1012     ab_patch = m.group(1) if m else ""
1014     defines = os.path.join(build_env.topsrcdir, "build", "defines.sh")
1015     with open(defines, "r") as fh:
1016         for line in fh.read().splitlines():
1017             line = line.strip()
1018             if not line or line.startswith("#"):
1019                 continue
1020             name, _, value = line.partition("=")
1021             name = name.strip()
1022             value = value.strip()
1023             if name != "EARLY_BETA_OR_EARLIER":
1024                 die(
1025                     "Only the EARLY_BETA_OR_EARLIER variable can be set in build/defines.sh"
1026                 )
1027             if value and any(x in app_version_display for x in "ab"):
1028                 is_early_beta_or_earlier = True
1030     # Only expose the major version milestone in the UA string and hide the
1031     # patch leve (bugs 572659 and 870868).
1032     #
1033     # Only expose major milestone and alpha version in the symbolversion
1034     # string; as the name suggests, we use it for symbol versioning on Linux.
1035     return namespace(
1036         version=milestone,
1037         uaversion="%s.0" % major_version,
1038         symbolversion="%s%s" % (major_version, ab_patch),
1039         is_nightly=is_nightly,
1040         is_release_or_beta=is_release_or_beta,
1041         is_early_beta_or_earlier=is_early_beta_or_earlier,
1042         is_esr=app_version_display.endswith("esr") or None,
1043         app_version=app_version,
1044         app_version_display=app_version_display,
1045     )
1048 set_config("GRE_MILESTONE", milestone.version)
1049 set_config("NIGHTLY_BUILD", milestone.is_nightly)
1050 set_define("NIGHTLY_BUILD", milestone.is_nightly)
1051 set_config("RELEASE_OR_BETA", milestone.is_release_or_beta)
1052 set_define("RELEASE_OR_BETA", milestone.is_release_or_beta)
1053 add_old_configure_assignment("RELEASE_OR_BETA", milestone.is_release_or_beta)
1054 set_config("MOZ_ESR", milestone.is_esr)
1055 set_define("MOZ_ESR", milestone.is_esr)
1056 set_config("EARLY_BETA_OR_EARLIER", milestone.is_early_beta_or_earlier)
1057 set_define("EARLY_BETA_OR_EARLIER", milestone.is_early_beta_or_earlier)
1058 add_old_configure_assignment(
1059     "EARLY_BETA_OR_EARLIER", milestone.is_early_beta_or_earlier
1061 set_define("MOZILLA_VERSION", depends(milestone)(lambda m: '"%s"' % m.version))
1062 set_config("MOZILLA_VERSION", milestone.version)
1063 set_define("MOZILLA_VERSION_U", milestone.version)
1064 set_define("MOZILLA_UAVERSION", depends(milestone)(lambda m: '"%s"' % m.uaversion))
1065 set_config("MOZILLA_SYMBOLVERSION", milestone.symbolversion)
1066 # JS configure still wants to look at these.
1067 add_old_configure_assignment("MOZILLA_VERSION", milestone.version)
1068 add_old_configure_assignment("MOZILLA_SYMBOLVERSION", milestone.symbolversion)
1070 set_config("MOZ_APP_VERSION", milestone.app_version)
1071 set_config("MOZ_APP_VERSION_DISPLAY", milestone.app_version_display)
1072 add_old_configure_assignment("MOZ_APP_VERSION", milestone.app_version)
1075 # The app update channel is 'default' when not supplied. The value is used in
1076 # the application's confvars.sh (and is made available to a project specific
1077 # moz.configure).
1078 option(
1079     "--enable-update-channel",
1080     nargs=1,
1081     help="Select application update channel",
1082     default="default",
1086 @depends("--enable-update-channel")
1087 def update_channel(channel):
1088     if not channel or channel[0] == "":
1089         return "default"
1090     return channel[0].lower()
1093 set_config("MOZ_UPDATE_CHANNEL", update_channel)
1094 set_define("MOZ_UPDATE_CHANNEL", update_channel)
1095 add_old_configure_assignment("MOZ_UPDATE_CHANNEL", update_channel)
1098 option(
1099     env="MOZBUILD_STATE_PATH",
1100     nargs=1,
1101     help="Path to a persistent state directory for the build system "
1102     "and related tools",
1106 @depends("MOZBUILD_STATE_PATH", "--help")
1107 @imports("os")
1108 def mozbuild_state_path(path, _):
1109     if path:
1110         return normalize_path(path[0])
1111     return normalize_path(os.path.expanduser(os.path.join("~", ".mozbuild")))
1114 # A template providing a shorthand for setting a variable. The created
1115 # option will only be settable with imply_option.
1116 # It is expected that a project-specific moz.configure will call imply_option
1117 # to set a value other than the default.
1118 # If required, the set_as_define and set_for_old_configure arguments
1119 # will additionally cause the variable to be set using set_define and
1120 # add_old_configure_assignment. util.configure would be an appropriate place for
1121 # this, but it uses add_old_configure_assignment, which is defined in this file.
1122 @template
1123 def project_flag(env=None, set_for_old_configure=False, set_as_define=False, **kwargs):
1125     if not env:
1126         configure_error("A project_flag must be passed a variable name to set.")
1128     opt = option(env=env, possible_origins=("implied",), **kwargs)
1130     @depends(opt.option)
1131     def option_implementation(value):
1132         if value:
1133             if len(value):
1134                 return value
1135             return bool(value)
1137     set_config(env, option_implementation)
1138     if set_as_define:
1139         set_define(env, option_implementation)
1140     if set_for_old_configure:
1141         add_old_configure_assignment(env, option_implementation)
1144 # milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set.
1147 @depends(milestone)
1148 def enabled_in_nightly(milestone):
1149     return milestone.is_nightly
1152 # Branding
1153 # ==============================================================
1154 option(
1155     "--with-app-basename",
1156     env="MOZ_APP_BASENAME",
1157     nargs=1,
1158     help="Typically stays consistent for multiple branded versions of a "
1159     'given application (e.g. Aurora and Firefox both use "Firefox"), but '
1160     "may vary for full rebrandings (e.g. Iceweasel). Used for "
1161     'application.ini\'s "Name" field, which controls profile location in '
1162     'the absence of a "Profile" field (see below), and various system '
1163     "integration hooks (Unix remoting, Windows MessageWindow name, etc.",
1167 @depends("--with-app-basename", target_is_android)
1168 def moz_app_basename(value, target_is_android):
1169     if value:
1170         return value[0]
1171     if target_is_android:
1172         return "Fennec"
1173     return "Firefox"
1176 set_config(
1177     "MOZ_APP_BASENAME",
1178     moz_app_basename,
1179     when=depends(build_project)(lambda p: p != "js"),