# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Set the MOZ_CONFIGURE_OPTIONS variable with all the options that
# were passed somehow (environment, command line, mozconfig)
@dependable
@imports(_from="mozbuild.shellutil", _import="quote")
@imports(_from="mozbuild.util", _import="ensure_unicode")
@imports(_from="mozbuild.util", _import="system_encoding")
@imports("__sandbox__")
def all_configure_options():
    result = []
    previous = None
    for option in __sandbox__._options.values():
        # __sandbox__._options contains items for both option.name and
        # option.env. But it's also an OrderedDict, meaning both are
        # consecutive.
        # Also ignore OLD_CONFIGURE and MOZCONFIG because they're not
        # interesting.
        if option == previous or option.env in ("OLD_CONFIGURE", "MOZCONFIG"):
            continue
        previous = option
        value = __sandbox__._value_for(option)
        # We only want options that were explicitly given on the command
        # line, the environment, or mozconfig, and that differ from the
        # defaults.
        if (
            value is not None
            and value.origin not in ("default", "implied")
            and value != option.default
        ):
            result.append(
                ensure_unicode(__sandbox__._raw_options[option], system_encoding)
            )
        # We however always include options that are sent to old configure
        # because we don't know their actual defaults. (Keep the conditions
        # separate for ease of understanding and ease of removal)
        elif (
            option.help == "Help missing for old configure options"
            and option in __sandbox__._raw_options
        ):
            result.append(
                ensure_unicode(__sandbox__._raw_options[option], system_encoding)
            )

    # We shouldn't need this, but currently, quote will return a byte string
    # if result is empty, and that's not wanted here.
    if not result:
        return ""

    return quote(*result)


set_config("MOZ_CONFIGURE_OPTIONS", all_configure_options)


@depends(target)
def fold_libs(target):
    return target.os in ("WINNT", "OSX", "Android")


set_config("MOZ_FOLD_LIBS", fold_libs)

# Profiling
# ==============================================================
# Some of the options here imply an option from js/moz.configure,
# so, need to be declared before the include.

option(
    "--enable-jprof",
    env="MOZ_JPROF",
    help="Enable jprof profiling tool (needs mozilla/tools/jprof)",
)


@depends("--enable-jprof")
def jprof(value):
    if value:
        return True


set_config("MOZ_JPROF", jprof)
set_define("MOZ_JPROF", jprof)
imply_option("--enable-profiling", jprof)


@depends(target)
def gecko_profiler(target):
    if target.os == "Android":
        return target.cpu in ("aarch64", "arm", "x86", "x86_64")
    elif target.kernel == "Linux":
        return target.cpu in ("aarch64", "arm", "x86", "x86_64", "mips64")
    elif target.kernel == "FreeBSD":
        return target.cpu in ("aarch64", "x86_64")
    return target.os in ("OSX", "WINNT")


@depends(gecko_profiler)
def gecko_profiler_define(value):
    if value:
        return True


set_config("MOZ_GECKO_PROFILER", gecko_profiler_define)
set_define("MOZ_GECKO_PROFILER", gecko_profiler_define)


# Whether code to parse ELF binaries should be compiled for the Gecko profiler
# (for symbol table dumping).
@depends(gecko_profiler, target)
def gecko_profiler_parse_elf(value, target):
    # Currently we only want to build this code on Linux (including Android) and BSD.
    # For Android, this is in order to dump symbols from Android system, where
    # on other platforms there exist alternatives that don't require bloating
    # up our binary size. For Linux more generally, we use this in profile
    # pre-symbolication support, since MozDescribeCodeAddress doesn't do
    # anything useful on that platform. (Ideally, we would update
    # MozDescribeCodeAddress to call into some Rust crates that parse ELF and
    # DWARF data, but build system issues currently prevent Rust from being
    # used in mozglue.)
    if value and (target.kernel == "Linux" or target.kernel == "FreeBSD"):
        return True


set_config("MOZ_GECKO_PROFILER_PARSE_ELF", gecko_profiler_parse_elf)
set_define("MOZ_GECKO_PROFILER_PARSE_ELF", gecko_profiler_parse_elf)

# enable this by default if the profiler is enabled
# Note: also requires jemalloc
set_config("MOZ_PROFILER_MEMORY", gecko_profiler_define)
set_define("MOZ_PROFILER_MEMORY", gecko_profiler_define)


@depends(
    "--enable-debug",
    milestone,
    build_project,
    # Artifact builds are included because the downloaded artifacts can
    # have DMD enabled.
    when=artifact_builds | depends(when="--enable-replace-malloc")(lambda: True),
)
def dmd_default(debug, milestone, build_project):
    return bool(build_project == "browser" and (debug or milestone.is_nightly))


option(
    "--enable-dmd",
    env="MOZ_DMD",
    default=dmd_default,
    help="{Enable|Disable} Dark Matter Detector (heap profiler). "
    "Also enables jemalloc, replace-malloc and profiling",
)


@depends("--enable-dmd")
def dmd(value):
    if value:
        return True


set_config("MOZ_DMD", dmd)
set_define("MOZ_DMD", dmd)
imply_option("--enable-profiling", dmd)
imply_option("--enable-jemalloc", dmd, when=compile_environment)
imply_option("--enable-replace-malloc", dmd, when=compile_environment)

# midir-based Web MIDI support
# ==============================================================
@depends(target)
def midir_linux_support(target):
    return (
        target.kernel == "Linux" and target.os != "Android" and target.cpu != "riscv64"
    )


@depends(target, midir_linux_support)
def midir_support(target, midir_linux_support):
    if target.os in ("WINNT", "OSX") or midir_linux_support:
        return True


set_config("MOZ_WEBMIDI_MIDIR_IMPL", midir_support)

# Enable various cubeb backends
# ==============================================================
@depends(target)
def audio_backends_default(target):
    if target.os == "Android":
        return (
            "aaudio",
            "opensl",
        )
    elif target.os in ("DragonFly", "FreeBSD", "SunOS"):
        return ("oss",)
    elif target.os == "OpenBSD":
        return ("sndio",)
    elif target.os == "OSX":
        return ("audiounit",)
    elif target.os == "NetBSD":
        return ("sunaudio",)
    elif target.os == "SunOS":
        return ("sunaudio",)
    elif target.os == "WINNT":
        return ("wasapi",)
    else:
        return ("pulseaudio",)


option(
    "--enable-audio-backends",
    nargs="+",
    choices=(
        "aaudio",
        "alsa",
        "audiounit",
        "jack",
        "opensl",
        "oss",
        "pulseaudio",
        "sndio",
        "sunaudio",
        "wasapi",
    ),
    default=audio_backends_default,
    help="{Enable|Disable} various cubeb backends",
)


