tor-browser

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

rust.configure (26042B)


      1 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
      2 # vim: set filetype=python:
      3 # This Source Code Form is subject to the terms of the Mozilla Public
      4 # License, v. 2.0. If a copy of the MPL was not distributed with this
      5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      6 
      7 
      8 # Rust is required by `rust_compiler` below. We allow_missing here
      9 # to propagate failures to the better error message there.
     10 option(env="RUSTC", nargs=1, help="Path to the rust compiler")
     11 option(env="CARGO", nargs=1, help="Path to the Cargo package manager")
     12 
     13 rustc = check_prog(
     14     "_RUSTC",
     15     ["rustc"],
     16     what="rustc",
     17     paths=rust_search_path,
     18     input="RUSTC",
     19     allow_missing=True,
     20 )
     21 cargo = check_prog(
     22     "_CARGO",
     23     ["cargo"],
     24     what="cargo",
     25     paths=rust_search_path,
     26     input="CARGO",
     27     allow_missing=True,
     28 )
     29 
     30 
     31 @template
     32 def unwrap_rustup(prog, name):
     33     # rustc and cargo can either be rustup wrappers, or they can be the actual,
     34     # plain executables. For cargo, on OSX, rustup sets DYLD_LIBRARY_PATH (at
     35     # least until https://github.com/rust-lang/rustup.rs/pull/1752 is merged
     36     # and shipped) and that can wreak havoc (see bug 1536486). Similarly, for
     37     # rustc, rustup silently honors toolchain overrides set by vendored crates
     38     # (see bug 1547196).
     39     #
     40     # In either case, we need to find the plain executables.
     41     #
     42     # To achieve that, try to run `PROG +stable`. When the rustup wrapper is in
     43     # use, it either prints PROG's help and exits with status 0, or prints
     44     # an error message (error: toolchain 'stable' is not installed) and exits
     45     # with status 1. In the cargo case, when plain cargo is in use, it exits
     46     # with a different error message (e.g. "error: no such subcommand:
     47     # `+stable`"), and exits with status 101.
     48     #
     49     # Unfortunately, in the rustc case, when plain rustc is in use,
     50     # `rustc +stable` will exit with status 1, complaining about a missing
     51     # "+stable" file. We'll examine the error output to try and distinguish
     52     # between failing rustup and failing rustc.
     53     @depends(prog, dependable(name))
     54     @imports(_from="__builtin__", _import="open")
     55     @imports("os")
     56     def unwrap(prog, name):
     57         if not prog:
     58             return
     59 
     60         def from_rustup_which():
     61             out = check_cmd_output("rustup", "which", name, executable=prog).rstrip()
     62             # If for some reason the above failed to return something, keep the
     63             # PROG we found originally.
     64             if out:
     65                 log.info("Actually using '%s'", out)
     66                 return out
     67 
     68             log.info("No `rustup which` output, using '%s'", prog)
     69             return prog
     70 
     71         (retcode, stdout, stderr) = get_cmd_output(prog, "+stable")
     72 
     73         if name == "cargo" and retcode != 101:
     74             prog = from_rustup_which()
     75         elif name == "rustc":
     76             if retcode == 0:
     77                 prog = from_rustup_which()
     78             elif "+stable" in stderr:
     79                 # PROG looks like plain `rustc`.
     80                 pass
     81             else:
     82                 # Assume PROG looks like `rustup`. This case is a little weird,
     83                 # insofar as the user doesn't have the "stable" toolchain
     84                 # installed, but go ahead and unwrap anyway: the user might
     85                 # have only certain versions, beta, or nightly installed, and
     86                 # we'll catch invalid versions later.
     87                 prog = from_rustup_which()
     88 
     89         return normalize_path(prog)
     90 
     91     return unwrap
     92 
     93 
     94 rustc = unwrap_rustup(rustc, "rustc")
     95 cargo = unwrap_rustup(cargo, "cargo")
     96 
     97 
     98 set_config("CARGO", cargo)
     99 set_config("RUSTC", rustc)
    100 
    101 
    102 @depends_if(rustc)
    103 @checking("rustc version", lambda info: info.version)
    104 def rustc_info(rustc):
    105     if not rustc:
    106         return
    107     out = check_cmd_output(rustc, "--version", "--verbose").splitlines()
    108     info = dict((s.strip() for s in line.split(":", 1)) for line in out[1:])
    109     return namespace(
    110         version=Version(info.get("release", "0")),
    111         commit=info.get("commit-hash", "unknown"),
    112         host=info["host"],
    113         llvm_version=Version(info.get("LLVM version", "0")),
    114     )
    115 
    116 
    117 set_config(
    118     "RUSTC_VERSION",
    119     depends(rustc_info)(lambda info: str(info.version) if info else None),
    120 )
    121 
    122 
    123 set_config(
    124     "RUSTC_LLVM_VERSION",
    125     depends(rustc_info)(lambda info: str(info.llvm_version) if info else None),
    126 )
    127 
    128 set_config(
    129     "MOZ_CLANG_NEWER_THAN_RUSTC_LLVM",
    130     depends(c_compiler, rustc_info)(
    131         lambda c_compiler, rustc_info: rustc_info
    132         and c_compiler.type == "clang"
    133         and c_compiler.version.major > rustc_info.llvm_version.major
    134     ),
    135 )
    136 
    137 
    138 @depends_if(cargo)
    139 @checking("cargo version", lambda info: info.version)
    140 @imports("re")
    141 def cargo_info(cargo):
    142     if not cargo:
    143         return
    144     out = check_cmd_output(cargo, "--version", "--verbose").splitlines()
    145     info = dict((s.strip() for s in line.split(":", 1)) for line in out[1:])
    146     version = info.get("release")
    147     # Older versions of cargo didn't support --verbose, in which case, they
    148     # only output a not-really-pleasant-to-parse output. Fortunately, they
    149     # don't error out, so we can just try some regexp matching on the output
    150     # we already got.
    151     if version is None:
    152         VERSION_FORMAT = r"^cargo (\d\.\d+\.\d+).*"
    153 
    154         m = re.search(VERSION_FORMAT, out[0])
    155         # Fail fast if cargo changes its output on us.
    156         if not m:
    157             die("Could not determine cargo version from output: %s", out)
    158         version = m.group(1)
    159 
    160     return namespace(
    161         version=Version(version),
    162     )
    163 
    164 
    165 @depends(rustc_info, cargo_info, target)
    166 @imports(_from="mozboot.util", _import="MINIMUM_RUST_VERSION")
    167 @imports(_from="textwrap", _import="dedent")
    168 def rust_compiler(rustc_info, cargo_info, target):
    169     if not rustc_info:
    170         die(
    171             dedent(
    172                 """\
    173         Rust compiler not found.
    174         To compile rust language sources, you must have 'rustc' in your path.
    175         See https://www.rust-lang.org/ for more information.
    176 
    177         You can install rust by running './mach bootstrap'
    178         or by directly running the installer from https://rustup.rs/
    179         """
    180             )
    181         )
    182     rustc_min_version = Version(MINIMUM_RUST_VERSION)
    183     cargo_min_version = rustc_min_version
    184 
    185     version = rustc_info.version
    186     is_nightly = "nightly" in version.version
    187     is_version_number_match = (
    188         version.major == rustc_min_version.major
    189         and version.minor == rustc_min_version.minor
    190         and version.patch == rustc_min_version.patch
    191     )
    192 
    193     if version < rustc_min_version or (is_version_number_match and is_nightly):
    194         die(
    195             dedent(
    196                 """\
    197         Rust compiler {} is too old.
    198 
    199         To compile Rust language sources please install at least
    200         version {} of the 'rustc' toolchain (or, if using nightly,
    201         at least one version newer than {}) and make sure it is
    202         first in your path.
    203 
    204         You can verify this by typing 'rustc --version'.
    205 
    206         If you have the 'rustup' tool installed you can upgrade
    207         to the latest release by typing 'rustup update'. The
    208         installer is available from https://rustup.rs/
    209         """.format(
    210                     version, rustc_min_version, rustc_min_version
    211                 )
    212             )
    213         )
    214 
    215     if target.kernel == "WINNT" and (version.major, version.minor) == (1, 56):
    216         die(
    217             dedent(
    218                 """\
    219         Rust compiler 1.56.* is not supported for Windows builds.
    220 
    221         Use a newer or an older version.
    222 
    223         See https://github.com/rust-lang/rust/issues/88576.
    224         """
    225             )
    226         )
    227 
    228     if not cargo_info:
    229         die(
    230             dedent(
    231                 """\
    232         Cargo package manager not found.
    233         To compile Rust language sources, you must have 'cargo' in your path.
    234         See https://www.rust-lang.org/ for more information.
    235 
    236         You can install cargo by running './mach bootstrap'
    237         or by directly running the installer from https://rustup.rs/
    238         """
    239             )
    240         )
    241 
    242     version = cargo_info.version
    243     if version < cargo_min_version:
    244         die(
    245             dedent(
    246                 """\
    247         Cargo package manager {} is too old.
    248 
    249         To compile Rust language sources please install at least
    250         version {} of 'cargo' and make sure it is first in your path.
    251 
    252         You can verify this by typing 'cargo --version'.
    253         """
    254             ).format(version, cargo_min_version)
    255         )
    256 
    257     return True
    258 
    259 
    260 @depends(rustc, when=rust_compiler)
    261 @imports(_from="__builtin__", _import="ValueError")
    262 def rust_supported_targets(rustc):
    263     out = check_cmd_output(rustc, "--print", "target-list").splitlines()
    264     data = {}
    265     for t in out:
    266         try:
    267             info = split_triplet(t, allow_wasi=True)
    268         except ValueError:
    269             if t.startswith("thumb"):
    270                 cpu, rest = t.split("-", 1)
    271                 retry = "-".join(("arm", rest))
    272             else:
    273                 continue
    274             try:
    275                 info = split_triplet(retry, allow_wasi=True)
    276             except ValueError:
    277                 continue
    278         key = (info.cpu, info.endianness, info.os)
    279         data.setdefault(key, []).append(namespace(rust_target=t, target=info))
    280     return data
    281 
    282 
    283 def detect_rustc_target(
    284     host_or_target, compiler_info, arm_target, rust_supported_targets
    285 ):
    286     # Rust's --target options are similar to, but not exactly the same
    287     # as, the autoconf-derived targets we use.  An example would be that
    288     # Rust uses distinct target triples for targetting the GNU C++ ABI
    289     # and the MSVC C++ ABI on Win32, whereas autoconf has a single
    290     # triple and relies on the user to ensure that everything is
    291     # compiled for the appropriate ABI.  We need to perform appropriate
    292     # munging to get the correct option to rustc.
    293     # We correlate the autoconf-derived targets with the list of targets
    294     # rustc gives us with --print target-list.
    295     candidates = rust_supported_targets.get(
    296         (host_or_target.cpu, host_or_target.endianness, host_or_target.os), []
    297     )
    298 
    299     def find_candidate(candidates):
    300         if len(candidates) == 1:
    301             return candidates[0].rust_target
    302         elif not candidates:
    303             return None
    304 
    305         # We have multiple candidates. There are two cases where we can try to
    306         # narrow further down using extra information from the build system.
    307         # - For windows targets, correlate with the C compiler type
    308         if host_or_target.kernel == "WINNT":
    309             if host_or_target.abi:
    310                 if host_or_target.abi == "msvc":
    311                     suffix = "windows-msvc"
    312                 elif host_or_target.abi == "mingw":
    313                     suffix = "windows-gnullvm"
    314             elif compiler_info.type in ("gcc", "clang"):
    315                 suffix = "windows-gnullvm"
    316             else:
    317                 suffix = "windows-msvc"
    318             narrowed = [
    319                 c for c in candidates if c.rust_target.endswith("-{}".format(suffix))
    320             ]
    321             if len(narrowed) == 1:
    322                 return narrowed[0].rust_target
    323             elif narrowed:
    324                 candidates = narrowed
    325 
    326             vendor_aliases = {"pc": ("w64", "windows")}
    327             narrowed = [
    328                 c
    329                 for c in candidates
    330                 if host_or_target.vendor in vendor_aliases.get(c.target.vendor, ())
    331             ]
    332 
    333             if len(narrowed) == 1:
    334                 return narrowed[0].rust_target
    335 
    336         # - For arm targets, correlate with arm_target
    337         #   we could be more thorough with the supported rust targets, but they
    338         #   don't support OSes that are supported to build Gecko anyways.
    339         #   Also, sadly, the only interface to check the rust target cpu features
    340         #   is --print target-spec-json, and it's unstable, so we have to rely on
    341         #   our own knowledge of what each arm target means.
    342         if host_or_target.cpu == "arm" and host_or_target.endianness == "little":
    343             prefixes = []
    344             if arm_target.arm_arch >= 7:
    345                 if arm_target.thumb2 and arm_target.fpu == "neon":
    346                     prefixes.append("thumbv7neon")
    347                 if arm_target.thumb2:
    348                     prefixes.append("thumbv7a")
    349                 prefixes.append("armv7")
    350             if arm_target.arm_arch >= 6:
    351                 prefixes.append("armv6")
    352                 if host_or_target.os != "Android":
    353                     # arm-* rust targets are armv6... except arm-linux-androideabi
    354                     prefixes.append("arm")
    355             if arm_target.arm_arch >= 5:
    356                 prefixes.append("armv5te")
    357                 if host_or_target.os == "Android":
    358                     # arm-* rust targets are armv6... except arm-linux-androideabi
    359                     prefixes.append("arm")
    360             if arm_target.arm_arch >= 4:
    361                 prefixes.append("armv4t")
    362             # rust freebsd targets are the only ones that don't have a 'hf' suffix
    363             # for hard-float. Technically, that means if the float abi ever is not
    364             # hard-float, this will pick a wrong target, but since rust only
    365             # supports hard-float, let's assume that means freebsd only support
    366             # hard-float.
    367             if arm_target.float_abi == "hard" and host_or_target.os != "FreeBSD":
    368                 suffix = "hf"
    369             else:
    370                 suffix = ""
    371             for p in prefixes:
    372                 for c in candidates:
    373                     if c.rust_target.startswith(
    374                         "{}-".format(p)
    375                     ) and c.rust_target.endswith(suffix):
    376                         return c.rust_target
    377 
    378         # See if we can narrow down on the exact alias.
    379         # We use the sub_configure_alias to keep support mingw32 triplets as input.
    380         narrowed = [
    381             c
    382             for c in candidates
    383             if c.target.sub_configure_alias == host_or_target.sub_configure_alias
    384         ]
    385         if len(narrowed) == 1:
    386             return narrowed[0].rust_target
    387         elif narrowed:
    388             candidates = narrowed
    389 
    390         # See if we can narrow down with the raw OS
    391         narrowed = [c for c in candidates if c.target.raw_os == host_or_target.raw_os]
    392         if len(narrowed) == 1:
    393             return narrowed[0].rust_target
    394         elif narrowed:
    395             candidates = narrowed
    396 
    397         # The wasm32-wasi target was renamed to wasm32-wasip1
    398         if host_or_target.raw_os == "wasi":
    399             narrowed = [c for c in candidates if c.target.raw_os == "wasip1"]
    400             if len(narrowed) == 1:
    401                 return narrowed[0].rust_target
    402             elif narrowed:
    403                 candidates = narrowed
    404 
    405         # See if we can narrow down with the raw OS and raw CPU
    406         narrowed = [
    407             c
    408             for c in candidates
    409             if c.target.raw_os == host_or_target.raw_os
    410             and c.target.raw_cpu == host_or_target.raw_cpu
    411         ]
    412         if len(narrowed) == 1:
    413             return narrowed[0].rust_target
    414 
    415         # Finally, see if the vendor can be used to disambiguate.
    416         narrowed = [c for c in candidates if c.target.vendor == host_or_target.vendor]
    417         if len(narrowed) == 1:
    418             return narrowed[0].rust_target
    419 
    420         return None
    421 
    422     rustc_target = find_candidate(candidates)
    423 
    424     if rustc_target is None:
    425         die("Don't know how to translate {} for rustc".format(host_or_target.alias))
    426 
    427     return rustc_target
    428 
    429 
    430 @imports("os")
    431 @imports(_from="textwrap", _import="dedent")
    432 @imports(_from="mozbuild.configure.util", _import="LineIO")
    433 @imports(_from="__builtin__", _import="open")
    434 def assert_rust_compile(host_or_target, rustc_target, rustc):
    435     # Check to see whether our rustc has a reasonably functional stdlib
    436     # for our chosen target.
    437     target_arg = "--target=" + rustc_target
    438     with create_temporary_file(suffix=".rs") as in_path, create_temporary_file(
    439         suffix=".rlib"
    440     ) as out_path:
    441         with open(in_path, "w") as fd:
    442             source = b'pub extern "C" fn hello() { println!("Hello world"); }'
    443             log.debug("Creating `%s` with content:", in_path)
    444             with LineIO(lambda l: log.debug("| %s", l)) as out:
    445                 out.write(source)
    446 
    447             fd.write(source.decode())
    448 
    449         cmd = [
    450             rustc,
    451             "--crate-type",
    452             "staticlib",
    453             target_arg,
    454             "-o",
    455             out_path,
    456             in_path,
    457         ]
    458 
    459         def failed():
    460             die(
    461                 dedent(
    462                     """\
    463             Cannot compile for {} with {}
    464             The target may be unsupported, or you may not have
    465             a rust std library for that target installed. Try:
    466 
    467               rustup target add {}
    468             """.format(
    469                         host_or_target.alias, rustc, rustc_target
    470                     )
    471                 )
    472             )
    473 
    474         check_cmd_output(*cmd, onerror=failed)
    475         if not os.path.exists(out_path) or os.path.getsize(out_path) == 0:
    476             failed()
    477 
    478 
    479 @depends(
    480     rustc,
    481     host,
    482     host_c_compiler,
    483     rustc_info.host,
    484     rust_supported_targets,
    485     arm_target,
    486     when=rust_compiler,
    487 )
    488 @checking("for rust host triplet")
    489 @imports(_from="textwrap", _import="dedent")
    490 def rust_host_triple(
    491     rustc, host, compiler_info, rustc_host, rust_supported_targets, arm_target
    492 ):
    493     rustc_target = detect_rustc_target(
    494         host, compiler_info, arm_target, rust_supported_targets
    495     )
    496     if rustc_target != rustc_host:
    497         if host.alias == rustc_target:
    498             configure_host = host.alias
    499         else:
    500             configure_host = "{}/{}".format(host.alias, rustc_target)
    501         die(
    502             dedent(
    503                 """\
    504         The rust compiler host ({rustc}) is not suitable for the configure host ({configure}).
    505 
    506         To resolve this, install and select a Rust toolchain for {rustc_target}:
    507           rustup default stable-{rustc_target}
    508 
    509         Then rerun configure.
    510         """.format(
    511                     rustc=rustc_host,
    512                     configure=configure_host,
    513                     rustc_target=rustc_target,
    514                 )
    515             )
    516         )
    517     assert_rust_compile(host, rustc_target, rustc)
    518     return rustc_target
    519 
    520 
    521 @depends(
    522     rustc, target, c_compiler, rust_supported_targets, arm_target, when=rust_compiler
    523 )
    524 @checking("for rust target triplet")
    525 def rust_target_triple(
    526     rustc, target, compiler_info, rust_supported_targets, arm_target
    527 ):
    528     rustc_target = detect_rustc_target(
    529         target, compiler_info, arm_target, rust_supported_targets
    530     )
    531     assert_rust_compile(target, rustc_target, rustc)
    532     return rustc_target
    533 
    534 
    535 set_config("RUST_TARGET", rust_target_triple)
    536 set_config("RUST_HOST_TARGET", rust_host_triple)
    537 
    538 
    539 # This is used for putting source info into symbol files.
    540 set_config("RUSTC_COMMIT", depends(rustc_info)(lambda i: i.commit))
    541 
    542 # Rustdoc is required by Rust tests below.
    543 option(env="RUSTDOC", nargs=1, help="Path to the rustdoc program")
    544 
    545 rustdoc = check_prog(
    546     "RUSTDOC",
    547     ["rustdoc"],
    548     paths=rust_search_path,
    549     input="RUSTDOC",
    550     allow_missing=True,
    551 )
    552 
    553 option(
    554     env="RUSTDOCFLAGS",
    555     nargs=1,
    556     help="Extra options for the rustdoc program",
    557 )
    558 set_config("RUSTDOCFLAGS", depends_if("RUSTDOCFLAGS")(lambda flags: flags[0]))
    559 
    560 # This option is separate from --enable-tests because Rust tests are particularly
    561 # expensive in terms of compile time (especially for code in libxul).
    562 option(
    563     "--enable-rust-tests",
    564     help="Enable building and running of Rust tests during `make check`",
    565 )
    566 
    567 
    568 @depends("--enable-rust-tests", rustdoc)
    569 def rust_tests(enable_rust_tests, rustdoc):
    570     if enable_rust_tests and not rustdoc:
    571         die("--enable-rust-tests requires rustdoc")
    572     return bool(enable_rust_tests)
    573 
    574 
    575 set_config("MOZ_RUST_TESTS", rust_tests)
    576 
    577 
    578 @depends(target, c_compiler, rustc)
    579 @imports("os")
    580 def rustc_natvis_ldflags(target, compiler_info, rustc):
    581     if target.kernel == "WINNT" and compiler_info.type == "clang-cl":
    582         sysroot = check_cmd_output(rustc, "--print", "sysroot").strip()
    583         etc = os.path.join(sysroot, "lib/rustlib/etc")
    584         ldflags = []
    585         if os.path.isdir(etc):
    586             for f in os.listdir(etc):
    587                 if f.endswith(".natvis"):
    588                     ldflags.append("-NATVIS:" + normsep(os.path.join(etc, f)))
    589         return ldflags
    590 
    591 
    592 set_config("RUSTC_NATVIS_LDFLAGS", rustc_natvis_ldflags)
    593 
    594 
    595 option(
    596     "--enable-rust-debug",
    597     default=depends(when="--enable-debug")(lambda: True),
    598     help="{Build|Do not build} Rust code with debug assertions turned on",
    599 )
    600 
    601 
    602 @depends(when="--enable-rust-debug")
    603 def debug_rust():
    604     return True
    605 
    606 
    607 set_config("MOZ_DEBUG_RUST", debug_rust)
    608 set_define("MOZ_DEBUG_RUST", debug_rust)
    609 
    610 # ==============================================================
    611 
    612 option(env="RUSTFLAGS", nargs=1, help="Rust compiler flags")
    613 set_config("RUSTFLAGS", depends("RUSTFLAGS")(lambda flags: flags))
    614 
    615 
    616 # Rust compiler flags
    617 # ==============================================================
    618 
    619 
    620 @depends(moz_optimize)
    621 def rustc_opt_level_default(moz_optimize):
    622     return "2" if moz_optimize else "0"
    623 
    624 
    625 option(
    626     env="RUSTC_OPT_LEVEL",
    627     default=rustc_opt_level_default,
    628     nargs=1,
    629     help="Rust compiler optimization level (-C opt-level=%s)",
    630 )
    631 
    632 
    633 @depends("RUSTC_OPT_LEVEL")
    634 def rustc_opt_level(opt_level_option):
    635     return opt_level_option[0]
    636 
    637 
    638 set_config("CARGO_PROFILE_RELEASE_OPT_LEVEL", rustc_opt_level)
    639 set_config("CARGO_PROFILE_DEV_OPT_LEVEL", rustc_opt_level)
    640 
    641 
    642 @depends(
    643     rustc_opt_level,
    644     debug_rust,
    645     target,
    646     "--enable-debug-symbols",
    647     "--enable-frame-pointers",
    648     path_remapping,
    649     path_remappings,
    650 )
    651 def rust_compile_flags(
    652     opt_level,
    653     debug_rust,
    654     target,
    655     debug_symbols,
    656     frame_pointers,
    657     path_remapping,
    658     path_remappings,
    659 ):
    660     # Cargo currently supports only two interesting profiles for building:
    661     # development and release. Those map (roughly) to --enable-debug and
    662     # --disable-debug in Gecko, respectively.
    663     #
    664     # But we'd also like to support an additional axis of control for
    665     # optimization level. Since Cargo only supports 2 profiles, we're in
    666     # a bit of a bind.
    667     #
    668     # Code here derives various compiler options given other configure options.
    669     # The options defined here effectively override defaults specified in
    670     # Cargo.toml files.
    671 
    672     debug_assertions = None
    673     debug_info = None
    674 
    675     # opt-level=0 implies -C debug-assertions, which may not be desired
    676     # unless Rust debugging is enabled.
    677     if opt_level == "0" and not debug_rust:
    678         debug_assertions = False
    679 
    680     if debug_symbols:
    681         debug_info = "2"
    682 
    683     opts = []
    684 
    685     if debug_assertions is not None:
    686         opts.append("debug-assertions=%s" % ("yes" if debug_assertions else "no"))
    687     if debug_info is not None:
    688         opts.append("debuginfo=%s" % debug_info)
    689     if frame_pointers:
    690         opts.append("force-frame-pointers=yes")
    691     # CFG for arm64 is crashy, see `def security_hardening_cflags`.
    692     if target.kernel == "WINNT" and target.cpu != "aarch64":
    693         opts.append("control-flow-guard=yes")
    694 
    695     flags = []
    696     for opt in opts:
    697         flags.extend(["-C", opt])
    698 
    699     if "rust" in path_remapping:
    700         # rustc has supported --remap-path-prefix since version 1.26, well
    701         # before our required minimum Rust version, so there's no need to
    702         # feature-detect or gate on versions.
    703         for old, new in path_remappings:
    704             flags.append(f"--remap-path-prefix={old}={new}")
    705 
    706     return flags
    707 
    708 
    709 # Rust incremental compilation
    710 # ==============================================================
    711 
    712 
    713 option("--disable-cargo-incremental", help="Disable incremental rust compilation")
    714 
    715 
    716 @depends(
    717     developer_options,
    718     debug_rust,
    719     moz_automation,
    720     code_coverage,
    721     "--disable-cargo-incremental",
    722     using_sccache,
    723     "RUSTC_WRAPPER",
    724 )
    725 @imports("os")
    726 def cargo_incremental(
    727     developer_options,
    728     debug_rust,
    729     automation,
    730     code_coverage,
    731     enabled,
    732     using_sccache,
    733     rustc_wrapper,
    734 ):
    735     """Return a value for the CARGO_INCREMENTAL environment variable."""
    736 
    737     if not enabled:
    738         return "0"
    739     elif enabled.origin != "default":
    740         return "1"
    741 
    742     # We never want to use incremental compilation in automation.  sccache
    743     # handles our automation use case much better than incremental compilation
    744     # would.
    745     if automation:
    746         return "0"
    747 
    748     # Coverage instrumentation doesn't play well with incremental compilation
    749     # https://github.com/rust-lang/rust/issues/50203.
    750     if code_coverage:
    751         return "0"
    752 
    753     # Incremental compilation doesn't work as well as it should, and if we're
    754     # using sccache, it's better to use sccache than incremental compilation.
    755     if not using_sccache and rustc_wrapper:
    756         rustc_wrapper = os.path.basename(rustc_wrapper[0])
    757         if os.path.splitext(rustc_wrapper)[0].lower() == "sccache":
    758             using_sccache = True
    759     if using_sccache:
    760         return "0"
    761 
    762     # Incremental compilation is automatically turned on for debug builds, so
    763     # we don't need to do anything special here.
    764     if debug_rust:
    765         return
    766 
    767     # Don't enable on --enable-release builds, because of the runtime
    768     # performance cost.
    769     if not developer_options:
    770         return
    771 
    772     # We're clear to use incremental compilation!
    773     return "1"
    774 
    775 
    776 set_config("CARGO_INCREMENTAL", cargo_incremental)
    777 
    778 
    779 @depends(rust_compile_flags, "--enable-warnings-as-errors")
    780 def rust_flags(compile_flags, warnings_as_errors):
    781     warning_flags = []
    782 
    783     # Note that cargo passes --cap-lints warn to rustc for third-party code, so
    784     # we don't need a very complicated setup.
    785     if warnings_as_errors:
    786         warning_flags.append("-Dwarnings")
    787     else:
    788         warning_flags.extend(("--cap-lints", "warn"))
    789 
    790     return compile_flags + warning_flags
    791 
    792 
    793 set_config("MOZ_RUST_DEFAULT_FLAGS", rust_flags)