From e1e8913d858abe71bb2ad03c265e07783500c04f Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Fri, 31 Oct 2025 11:41:36 -0400 Subject: [PATCH] bootstrap: Use cargo's `build.warnings=deny` rather than -Dwarnings This has two major advantages. First, it makes us less dependent on the rustc shim, which is nice but not super important. More importantly, it gives us much nicer caching properties. Previously, `x build --warnings warn && x build --warnings deny` would rebuild all of bootstrap, and if there were any warnings emitted on the last build of the compiler, they would *not* fail the build, because cargo would cache the output rather than rerunning the shim. After this change, bootstrap rebuilds instantly, and cargo knows that it should fail the build but *without* invalidating the cache. Here is an example of rebuilding bootstrap after a switch from warn->deny: ``` INFO: Downloading and building bootstrap before processing --help command. See src/bootstrap/README.md for help with common commands. Building bootstrap Compiling bootstrap v0.0.0 (/Users/jyn/src/ferrocene3/src/bootstrap) warning: unused variable: `x` --> src/bootstrap/src/core/builder/mod.rs:1792:13 | 1792 | let x: (); | ^ | = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default help: if this is intentional, prefix it with an underscore | 1792 | let _x: (); | + help: you might have meant to pattern match on the similarly named constant `_` | 1792 - let x: (); 1792 + let utils::render_tests::_: (); | warning: `bootstrap` (lib) generated 1 warning (run `cargo fix --lib -p bootstrap` to apply 1 suggestion) Finished `dev` profile [unoptimized] target(s) in 5.14s error: warnings are denied by `build.warnings` configuration failed to run: /Users/jyn/src/ferrocene3/build/aarch64-apple-darwin/stage0/bin/cargo build --jobs=default --manifest-path /Users/jyn/src/ferrocene3/src/bootstrap/Cargo.toml -Zroot-dir=/Users/jyn/src/ferrocene3 -Zwarnings ``` building the compiler from scratch with `deny`: https://gist.github.com/jyn514/493ed26c2aa6f61bf47c21e75efa2175 and rebuilding the compiler after switching from deny->warn (note that it reuses the whole cache, there are no invalidations): ``` $ x c compiler Building bootstrap Finished `dev` profile [unoptimized] target(s) in 0.15s Checking stage1 compiler artifacts{rustc-main, rustc_abi, rustc_arena, rustc_ast, rustc_ast_ir, rustc_ast_lowering, rustc_ast_passes, rustc_ast_pretty, rustc_attr_parsing, rustc_baked_icu_data, rustc_borrowck, rustc_builtin_macros, rustc_codegen_llvm, rustc_codegen_ssa, rustc_const_eval, rustc_data_structures, rustc_driver, rustc_driver_impl, rustc_error_codes, rustc_error_messages, rustc_errors, rustc_expand, rustc_feature, rustc_fluent_macro, rustc_fs_util, rustc_graphviz, rustc_hashes, rustc_hir, rustc_hir_analysis, rustc_hir_id, rustc_hir_pretty, rustc_hir_typeck, rustc_incremental, rustc_index, rustc_index_macros, rustc_infer, rustc_interface, rustc_lexer, rustc_lint, rustc_lint_defs, rustc_llvm, rustc_log, rustc_macros, rustc_metadata, rustc_middle, rustc_mir_build, rustc_mir_dataflow, rustc_mir_transform, rustc_monomorphize, rustc_next_trait_solver, rustc_parse, rustc_parse_format, rustc_passes, rustc_pattern_analysis, rustc_privacy, rustc_proc_macro, rustc_public, rustc_public_bridge, rustc_query_impl, rustc_query_system, rustc_resolve, rustc_sanitizers, rustc_serialize, rustc_session, rustc_span, rustc_symbol_mangling, rustc_target, rustc_thread_pool, rustc_trait_selection, rustc_traits, rustc_transmute, rustc_ty_utils, rustc_type_ir, rustc_type_ir_macros, rustc_windows_rc} (stage0 -> stage1, aarch64-apple-darwin) warning: function `foo` is never used --> compiler/rustc_hashes/src/lib.rs:16:4 | 16 | fn foo() {} | ^^^ | = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default warning: `rustc_hashes` (lib) generated 1 warning Finished `release` profile [optimized + debuginfo] target(s) in 0.53s Build completed successfully in 0:00:08 ``` --- src/bootstrap/bootstrap.py | 32 ++++++++++++------------- src/bootstrap/bootstrap_test.py | 10 ++++---- src/bootstrap/src/core/builder/cargo.rs | 11 +++++++-- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 4dd465edb0df9..2e16f2cf27e7b 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1113,6 +1113,19 @@ def build_bootstrap_cmd(self, env): else: env["RUSTFLAGS"] = "-Zallow-features=" + if not os.path.isfile(self.cargo()): + raise Exception("no cargo executable found at `{}`".format(self.cargo())) + args = [ + self.cargo(), + "build", + "--jobs=" + self.jobs, + "--manifest-path", + os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"), + "-Zroot-dir=" + self.rust_root, + ] + # verbose cargo output is very noisy, so only enable it with -vv + args.extend("--verbose" for _ in range(self.verbose - 1)) + target_features = [] if self.get_toml("crt-static", build_section) == "true": target_features += ["+crt-static"] @@ -1131,28 +1144,15 @@ def build_bootstrap_cmd(self, env): else: deny_warnings = self.warnings == "deny" if deny_warnings: - env["RUSTFLAGS"] += " -Dwarnings" + args += ["-Zwarnings"] + env["CARGO_BUILD_WARNINGS"] = "deny" # Add RUSTFLAGS_BOOTSTRAP to RUSTFLAGS for bootstrap compilation. # Note that RUSTFLAGS_BOOTSTRAP should always be added to the end of - # RUSTFLAGS to be actually effective (e.g., if we have `-Dwarnings` in - # RUSTFLAGS, passing `-Awarnings` from RUSTFLAGS_BOOTSTRAP should override it). + # RUSTFLAGS, since that causes RUSTFLAGS_BOOTSTRAP to override RUSTFLAGS. if "RUSTFLAGS_BOOTSTRAP" in env: env["RUSTFLAGS"] += " " + env["RUSTFLAGS_BOOTSTRAP"] - if not os.path.isfile(self.cargo()): - raise Exception("no cargo executable found at `{}`".format(self.cargo())) - args = [ - self.cargo(), - "build", - "--jobs=" + self.jobs, - "--manifest-path", - os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"), - "-Zroot-dir=" + self.rust_root, - ] - # verbose cargo output is very noisy, so only enable it with -vv - args.extend("--verbose" for _ in range(self.verbose - 1)) - if "BOOTSTRAP_TRACING" in env: args.append("--features=tracing") diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py index 9e12982a43dc3..1dbe997d23f3a 100644 --- a/src/bootstrap/bootstrap_test.py +++ b/src/bootstrap/bootstrap_test.py @@ -244,8 +244,10 @@ def test_warnings(self): if toml_warnings is not None: configure_args = ["--set", "rust.deny-warnings=" + toml_warnings] - _, env = self.build_args(configure_args, args=["--warnings=warn"]) - self.assertFalse("-Dwarnings" in env["RUSTFLAGS"]) + args, env = self.build_args(configure_args, args=["--warnings=warn"]) + self.assertFalse("CARGO_BUILD_WARNINGS" in env) + self.assertFalse("-Zwarnings" in args) - _, env = self.build_args(configure_args, args=["--warnings=deny"]) - self.assertTrue("-Dwarnings" in env["RUSTFLAGS"]) + args, env = self.build_args(configure_args, args=["--warnings=deny"]) + self.assertEqual("deny", env["CARGO_BUILD_WARNINGS"]) + self.assertTrue("-Zwarnings" in args) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index c2029f97391d4..76eb03c8d499d 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -459,6 +459,11 @@ impl Builder<'_> { let out_dir = self.stage_out(compiler, mode); cargo.env("CARGO_TARGET_DIR", &out_dir); + // Set this unconditionally. Cargo silently ignores `CARGO_BUILD_WARNINGS` when `-Z + // warnings` isn't present, which is hard to debug, and it's not worth the effort to keep + // them in sync. + cargo.arg("-Zwarnings"); + // Bootstrap makes a lot of assumptions about the artifacts produced in the target // directory. If users override the "build directory" using `build-dir` // (https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-dir), then @@ -1159,8 +1164,10 @@ impl Builder<'_> { lint_flags.push("-Wunused_lifetimes"); if self.config.deny_warnings { - lint_flags.push("-Dwarnings"); - rustdocflags.arg("-Dwarnings"); + // We use this instead of `lint_flags` so that we don't have to rebuild all + // workspace dependencies when `deny-warnings` changes, but we still get an error + // immediately instead of having to wait until the next rebuild. + cargo.env("CARGO_BUILD_WARNINGS", "deny"); } rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");