@depends("--enable-audio-backends", target)
def imply_aaudio(values, target):
    if any("aaudio" in value for value in values) and target.os != "Android":
        die("Cannot enable AAudio on %s", target.os)
    return any("aaudio" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_alsa(values, target):
    if (
        any("alsa" in value for value in values)
        and target.kernel != "Linux"
        and target.os != "FreeBSD"
    ):
        die("Cannot enable ALSA on %s", target.os)
    return any("alsa" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_audiounit(values, target):
    if (
        any("audiounit" in value for value in values)
        and target.os != "OSX"
        and target.kernel != "Darwin"
    ):
        die("Cannot enable AudioUnit on %s", target.os)
    return any("audiounit" in value for value in values) or None


@depends("--enable-audio-backends")
def imply_jack(values):
    return any("jack" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_opensl(values, target):
    if any("opensl" in value for value in values) and target.os != "Android":
        die("Cannot enable OpenSL on %s", target.os)
    return any("opensl" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_oss(values, target):
    if any("oss" in value for value in values) and (
        target.os == "Android" or target.os == "OSX" or target.os == "WINNT"
    ):
        die("Cannot enable OSS on %s", target.os)
    return any("oss" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_pulseaudio(values, target):
    if any("pulseaudio" in value for value in values) and (
        target.os == "Android" or target.os == "OSX" or target.os == "WINNT"
    ):
        die("Cannot enable PulseAudio on %s", target.os)
    return any("pulseaudio" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_sndio(values, target):
    if any("sndio" in value for value in values) and (
        target.os == "Android" or target.os == "OSX" or target.os == "WINNT"
    ):
        die("Cannot enable sndio on %s", target.os)
    return any("sndio" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_sunaudio(values, target):
    if any("sunaudio" in value for value in values) and (
        target.os != "NetBSD" and target.os != "SunOS"
    ):
        die("Cannot enable sunaudio on %s", target.os)
    return any("sunaudio" in value for value in values) or None


@depends("--enable-audio-backends", target)
def imply_wasapi(values, target):
    if any("wasapi" in value for value in values) and target.os != "WINNT":
        die("Cannot enable WASAPI on %s", target.os)
    return any("wasapi" in value for value in values) or None


set_config("MOZ_AAUDIO", imply_aaudio, when="--enable-audio-backends")

imply_option("--enable-alsa", imply_alsa, reason="--enable-audio-backends")

set_config("MOZ_AUDIOUNIT_RUST", imply_audiounit, when="--enable-audio-backends")

imply_option("--enable-jack", imply_jack, reason="--enable-audio-backends")

set_config("MOZ_OPENSL", imply_opensl, when="--enable-audio-backends")

set_config("MOZ_OSS", imply_oss, when="--enable-audio-backends")

imply_option("--enable-pulseaudio", imply_pulseaudio, reason="--enable-audio-backends")

imply_option("--enable-sndio", imply_sndio, reason="--enable-audio-backends")

set_config("MOZ_SUNAUDIO", imply_sunaudio, when="--enable-audio-backends")

set_config("MOZ_WASAPI", imply_wasapi, when="--enable-audio-backends")

# ALSA cubeb backend
# ==============================================================
option("--enable-alsa", env="MOZ_ALSA", help="Enable ALSA audio backend.")


@depends("--enable-alsa", midir_linux_support)
def enable_alsa_or_midir_linux_support(alsa_enabled, midir_linux_support):
    return alsa_enabled or midir_linux_support


pkg_check_modules("MOZ_ALSA", "alsa", when=enable_alsa_or_midir_linux_support)

set_config("MOZ_ALSA", True, when="--enable-alsa")
set_define("MOZ_ALSA", True, when="--enable-alsa")

# JACK cubeb backend
# ==============================================================
system_lib_option("--enable-jack", env="MOZ_JACK", help="Enable JACK audio backend.")

jack = pkg_check_modules("MOZ_JACK", "jack", when="--enable-jack")

set_config("MOZ_JACK", depends_if(jack)(lambda _: True))

# PulseAudio cubeb backend
# ==============================================================
option(
    "--enable-pulseaudio",
    env="MOZ_PULSEAUDIO",
    help="{Enable|Disable} PulseAudio audio backend.",
)

pulseaudio = pkg_check_modules("MOZ_PULSEAUDIO", "libpulse", when="--enable-pulseaudio")

set_config("MOZ_PULSEAUDIO", depends_if(pulseaudio)(lambda _: True))
set_define("MOZ_PULSEAUDIO", depends_if(pulseaudio)(lambda _: True))

# sndio cubeb backend
# ==============================================================
system_lib_option("--enable-sndio", env="MOZ_SNDIO", help="Enable sndio audio backend.")

sndio = pkg_check_modules("MOZ_SNDIO", "sndio", when="--enable-sndio")

set_config("MOZ_SNDIO", depends_if(sndio)(lambda _: True))

# Javascript engine
# ==============================================================
include("../js/moz.configure")


# NodeJS
# ==============================================================
include("../build/moz.configure/node.configure")

# JsonCpp
# ==============================================================
set_define("JSON_USE_EXCEPTION", 0)

# L10N
# ==============================================================
option("--with-l10n-base", nargs=1, env="L10NBASEDIR", help="Path to l10n repositories")


@depends("--with-l10n-base", "MOZ_AUTOMATION", build_environment)
@imports(_from="os.path", _import="isdir")
@imports(_from="os.path", _import="expanduser")
@imports(_from="os", _import="environ")
def l10n_base(value, automation, build_env):
    if value:
        path = value[0]
        if not isdir(path):
            die("Invalid value --with-l10n-base, %s doesn't exist", path)
    elif automation:
        path = os.path.join(build_env.topsrcdir, "../l10n-central")
    else:
        path = os.path.join(
            environ.get(
                "MOZBUILD_STATE_PATH", expanduser(os.path.join("~", ".mozbuild"))
            ),
            "l10n-central",
        )
    return os.path.realpath(os.path.abspath(path))


set_config("L10NBASEDIR", l10n_base)


# Default toolkit
# ==============================================================
@depends(target)
def toolkit_choices(target):
    if target.os == "WINNT":
        return ("cairo-windows",)
    elif target.os == "OSX":
        return ("cairo-cocoa",)
    elif target.os == "Android":
        return ("cairo-android",)
    else:
        return (
            "cairo-gtk3",
            "cairo-gtk3-wayland",
            "cairo-gtk3-wayland-only",
            "cairo-gtk3-x11-wayland",
        )


@depends(toolkit_choices)
def toolkit_default(choices):
    return choices[0]


option(
    "--enable-default-toolkit",
    nargs=1,
    choices=toolkit_choices,
    default=toolkit_default,
    help="Select default toolkit",
)


@depends("--enable-default-toolkit")
def full_toolkit(value):
    if value:
        return value[0]


@depends(full_toolkit)
def toolkit(toolkit):
    if toolkit.startswith("cairo-gtk3"):
        widget_toolkit = "gtk"
    else:
        widget_toolkit = toolkit.replace("cairo-", "")
    return widget_toolkit


set_config("MOZ_WIDGET_TOOLKIT", toolkit)
add_old_configure_assignment("MOZ_WIDGET_TOOLKIT", toolkit)


@depends(toolkit)
def toolkit_define(toolkit):
    if toolkit != "windows":
        return "MOZ_WIDGET_%s" % toolkit.upper()


set_define(toolkit_define, True)


@depends(toolkit)
def toolkit_gtk(toolkit):
    return toolkit == "gtk"


# Wayland support
# ==============================================================
wayland_headers = pkg_check_modules(
    "MOZ_WAYLAND",
    "gtk+-wayland-3.0 >= 3.14 xkbcommon >= 0.4.1",
    allow_missing=depends(full_toolkit)(lambda t: t == "cairo-gtk3"),
    when=toolkit_gtk,
)


@depends(wayland_headers, toolkit_gtk, artifact_builds)
def wayland_headers(wayland, toolkit_gtk, artifacts):
    if toolkit_gtk and artifacts:
        return True
    return wayland


set_config("MOZ_WAYLAND", depends_if(wayland_headers)(lambda _: True))
set_define("MOZ_WAYLAND", depends_if(wayland_headers)(lambda _: True))

# Hardware-accelerated video decode with VAAPI and V4L2
# ==============================================================


@depends(target, wayland_headers)
def vaapi(target, wayland_headers):
    # VAAPI is only used in Wayland. It's mostly used on x86(-64) but is
    # sometimes used on ARM/ARM64 SOCs. Wayland implies GTK and Linux.
    if target.cpu in ("arm", "aarch64", "x86", "x86_64") and wayland_headers:
        return True


@depends(target, wayland_headers)
def v4l2(target, wayland_headers):
    # V4L2 decode is only used in Wayland, and generally only appears on
    # embedded SOCs. Wayland implies GTK and Linux.
    if target.cpu in ("arm", "aarch64", "riscv64") and wayland_headers:
        return True


set_config("MOZ_ENABLE_VAAPI", True, when=vaapi)
set_config("MOZ_ENABLE_V4L2", True, when=v4l2)
set_define("MOZ_ENABLE_VAAPI", True, when=vaapi)
set_define("MOZ_ENABLE_V4L2", True, when=v4l2)

# GL Provider
# ==============================================================
option("--with-gl-provider", nargs=1, help="Set GL provider backend type")


@depends("--with-gl-provider")
def gl_provider(value):
    if value:
        return value[0]


@depends(gl_provider)
def gl_provider_define(provider):
    if provider:
        return "GLContextProvider%s" % provider


set_define("MOZ_GL_PROVIDER", gl_provider_define)


@depends(gl_provider, toolkit_gtk)
def gl_default_provider(value, toolkit_gtk):
    if value:
        return value
    elif toolkit_gtk:
        return "EGL"


set_config("MOZ_GL_PROVIDER", gl_provider)
set_config("MOZ_GL_DEFAULT_PROVIDER", gl_default_provider)


@depends(gl_default_provider)
def gl_provider_define(provider):
    if provider:
        return "GL_PROVIDER_%s" % provider


set_define(gl_provider_define, True)


# PDF printing
# ==============================================================
@depends(toolkit)
def pdf_printing(toolkit):
    if toolkit in ("windows", "gtk", "android"):
        return True


set_config("MOZ_PDF_PRINTING", pdf_printing)
set_define("MOZ_PDF_PRINTING", pdf_printing)


# Event loop instrumentation
# ==============================================================
option(env="MOZ_INSTRUMENT_EVENT_LOOP", help="Force-enable event loop instrumentation")


@depends("MOZ_INSTRUMENT_EVENT_LOOP", toolkit)
def instrument_event_loop(value, toolkit):
    if value or (
        toolkit in ("windows", "gtk", "cocoa", "android") and value.origin == "default"
    ):
        return True


set_config("MOZ_INSTRUMENT_EVENT_LOOP", instrument_event_loop)
set_define("MOZ_INSTRUMENT_EVENT_LOOP", instrument_event_loop)


# Fontconfig Freetype
# ==============================================================
option(env="USE_FC_FREETYPE", help="Force-enable the use of fontconfig freetype")


@depends("USE_FC_FREETYPE", toolkit)
def fc_freetype(value, toolkit):
    if value or (toolkit == "gtk" and value.origin == "default"):
        return True


set_define("USE_FC_FREETYPE", fc_freetype)

# Pango
# ==============================================================
pkg_check_modules("MOZ_PANGO", "pango >= 1.22.0", when=toolkit_gtk)

# Fontconfig
# ==============================================================
fontconfig_info = pkg_check_modules(
    "_FONTCONFIG", "fontconfig >= 2.7.0", when=fc_freetype
)


@depends(fc_freetype)
def check_for_freetype2(fc_freetype):
    if fc_freetype:
        return True


# Check for freetype2. Flags are combined with fontconfig flags.
freetype2_info = pkg_check_modules(
    "_FT2", "freetype2 >= 9.10.3", when=check_for_freetype2
)


@depends(fontconfig_info, freetype2_info)
def freetype2_combined_info(fontconfig_info, freetype2_info):
    if not freetype2_info:
        return
    if not fontconfig_info:
        return freetype2_info
    return namespace(
        cflags=freetype2_info.cflags + fontconfig_info.cflags,
        libs=freetype2_info.libs + fontconfig_info.libs,
    )


set_define("MOZ_HAVE_FREETYPE2", depends_if(freetype2_info)(lambda _: True))

# Apple platform decoder support
# ==============================================================
@depends(toolkit)
def applemedia(toolkit):
    if toolkit in ("cocoa", "uikit"):
        return True


set_config("MOZ_APPLEMEDIA", applemedia)
set_define("MOZ_APPLEMEDIA", applemedia)

# Windows Media Foundation support
# ==============================================================
option("--disable-wmf", help="Disable support for Windows Media Foundation")


@depends("--disable-wmf", target, "--help")
def wmf(value, target, _):
    enabled = bool(value)
    if value.origin == "default":
        # Enable Windows Media Foundation support by default.
        # Note our minimum SDK version is Windows 7 SDK, so we are (currently)
        # guaranteed to have a recent-enough SDK to build WMF.
        enabled = target.os == "WINNT"
    if enabled and target.os != "WINNT":
        die("Cannot enable Windows Media Foundation support on %s", target.os)
    if enabled:
        return True


@depends(c_compiler, when=wmf)
def wmfmediaengine(c_compiler):
    return c_compiler and c_compiler.type == "clang-cl"


set_config("MOZ_WMF", wmf)
set_define("MOZ_WMF", wmf)

set_config("MOZ_WMF_MEDIA_ENGINE", True, when=wmfmediaengine)
set_define("MOZ_WMF_MEDIA_ENGINE", True, when=wmfmediaengine)

# FFmpeg H264/AAC Decoding Support
# ==============================================================
option("--disable-ffmpeg", help="Disable FFmpeg for fragmented H264/AAC decoding")


@depends("--disable-ffmpeg", target)
def ffmpeg(value, target):
    enabled = bool(value)
    if value.origin == "default":
        enabled = target.os not in ("Android", "WINNT")
    if enabled:
        return True


set_config("MOZ_FFMPEG", ffmpeg)
set_define("MOZ_FFMPEG", ffmpeg)
imply_option("--enable-fmp4", ffmpeg, "--enable-ffmpeg")

# AV1 Video Codec Support
# ==============================================================
option("--disable-av1", help="Disable av1 video support")


@depends("--enable-av1")
def av1(value):
    if value:
        return True


@depends(target, when=av1 & compile_environment)
def dav1d_asm(target):
    if target.cpu in ("aarch64", "x86", "x86_64"):
        return True


@depends(target, when=av1 & compile_environment)
def dav1d_nasm(target):
    if target.cpu in ("x86", "x86_64"):
        return namespace(version="2.14", what="AV1")


set_config("MOZ_DAV1D_ASM", dav1d_asm)
set_define("MOZ_DAV1D_ASM", dav1d_asm)
set_config("MOZ_AV1", av1)
set_define("MOZ_AV1", av1)

# JXL Image Codec Support
# ==============================================================
option("--disable-jxl", help="Disable jxl image support")


@depends("--disable-jxl", milestone.is_nightly)
def jxl(value, is_nightly):
    if is_nightly and value:
        return True


set_config("MOZ_JXL", jxl)
set_define("MOZ_JXL", jxl)

# Built-in fragmented MP4 support.
# ==============================================================
option(
    "--disable-fmp4",
    env="MOZ_FMP4",
    help="Disable support for in built Fragmented MP4 parsing",
)


@depends("--disable-fmp4", target, wmf, applemedia)
def fmp4(value, target, wmf, applemedia):
    enabled = bool(value)
    if value.origin == "default":
        # target.os == 'Android' includes all B2G versions
        enabled = wmf or applemedia or target.os == "Android"
    if enabled:
        return True


set_config("MOZ_FMP4", fmp4)
set_define("MOZ_FMP4", fmp4)


@depends(target)
def sample_type_is_s16(target):
    # Use integers over floats for audio on Android regardless of the CPU
    # architecture, because audio backends for Android don't support floats.
    # We also use integers on ARM because it's more efficient.
    if target.os == "Android" or target.cpu == "arm":
        return True


@depends(sample_type_is_s16)
def sample_type_is_float(t):
    if not t:
        return True


set_config("MOZ_SAMPLE_TYPE_S16", sample_type_is_s16)
set_define("MOZ_SAMPLE_TYPE_S16", sample_type_is_s16)
set_config("MOZ_SAMPLE_TYPE_FLOAT32", sample_type_is_float)
set_define("MOZ_SAMPLE_TYPE_FLOAT32", sample_type_is_float)

set_define("MOZ_VORBIS", sample_type_is_float)
set_config("MOZ_VORBIS", sample_type_is_float)
set_define("MOZ_TREMOR", sample_type_is_s16)
set_config("MOZ_TREMOR", sample_type_is_s16)

option(
    "--disable-real-time-tracing",
    help="Disable tracing of real-time audio callbacks",
)

set_config("MOZ_REAL_TIME_TRACING", True, when="--enable-real-time-tracing")
set_define("MOZ_REAL_TIME_TRACING", True, when="--enable-real-time-tracing")

# OpenMAX IL Decoding Support
# ==============================================================
option("--enable-openmax", help="Enable OpenMAX IL for video/audio decoding")


@depends("--enable-openmax")
def openmax(value):
    enabled = bool(value)
    if enabled:
        return True


set_config("MOZ_OMX", openmax)
set_define("MOZ_OMX", openmax)

# EME Support
# ==============================================================
@depends(target, wmf)
def eme_choices(target, wmf):
    if (
        target.kernel in ("WINNT", "Linux")
        and target.os != "Android"
        and target.cpu in ("x86", "x86_64")
    ):
        if wmf:
            return ("widevine", "wmfcdm")
        return ("widevine",)
    if target.kernel == "WINNT" and target.cpu == "aarch64":
        return ("widevine",)
    if target.os in ("OSX"):
        return ("widevine",)


# Widevine is enabled by default in desktop browser builds, except
# on aarch64 Windows.
@depends(build_project, eme_choices, target)
def eme_default(build_project, choices, target):
    if build_project == "browser":
        if target.kernel != "WINNT" or target.cpu != "aarch64":
            return choices


option(
    "--enable-eme",
    nargs="+",
    choices=eme_choices,
    default=eme_default,
    when=eme_choices,
    help="{Enable|Disable} support for Encrypted Media Extensions",
)


@depends("--enable-eme", fmp4, when=eme_choices)
def eme(enabled, fmp4):
    if enabled and enabled.origin != "default" and not fmp4:
        die("Encrypted Media Extension support requires " "Fragmented MP4 support")


@depends("--enable-eme", when=eme_choices)
def eme_modules(value):
    return value


# Fallback to an empty list when eme_choices is empty, setting eme_modules to
# None.
set_config("MOZ_EME_MODULES", eme_modules | dependable([]))


@depends(eme_modules, target, when=eme_modules)
def eme_win32_artifact(modules, target):
    if "widevine" in modules and target.kernel == "WINNT" and target.cpu == "aarch64":
        return True


set_config("MOZ_EME_WIN32_ARTIFACT", eme_win32_artifact)


# Media Foundation CDM support
# ==============================================================
@depends(eme_modules, when=wmfmediaengine)
def wmfcdm(modules):
    if "wmfcdm" in modules:
        return True


set_config("MOZ_WMF_CDM", True, when=wmfcdm)
set_define("MOZ_WMF_CDM", True, when=wmfcdm)


option(
    name="--enable-chrome-format",
    help="Select FORMAT of chrome files during packaging.",
    nargs=1,
    choices=("omni", "jar", "flat"),
    default="omni",
)


@depends("--enable-chrome-format")
def packager_format(value):
    return value[0]


set_config("MOZ_PACKAGER_FORMAT", packager_format)

# The packager minifies two different types of files: non-JS (mostly property
# files for l10n), and JS.  Setting MOZ_PACKAGER_MINIFY only minifies the
# former.  Firefox doesn't yet minify JS, due to concerns about debuggability.
#
# Also, the JS minification setup really only works correctly on Android:
# we need extra setup to use the newly-built shell for Linux and Windows,
# and cross-compilation for macOS requires some extra care.


@depends(target_is_android, "--enable-debug", milestone.is_nightly)
def enable_minify_default(is_android, debug, is_nightly):
    if is_android and not debug and not is_nightly:
        return ("properties", "js")
    return ("properties",)


option(
    name="--enable-minify",
    help="Select types of files to minify during packaging.",
    nargs="*",
    choices=("properties", "js"),
    default=enable_minify_default,
)


@depends("--enable-minify")
def enable_minify(value):
    if "js" in value and "properties" not in value:
        die("--enable-minify=js requires --enable-minify=properties.")
    return namespace(
        properties="properties" in value,
        js="js" in value,
    )


set_config("MOZ_PACKAGER_MINIFY", True, when=enable_minify.properties)
set_config("MOZ_PACKAGER_MINIFY_JS", True, when=enable_minify.js)


@depends(host, build_project)
def jar_maker_format(host, build_project):
    # Multilocales for mobile/android use the same mergedirs for all locales,
    # so we can't use symlinks for those builds.
    if host.os == "WINNT" or build_project == "mobile/android":
        return "flat"
    return "symlink"


set_config("MOZ_JAR_MAKER_FILE_FORMAT", jar_maker_format)


@depends(toolkit)
def omnijar_name(toolkit):
    # Fennec's static resources live in the assets/ folder of the
    # APK.  Adding a path to the name here works because we only
    # have one omnijar file in the final package (which is not the
    # case on desktop).
    return "assets/omni.ja" if toolkit == "android" else "omni.ja"


set_config("OMNIJAR_NAME", omnijar_name)

project_flag("MOZ_PLACES", help="Build Places if required", set_as_define=True)

project_flag(
    "MOZ_SERVICES_HEALTHREPORT",
    help="Build Firefox Health Reporter Service",
    set_as_define=True,
)

project_flag(
    "MOZ_NORMANDY",
    help="Enable Normandy recipe runner",
    set_as_define=True,
)

project_flag("MOZ_SERVICES_SYNC", help="Build Sync Services if required")

project_flag(
    "MOZ_ANDROID_HISTORY",
    help="Enable Android History instead of Places",
    set_as_define=True,
)

project_flag(
    "MOZ_DEDICATED_PROFILES",
    help="Enable dedicated profiles per install",
    set_as_define=True,
)

project_flag(
    "MOZ_BLOCK_PROFILE_DOWNGRADE",
    help="Block users from starting profiles last used by a newer build",
    set_as_define=True,
)


@depends("MOZ_PLACES", "MOZ_ANDROID_HISTORY")
def check_places_and_android_history(places, android_history):
    if places and android_history:
        die("Cannot use MOZ_ANDROID_HISTORY alongside MOZ_PLACES.")


option(
    env="MOZ_TELEMETRY_REPORTING",
    default=mozilla_official,
    help="Enable telemetry reporting",
)

set_define("MOZ_TELEMETRY_REPORTING", True, when="MOZ_TELEMETRY_REPORTING")


@depends("MOZ_TELEMETRY_REPORTING", milestone.is_nightly)
def telemetry_on_by_default(reporting, is_nightly):
    return reporting and is_nightly


set_define("MOZ_TELEMETRY_ON_BY_DEFAULT", True, when=telemetry_on_by_default)


# gpsd support
# ==============================================================
system_lib_option("--enable-gpsd", env="MOZ_GPSD", help="Enable gpsd support")


@depends("--enable-gpsd")
def gpsd(value):
    return bool(value)


system_gpsd = pkg_check_modules("MOZ_GPSD", "libgps >= 3.11", when=gpsd)

set_config("MOZ_GPSD", depends_if(system_gpsd)(lambda _: True))

# Miscellaneous programs
# ==============================================================

check_prog("TAR", ("gnutar", "gtar", "tar"))
check_prog("UNZIP", ("unzip",))

# Key files
# ==============================================================
include("../build/moz.configure/keyfiles.configure")

simple_keyfile("Mozilla API")

simple_keyfile("Google Location Service API")

simple_keyfile("Google Safebrowsing API")

id_and_secret_keyfile("Bing API")

simple_keyfile("Adjust SDK")

id_and_secret_keyfile("Leanplum SDK")

simple_keyfile("Pocket API")


# WebRender Debugger integration
# ==============================================================

option(
    "--enable-webrender-debugger", help="Build the websocket debug server in WebRender"
)

set_config(
    "MOZ_WEBRENDER_DEBUGGER", depends_if("--enable-webrender-debugger")(lambda _: True)
)

# Additional system headers defined at the application level
# ==============================================================

option(
    "--enable-app-system-headers",
    env="MOZ_APP_SYSTEM_HEADERS",
    help="Use additional system headers defined in $MOZ_BUILD_APP/app-system-headers.mozbuild",
)


@depends("--enable-app-system-headers")
def app_system_headers(value):
    if value:
        return True


set_config("MOZ_APP_SYSTEM_HEADERS", app_system_headers)
set_define("MOZ_APP_SYSTEM_HEADERS", app_system_headers)

# Printing
# ==============================================================
option("--disable-printing", help="Disable printing support")


@depends("--disable-printing")
def printing(value):
    if value:
        return True


set_config("NS_PRINTING", printing)
set_define("NS_PRINTING", printing)
set_define("NS_PRINT_PREVIEW", printing)

# Speech-dispatcher support
# ==============================================================
@depends(toolkit)
def no_speechd_on_non_gtk(toolkit):
    if toolkit != "gtk":
        return False


imply_option(
    "--enable-synth-speechd", no_speechd_on_non_gtk, reason="--enable-default-toolkit"
)

option("--disable-synth-speechd", help="Disable speech-dispatcher support")

set_config("MOZ_SYNTH_SPEECHD", depends_if("--disable-synth-speechd")(lambda _: True))

# Speech API
# ==============================================================
option("--disable-webspeech", help="Disable support for HTML Speech API")


@depends("--disable-webspeech")
def webspeech(value):
    if value:
        return True


set_config("MOZ_WEBSPEECH", webspeech)
set_define("MOZ_WEBSPEECH", webspeech)

# Speech API test backend
# ==============================================================
option(
    "--enable-webspeechtestbackend",
    default=webspeech,
    help="{Enable|Disable} support for HTML Speech API Test Backend",
)


@depends_if("--enable-webspeechtestbackend")
def webspeech_test_backend(value):
    return True


set_config("MOZ_WEBSPEECH_TEST_BACKEND", webspeech_test_backend)
set_define("MOZ_WEBSPEECH_TEST_BACKEND", webspeech_test_backend)

# Graphics
# ==============================================================
@depends(target, milestone)
def skia_pdf_default(target, milestone):
    return milestone.is_nightly and target.os != "WINNT"


option("--enable-skia-pdf", default=skia_pdf_default, help="{Enable|Disable} Skia PDF")

set_config("MOZ_ENABLE_SKIA_PDF", True, when="--enable-skia-pdf")
set_define("MOZ_ENABLE_SKIA_PDF", True, when="--enable-skia-pdf")

set_config(
    "SKIA_INCLUDES",
    [
        "/gfx/skia",
        "/gfx/skia/skia",
    ],
)

system_lib_option(
    "--with-system-webp", help="Use system libwebp (located with pkgconfig)"
)

system_webp = pkg_check_modules(
    "MOZ_WEBP", "libwebp >= 1.0.2 libwebpdemux >= 1.0.2", when="--with-system-webp"
)

set_config("MOZ_SYSTEM_WEBP", depends(when=system_webp)(lambda: True))

# Build Freetype in the tree
# ==============================================================
@depends(target, "--enable-skia-pdf")
def tree_freetype(target, skia_pdf):
    if target.os == "Android" or (skia_pdf and target.os == "WINNT"):
        return True


set_define("MOZ_TREE_FREETYPE", tree_freetype)
set_config("MOZ_TREE_FREETYPE", tree_freetype)

set_define("HAVE_FT_BITMAP_SIZE_Y_PPEM", tree_freetype)
set_define("HAVE_FT_GLYPHSLOT_EMBOLDEN", tree_freetype)
set_define("HAVE_FT_LOAD_SFNT_TABLE", tree_freetype)


@depends(freetype2_combined_info, tree_freetype, build_environment)
def ft2_info(freetype2_combined_info, tree_freetype, build_env):
    if tree_freetype:
        return namespace(
            cflags=("-I%s/modules/freetype2/include" % build_env.topsrcdir,), libs=()
        )
    if freetype2_combined_info:
        return freetype2_combined_info


set_config("FT2_LIBS", ft2_info.libs)


@depends(target, tree_freetype, freetype2_info)
def enable_cairo_ft(target, tree_freetype, freetype2_info):
    # Avoid defining MOZ_ENABLE_CAIRO_FT on Windows platforms because
    # "cairo-ft-font.c" includes <dlfcn.h>, which only exists on posix platforms
    return freetype2_info or (tree_freetype and target.os != "WINNT")


set_config("MOZ_ENABLE_CAIRO_FT", True, when=enable_cairo_ft)
set_config("CAIRO_FT_CFLAGS", ft2_info.cflags, when=enable_cairo_ft)


# WebDriver (HTTP / BiDi)
# ==============================================================
#
# WebDriver is a remote control interface that enables introspection and
# control of user agents. It provides a platform- and language-neutral wire
# protocol as a way for out-of-process programs to remotely instruct the
# behavior of web browsers.
#
# The Gecko implementation is backed by Marionette and Remote Agent.
# Both protocols are not really toolkit features, as much as Gecko engine
# features. But they are enabled based on the toolkit, so here it lives.
#
# Marionette remote protocol
# -----------------------------------------------------------
#
# Marionette is the Gecko remote protocol used for various remote control,
# automation, and testing purposes throughout Gecko-based applications like
# Firefox, Thunderbird, and any mobile browser built upon GeckoView.
#
# It also backs ../testing/geckodriver, which is Mozilla's WebDriver
# implementation.
#
# The source of Marionette lives in ../remote/marionette.
#
# For more information, see:
# https://firefox-source-docs.mozilla.org/testing/marionette/index.html
#
# Remote Agent (WebDriver BiDi / partial CDP)
# -----------------------------------------------------------
#
# The primary purpose is the implementation of the WebDriver BiDi specification.
# But it also complements the existing Firefox Developer Tools Remote Debugging
# Protocol (RDP) by implementing a subset of the Chrome DevTools Protocol (CDP).
#
# The source of Remote Agent lives in ../remote.
#
# For more information, see:
# https://firefox-source-docs.mozilla.org/remote/index.html


option(
    "--disable-webdriver",
    help="Disable support for WebDriver remote protocols",
)


@depends("--disable-webdriver")
def webdriver(enabled):
    if enabled:
        return True


set_config("ENABLE_WEBDRIVER", webdriver)
set_define("ENABLE_WEBDRIVER", webdriver)


# geckodriver WebDriver implementation
# ==============================================================
#
# Turn off geckodriver for build configs we don't handle yet,
# but allow --enable-geckodriver to override when compile environment is available.
# --disable-tests implies disabling geckodriver.
# Disable building in CI


@depends(
    "--enable-tests", target, cross_compiling, hazard_analysis, asan, "MOZ_AUTOMATION"
)
def geckodriver_default(enable_tests, target, cross_compile, hazard, asan, automation):
    if not enable_tests:
        return False
    if hazard or target.os == "Android" or (asan and cross_compile):
        return False
    if automation:
        return False
    return True


option(
    "--enable-geckodriver",
    default=geckodriver_default,
    when="--enable-compile-environment",
    help="{Build|Do not build} geckodriver",
)


@depends("--enable-geckodriver", when="--enable-compile-environment")
def geckodriver(enabled):
    if enabled:
        return True


set_config("MOZ_GECKODRIVER", geckodriver)


# WebRTC
# ========================================================
@depends(target)
def webrtc_default(target):
    # Turn off webrtc for OS's we don't handle yet, but allow
    # --enable-webrtc to override.
    os_match = target.kernel in (
        "Linux",
        "WINNT",
        "Darwin",
        "DragonFly",
        "FreeBSD",
        "kFreeBSD",
        "NetBSD",
        "OpenBSD",
    )

    cpu_match = target.cpu in (
        "x86_64",
        "arm",
        "aarch64",
        "x86",
        "ia64",
        "mips32",
        "mips64",
        "ppc",
        "ppc64",
        "riscv64",
    )

    return os_match and cpu_match and target.endianness == "little"


option(
    "--disable-webrtc",
    default=webrtc_default,
    help="{Enable|Disable} support for WebRTC",
)


@depends("--disable-webrtc")
def webrtc(enabled):
    if enabled:
        return True


set_config("MOZ_WEBRTC", webrtc)
set_define("MOZ_WEBRTC", webrtc)
set_config("MOZ_SCTP", webrtc)
set_define("MOZ_SCTP", webrtc)
set_config("MOZ_SRTP", webrtc)
set_define("MOZ_SRTP", webrtc)
set_config("MOZ_WEBRTC_SIGNALING", webrtc)
set_define("MOZ_WEBRTC_SIGNALING", webrtc)
set_config("MOZ_PEERCONNECTION", webrtc)
set_define("MOZ_PEERCONNECTION", webrtc)
# MOZ_WEBRTC_ASSERT_ALWAYS turns on a number of safety asserts in
# opt/production builds (via MOZ_CRASH())
set_config("MOZ_WEBRTC_ASSERT_ALWAYS", webrtc)
set_define("MOZ_WEBRTC_ASSERT_ALWAYS", webrtc)

# RAW media
# ==============================================================


@depends(target, webrtc)
def raw_media_default(target, webrtc):
    if target.os == "Android":
        return True
    if webrtc:
        return True


option(
    "--enable-raw",
    default=raw_media_default,
    help="{Enable|Disable} support for RAW media",
)

set_config("MOZ_RAW", depends_if("--enable-raw")(lambda _: True))
set_define("MOZ_RAW", depends_if("--enable-raw")(lambda _: True))


# X11
# ==============================================================
@depends(webrtc, when=toolkit_gtk)
def x11_libs(webrtc):
    libs = [
        "x11",
        "xcb",
        "xcb-shm",
        "x11-xcb",
        "xext",
        "xrandr >= 1.4.0",
    ]
    if webrtc:
        # third_party/libwebrtc/webrtc/webrtc_gn/moz.build adds those
        # manually, ensure they're available.
        libs += [
            "xcomposite",
            "xcursor",
            "xdamage",
            "xfixes",
            "xi",
            "xtst",
        ]
    return libs


x11_headers = pkg_check_modules(
    "MOZ_X11",
    x11_libs,
    allow_missing=depends(full_toolkit)(lambda t: t == "cairo-gtk3-wayland"),
    when=depends(full_toolkit)(
        lambda t: t in ("cairo-gtk3", "cairo-gtk3-wayland", "cairo-gtk3-x11-wayland")
    ),
)


set_config("MOZ_X11", True, when=x11_headers)
set_define("MOZ_X11", True, when=x11_headers)

pkg_check_modules(
    "MOZ_X11_SM",
    ["ice", "sm"],
    cflags_only=True,
    allow_missing=depends(full_toolkit)(lambda t: t == "cairo-gtk3-wayland"),
    when=depends(full_toolkit)(
        lambda t: t in ("cairo-gtk3", "cairo-gtk3-wayland", "cairo-gtk3-x11-wayland")
    ),
)


# ASan Reporter Addon
# ==============================================================
option(
    "--enable-address-sanitizer-reporter",
    help="Enable Address Sanitizer Reporter Extension",
)


@depends("--enable-address-sanitizer-reporter")
def enable_asan_reporter(value):
    if value:
        return True


set_config("MOZ_ASAN_REPORTER", enable_asan_reporter)
set_define("MOZ_ASAN_REPORTER", enable_asan_reporter)

# Elfhack
# ==============================================================
with only_when("--enable-compile-environment"):

    @depends(host, target)
    def has_elfhack(host, target):
        return (
            target.kernel == "Linux"
            and host.kernel == "Linux"
            and target.cpu in ("arm", "aarch64", "x86", "x86_64")
        )

    @depends("--enable-release", enable_linker)
    def default_elfhack(release, linker):
        # Disable elfhack when explicitly building with --enable-linker=lld
        if linker and linker.origin != "default" and linker[0] in ("lld", "mold"):
            return False
        return bool(release)

    with only_when(has_elfhack):
        option(
            "--disable-elf-hack",
            default=default_elfhack,
            help="{Enable|Disable} elf hacks",
        )

        @depends(select_linker, when="--enable-elf-hack")
        def use_elf_hack(linker):
            if linker and linker.KIND == "lld":
                die(
                    "Cannot enable elfhack with lld."
                    " Use --enable-linker=bfd, --enable-linker=gold, or --disable-elf-hack"
                )
            return True

        set_config("USE_ELF_HACK", use_elf_hack)


@depends(build_environment)
def idl_roots(build_env):
    return namespace(
        ipdl_root=os.path.join(build_env.topobjdir, "ipc", "ipdl"),
        webidl_root=os.path.join(build_env.topobjdir, "dom", "bindings"),
        xpcom_root=os.path.join(build_env.topobjdir, "xpcom", "components"),
    )


set_config("WEBIDL_ROOT", idl_roots.webidl_root)
set_config("IPDL_ROOT", idl_roots.ipdl_root)
set_config("XPCOM_ROOT", idl_roots.xpcom_root)

# Proxy bypass protection
# ==============================================================

option(
    "--enable-proxy-bypass-protection",
    help="Prevent suspected or confirmed proxy bypasses",
)


@depends_if("--enable-proxy-bypass-protection")
def proxy_bypass_protection(_):
    return True


set_config("MOZ_PROXY_BYPASS_PROTECTION", proxy_bypass_protection)
set_define("MOZ_PROXY_BYPASS_PROTECTION", proxy_bypass_protection)

# Proxy direct failover
# ==============================================================

option(
    "--disable-proxy-direct-failover",
    help="Disable direct failover for system requests",
)


@depends_if("--disable-proxy-direct-failover")
def proxy_direct_failover(value):
    if value:
        return True


set_config("MOZ_PROXY_DIRECT_FAILOVER", proxy_direct_failover)
set_define("MOZ_PROXY_DIRECT_FAILOVER", proxy_direct_failover)

# MIDL
# ==============================================================


@depends(c_compiler, toolchain_prefix)
def midl_names(c_compiler, toolchain_prefix):
    if c_compiler and c_compiler.type in ["gcc", "clang"]:
        # mingw
        widl = ("widl",)
        if toolchain_prefix:
            prefixed = tuple("%s%s" % (p, "widl") for p in toolchain_prefix)
            widl = prefixed + widl
        return widl

    return ("midl.exe",)


@depends(target, "--enable-compile-environment")
def check_for_midl(target, compile_environment):
    if target.os != "WINNT":
        return

    if compile_environment:
        return True


midl = check_prog(
    "MIDL",
    midl_names,
    when=check_for_midl,
    allow_missing=True,
    paths=sdk_bin_path,
    # MIDL being used from a python wrapper script, we can live with it
    # having spaces.
    allow_spaces=True,
)

option(env="MIDL_FLAGS", nargs=1, help="Extra flags to pass to MIDL")


@depends(
    "MIDL_FLAGS",
    target,
    midl,
    when=depends(midl, target)(lambda m, t: m and t.kernel == "WINNT"),
)
def midl_flags(flags, target, midl):
    if flags:
        flags = flags[0].split()
    else:
        flags = []

    if not midl.endswith("widl"):
        env = {
            "x86": "win32",
            "x86_64": "x64",
            "aarch64": "arm64",
        }[target.cpu]
        return flags + ["-nologo", "-no_cpp", "-env", env]

    # widl
    return flags + {
        "x86": ["--win32", "-m32"],
        "x86_64": ["--win64", "-m64"],
    }[target.cpu]


set_config("MIDL_FLAGS", midl_flags)

# Accessibility
# ==============================================================

option("--disable-accessibility", help="Disable accessibility support")


@depends("--enable-accessibility", check_for_midl, midl, c_compiler)
def accessibility(value, check_for_midl, midl, c_compiler):
    enabled = bool(value)

    if not enabled:
        return

    if check_for_midl and not midl:
        if c_compiler and c_compiler.type in ("gcc", "clang"):
            die(
                "You have accessibility enabled, but widl could not be found. "
                "Add --disable-accessibility to your mozconfig or install widl. "
                "See https://developer.mozilla.org/en-US/docs/Cross_Compile_Mozilla_for_Mingw32 for details."
            )
        else:
            die(
                "MIDL could not be found. "
                "Building accessibility without MIDL is not supported."
            )

    return enabled


set_config("ACCESSIBILITY", accessibility)
set_define("ACCESSIBILITY", accessibility)


@depends(moz_debug, developer_options)
def a11y_log(debug, developer_options):
    return debug or developer_options


set_config("A11Y_LOG", True, when=a11y_log)
set_define("A11Y_LOG", True, when=a11y_log)


# Addon signing
# ==============================================================
@depends(milestone)
def require_signing(milestone):
    return milestone.is_release_or_beta and not milestone.is_esr


option(
    env="MOZ_REQUIRE_SIGNING",
    default=require_signing,
    help="Enforce that add-ons are signed by the trusted root",
)

set_config("MOZ_REQUIRE_SIGNING", True, when="MOZ_REQUIRE_SIGNING")
set_define("MOZ_REQUIRE_SIGNING", True, when="MOZ_REQUIRE_SIGNING")

option(
    "--with-unsigned-addon-scopes",
    nargs="+",
    choices=("app", "system"),
    help="Addon scopes where signature is not required",
)


@depends("--with-unsigned-addon-scopes")
def unsigned_addon_scopes(scopes):
    return namespace(
        app="app" in scopes or None,
        system="system" in scopes or None,
    )


set_config("MOZ_UNSIGNED_APP_SCOPE", unsigned_addon_scopes.app)
set_config("MOZ_UNSIGNED_SYSTEM_SCOPE", unsigned_addon_scopes.system)


# Addon sideloading
# ==============================================================
option(
    "--allow-addon-sideload",
    default=milestone.is_esr,
    help="Addon sideloading is allowed",
)


set_config("MOZ_ALLOW_ADDON_SIDELOAD", True, when="--allow-addon-sideload")

# WebExtensions API WebIDL bindings
# ==============================================================


@depends(milestone)
def extensions_webidl_bindings_default(milestone):
    # Only enable the webidl bindings for the WebExtensions APIs
    # in Nightly.
    return milestone.is_nightly


option(
    "--enable-extensions-webidl-bindings",
    default=extensions_webidl_bindings_default,
    help="{Enable|Disable} building experimental WebExtensions WebIDL bindings",
)


@depends("--enable-extensions-webidl-bindings")
def extensions_webidl_enabled(value):
    return bool(value)


set_config("MOZ_WEBEXT_WEBIDL_ENABLED", extensions_webidl_enabled)

# Launcher process (Windows only)
# ==============================================================


@depends(target)
def launcher_process_default(target):
    return target.os == "WINNT"


option(
    "--enable-launcher-process",
    default=launcher_process_default,
    help="{Enable|Disable} launcher process by default",
)


@depends("--enable-launcher-process", target)
def launcher(value, target):
    enabled = bool(value)
    if enabled and target.os != "WINNT":
        die("Cannot enable launcher process on %s", target.os)
    if enabled:
        return True


set_config("MOZ_LAUNCHER_PROCESS", launcher)
set_define("MOZ_LAUNCHER_PROCESS", launcher)

# llvm-dlltool (Windows only)
# ==============================================================


@depends(build_project, target, "--enable-compile-environment")
def check_for_llvm_dlltool(build_project, target, compile_environment):
    if build_project != "browser":
        return

    if target.os != "WINNT":
        return

    return compile_environment


llvm_dlltool = check_prog(
    "LLVM_DLLTOOL",
    ("llvm-dlltool",),
    what="llvm-dlltool",
    when=check_for_llvm_dlltool,
    paths=clang_search_path,
)


@depends(target, when=llvm_dlltool)
def llvm_dlltool_flags(target):
    arch = {
        "x86": "i386",
        "x86_64": "i386:x86-64",
        "aarch64": "arm64",
    }[target.cpu]

    return ["-m", arch]


set_config("LLVM_DLLTOOL_FLAGS", llvm_dlltool_flags)

# BITS download (Windows only)
# ==============================================================

option(
    "--enable-bits-download",
    when=target_is_windows,
    default=target_is_windows,
    help="{Enable|Disable} building BITS download support",
)

set_define(
    "MOZ_BITS_DOWNLOAD",
    depends_if("--enable-bits-download", when=target_is_windows)(lambda _: True),
)
set_config(
    "MOZ_BITS_DOWNLOAD",
    depends_if("--enable-bits-download", when=target_is_windows)(lambda _: True),
)

# Bundled fonts on desktop platform
# ==============================================================


@depends(target)
def bundled_fonts_default(target):
    return target.os == "WINNT" or target.kernel == "Linux"


@depends(build_project)
def allow_bundled_fonts(project):
    return project == "browser" or project == "comm/mail"


option(
    "--enable-bundled-fonts",
    default=bundled_fonts_default,
    when=allow_bundled_fonts,
    help="{Enable|Disable} support for bundled fonts on desktop platforms",
)

set_define(
    "MOZ_BUNDLED_FONTS",
    depends_if("--enable-bundled-fonts", when=allow_bundled_fonts)(lambda _: True),
)

# Reflow counting
# ==============================================================


@depends(moz_debug)
def reflow_perf(debug):
    if debug:
        return True


option(
    "--enable-reflow-perf",
    default=reflow_perf,
    help="{Enable|Disable} reflow performance tracing",
)

# The difference in conditions here comes from the initial implementation
# in old-configure, which was unexplained there as well.
set_define("MOZ_REFLOW_PERF", depends_if("--enable-reflow-perf")(lambda _: True))
set_define("MOZ_REFLOW_PERF_DSP", reflow_perf)

# Layout debugger
# ==============================================================


@depends(moz_debug)
def layout_debugger(debug):
    if debug:
        return True


option(
    "--enable-layout-debugger",
    default=layout_debugger,
    help="{Enable|Disable} layout debugger",
)

set_config("MOZ_LAYOUT_DEBUGGER", True, when="--enable-layout-debugger")
set_define("MOZ_LAYOUT_DEBUGGER", True, when="--enable-layout-debugger")


# Shader Compiler for Windows (and MinGW Cross Compile)
# ==============================================================

with only_when(compile_environment):
    fxc = check_prog(
        "FXC",
        ("fxc.exe", "fxc2.exe"),
        when=depends(target)(lambda t: t.kernel == "WINNT"),
        paths=sdk_bin_path,
        # FXC being used from a python wrapper script, we can live with it
        # having spaces.
        allow_spaces=True,
    )


# VPX
# ===

with only_when(compile_environment):
    system_lib_option(
        "--with-system-libvpx", help="Use system libvpx (located with pkgconfig)"
    )

    with only_when("--with-system-libvpx"):
        vpx = pkg_check_modules("MOZ_LIBVPX", "vpx >= 1.10.0")

        check_header(
            "vpx/vpx_decoder.h",
            flags=vpx.cflags,
            onerror=lambda: die(
                "Couldn't find vpx/vpx_decoder.h, which is required to build "
                "with system libvpx. Use --without-system-libvpx to build "
                "with in-tree libvpx."
            ),
        )

        check_symbol(
            "vpx_codec_dec_init_ver",
            flags=vpx.libs,
            onerror=lambda: die(
                "--with-system-libvpx requested but symbol vpx_codec_dec_init_ver "
                "not found"
            ),
        )

        set_config("MOZ_SYSTEM_LIBVPX", True)

    @depends("--with-system-libvpx", target)
    def in_tree_vpx(system_libvpx, target):
        if system_libvpx:
            return

        arm_asm = (target.cpu == "arm") or None
        return namespace(arm_asm=arm_asm)

    @depends(target, when=in_tree_vpx)
    def vpx_nasm(target):
        if target.cpu in ("x86", "x86_64"):
            if target.kernel == "WINNT":
                # Version 2.03 is needed for automatic safeseh support.
                return namespace(version="2.03", what="VPX")
            return namespace(what="VPX")

    @depends(in_tree_vpx, vpx_nasm, target, neon_flags)
    def vpx_as_flags(vpx, vpx_nasm, target, neon_flags):
        if vpx and vpx.arm_asm:
            # These flags are a lie; they're just used to enable the requisite
            # opcodes; actual arch detection is done at runtime.
            return neon_flags
        elif vpx and vpx_nasm and target.os != "WINNT" and target.cpu != "x86_64":
            return ("-DPIC",)

    set_config("VPX_USE_NASM", True, when=vpx_nasm)
    set_config("VPX_ASFLAGS", vpx_as_flags)


# JPEG
# ====

with only_when(compile_environment):
    system_lib_option(
        "--with-system-jpeg",
        nargs="?",
        help="Use system libjpeg (installed at given prefix)",
    )

    @depends_if("--with-system-jpeg")
    def jpeg_flags(value):
        if len(value):
            return namespace(
                cflags=("-I%s/include" % value[0],),
                ldflags=("-L%s/lib" % value[0], "-ljpeg"),
            )
        return namespace(
            ldflags=("-ljpeg",),
        )

    with only_when("--with-system-jpeg"):
        check_symbol(
            "jpeg_destroy_compress",
            flags=jpeg_flags.ldflags,
            onerror=lambda: die(
                "--with-system-jpeg requested but symbol "
                "jpeg_destroy_compress not found."
            ),
        )

        c_compiler.try_compile(
            includes=[
                "stdio.h",
                "sys/types.h",
                "jpeglib.h",
            ],
            body="""
                #if JPEG_LIB_VERSION < 62
                #error Insufficient JPEG library version
                #endif
            """,
            flags=jpeg_flags.cflags,
            check_msg="for sufficient jpeg library version",
            onerror=lambda: die(
                "Insufficient JPEG library version for "
                "--with-system-jpeg (62 required)"
            ),
        )

        c_compiler.try_compile(
            includes=[
                "stdio.h",
                "sys/types.h",
                "jpeglib.h",
            ],
            body="""
                #ifndef JCS_EXTENSIONS
                #error libjpeg-turbo JCS_EXTENSIONS required
                #endif
            """,
            flags=jpeg_flags.cflags,
            check_msg="for sufficient libjpeg-turbo JCS_EXTENSIONS",
            onerror=lambda: die(
                "libjpeg-turbo JCS_EXTENSIONS required for " "--with-system-jpeg"
            ),
        )

        set_config("MOZ_JPEG_CFLAGS", jpeg_flags.cflags)
        set_config("MOZ_JPEG_LIBS", jpeg_flags.ldflags)

    @depends("--with-system-jpeg", target, neon_flags)
    def in_tree_jpeg_arm(system_jpeg, target, neon_flags):
        if system_jpeg:
            return

        if target.cpu == "arm":
            return neon_flags
        elif target.cpu == "aarch64":
            return ("-march=armv8-a",)

    @depends("--with-system-jpeg", target)
    def in_tree_jpeg_mips64(system_jpeg, target):
        if system_jpeg:
            return

        if target.cpu == "mips64":
            return ("-Wa,-mloongson-mmi", "-mloongson-ext")

    # Compiler check from https://github.com/libjpeg-turbo/libjpeg-turbo/blob/57ba02a408a9a55ccff25aae8b164632a3a4f177/simd/CMakeLists.txt#L419
    jpeg_mips64_mmi = c_compiler.try_compile(
        body='int c = 0, a = 0, b = 0; asm("paddb %0, %1, %2" : "=f" (c) : "f" (a), "f" (b));',
        check_msg="for loongson mmi support",
        flags=in_tree_jpeg_mips64,
        when=in_tree_jpeg_mips64,
    )

    @depends(
        "--with-system-jpeg",
        target,
        in_tree_jpeg_arm,
        in_tree_jpeg_mips64,
        jpeg_mips64_mmi,
    )
    def in_tree_jpeg(
        system_jpeg, target, in_tree_jpeg_arm, in_tree_jpeg_mips64, jpeg_mips64_mmi
    ):
        if system_jpeg:
            return

        if target.cpu in ("arm", "aarch64"):
            return in_tree_jpeg_arm
        elif target.kernel == "Darwin":
            if target.cpu == "x86":
                return ("-DPIC", "-DMACHO")
            elif target.cpu == "x86_64":
                return ("-D__x86_64__", "-DPIC", "-DMACHO")
        elif target.kernel == "WINNT":
            if target.cpu == "x86":
                return ("-DPIC", "-DWIN32")
            elif target.cpu == "x86_64":
                return ("-D__x86_64__", "-DPIC", "-DWIN64", "-DMSVC")
        elif target.cpu == "mips32":
            return ("-mdspr2",)
        elif target.cpu == "mips64" and jpeg_mips64_mmi:
            return in_tree_jpeg_mips64
        elif target.cpu == "x86":
            return ("-DPIC", "-DELF")
        elif target.cpu == "x86_64":
            return ("-D__x86_64__", "-DPIC", "-DELF")

    @depends(target, when=depends("--with-system-jpeg")(lambda x: not x))
    def jpeg_nasm(target):
        if target.cpu in ("x86", "x86_64"):
            # libjpeg-turbo 2.0.6 requires nasm 2.10.
            return namespace(version="2.10", what="JPEG")

    # Compiler checks from https://github.com/libjpeg-turbo/libjpeg-turbo/blob/57ba02a408a9a55ccff25aae8b164632a3a4f177/simd/CMakeLists.txt#L258
    jpeg_arm_neon_vld1_s16_x3 = c_compiler.try_compile(
        includes=["arm_neon.h"],
        body="int16_t input[12] = {}; int16x4x3_t output = vld1_s16_x3(input);",
        check_msg="for vld1_s16_x3 in arm_neon.h",
        flags=in_tree_jpeg_arm,
        when=in_tree_jpeg_arm,
    )

    jpeg_arm_neon_vld1_u16_x2 = c_compiler.try_compile(
        includes=["arm_neon.h"],
        body="uint16_t input[8] = {}; uint16x4x2_t output = vld1_u16_x2(input);",
        check_msg="for vld1_u16_x2 in arm_neon.h",
        flags=in_tree_jpeg_arm,
        when=in_tree_jpeg_arm,
    )

    jpeg_arm_neon_vld1q_u8_x4 = c_compiler.try_compile(
        includes=["arm_neon.h"],
        body="uint8_t input[64] = {}; uint8x16x4_t output = vld1q_u8_x4(input);",
        check_msg="for vld1q_u8_x4 in arm_neon.h",
        flags=in_tree_jpeg_arm,
        when=in_tree_jpeg_arm,
    )

    set_config("LIBJPEG_TURBO_USE_NASM", True, when=jpeg_nasm)
    set_config("LIBJPEG_TURBO_SIMD_FLAGS", in_tree_jpeg)
    set_config("LIBJPEG_TURBO_HAVE_VLD1_S16_X3", jpeg_arm_neon_vld1_s16_x3)
    set_config("LIBJPEG_TURBO_HAVE_VLD1_U16_X2", jpeg_arm_neon_vld1_u16_x2)
    set_config("LIBJPEG_TURBO_HAVE_VLD1Q_U8_X4", jpeg_arm_neon_vld1q_u8_x4)
    set_config(
        "LIBJPEG_TURBO_NEON_INTRINSICS",
        jpeg_arm_neon_vld1_s16_x3
        & jpeg_arm_neon_vld1_u16_x2
        & jpeg_arm_neon_vld1q_u8_x4,
    )


# PNG
# ===
with only_when(compile_environment):
    system_lib_option(
        "--with-system-png",
        nargs="?",
        help="Use system libpng",
    )

    @depends("--with-system-png")
    def deprecated_system_png_path(value):
        if len(value) == 1:
            die(
                "--with-system-png=PATH is not supported anymore. Please use "
                "--with-system-png and set any necessary pkg-config environment variable."
            )

    png = pkg_check_modules("MOZ_PNG", "libpng >= 1.6.35", when="--with-system-png")

    check_symbol(
        "png_get_acTL",
        flags=png.libs,
        onerror=lambda: die(
            "--with-system-png won't work because the system's libpng doesn't have APNG support"
        ),
        when="--with-system-png",
    )

    set_config("MOZ_SYSTEM_PNG", True, when="--with-system-png")


# FFmpeg's ffvpx configuration
# ==============================================================
with only_when(compile_environment):

    @depends(target)
    def libav_fft(target):
        if target.os == "Android" and target.cpu != "arm":
            return True
        return target.kernel in ("WINNT", "Darwin") or target.cpu == "x86_64"

    set_config("MOZ_LIBAV_FFT", depends(when=libav_fft)(lambda: True))
    set_define("MOZ_LIBAV_FFT", depends(when=libav_fft)(lambda: True))


# Artifact builds need MOZ_FFVPX defined as if compilation happened.
with only_when(compile_environment | artifact_builds):

    @depends(target)
    def ffvpx(target):
        enable = use_nasm = True
        flac_only = False
        flags = []

        if target.kernel == "WINNT":
            if target.cpu == "x86":
                # 32-bit windows need to prefix symbols with an underscore.
                flags = ["-DPIC", "-DWIN32", "-DPREFIX", "-Pconfig_win32.asm"]
            elif target.cpu == "x86_64":
                flags = [
                    "-D__x86_64__",
                    "-DPIC",
                    "-DWIN64",
                    "-DMSVC",
                    "-Pconfig_win64.asm",
                ]
            elif target.cpu == "aarch64":
                flags = ["-DPIC", "-DWIN64"]
                use_nasm = False
        elif target.kernel == "Darwin":
            # 32/64-bit macosx assemblers need to prefix symbols with an
            # underscore.
            flags = ["-DPIC", "-DMACHO", "-DPREFIX"]
            if target.cpu == "x86_64":
                flags += [
                    "-D__x86_64__",
                    "-Pconfig_darwin64.asm",
                ]
            elif target.cpu == "aarch64":
                use_nasm = False
        elif target.cpu == "x86_64":
            flags = ["-D__x86_64__", "-DPIC", "-DELF", "-Pconfig_unix64.asm"]
        elif target.cpu in ("x86", "arm", "aarch64"):
            flac_only = True
        else:
            enable = False

        if flac_only or not enable:
            use_nasm = False

        return namespace(
            enable=enable,
            use_nasm=use_nasm,
            flac_only=flac_only,
            flags=flags,
        )

    @depends(when=ffvpx.use_nasm)
    def ffvpx_nasm():
        # nasm 2.10 for AVX-2 support.
        return namespace(version="2.10", what="FFVPX")

    # ffvpx_nasm can't indirectly depend on vpx_as_flags, because it depends
    # on a compiler test, so we have to do a little bit of dance here.
    @depends(ffvpx, vpx_as_flags, target)
    def ffvpx(ffvpx, vpx_as_flags, target):
        if ffvpx and vpx_as_flags and target.cpu in ("arm", "aarch64"):
            ffvpx.flags.extend(vpx_as_flags)
        return ffvpx

    set_config("MOZ_FFVPX", True, when=ffvpx.enable)
    set_define("MOZ_FFVPX", True, when=ffvpx.enable)
    set_config("MOZ_FFVPX_AUDIOONLY", True, when=ffvpx.flac_only)
    set_define("MOZ_FFVPX_AUDIOONLY", True, when=ffvpx.flac_only)
    set_config("FFVPX_ASFLAGS", ffvpx.flags)
    set_config("FFVPX_USE_NASM", True, when=ffvpx.use_nasm)


# nasm detection
# ==============================================================
@depends(dav1d_nasm, vpx_nasm, jpeg_nasm, ffvpx_nasm, when=compile_environment)
def need_nasm(*requirements):
    requires = {
        x.what: x.version if hasattr(x, "version") else True for x in requirements if x
    }
    if requires:
        items = sorted(requires.keys())
        if len(items) > 1:
            what = " and ".join((", ".join(items[:-1]), items[-1]))
        else:
            what = items[0]
        versioned = {k: v for (k, v) in requires.items() if v is not True}
        return namespace(what=what, versioned=versioned)


nasm = check_prog(
    "NASM",
    ["nasm"],
    allow_missing=True,
    bootstrap="nasm",
    when=need_nasm,
)


@depends(nasm, need_nasm.what)
def check_nasm(nasm, what):
    if not nasm and what:
        die("Nasm is required to build with %s, but it was not found." % what)
    return nasm


@depends_if(check_nasm)
@checking("nasm version")
def nasm_version(nasm):
    version = (
        check_cmd_output(nasm, "-v", onerror=lambda: die("Failed to get nasm version."))
        .splitlines()[0]
        .split()[2]
    )
    return Version(version)


@depends(nasm_version, need_nasm.versioned, when=need_nasm.versioned)
def check_nasm_version(nasm_version, versioned):
    by_version = sorted(versioned.items(), key=lambda x: x[1])
    what, version = by_version[-1]
    if nasm_version < version:
        die(
            "Nasm version %s or greater is required to build with %s." % (version, what)
        )
    return nasm_version


@depends(target, when=check_nasm_version)
def nasm_asflags(target):
    asflags = {
        ("OSX", "x86"): ["-f", "macho32"],
        ("OSX", "x86_64"): ["-f", "macho64"],
        ("WINNT", "x86"): ["-f", "win32"],
        ("WINNT", "x86_64"): ["-f", "win64"],
    }.get((target.os, target.cpu), None)
    if asflags is None:
        # We're assuming every x86 platform we support that's
        # not Windows or Mac is ELF.
        if target.cpu == "x86":
            asflags = ["-f", "elf32"]
        elif target.cpu == "x86_64":
            asflags = ["-f", "elf64"]
    return asflags


set_config("NASM_ASFLAGS", nasm_asflags)


# ANGLE OpenGL->D3D translator for WebGL
# ==============================================================

with only_when(compile_environment & target_is_windows):

    def d3d_compiler_dll_result(value):
        if not value.path:
            return "provided by the OS"
        return value.path

    @depends(target, valid_windows_sdk_dir, fxc)
    @checking("for D3D compiler DLL", d3d_compiler_dll_result)
    @imports("os.path")
    def d3d_compiler_dll(target, windows_sdk_dir, fxc):
        suffix = {
            "x86_64": "x64",
        }.get(target.cpu, target.cpu)

        name = "d3dcompiler_47.dll"

        if target.cpu == "aarch64":
            # AArch64 Windows comes with d3dcompiler_47.dll installed
            return namespace(name=name, path=None)

        if windows_sdk_dir:
            path = os.path.join(windows_sdk_dir.path, "Redist", "D3D", suffix, name)
            error_extra = "in Windows SDK at {}".format(windows_sdk_dir.path)
        else:
            path = os.path.join(os.path.dirname(fxc), name)
            error_extra = "alongside FXC at {}".format(fxc)

        if os.path.exists(path):
            return namespace(name=name, path=path)
        die("Could not find {} {}".format(name, error_extra))

    set_config("MOZ_ANGLE_RENDERER", True)
    set_config(
        "MOZ_D3DCOMPILER_VISTA_DLL", d3d_compiler_dll.name, when=d3d_compiler_dll.path
    )
    set_config("MOZ_D3DCOMPILER_VISTA_DLL_PATH", d3d_compiler_dll.path)

# Remoting protocol support
# ==============================================================


@depends(toolkit)
def has_remote(toolkit):
    if toolkit in ("gtk", "windows", "cocoa"):
        return True


set_config("MOZ_HAS_REMOTE", has_remote)
set_define("MOZ_HAS_REMOTE", has_remote)

# RLBox Library Sandboxing wasm support
# ==============================================================


def wasm_sandboxing_libraries():
    return (
        "graphite",
        "ogg",
        "hunspell",
        "expat",
        "woff2",
    )


@depends(dependable(wasm_sandboxing_libraries), build_project)
def default_wasm_sandboxing_libraries(libraries, build_project):
    if build_project != "tools/rusttests":
        non_default_libs = set()

        return tuple(l for l in libraries if l not in non_default_libs)


option(
    "--with-wasm-sandboxed-libraries",
    env="WASM_SANDBOXED_LIBRARIES",
    help="{Enable wasm sandboxing for the selected libraries|Disable wasm sandboxing}",
    nargs="+",
    choices=dependable(wasm_sandboxing_libraries),
    default=default_wasm_sandboxing_libraries,
)


@depends("--with-wasm-sandboxed-libraries")
def requires_wasm_sandboxing(libraries):
    if libraries:
        return True


set_config("MOZ_USING_WASM_SANDBOXING", requires_wasm_sandboxing)
set_define("MOZ_USING_WASM_SANDBOXING", requires_wasm_sandboxing)

with only_when(requires_wasm_sandboxing & compile_environment):
    option(
        "--with-wasi-sysroot",
        env="WASI_SYSROOT",
        nargs=1,
        help="Path to wasi sysroot for wasm sandboxing",
    )

    @depends("--with-wasi-sysroot", requires_wasm_sandboxing)
    def bootstrap_wasi_sysroot(wasi_sysroot, requires_wasm_sandboxing):
        return requires_wasm_sandboxing and not wasi_sysroot

    @depends(
        "--with-wasi-sysroot",
        bootstrap_path("sysroot-wasm32-wasi", when=bootstrap_wasi_sysroot),
    )
    @imports("os")
    def wasi_sysroot(wasi_sysroot, bootstrapped_sysroot):
        if not wasi_sysroot:
            return bootstrapped_sysroot

        wasi_sysroot = wasi_sysroot[0]
        if not os.path.isdir(wasi_sysroot):
            die("Argument to --with-wasi-sysroot must be a directory")
        if not os.path.isabs(wasi_sysroot):
            die("Argument to --with-wasi-sysroot must be an absolute path")

        return wasi_sysroot

    @depends(wasi_sysroot)
    def wasi_sysroot_flags(wasi_sysroot):
        if wasi_sysroot:
            log.info("Using wasi sysroot in %s", wasi_sysroot)
            return ["--sysroot=%s" % wasi_sysroot]
        return []

    set_config("WASI_SYSROOT", wasi_sysroot)

    def wasm_compiler_with_flags(compiler, sysroot_flags):
        if compiler:
            return (
                compiler.wrapper + [compiler.compiler] + compiler.flags + sysroot_flags
            )

    @template
    def wasm_compiler_error(msg):
        @depends("--with-wasm-sandboxed-libraries")
        def wasm_compiler_error(sandboxed_libs):
            suggest_disable = ""
            if sandboxed_libs.origin == "default":
                suggest_disable = " Or build with --without-wasm-sandboxed-libraries."
            return lambda: die(msg + suggest_disable)

        return wasm_compiler_error

    @template
    def check_wasm_compiler(compiler, language):
        compiler.try_compile(
            includes=["cstring" if language == "C++" else "string.h"],
            flags=wasi_sysroot_flags,
            check_msg="the wasm %s compiler can find wasi headers" % language,
            onerror=wasm_compiler_error(
                "Cannot find wasi headers or problem with the wasm compiler. "
                "Please fix the problem."
            ),
        )

        compiler.try_run(
            flags=wasi_sysroot_flags,
            check_msg="the wasm %s linker can find wasi libraries" % language,
            onerror=wasm_compiler_error(
                "Cannot find wasi libraries or problem with the wasm linker. "
                "Please fix the problem."
            ),
        )

    wasm_cc = compiler("C", wasm, other_compiler=c_compiler)
    check_wasm_compiler(wasm_cc, "C")

    @depends(wasm_cc, wasi_sysroot_flags)
    def wasm_cc_with_flags(wasm_cc, wasi_sysroot_flags):
        return wasm_compiler_with_flags(wasm_cc, wasi_sysroot_flags)

    set_config("WASM_CC", wasm_cc_with_flags)

    wasm_cxx = compiler(
        "C++",
        wasm,
        c_compiler=wasm_cc,
        other_compiler=cxx_compiler,
        other_c_compiler=c_compiler,
    )
    check_wasm_compiler(wasm_cxx, "C++")

    @depends(wasm_cxx, wasi_sysroot_flags)
    def wasm_cxx_with_flags(wasm_cxx, wasi_sysroot_flags):
        return wasm_compiler_with_flags(wasm_cxx, wasi_sysroot_flags)

    set_config("WASM_CXX", wasm_cxx_with_flags)

    wasm_compile_flags = dependable(["-fno-exceptions", "-fno-strict-aliasing"])
    option(env="WASM_CFLAGS", nargs=1, help="Options to pass to WASM_CC")

    @depends("WASM_CFLAGS", wasm_compile_flags)
    def wasm_cflags(value, wasm_compile_flags):
        if value:
            return wasm_compile_flags + value
        else:
            return wasm_compile_flags

    set_config("WASM_CFLAGS", wasm_cflags)

    option(env="WASM_CXXFLAGS", nargs=1, help="Options to pass to WASM_CXX")

    @depends("WASM_CXXFLAGS", wasm_compile_flags)
    def wasm_cxxflags(value, wasm_compile_flags):
        if value:
            return wasm_compile_flags + value
        else:
            return wasm_compile_flags

    set_config("WASM_CXXFLAGS", wasm_cxxflags)


@depends("--with-wasm-sandboxed-libraries")
def wasm_sandboxing(libraries):
    if not libraries:
        return

    return namespace(**{name: True for name in libraries})


@template
def wasm_sandboxing_config_defines():
    for lib in wasm_sandboxing_libraries():
        set_config(
            "MOZ_WASM_SANDBOXING_%s" % lib.upper(), getattr(wasm_sandboxing, lib)
        )
        set_define(
            "MOZ_WASM_SANDBOXING_%s" % lib.upper(), getattr(wasm_sandboxing, lib)
        )


wasm_sandboxing_config_defines()


# new Notification Store implementation
# ==============================================================


@depends(milestone)
def new_notification_store(milestone):
    if milestone.is_nightly:
        return True


set_config("MOZ_NEW_NOTIFICATION_STORE", True, when=new_notification_store)
set_define("MOZ_NEW_NOTIFICATION_STORE", True, when=new_notification_store)


# Glean SDK Integration Crate
# ==============================================================


@depends(target)
def glean_android(target):
    return target.os == "Android"


set_config("MOZ_GLEAN_ANDROID", True, when=glean_android)
set_define("MOZ_GLEAN_ANDROID", True, when=glean_android)


# dump_syms
# ==============================================================

check_prog(
    "DUMP_SYMS",
    ["dump_syms"],
    allow_missing=True,
    bootstrap="dump_syms",
    when=compile_environment,
)


@depends(valid_windows_sdk_dir, host)
@imports(_from="os", _import="environ")
def pdbstr_paths(valid_windows_sdk_dir, host):
    if not valid_windows_sdk_dir:
        return

    vc_host = {
        "x86": "x86",
        "x86_64": "x64",
    }.get(host.cpu)

    return [
        environ["PATH"],
        os.path.join(valid_windows_sdk_dir.path, "Debuggers", vc_host, "srcsrv"),
    ]


check_prog(
    "PDBSTR",
    ["pdbstr.exe"],
    allow_missing=True,
    when=compile_environment & target_is_windows,
    paths=pdbstr_paths,
    allow_spaces=True,
)


@depends("MOZ_AUTOMATION", c_compiler)
def allow_missing_winchecksec(automation, c_compiler):
    if not automation:
        return True
    if c_compiler and c_compiler.type != "clang-cl":
        return True


check_prog(
    "WINCHECKSEC",
    ["winchecksec.exe", "winchecksec"],
    bootstrap="winchecksec",
    allow_missing=allow_missing_winchecksec,
    when=compile_environment & target_is_windows,
)

# Fork server
@depends(target, build_project)
def forkserver_default(target, build_project):
    return build_project == "browser" and (
        (target.os == "GNU" and target.kernel == "Linux")
        or target.os == "FreeBSD"
        or target.os == "OpenBSD"
    )


option(
    "--enable-forkserver",
    default=forkserver_default,
    env="MOZ_ENABLE_FORKSERVER",
    help="{Enable|Disable} fork server",
)


@depends("--enable-forkserver", target)
def forkserver_flag(value, target):
    if (
        target.os == "Android"
        or (target.os == "GNU" and target.kernel == "Linux")
        or target.os == "FreeBSD"
        or target.os == "OpenBSD"
    ):
        return bool(value)
    pass


set_config("MOZ_ENABLE_FORKSERVER", forkserver_flag)
set_define("MOZ_ENABLE_FORKSERVER", forkserver_flag, forkserver_flag)

# Crash Reporter
# ==============================================================

with only_when(compile_environment & target_has_linux_kernel):
    # Check if we need to use the breakpad_getcontext fallback.
    getcontext = check_symbol("getcontext")
    set_config("HAVE_GETCONTEXT", getcontext)
    set_define("HAVE_GETCONTEXT", getcontext)

# NSS
# ==============================================================
include("../build/moz.configure/nss.configure")


# Enable or disable running in background task mode: headless for
# periodic, short-lived, maintenance tasks.
# ==============================================================================
option(
    "--disable-backgroundtasks",
    help="Disable running in background task mode",
)
set_config("MOZ_BACKGROUNDTASKS", True, when="--enable-backgroundtasks")
set_define("MOZ_BACKGROUNDTASKS", True, when="--enable-backgroundtasks")


# Update-related programs: updater, maintenance service, update agent,
# default browser agent.
# ==============================================================
include("../build/moz.configure/update-programs.configure")


# Mobile optimizations
# ==============================================================
option(
    "--enable-mobile-optimize",
    default=target_is_android,
    help="{Enable|Disable} mobile optimizations",
)

set_define("MOZ_GFX_OPTIMIZE_MOBILE", True, when="--enable-mobile-optimize")
# We ignore "paint will resample" on mobile for performance.
# We may want to revisit this later.
set_define("MOZ_IGNORE_PAINT_WILL_RESAMPLE", True, when="--enable-mobile-optimize")

# Pref extensions
# ==============================================================
option("--disable-pref-extensions", help="Disable pref extensions such as autoconfig")
set_config("MOZ_PREF_EXTENSIONS", True, when="--enable-pref-extensions")

# Offer a way to disable the startup cache
# ==============================================================
option("--disable-startupcache", help="Disable startup cache")


@depends("--enable-startupcache")
def enable_startupcache(value):
    if value:
        return True


set_define(
    "MOZ_DISABLE_STARTUPCACHE", True, when=depends(enable_startupcache)(lambda x: not x)
)


# Branding
# ==============================================================
option(
    env="MOZ_APP_REMOTINGNAME",
    nargs=1,
    help="Used for the internal program name, which affects profile name "
    "and remoting. If not set, defaults to MOZ_APP_NAME if the update channel "
    "is release, and MOZ_APP_NAME-MOZ_UPDATE_CHANNEL otherwise.",
)


@depends("MOZ_APP_REMOTINGNAME", moz_app_name, update_channel)
def moz_app_remotingname(value, moz_app_name, update_channel):
    if value:
        return value[0]
    if update_channel == "release":
        return moz_app_name
    return moz_app_name + "-" + update_channel


set_config("MOZ_APP_REMOTINGNAME", moz_app_remotingname)

option(
    env="ANDROID_PACKAGE_NAME",
    nargs=1,
    help="Name of the Android package (default org.mozilla.$MOZ_APP_NAME)",
)


@depends("ANDROID_PACKAGE_NAME", moz_app_name)
def android_package_name(value, moz_app_name):
    if value:
        return value[0]
    if moz_app_name == "fennec":
        return "org.mozilla.fennec_aurora"
    return "org.mozilla.%s" % moz_app_name


set_config("ANDROID_PACKAGE_NAME", android_package_name)


# Miscellaneous options
# ==============================================================
option(env="MOZ_WINCONSOLE", nargs="?", help="Whether we can create a console window.")
set_define("MOZ_WINCONSOLE", True, when=depends("MOZ_WINCONSOLE")(lambda x: x))


# Alternative Crashreporter setting
option(
    "--with-crashreporter-url",
    env="MOZ_CRASHREPORTER_URL",
    default="https://crash-reports.mozilla.com/",
    nargs=1,
    help="Set an alternative crashreporter url",
)

set_config(
    "MOZ_CRASHREPORTER_URL",
    depends("--with-crashreporter-url")(lambda x: x[0].rstrip("/")),
)


# Crash reporter options
# ==============================================================
@depends(target)
def oxidized_breakpad(target):
    if target.kernel == "Linux" and target.os != "Android":
        return target.cpu in ("x86", "x86_64")
    return False


set_config("MOZ_OXIDIZED_BREAKPAD", True, when=oxidized_breakpad)
set_define("MOZ_OXIDIZED_BREAKPAD", True, when=oxidized_breakpad)


# Wine
# ==============================================================
@depends(target, host)
def want_wine(target, host):
    return target.kernel == "WINNT" and host.kernel != "WINNT"


wine = check_prog(
    "WINE",
    ["wine64", "wine"],
    when=want_wine,
    bootstrap="wine/bin",
)

# DOM Streams
# ==============================================================
# Set this to true so the JS engine knows we're doing a browser build.
set_config("MOZ_DOM_STREAMS", True)
set_define("MOZ_DOM_STREAMS", True)

# libevent
# ==============================================================
with only_when(compile_environment):
    system_lib_option(
        "--with-system-libevent",
        nargs="?",
        help="Use system libevent",
    )

    @depends("--with-system-libevent")
    def deprecated_system_libevent_path(value):
        if len(value) == 1:
            die(
                "--with-system-libevent=PATH is not supported anymore. Please use "
                "--with-system-libevent and set any necessary pkg-config environment variable."
            )

    pkg_check_modules("MOZ_LIBEVENT", "libevent", when="--with-system-libevent")

    set_config("MOZ_SYSTEM_LIBEVENT", True, when="--with-system-libevent")


# Crash reporting
# ==============================================================
@depends(target, developer_options, artifact_builds)
def crashreporter_default(target, developer_options, artifacts):
    if target.kernel in ("WINNT", "Darwin"):
        return True
    if target.kernel == "Linux" and target.cpu in ("x86", "x86_64", "arm", "aarch64"):
        # The crash reporter prevents crash stacktraces to be logged in the
        # logs on Android, so we leave it out by default in developer builds.
        return target.os != "Android" or not developer_options or artifacts


option(
    "--enable-crashreporter",
    default=crashreporter_default,
    help="{Enable|Disable} crash reporting",
)


set_config("MOZ_CRASHREPORTER", True, when="--enable-crashreporter")
set_define("MOZ_CRASHREPORTER", True, when="--enable-crashreporter")

with only_when(compile_environment):
    with only_when("--enable-crashreporter"):
        pkg_check_modules(
            "MOZ_GTHREAD",
            "gthread-2.0",
            when=depends(target)(lambda t: t.os == "GNU" and t.kernel == "Linux"),
        )

        set_config(
            "MOZ_CRASHREPORTER_INJECTOR",
            True,
            when=depends(target)(lambda t: t.os == "WINNT" and t.bitness == 32),
        )
        set_define(
            "MOZ_CRASHREPORTER_INJECTOR",
            True,
            when=depends(target)(lambda t: t.os == "WINNT" and t.bitness == 32),
        )


# If we have any service that uploads data (and requires data submission
# policy alert), set MOZ_DATA_REPORTING.
# ==============================================================
@depends(
    "MOZ_TELEMETRY_REPORTING",
    "MOZ_SERVICES_HEALTHREPORT",
    "--enable-crashreporter",
    "MOZ_NORMANDY",
)
def data_reporting(telemetry, healthreport, crashreporter, normandy):
    return telemetry or healthreport or crashreporter or normandy


set_config("MOZ_DATA_REPORTING", True, when=data_reporting)
set_define("MOZ_DATA_REPORTING", True, when=data_reporting)


# Gtk+
# ==============================================================
with only_when(toolkit_gtk):
    pkg_check_modules(
        "MOZ_GTK3",
        "gtk+-3.0 >= 3.14.0 gtk+-unix-print-3.0 glib-2.0 gobject-2.0 gio-unix-2.0",
    )

    set_define("GDK_VERSION_MIN_REQUIRED", "GDK_VERSION_3_14")
    set_define("GDK_VERSION_MAX_ALLOWED", "GDK_VERSION_3_14")

    pkg_check_modules("GLIB", "glib-2.0 >= 2.42 gobject-2.0")

    set_define("GLIB_VERSION_MIN_REQUIRED", "GLIB_VERSION_2_42")
    set_define("GLIB_VERSION_MAX_ALLOWED", "GLIB_VERSION_2_42")

    set_define("MOZ_ACCESSIBILITY_ATK", True, when=accessibility)

# DBus
# ==============================================================
with only_when(toolkit_gtk):
    option("--disable-dbus", help="Disable dbus support")

    with only_when("--enable-dbus"):
        pkg_check_modules("MOZ_DBUS", "dbus-1 >= 0.60")
        pkg_check_modules("MOZ_DBUS_GLIB", "dbus-glib-1 >= 0.60")

        set_config("MOZ_ENABLE_DBUS", True)
        set_define("MOZ_ENABLE_DBUS", True)


# Necko's wifi scanner
# ==============================================================
@depends(target)
def necko_wifi_when(target):
    return target.os in ("WINNT", "OSX", "DragonFly", "FreeBSD") or (
        target.kernel == "Linux" and target.os == "GNU"
    )


option("--disable-necko-wifi", help="Disable necko wifi scanner", when=necko_wifi_when)

set_config("NECKO_WIFI", True, when="--enable-necko-wifi")
set_define("NECKO_WIFI", True, when="--enable-necko-wifi")


@depends(
    depends("--enable-necko-wifi", when=necko_wifi_when)(lambda x: x),
    depends("--enable-dbus", when=toolkit_gtk)(lambda x: x),
    when=depends(target)(lambda t: t.os == "GNU" and t.kernel == "Linux"),
)
def necko_wifi_dbus(necko_wifi, dbus):
    if necko_wifi and not dbus:
        die(
            "Necko WiFi scanning needs DBus on your platform, remove --disable-dbus"
            " or use --disable-necko-wifi"
        )
    return necko_wifi and dbus


set_config("NECKO_WIFI_DBUS", True, when=necko_wifi_dbus)
set_define("NECKO_WIFI_DBUS", True, when=necko_wifi_dbus)


# Frontend JS debug mode
# ==============================================================
option("--enable-debug-js-modules", help="Enable debug mode for frontend JS libraries")

set_config("DEBUG_JS_MODULES", True, when="--enable-debug-js-modules")


# moz_dump_painting
# ==============================================================
option("--enable-dump-painting", help="Enable paint debugging")

set_define(
    "MOZ_DUMP_PAINTING",
    True,
    when=depends("--enable-dump-painting", "--enable-debug")(
        lambda painting, debug: painting or debug
    ),
)
set_define("MOZ_LAYERS_HAVE_LOG", True, when="--enable-dump-painting")


# libproxy support
# ==============================================================
with only_when(toolkit_gtk):
    system_lib_option("--enable-libproxy", help="Enable libproxy support")

    with only_when("--enable-libproxy"):
        pkg_check_modules("MOZ_LIBPROXY", "libproxy-1.0")

        set_config("MOZ_ENABLE_LIBPROXY", True)
        set_define("MOZ_ENABLE_LIBPROXY", True)


# Enable runtime logging
# ==============================================================
set_define("MOZ_LOGGING", True)
set_define("FORCE_PR_LOG", True)

# This will enable logging of addref, release, ctor, dtor.
# ==============================================================
option(
    "--enable-logrefcnt",
    default=moz_debug,
    help="{Enable|Disable} logging of refcounts",
)

set_define("NS_BUILD_REFCNT_LOGGING", True, when="--enable-logrefcnt")


# NegotiateAuth
# ==============================================================
option("--disable-negotiateauth", help="Disable GSS-API negotiation")

set_config("MOZ_AUTH_EXTENSION", True, when="--enable-negotiateauth")
set_define("MOZ_AUTH_EXTENSION", True, when="--enable-negotiateauth")


# Parental control
# ==============================================================
option("--disable-parental-controls", help="Do not build parental controls")

set_config(
    "MOZ_DISABLE_PARENTAL_CONTROLS",
    True,
    when=depends("--enable-parental-controls")(lambda x: not x),
)
set_define(
    "MOZ_DISABLE_PARENTAL_CONTROLS",
    True,
    when=depends("--enable-parental-controls")(lambda x: not x),
)


# Sandboxing support
# ==============================================================
@depends(target, tsan, asan)
def sandbox_default(target, tsan, asan):
    # Only enable the sandbox by default on Linux, OpenBSD, macOS, and Windows
    if target.kernel == "Linux" and target.os == "GNU":
        # Bug 1182565: TSan conflicts with sandboxing on Linux.
        # Bug 1287971: LSan also conflicts with sandboxing on Linux.
        if tsan or asan:
            return False
        # Linux sandbox is only available on x86{,_64} and arm{,64}.
        return target.cpu in ("x86", "x86_64", "arm", "aarch64")
    return target.kernel in ("WINNT", "Darwin", "OpenBSD")


option(
    "--enable-sandbox",
    default=sandbox_default,
    help="{Enable|Disable} sandboxing support",
)

set_config("MOZ_SANDBOX", True, when="--enable-sandbox")
set_define("MOZ_SANDBOX", True, when="--enable-sandbox")

with only_when(depends(target.kernel)(lambda k: k not in ("Darwin", "WINNT"))):
    set_define("MOZ_CONTENT_TEMP_DIR", True, when="--enable-sandbox")

# Searching of system directories for extensions.
# ==============================================================
# Note: this switch is meant to be used for test builds whose behavior should
# not depend on what happens to be installed on the local machine.
option(
    "--disable-system-extension-dirs",
    help="Disable searching system- and account-global directories for extensions"
    " of any kind; use only profile-specific extension directories",
)

set_define("ENABLE_SYSTEM_EXTENSION_DIRS", True, when="--enable-system-extension-dirs")


# Pixman
# ==============================================================
with only_when(compile_environment):
    system_lib_option(
        "--enable-system-pixman", help="Use system pixman (located with pkgconfig)"
    )

    @depends("--enable-system-pixman")
    def in_tree_pixman(pixman):
        return not pixman

    set_config("MOZ_TREE_PIXMAN", True, when=in_tree_pixman)
    set_define("MOZ_TREE_PIXMAN", True, when=in_tree_pixman)

    pkg_check_modules("MOZ_PIXMAN", "pixman-1 >= 0.36.0", when="--enable-system-pixman")
    # Set MOZ_PIXMAN_CFLAGS to an explicit empty value when --enable-system-pixman is *not* used,
    # for layout/style/extra-bindgen-flags
    set_config("MOZ_PIXMAN_CFLAGS", [], when=in_tree_pixman)


# Universalchardet
# ==============================================================
with only_when(compile_environment):
    option("--disable-universalchardet", help="Disable universal encoding detection")

    set_config("MOZ_UNIVERSALCHARDET", True, when="--enable-universalchardet")


# Disable zipwriter
# ==============================================================
with only_when(compile_environment):
    option("--disable-zipwriter", help="Disable zipwriter component")

    set_config("MOZ_ZIPWRITER", True, when="--enable-zipwriter")


# Location of the mozilla user directory
# ==============================================================
with only_when(compile_environment):

    @depends(target)
    def default_user_appdir(target):
        if target.kernel in ("WINNT", "Darwin"):
            return "Mozilla"
        return ".mozilla"

    option(
        "--with-user-appdir",
        nargs=1,
        default=default_user_appdir,
        help="Set user-specific appdir",
    )

    @depends("--with-user-appdir")
    def user_appdir(appdir):
        if not appdir:
            die("--without-user-appdir is not a valid option.")
        if "/" in appdir[0]:
            die("--with-user-appdir must be a single relative path.")
        return '"{}"'.format(appdir[0])

    set_define("MOZ_USER_DIR", user_appdir)


# Check for sin_len and sin6_len - used by SCTP; only appears in Mac/*BSD generally
# ==============================================================
with only_when(compile_environment):
    have_sin_len = c_compiler.try_compile(
        includes=["netinet/in.h"],
        body="struct sockaddr_in x; void *foo = (void*) &x.sin_len;",
        check_msg="for sin_len in struct sockaddr_in",
    )
    have_sin6_len = c_compiler.try_compile(
        includes=["netinet/in.h"],
        body="struct sockaddr_in6 x; void *foo = (void*) &x.sin6_len;",
        check_msg="for sin_len6 in struct sockaddr_in6",
    )
    set_define("HAVE_SIN_LEN", have_sin_len)
    set_define("HAVE_SIN6_LEN", have_sin6_len)
    # HAVE_CONN_LEN must be the same as HAVE_SIN_LEN and HAVE_SIN6_LEN
    set_define("HAVE_SCONN_LEN", have_sin_len & have_sin6_len)
    set_define(
        "HAVE_SA_LEN",
        c_compiler.try_compile(
            includes=["netinet/in.h"],
            body="struct sockaddr x; void *foo = (void*) &x.sa_len;",
            check_msg="for sa_len in struct sockaddr",
        ),
    )


# Check for pthread_cond_timedwait_monotonic_np
# ==============================================================
with only_when(compile_environment):
    set_define(
        "HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC",
        c_compiler.try_compile(
            includes=["pthread.h"],
            body="pthread_cond_timedwait_monotonic_np(0, 0, 0);",
            # -Werror to catch any "implicit declaration" warning that means the function
            # is not supported.
            flags=["-Werror=implicit-function-declaration"],
            check_msg="for pthread_cond_timedwait_monotonic_np",
        ),
    )


# Custom dynamic linker for Android
# ==============================================================
with only_when(target_has_linux_kernel & compile_environment):
    option(
        env="MOZ_LINKER",
        default=depends(target.os, when="--enable-jemalloc")(
            lambda os: os == "Android"
        ),
        help="{Enable|Disable} custom dynamic linker",
    )

    set_config("MOZ_LINKER", True, when="MOZ_LINKER")
    set_define("MOZ_LINKER", True, when="MOZ_LINKER")
    add_old_configure_assignment("MOZ_LINKER", True, when="MOZ_LINKER")

    moz_linker = depends(when="MOZ_LINKER")(lambda: True)


# 32-bits ethtool_cmd.speed
# ==============================================================
with only_when(target_has_linux_kernel & compile_environment):
    set_config(
        "MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI",
        c_compiler.try_compile(
            includes=["linux/ethtool.h"],
            body="struct ethtool_cmd cmd; cmd.speed_hi = 0;",
            check_msg="for 32-bits ethtool_cmd.speed",
        ),
    )

# Gamepad support
# ==============================================================
check_header(
    "linux/joystick.h",
    onerror=lambda: die(
        "Can't find header linux/joystick.h, needed for gamepad support."
        " Please install Linux kernel headers."
    ),
    when=target_has_linux_kernel & compile_environment,
)

# Smart card support
# ==============================================================
@depends(build_project)
def disable_smart_cards(build_project):
    return build_project == "mobile/android"


set_config("MOZ_NO_SMART_CARDS", True, when=disable_smart_cards)
set_define("MOZ_NO_SMART_CARDS", True, when=disable_smart_cards)

# Enable UniFFI fixtures
# ==============================================================
# These are used to test the uniffi-bindgen-gecko-js code generation.  They
# should not be enabled in release builds.

option(
    "--enable-uniffi-fixtures",
    help="Enable UniFFI Fixtures/Examples",
)

set_config("MOZ_UNIFFI_FIXTURES", True, when="--enable-uniffi-fixtures")

# Checks for library functions
# ==============================================================
with only_when(compile_environment & depends(target.os)(lambda os: os != "WINNT")):
    set_define("HAVE_STAT64", check_symbol("stat64"))
    set_define("HAVE_LSTAT64", check_symbol("lstat64"))
    set_define("HAVE_TRUNCATE64", check_symbol("truncate64"))
    set_define("HAVE_STATVFS64", check_symbol("statvfs64"))
    set_define("HAVE_STATVFS", check_symbol("statvfs"))
    set_define("HAVE_STATFS64", check_symbol("statfs64"))
    set_define("HAVE_STATFS", check_symbol("statfs"))
    set_define("HAVE_LUTIMES", check_symbol("lutimes"))
    set_define("HAVE_POSIX_FADVISE", check_symbol("posix_fadvise"))
    set_define("HAVE_POSIX_FALLOCATE", check_symbol("posix_fallocate"))

    set_define("HAVE_ARC4RANDOM", check_symbol("arc4random"))
    set_define("HAVE_ARC4RANDOM_BUF", check_symbol("arc4random_buf"))
    set_define("HAVE_MALLINFO", check_symbol("mallinfo"))

# System policies
# ==============================================================

option(
    "--disable-system-policies",
    help="Disable reading policies from Windows registry, macOS's file system attributes, and /etc/firefox",
)

set_config("MOZ_SYSTEM_POLICIES", True, when="--enable-system-policies")

# Allow disabling the creation a legacy profile
# ==============================================================

option(
    "--disable-legacy-profile-creation",
    help="Disable the creation a legacy profile, to be used by old versions "
    "of Firefox, when no profiles exist.",
)

set_config("MOZ_CREATE_LEGACY_PROFILE", True, when="--enable-legacy-profile-creation")


# STL wrapping
# ==============================================================
set_config("WRAP_STL_INCLUDES", True)
set_config(
    "STL_FLAGS",
    depends(build_environment.dist)(lambda dist: [f"-I{dist}/stl_wrappers"]),
)


# Perl detection
# ==============================================================
@depends(target)
def need_perl(target):
    # Ideally, we'd also depend on gnu_as here, but that adds complications.
    return target.cpu == "arm"


perl = check_prog("PERL", ("perl5", "perl"), when=need_perl)


@template
def perl_version_check(min_version):
    @depends(perl)
    @checking("for minimum required perl version >= %s" % min_version)
    def get_perl_version(perl):
        return Version(
            check_cmd_output(
                perl,
                "-e",
                "print $]",
                onerror=lambda: die("Failed to get perl version."),
            )
        )

    @depends(get_perl_version)
    def check_perl_version(version):
        if version < min_version:
            die("Perl %s or higher is required.", min_version)

    @depends(perl)
    @checking("for full perl installation")
    @imports("subprocess")
    def has_full_perl_installation(perl):
        ret = subprocess.call([perl, "-e", "use Config; exit(!-d $Config{archlib})"])
        return ret == 0

    @depends(has_full_perl_installation)
    def require_full_perl_installation(has_full_perl_installation):
        if not has_full_perl_installation:
            die(
                "Cannot find Config.pm or $Config{archlib}. "
                "A full perl installation is required."
            )


with only_when(need_perl):
    perl_version_check("5.006")