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
7include("build/moz.configure/init.configure")
8
9# Note:
10# - Gecko-specific options and rules should go in toolkit/moz.configure.
11# - Firefox-specific options and rules should go in browser/moz.configure.
12# - Fennec-specific options and rules should go in
13#   mobile/android/moz.configure.
14# - Spidermonkey-specific options and rules should go in js/moz.configure.
15# - etc.
16
17option(
18    "--enable-artifact-builds",
19    env="MOZ_ARTIFACT_BUILDS",
20    help="Download and use prebuilt binary artifacts.",
21)
22
23
24@depends("--enable-artifact-builds")
25def artifact_builds(value):
26    if value:
27        return True
28
29
30set_config("MOZ_ARTIFACT_BUILDS", artifact_builds)
31
32imply_option(
33    "--enable-artifact-build-symbols",
34    depends(artifact_builds)(lambda v: False if v is None else None),
35    reason="--disable-artifact-builds",
36)
37
38option(
39    "--enable-artifact-build-symbols",
40    nargs="?",
41    choices=("full",),
42    help="Download symbols when artifact builds are enabled.",
43)
44
45
46@depends("--enable-artifact-build-symbols", "MOZ_AUTOMATION", target)
47def enable_artifact_build_symbols(value, automation, target):
48    if len(value):
49        return value[0]
50    if bool(value):
51        if target.os == "Android" and not automation:
52            return "full"
53        return True
54    return None
55
56
57set_config("MOZ_ARTIFACT_BUILD_SYMBOLS", enable_artifact_build_symbols)
58
59
60@depends("--enable-artifact-builds")
61def imply_disable_compile_environment(value):
62    if value:
63        return False
64
65
66option(
67    env="MOZ_COPY_PDBS",
68    help="For builds that do not support symbols in the normal fashion,"
69    " generate and copy them into the resulting build archive.",
70)
71
72set_config("MOZ_COPY_PDBS", depends_if("MOZ_COPY_PDBS")(lambda _: True))
73
74imply_option("--enable-compile-environment", imply_disable_compile_environment)
75
76option("--disable-compile-environment", help="Disable compiler/library checks")
77
78
79@depends("--disable-compile-environment")
80def compile_environment(compile_env):
81    if compile_env:
82        return True
83
84
85set_config("COMPILE_ENVIRONMENT", compile_environment)
86add_old_configure_assignment("COMPILE_ENVIRONMENT", compile_environment)
87
88option("--disable-tests", help="Do not build test libraries & programs")
89
90
91@depends("--disable-tests")
92def enable_tests(value):
93    if value:
94        return True
95
96
97set_config("ENABLE_TESTS", enable_tests)
98set_define("ENABLE_TESTS", enable_tests)
99
100
101@depends(enable_tests)
102def gtest_has_rtti(value):
103    if value:
104        return "0"
105
106
107set_define("GTEST_HAS_RTTI", gtest_has_rtti)
108
109
110@depends(target, enable_tests)
111def linux_gtest_defines(target, enable_tests):
112    if enable_tests and target.os == "Android":
113        return namespace(os_linux_android=True, use_own_tr1_tuple=True, has_clone="0")
114
115
116set_define("GTEST_OS_LINUX_ANDROID", linux_gtest_defines.os_linux_android)
117set_define("GTEST_USE_OWN_TR1_TUPLE", linux_gtest_defines.use_own_tr1_tuple)
118set_define("GTEST_HAS_CLONE", linux_gtest_defines.has_clone)
119
120option(
121    "--enable-debug",
122    nargs="?",
123    help="Enable building with developer debug info "
124    "(using the given compiler flags).",
125)
126
127
128@depends("--enable-debug")
129def moz_debug(debug):
130    if debug:
131        return bool(debug)
132
133
134set_config("MOZ_DEBUG", moz_debug)
135set_define("MOZ_DEBUG", moz_debug)
136# Override any value MOZ_DEBUG may have from the environment when passing it
137# down to old-configure.
138add_old_configure_assignment("MOZ_DEBUG", depends("--enable-debug")(lambda x: bool(x)))
139
140option(
141    "--with-debug-label",
142    nargs="+",
143    help="Debug DEBUG_<value> for each comma-separated value given",
144)
145
146
147@depends(moz_debug, "--with-debug-label")
148def debug_defines(debug, labels):
149    if debug:
150        return ["DEBUG"] + ["DEBUG_%s" % label for label in labels]
151    return ["NDEBUG", "TRIMMED"]
152
153
154set_config("MOZ_DEBUG_DEFINES", debug_defines)
155
156option(env="MOZ_PGO", help="Build with profile guided optimizations")
157
158set_config("MOZ_PGO", depends("MOZ_PGO")(lambda x: bool(x)))
159
160
161imply_option("--enable-release", mozilla_official)
162imply_option("--enable-release", depends_if("MOZ_AUTOMATION")(lambda x: True))
163
164option(
165    "--enable-release",
166    default=milestone.is_release_or_beta,
167    help="{Build|Do not build} with more conservative, release "
168    "engineering-oriented options.{ This may slow down builds.|}",
169)
170
171
172@depends("--enable-release")
173def developer_options(value):
174    if not value:
175        return True
176
177
178add_old_configure_assignment("DEVELOPER_OPTIONS", developer_options)
179set_config("DEVELOPER_OPTIONS", developer_options)
180
181
182option(
183    env="MOZ_FETCHES_DIR",
184    nargs=1,
185    when="MOZ_AUTOMATION",
186    help="Directory containing fetched artifacts",
187)
188
189
190@depends("MOZ_FETCHES_DIR", when="MOZ_AUTOMATION")
191def moz_fetches_dir(value):
192    if value:
193        return value[0]
194
195
196@depends(vcs_checkout_type, milestone.is_nightly, "MOZ_AUTOMATION")
197def bootstrap_default(vcs_checkout_type, is_nightly, automation):
198    if automation:
199        return False
200    # We only enable if building off a VCS checkout of central.
201    if is_nightly and vcs_checkout_type:
202        return True
203
204
205option(
206    "--enable-bootstrap",
207    default=bootstrap_default,
208    help="{Automatically bootstrap or update some toolchains|Disable bootstrap or update of toolchains}",
209)
210
211
212@depends(developer_options, "--enable-bootstrap", moz_fetches_dir)
213def bootstrap_search_path_order(developer_options, bootstrap, moz_fetches_dir):
214    if moz_fetches_dir:
215        log.debug("Prioritizing MOZ_FETCHES_DIR in toolchain path.")
216        return "prepend"
217
218    if bootstrap:
219        log.debug(
220            "Prioritizing mozbuild state dir in toolchain paths because "
221            "bootstrap mode is enabled."
222        )
223        return "prepend"
224
225    if developer_options:
226        log.debug(
227            "Prioritizing mozbuild state dir in toolchain paths because "
228            "you are not building in release mode."
229        )
230        return "prepend"
231
232    log.debug(
233        "Prioritizing system over mozbuild state dir in "
234        "toolchain paths because you are building in "
235        "release mode."
236    )
237    return "append"
238
239
240toolchains_base_dir = moz_fetches_dir | mozbuild_state_path
241
242
243@dependable
244@imports("os")
245@imports(_from="os", _import="environ")
246def original_path():
247    return environ["PATH"].split(os.pathsep)
248
249
250@template
251def bootstrap_toolchain_tasks(host_or_target):
252    @depends(host_or_target, when="--enable-bootstrap")
253    @imports("os")
254    @imports(_from="mozbuild.toolchains", _import="toolchain_task_definitions")
255    @imports(_from="__builtin__", _import="Exception")
256    def bootstrap_toolchain_tasks(host_or_target):
257        prefix = {
258            ("x86", "GNU", "Linux"): "linux32",
259            ("x86_64", "GNU", "Linux"): "linux64",
260            ("aarch64", "GNU", "Linux"): "linux64-aarch64",
261            ("x86_64", "OSX", "Darwin"): "macosx64",
262            ("aarch64", "OSX", "Darwin"): "macosx64-aarch64",
263            ("x86_64", "WINNT", "WINNT"): "win64",
264        }.get((host_or_target.cpu, host_or_target.os, host_or_target.kernel))
265        if prefix:
266            try:
267                return namespace(prefix=prefix, tasks=toolchain_task_definitions())
268            except Exception:
269                return None
270
271    return bootstrap_toolchain_tasks
272
273
274host_bootstrap_toolchain_tasks = bootstrap_toolchain_tasks(host)
275target_bootstrap_toolchain_tasks = bootstrap_toolchain_tasks(target)
276
277
278@template
279def bootstrap_path(path, **kwargs):
280    when = kwargs.pop("when", None)
281    context = kwargs.pop("context", host)
282    if kwargs:
283        configure_error(
284            "bootstrap_path only takes `when` or `context` as keyword arguments"
285        )
286
287    path_parts = path.split("/")
288    bootstrap_toolchain_tasks = {
289        host: host_bootstrap_toolchain_tasks,
290        target: target_bootstrap_toolchain_tasks,
291    }[context]
292
293    @depends(
294        "--enable-bootstrap",
295        toolchains_base_dir,
296        bootstrap_toolchain_tasks,
297        shell,
298        check_build_environment,
299        when=when,
300    )
301    @imports("os")
302    @imports("subprocess")
303    @imports(_from="mozbuild.util", _import="ensureParentDir")
304    @imports(_from="__builtin__", _import="open")
305    @imports(_from="__builtin__", _import="Exception")
306    def bootstrap_path(bootstrap, toolchains_base_dir, tasks, shell, build_env):
307        def try_bootstrap(exists):
308            if not tasks:
309                return False
310            label = "toolchain-{}-{}".format(
311                tasks.prefix, path_parts[0].replace("_", "-")
312            )
313            task = tasks.tasks.get(label)
314            if not task:
315                return False
316            task_index = task.optimization.get("index-search")
317            if not task_index:
318                return False
319            task_index = task_index[0].split(".")[-1]
320            artifact = task.attributes["toolchain-artifact"]
321            # `mach artifact toolchain` doesn't support authentication for
322            # private artifacts.
323            if not artifact.startswith("public/"):
324                return False
325            index_file = os.path.join(toolchains_base_dir, "indices", path_parts[0])
326            try:
327                with open(index_file) as fh:
328                    index = fh.read().strip()
329            except Exception:
330                index = None
331            if index == task_index and exists:
332                return True
333            log.info(
334                "%s bootstrapped toolchain in %s",
335                "Updating" if exists else "Installing",
336                os.path.join(toolchains_base_dir, path_parts[0]),
337            )
338            subprocess.run(
339                [
340                    shell,
341                    os.path.join(build_env.topsrcdir, "mach"),
342                    "--log-no-times",
343                    "artifact",
344                    "toolchain",
345                    "--from-build",
346                    label,
347                ],
348                cwd=toolchains_base_dir,
349                check=True,
350            )
351            ensureParentDir(index_file)
352            with open(index_file, "w") as fh:
353                fh.write(task_index)
354            return True
355
356        path = os.path.join(toolchains_base_dir, *path_parts)
357        if bootstrap:
358            try:
359                if not try_bootstrap(os.path.exists(path)):
360                    # If there aren't toolchain artifacts to use for this build,
361                    # don't return a path.
362                    return None
363            except Exception as e:
364                log.error("%s", e)
365                die("If you can't fix the above, retry with --disable-bootstrap.")
366        # We re-test whether the path exists because it may have been created by
367        # try_bootstrap. Automation will not have gone through the bootstrap
368        # process, but we want to return the path if it exists.
369        if os.path.exists(path):
370            return path
371
372    return bootstrap_path
373
374
375@template
376def bootstrap_search_path(path, paths=original_path, **kwargs):
377    @depends(
378        bootstrap_path(path, **kwargs),
379        bootstrap_search_path_order,
380        paths,
381        original_path,
382    )
383    def bootstrap_search_path(path, order, paths, original_path):
384        if paths is None:
385            paths = original_path
386        if not path:
387            return paths
388        if order == "prepend":
389            return [path] + paths
390        return paths + [path]
391
392    return bootstrap_search_path
393
394
395# The execution model of the configure sandbox doesn't allow for
396# check_prog to use bootstrap_search_path directly because check_prog
397# comes first, so we use a trick to allow it. No use of check_prog
398# happening before here won't allow bootstrap.
399@template
400def check_prog(*args, **kwargs):
401    kwargs["bootstrap_search_path"] = bootstrap_search_path
402    return check_prog(*args, **kwargs)
403
404
405@depends(target, host)
406def want_wine(target, host):
407    return target.kernel == "WINNT" and host.kernel != "WINNT"
408
409
410wine = check_prog(
411    "WINE",
412    ["wine64", "wine"],
413    allow_missing=True,
414    when=want_wine,
415    bootstrap="wine/bin",
416)
417check_prog("WGET", ("wget",), allow_missing=True)
418
419
420include("build/moz.configure/toolchain.configure", when="--enable-compile-environment")
421
422include("build/moz.configure/pkg.configure")
423# Make this assignment here rather than in pkg.configure to avoid
424# requiring this file in unit tests.
425add_old_configure_assignment("PKG_CONFIG", pkg_config)
426
427include("build/moz.configure/memory.configure", when="--enable-compile-environment")
428include("build/moz.configure/headers.configure", when="--enable-compile-environment")
429include("build/moz.configure/warnings.configure", when="--enable-compile-environment")
430include("build/moz.configure/flags.configure", when="--enable-compile-environment")
431include("build/moz.configure/lto-pgo.configure", when="--enable-compile-environment")
432# rust.configure is included by js/moz.configure.
433
434option("--enable-valgrind", help="Enable Valgrind integration hooks")
435
436valgrind_h = check_header("valgrind/valgrind.h", when="--enable-valgrind")
437
438
439@depends("--enable-valgrind", valgrind_h)
440def check_valgrind(valgrind, valgrind_h):
441    if valgrind:
442        if not valgrind_h:
443            die("--enable-valgrind specified but Valgrind is not installed")
444        return True
445
446
447set_define("MOZ_VALGRIND", check_valgrind)
448set_config("MOZ_VALGRIND", check_valgrind)
449
450
451@depends(target, host)
452def is_openbsd(target, host):
453    return target.kernel == "OpenBSD" or host.kernel == "OpenBSD"
454
455
456option(
457    env="SO_VERSION",
458    nargs=1,
459    default="1.0",
460    when=is_openbsd,
461    help="Shared library version for OpenBSD systems",
462)
463
464
465@depends("SO_VERSION", when=is_openbsd)
466def so_version(value):
467    return value
468
469
470@template
471def library_name_info_template(host_or_target):
472    assert host_or_target in {host, target}
473    compiler = {
474        host: host_c_compiler,
475        target: c_compiler,
476    }[host_or_target]
477
478    @depends(host_or_target, compiler, so_version)
479    def library_name_info_impl(host_or_target, compiler, so_version):
480        if host_or_target.kernel == "WINNT":
481            # There aren't artifacts for mingw builds, so it's OK that the
482            # results are inaccurate in that case.
483            if compiler and compiler.type != "clang-cl":
484                return namespace(
485                    dll=namespace(prefix="", suffix=".dll"),
486                    lib=namespace(prefix="lib", suffix="a"),
487                    import_lib=namespace(prefix="lib", suffix="a"),
488                    obj=namespace(prefix="", suffix="o"),
489                )
490
491            return namespace(
492                dll=namespace(prefix="", suffix=".dll"),
493                lib=namespace(prefix="", suffix="lib"),
494                import_lib=namespace(prefix="", suffix="lib"),
495                obj=namespace(prefix="", suffix="obj"),
496            )
497
498        elif host_or_target.kernel == "Darwin":
499            return namespace(
500                dll=namespace(prefix="lib", suffix=".dylib"),
501                lib=namespace(prefix="lib", suffix="a"),
502                import_lib=namespace(prefix=None, suffix=""),
503                obj=namespace(prefix="", suffix="o"),
504            )
505        elif so_version:
506            so = ".so.%s" % so_version
507        else:
508            so = ".so"
509
510        return namespace(
511            dll=namespace(prefix="lib", suffix=so),
512            lib=namespace(prefix="lib", suffix="a"),
513            import_lib=namespace(prefix=None, suffix=""),
514            obj=namespace(prefix="", suffix="o"),
515        )
516
517    return library_name_info_impl
518
519
520host_library_name_info = library_name_info_template(host)
521library_name_info = library_name_info_template(target)
522
523set_config("DLL_PREFIX", library_name_info.dll.prefix)
524set_config("DLL_SUFFIX", library_name_info.dll.suffix)
525set_config("HOST_DLL_PREFIX", host_library_name_info.dll.prefix)
526set_config("HOST_DLL_SUFFIX", host_library_name_info.dll.suffix)
527set_config("LIB_PREFIX", library_name_info.lib.prefix)
528set_config("LIB_SUFFIX", library_name_info.lib.suffix)
529set_config("OBJ_SUFFIX", library_name_info.obj.suffix)
530# Lots of compilation tests depend on this variable being present.
531add_old_configure_assignment("OBJ_SUFFIX", library_name_info.obj.suffix)
532set_config("IMPORT_LIB_SUFFIX", library_name_info.import_lib.suffix)
533set_define(
534    "MOZ_DLL_PREFIX", depends(library_name_info.dll.prefix)(lambda s: '"%s"' % s)
535)
536set_define(
537    "MOZ_DLL_SUFFIX", depends(library_name_info.dll.suffix)(lambda s: '"%s"' % s)
538)
539set_config("WASM_OBJ_SUFFIX", "wasm")
540
541# Make `profiling` available to this file even when js/moz.configure
542# doesn't end up included.
543profiling = dependable(False)
544# Same for js_standalone
545js_standalone = dependable(False)
546# Same for fold_libs
547fold_libs = dependable(False)
548
549include(include_project_configure)
550
551
552@depends("--help")
553@imports(_from="mozbuild.backend", _import="backends")
554def build_backends_choices(_):
555    return tuple(backends)
556
557
558@deprecated_option("--enable-build-backend", nargs="+", choices=build_backends_choices)
559def build_backend(backends):
560    if backends:
561        return tuple("+%s" % b for b in backends)
562
563
564imply_option("--build-backends", build_backend)
565
566
567@depends(
568    "--enable-artifact-builds",
569    "--disable-compile-environment",
570    "--enable-build-backend",
571    "--enable-project",
572    "--enable-application",
573    "--help",
574)
575@imports("sys")
576def build_backend_defaults(
577    artifact_builds, compile_environment, requested_backends, project, application, _
578):
579    if application:
580        project = application[0]
581    elif project:
582        project = project[0]
583
584    if "Tup" in requested_backends:
585        # As a special case, if Tup was requested, do not combine it with any
586        # Make based backend by default.
587        all_backends = []
588    elif artifact_builds:
589        all_backends = ["FasterMake+RecursiveMake"]
590    else:
591        all_backends = ["RecursiveMake", "FasterMake"]
592    # Normally, we'd use target.os == 'WINNT', but a dependency on target
593    # would require target to depend on --help, as well as host and shell,
594    # and this is not a can of worms we can open at the moment.
595    if (
596        sys.platform == "win32"
597        and compile_environment
598        and project not in ("mobile/android", "memory", "tools/update-programs")
599    ):
600        all_backends.append("VisualStudio")
601    return tuple(all_backends) or None
602
603
604option(
605    "--build-backends",
606    nargs="+",
607    default=build_backend_defaults,
608    choices=build_backends_choices,
609    help="Build backends to generate",
610)
611
612
613@depends("--build-backends")
614def build_backends(backends):
615    return backends
616
617
618set_config("BUILD_BACKENDS", build_backends)
619
620
621@depends(check_build_environment, build_backends)
622@imports("glob")
623def check_objdir_backend_reuse(build_env, backends):
624    # "Make based" might be RecursiveMake or a hybrid backend, so "Make" is
625    # intentionally vague for use with the substring match below.
626    incompatible_backends = (("Tup", "Make"), ("Make", "Tup"))
627    for backend_file in glob.iglob(
628        os.path.join(build_env.topobjdir, "backend.*Backend")
629    ):
630        for prev, curr in incompatible_backends:
631            if prev in backend_file and any(curr in b for b in backends):
632                die(
633                    "The active objdir, %s, was previously "
634                    "used to build with a %s based backend. "
635                    "Change objdirs (by setting MOZ_OBJDIR in "
636                    "your mozconfig) or clobber to continue.\n",
637                    build_env.topobjdir,
638                    prev,
639                )
640
641
642option(
643    "--disable-gtest-in-build",
644    help="Force disable building the gtest libxul during the build.",
645    when="--enable-compile-environment",
646)
647
648# Determine whether to build the gtest xul. This happens in automation
649# on Android and Desktop platforms with the exception of:
650#  - Windows PGO, where linking xul-gtest.dll takes too long;
651#  - Android other than x86_64, where gtest is not required.
652
653
654@depends(
655    build_project,
656    target,
657    "MOZ_AUTOMATION",
658    "--disable-gtest-in-build",
659    enable_tests,
660    when="--enable-compile-environment",
661)
662def build_gtest(build_project, target, automation, enabled, enable_tests):
663    if not enable_tests or not enabled:
664        return None
665    if (
666        automation
667        and build_project in ("browser", "comm/mail", "mobile/android")
668        and not (target.os == "Android" and target.cpu != "x86_64")
669    ):
670        return True
671
672
673set_config("LINK_GTEST_DURING_COMPILE", build_gtest)
674
675# Localization
676# ==============================================================
677option(
678    "--enable-ui-locale",
679    default="en-US",
680    help="Select the user interface locale (default: en-US)",
681)
682
683set_config("MOZ_UI_LOCALE", depends("--enable-ui-locale")(lambda x: x))
684
685# clang-plugin location
686# ==============================================================
687
688
689@depends(host_library_name_info, check_build_environment, when="--enable-clang-plugin")
690def clang_plugin_path(library_name_info, build_env):
691    topobjdir = build_env.topobjdir
692    if topobjdir.endswith("/js/src"):
693        topobjdir = topobjdir[:-7]
694    return os.path.abspath(
695        os.path.join(
696            topobjdir,
697            "build",
698            "clang-plugin",
699            "%sclang-plugin%s"
700            % (library_name_info.dll.prefix, library_name_info.dll.suffix),
701        )
702    )
703
704
705set_config("CLANG_PLUGIN", clang_plugin_path)
706add_old_configure_assignment("CLANG_PLUGIN", clang_plugin_path)
707
708
709# Awk detection
710# ==============================================================
711awk = check_prog("AWK", ("gawk", "mawk", "nawk", "awk"))
712
713# Until the AWK variable is not necessary in old-configure
714
715
716@depends(awk)
717def awk_for_old_configure(value):
718    return value
719
720
721add_old_configure_assignment("AWK", awk_for_old_configure)
722
723
724# Perl detection
725# ==============================================================
726perl = check_prog("PERL", ("perl5", "perl"))
727
728# Until the PERL variable is not necessary in old-configure
729
730
731@depends(perl)
732def perl_for_old_configure(value):
733    return value
734
735
736add_old_configure_assignment("PERL", perl_for_old_configure)
737
738
739@template
740def perl_version_check(min_version):
741    @depends(perl)
742    @checking("for minimum required perl version >= %s" % min_version)
743    def get_perl_version(perl):
744        return Version(
745            check_cmd_output(
746                perl,
747                "-e",
748                "print $]",
749                onerror=lambda: die("Failed to get perl version."),
750            )
751        )
752
753    @depends(get_perl_version)
754    def check_perl_version(version):
755        if version < min_version:
756            die("Perl %s or higher is required.", min_version)
757
758    @depends(perl)
759    @checking("for full perl installation")
760    @imports("subprocess")
761    def has_full_perl_installation(perl):
762        ret = subprocess.call([perl, "-e", "use Config; exit(!-d $Config{archlib})"])
763        return ret == 0
764
765    @depends(has_full_perl_installation)
766    def require_full_perl_installation(has_full_perl_installation):
767        if not has_full_perl_installation:
768            die(
769                "Cannot find Config.pm or $Config{archlib}. "
770                "A full perl installation is required."
771            )
772
773
774perl_version_check("5.006")
775
776
777# GNU make detection
778# ==============================================================
779option(env="MAKE", nargs=1, help="Path to GNU make")
780
781
782@depends("MAKE", host)
783def possible_makes(make, host):
784    candidates = []
785    if host.kernel == "WINNT":
786        candidates.append("mingw32-make")
787    if make:
788        candidates.append(make[0])
789    if host.kernel == "WINNT":
790        candidates.extend(("mozmake", "make", "gmake"))
791    else:
792        candidates.extend(("gmake", "make"))
793    return candidates
794
795
796check_prog("GMAKE", possible_makes, bootstrap="mozmake")
797
798# watchman detection
799# ==============================================================
800
801option(env="WATCHMAN", nargs=1, help="Path to the watchman program")
802
803
804@depends(host, "WATCHMAN")
805@checking("for watchman", callback=lambda w: w.path if w else "not found")
806def watchman(host, prog):
807    # On Windows, `watchman` is only supported on 64-bit hosts.
808    if host.os == "WINNT" and host.cpu != "x86_64":
809        return
810
811    if not prog:
812        prog = find_program("watchman")
813
814    if not prog:
815        return
816
817    # `watchman version` will talk to the Watchman daemon service.
818    # This can hang due to permissions problems. e.g.
819    # https://github.com/facebook/watchman/issues/376. So use
820    # `watchman --version` to prevent a class of failures.
821    out = check_cmd_output(prog, "--version", onerror=lambda: None)
822    if out is None:
823        return
824
825    return namespace(path=prog, version=Version(out.strip()))
826
827
828@depends_if(watchman)
829@checking("for watchman version")
830def watchman_version(w):
831    return w.version
832
833
834set_config("WATCHMAN", watchman.path)
835
836
837@depends_all(hg_version, hg_config, watchman)
838@checking("for watchman Mercurial integration")
839@imports("os")
840def watchman_hg(hg_version, hg_config, watchman):
841    if hg_version < Version("3.8"):
842        return "no (Mercurial 3.8+ required)"
843
844    ext_enabled = False
845    mode_disabled = False
846
847    for k in ("extensions.fsmonitor", "extensions.hgext.fsmonitor"):
848        if k in hg_config and hg_config[k] != "!":
849            ext_enabled = True
850
851    mode_disabled = hg_config.get("fsmonitor.mode") == "off"
852
853    if not ext_enabled:
854        return "no (fsmonitor extension not enabled)"
855    if mode_disabled:
856        return "no (fsmonitor.mode=off disables fsmonitor)"
857
858    return True
859
860
861# Miscellaneous programs
862# ==============================================================
863check_prog("XARGS", ("xargs",))
864
865
866@depends(target)
867def extra_programs(target):
868    if target.kernel == "Darwin":
869        return namespace(
870            DSYMUTIL=("dsymutil", "llvm-dsymutil"),
871            MKFSHFS=("newfs_hfs", "mkfs.hfsplus"),
872            HFS_TOOL=("hfsplus",),
873        )
874    if target.os == "GNU" and target.kernel == "Linux":
875        return namespace(RPMBUILD=("rpmbuild",))
876
877
878check_prog("DSYMUTIL", extra_programs.DSYMUTIL, allow_missing=True)
879check_prog("MKFSHFS", extra_programs.MKFSHFS, allow_missing=True)
880check_prog("HFS_TOOL", extra_programs.HFS_TOOL, allow_missing=True)
881check_prog("RPMBUILD", extra_programs.RPMBUILD, allow_missing=True)
882
883
884@depends(target)
885@imports("os")
886def makensis_progs(target):
887    if target.kernel != "WINNT":
888        return
889
890    candidates = [
891        "makensis-3.01",
892        "makensis-3.0b3",
893        "makensis-3.0b1",
894        "makensis",
895    ]
896
897    # Look for nsis installed by msys environment. But only the 32-bit version.
898    # We use an absolute path and insert as the first entry so it is preferred
899    # over a 64-bit exe that may be in PATH.
900    if "MSYSTEM_PREFIX" in os.environ:
901        prefix = os.path.dirname(os.environ["MSYSTEM_PREFIX"])
902        candidates.insert(0, os.path.join(prefix, "mingw32", "bin", "makensis.exe"))
903
904    return tuple(candidates)
905
906
907nsis = check_prog("MAKENSISU", makensis_progs, allow_missing=True)
908
909# Make sure the version of makensis is up to date.
910
911
912@depends(nsis, wine)
913@checking("for NSIS version")
914@imports("re")
915def nsis_version(nsis, wine):
916    if not nsis:
917        return None
918    nsis_min_version = "3.0b1"
919
920    def onerror():
921        return die("Failed to get nsis version.")
922
923    if wine and nsis.lower().endswith(".exe"):
924        out = check_cmd_output(wine, nsis, "-version", onerror=onerror)
925    else:
926        out = check_cmd_output(nsis, "-version", onerror=onerror)
927
928    m = re.search(r"(?<=v)[0-9]+\.[0-9]+((a|b|rc)[0-9]+)?", out)
929
930    if not m:
931        raise FatalCheckError("Unknown version of makensis")
932    ver = Version(m.group(0))
933
934    # Versions comparisons don't quite work well with beta versions, so ensure
935    # it works for the non-beta version.
936    if ver < nsis_min_version and (ver >= "3.0a" or ver < "3"):
937        raise FatalCheckError(
938            "To build the installer you must have NSIS"
939            " version %s or greater in your path" % nsis_min_version
940        )
941
942    return ver
943
944
945# And that makensis is 32-bit (but only on Windows).
946@depends_if(nsis, when=depends(host)(lambda h: h.kernel == "WINNT"))
947@checking("for 32-bit NSIS")
948def nsis_binary_type(nsis):
949    bin_type = windows_binary_type(nsis)
950    if bin_type != "win32":
951        raise FatalCheckError("%s is not a 32-bit Windows application" % nsis)
952
953    return "yes"
954
955
956# And any flags we have to give to makensis
957@depends(host)
958def nsis_flags(host):
959    if host.kernel != "WINNT":
960        return "-nocd"
961    return ""
962
963
964set_config("MAKENSISU_FLAGS", nsis_flags)
965
966check_prog("7Z", ("7z", "7za"), allow_missing=True, when=target_is_windows)
967check_prog("UPX", ("upx",), allow_missing=True, when=target_is_windows)
968
969
970@depends(host_c_compiler, c_compiler, bindgen_config_paths)
971def llvm_objdump(host_c_compiler, c_compiler, bindgen_config_paths):
972    clang = None
973    for compiler in (host_c_compiler, c_compiler):
974        if compiler and compiler.type == "clang":
975            clang = compiler.compiler
976            break
977        elif compiler and compiler.type == "clang-cl":
978            clang = os.path.join(os.path.dirname(compiler.compiler), "clang")
979            break
980
981    if not clang and bindgen_config_paths:
982        clang = bindgen_config_paths.clang_path
983    llvm_objdump = "llvm-objdump"
984    if clang:
985        out = check_cmd_output(
986            clang, "--print-prog-name=llvm-objdump", onerror=lambda: None
987        )
988        if out:
989            llvm_objdump = out.rstrip()
990    return (llvm_objdump,)
991
992
993llvm_objdump = check_prog(
994    "LLVM_OBJDUMP",
995    llvm_objdump,
996    what="llvm-objdump",
997    when="--enable-compile-environment",
998    paths=clang_search_path,
999)
1000
1001add_old_configure_assignment("LLVM_OBJDUMP", llvm_objdump)
1002
1003
1004option("--enable-dtrace", help="Build with dtrace support")
1005
1006dtrace = check_header(
1007    "sys/sdt.h",
1008    when="--enable-dtrace",
1009    onerror=lambda: die("dtrace enabled but sys/sdt.h not found"),
1010)
1011
1012set_config("HAVE_DTRACE", True, when=dtrace)
1013set_define("INCLUDE_MOZILLA_DTRACE", True, when=dtrace)
1014add_old_configure_assignment("enable_dtrace", "yes", when=dtrace)
1015
1016
1017option("--disable-icf", help="Disable Identical Code Folding")
1018
1019add_old_configure_assignment(
1020    "MOZ_DISABLE_ICF", "1", when=depends("--enable-icf")(lambda x: not x)
1021)
1022
1023
1024option(
1025    "--enable-strip",
1026    when=compile_environment,
1027    help="Enable stripping of libs & executables",
1028)
1029
1030# This should be handled as a `when` once bug 1617793 is fixed.
1031
1032
1033@depends("--enable-strip", c_compiler, when=compile_environment)
1034def enable_strip(strip, c_compiler):
1035    if strip and c_compiler.type != "clang-cl":
1036        return True
1037
1038
1039set_config("ENABLE_STRIP", enable_strip)
1040
1041option(
1042    "--disable-install-strip",
1043    when=compile_environment,
1044    help="Enable stripping of libs & executables when packaging",
1045)
1046
1047# This should be handled as a `when` once bug 1617793 is fixed.
1048
1049
1050@depends("--enable-install-strip", c_compiler, when=compile_environment)
1051def enable_install_strip(strip, c_compiler):
1052    if strip and c_compiler.type != "clang-cl":
1053        return True
1054
1055
1056set_config("PKG_STRIP", enable_install_strip)
1057
1058
1059@depends("--enable-strip", "--enable-install-strip", when=compile_environment)
1060def strip(strip, install_strip):
1061    return strip or install_strip
1062
1063
1064option(env="STRIP_FLAGS", nargs=1, when=strip, help="Flags for the strip command")
1065
1066
1067@depends("STRIP_FLAGS", profiling, target, when=strip)
1068def strip_flags(flags, profiling, target):
1069    if flags:
1070        return flags[0].split()
1071    if profiling:
1072        # Only strip debug info and symbols when profiling is enabled, keeping
1073        # local symbols.
1074        if target.kernel == "Darwin":
1075            return ["-S"]
1076        elif target.os == "Android":
1077            # The tooling we use with Android supports detached symbols, and the
1078            # size increase caused by local symbols are too much for mobile. So,
1079            # don't restrict the amount of stripping with a flag.
1080            return
1081        else:
1082            return ["--strip-debug"]
1083    # Otherwise strip everything we can, which happens without flags on non-Darwin.
1084    # On Darwin, it tries to strip things it can't, so we need to limit its scope.
1085    elif target.kernel == "Darwin":
1086        return ["-x", "-S"]
1087
1088
1089set_config("STRIP_FLAGS", strip_flags)
1090
1091
1092@depends(js_standalone, target)
1093def system_zlib_default(js_standalone, target):
1094    return js_standalone and target.kernel != "WINNT"
1095
1096
1097option(
1098    "--with-system-zlib",
1099    nargs="?",
1100    default=system_zlib_default,
1101    help="{Use|Do not use} system libz",
1102)
1103
1104
1105@depends("--with-system-zlib")
1106def deprecated_system_zlib_path(value):
1107    if len(value) == 1:
1108        die(
1109            "--with-system-zlib=PATH is not supported anymore. Please use "
1110            "--with-system-zlib and set any necessary pkg-config environment variable."
1111        )
1112
1113
1114pkg_check_modules("MOZ_ZLIB", "zlib >= 1.2.3", when="--with-system-zlib")
1115
1116set_config("MOZ_SYSTEM_ZLIB", True, when="--with-system-zlib")
1117add_old_configure_assignment("MOZ_SYSTEM_ZLIB", True, when="--with-system-zlib")
1118
1119
1120# Please do not add configure checks from here on.
1121
1122# Fallthrough to autoconf-based configure
1123include("build/moz.configure/old.configure")
1124
1125# JS Subconfigure.
1126include("js/sub.configure", when=compile_environment & toolkit)
1127
1128
1129@depends(check_build_environment, build_project)
1130@imports("__sandbox__")
1131@imports("glob")
1132@imports(_from="os.path", _import="exists")
1133def config_status_deps(build_env, build_project):
1134
1135    topsrcdir = build_env.topsrcdir
1136    topobjdir = build_env.topobjdir
1137
1138    if not topobjdir.endswith("js/src"):
1139        extra_deps = [os.path.join(topobjdir, ".mozconfig.json")]
1140    else:
1141        # mozconfig changes may impact js configure.
1142        extra_deps = [os.path.join(topobjdir[:-7], ".mozconfig.json")]
1143
1144    confvars = os.path.join(topsrcdir, build_project, "confvars.sh")
1145    if exists(confvars):
1146        extra_deps.append(confvars)
1147
1148    return (
1149        list(__sandbox__._all_paths)
1150        + extra_deps
1151        + [
1152            os.path.join(topsrcdir, "CLOBBER"),
1153            os.path.join(topsrcdir, "configure.in"),
1154            os.path.join(topsrcdir, "js", "src", "configure.in"),
1155            os.path.join(topsrcdir, "nsprpub", "configure"),
1156            os.path.join(topsrcdir, "config", "milestone.txt"),
1157            os.path.join(topsrcdir, "browser", "config", "version.txt"),
1158            os.path.join(topsrcdir, "browser", "config", "version_display.txt"),
1159            os.path.join(topsrcdir, "build", "build_virtualenv_packages.txt"),
1160            os.path.join(topsrcdir, "build", "common_virtualenv_packages.txt"),
1161            os.path.join(topsrcdir, "build", "mach_virtualenv_packages.txt"),
1162            os.path.join(topsrcdir, "python", "mozbuild", "mozbuild", "virtualenv.py"),
1163            os.path.join(topsrcdir, "aclocal.m4"),
1164            os.path.join(topsrcdir, "old-configure.in"),
1165            os.path.join(topsrcdir, "js", "src", "aclocal.m4"),
1166            os.path.join(topsrcdir, "js", "src", "old-configure.in"),
1167        ]
1168        + glob.glob(os.path.join(topsrcdir, "build", "autoconf", "*.m4"))
1169    )
1170
1171
1172set_config("CONFIG_STATUS_DEPS", config_status_deps)
1173# Please do not add anything after setting config_dep_paths.
1174