about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
authorThe Miri Cronjob Bot <miri@cron.bot>2025-09-05 05:00:55 +0000
committerThe Miri Cronjob Bot <miri@cron.bot>2025-09-05 05:00:55 +0000
commit7c5fdb072e3d07674ba88153a4cac4f792c20fdc (patch)
tree78897a875065b81c78004b1cd0f83e78254fb11d /src/bootstrap
parent9a1eb85394e3612213e829ed871eb281079a2364 (diff)
parentb6f8824315367f73e43efe08f6e5a88cc3f117ce (diff)
downloadrust-7c5fdb072e3d07674ba88153a4cac4f792c20fdc.tar.gz
rust-7c5fdb072e3d07674ba88153a4cac4f792c20fdc.zip
Merge ref 'b3cfb8faf84c' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7
Filtered ref: 8995aa7743caf019203bc853f27af6006705ae30
Upstream diff: https://github.com/rust-lang/rust/compare/9385c64c95d971329e62917adc4349c8ccdbafe0...b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7

This merge was created using https://github.com/rust-lang/josh-sync.
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/Cargo.lock138
-rw-r--r--src/bootstrap/Cargo.toml4
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs19
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs21
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs20
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs162
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs18
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs30
-rw-r--r--src/bootstrap/src/core/builder/tests.rs45
-rw-r--r--src/bootstrap/src/core/config/config.rs3
-rw-r--r--src/bootstrap/src/core/config/toml/rust.rs2
-rw-r--r--src/bootstrap/src/lib.rs23
-rw-r--r--src/bootstrap/src/utils/cc_detect/tests.rs21
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
17 files changed, 317 insertions, 202 deletions
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index 1535d087033..fe832652d25 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -269,14 +269,14 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
 name = "filetime"
-version = "0.2.25"
+version = "0.2.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
+checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed"
 dependencies = [
  "cfg-if",
  "libc",
  "libredox",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -322,11 +322,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "home"
-version = "0.5.9"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -364,12 +364,12 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "junction"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16"
+checksum = "c52f6e1bf39a7894f618c9d378904a11dbd7e10fe3ec20d1173600e79b1408d8"
 dependencies = [
  "scopeguard",
- "windows-sys 0.52.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -434,6 +434,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
 [[package]]
+name = "normpath"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
 name = "ntapi"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -497,12 +506,13 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 
 [[package]]
 name = "opener"
-version = "0.5.2"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005"
+checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223"
 dependencies = [
  "bstr",
- "winapi",
+ "normpath",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -757,15 +767,15 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.19.0"
+version = "3.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600"
+checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
 dependencies = [
  "fastrand",
  "getrandom",
  "once_cell",
  "rustix",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -929,11 +939,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
 dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -1045,20 +1055,20 @@ dependencies = [
 
 [[package]]
 name = "windows-sys"
-version = "0.52.0"
+version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "windows-sys"
-version = "0.59.0"
+version = "0.60.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.53.2",
 ]
 
 [[package]]
@@ -1067,14 +1077,30 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
+dependencies = [
+ "windows_aarch64_gnullvm 0.53.0",
+ "windows_aarch64_msvc 0.53.0",
+ "windows_i686_gnu 0.53.0",
+ "windows_i686_gnullvm 0.53.0",
+ "windows_i686_msvc 0.53.0",
+ "windows_x86_64_gnu 0.53.0",
+ "windows_x86_64_gnullvm 0.53.0",
+ "windows_x86_64_msvc 0.53.0",
 ]
 
 [[package]]
@@ -1084,48 +1110,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
+[[package]]
 name = "windows_aarch64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
+[[package]]
 name = "windows_i686_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
+name = "windows_i686_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
+[[package]]
 name = "windows_i686_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
+[[package]]
 name = "windows_i686_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
+name = "windows_i686_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
+[[package]]
 name = "windows_x86_64_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
+[[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
+[[package]]
 name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
+[[package]]
 name = "wit-bindgen-rt"
 version = "0.39.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index c7175584465..9a76a7dda2a 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -42,7 +42,7 @@ home = "0.5"
 ignore = "0.4"
 libc = "0.2"
 object = { version = "0.36.3", default-features = false, features = ["archive", "coff", "read_core", "std", "unaligned"] }
-opener = "0.5"
+opener = "0.8"
 semver = "1.0"
 serde = "1.0"
 # Directly use serde_derive rather than through the derive feature of serde to allow building both
@@ -67,7 +67,7 @@ tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter
 tempfile = { version = "3.15.0", optional = true }
 
 [target.'cfg(windows)'.dependencies.junction]
-version = "1.0.0"
+version = "1.3.0"
 
 [target.'cfg(windows)'.dependencies.windows]
 version = "0.61"
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index a604e7c0585..49d12b64da5 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -389,7 +389,7 @@ impl Step for Rustc {
 
 /// Represents a compiler that can check something.
 ///
-/// If the compiler was created for `Mode::ToolRustc` or `Mode::Codegen`, it will also contain
+/// If the compiler was created for `Mode::ToolRustcPrivate` or `Mode::Codegen`, it will also contain
 /// .rmeta artifacts from rustc that was already checked using `build_compiler`.
 ///
 /// All steps that use this struct in a "general way" (i.e. they don't know exactly what kind of
@@ -469,7 +469,7 @@ pub fn prepare_compiler_for_check(
                 build_compiler
             }
         }
-        Mode::ToolRustc | Mode::Codegen => {
+        Mode::ToolRustcPrivate | Mode::Codegen => {
             // Check Rustc to produce the required rmeta artifacts for rustc_private, and then
             // return the build compiler that was used to check rustc.
             // We do not need to check examples/tests/etc. of Rustc for rustc_private, so we pass
@@ -767,19 +767,22 @@ fn run_tool_check_step(
 tool_check_step!(Rustdoc {
     path: "src/tools/rustdoc",
     alt_path: "src/librustdoc",
-    mode: |_builder| Mode::ToolRustc
+    mode: |_builder| Mode::ToolRustcPrivate
 });
 // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
 // of a submodule. Since the SourceType only drives the deny-warnings
 // behavior, treat it as in-tree so that any new warnings in clippy will be
 // rejected.
-tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustc });
-tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustc });
-tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: |_builder| Mode::ToolRustc });
-tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustc });
+tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustcPrivate });
+tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustcPrivate });
+tool_check_step!(CargoMiri {
+    path: "src/tools/miri/cargo-miri",
+    mode: |_builder| Mode::ToolRustcPrivate
+});
+tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustcPrivate });
 tool_check_step!(RustAnalyzer {
     path: "src/tools/rust-analyzer",
-    mode: |_builder| Mode::ToolRustc,
+    mode: |_builder| Mode::ToolRustcPrivate,
     allow_features: tool::RustAnalyzer::ALLOW_FEATURES,
     enable_features: ["in-rust-tree"],
 });
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index 05f8b240291..2083c675e1f 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -366,8 +366,13 @@ impl Step for CodegenGcc {
         );
         self.build_compiler.configure_cargo(&mut cargo);
 
-        let _guard =
-            builder.msg(Kind::Clippy, "rustc_codegen_gcc", Mode::ToolRustc, build_compiler, target);
+        let _guard = builder.msg(
+            Kind::Clippy,
+            "rustc_codegen_gcc",
+            Mode::ToolRustcPrivate,
+            build_compiler,
+            target,
+        );
 
         let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::Codegen, target))
             .with_prefix("rustc_codegen_gcc-check");
@@ -478,8 +483,8 @@ lint_any!(
     Bootstrap, "src/bootstrap", "bootstrap", Mode::ToolTarget;
     BuildHelper, "src/build_helper", "build_helper", Mode::ToolTarget;
     BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolTarget;
-    CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustc;
-    Clippy, "src/tools/clippy", "clippy", Mode::ToolRustc;
+    CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", Mode::ToolRustcPrivate;
+    Clippy, "src/tools/clippy", "clippy", Mode::ToolRustcPrivate;
     CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata", Mode::ToolTarget;
     Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolTarget;
     CoverageDump, "src/tools/coverage-dump", "coverage-dump", Mode::ToolTarget;
@@ -487,14 +492,14 @@ lint_any!(
     Jsondoclint, "src/tools/jsondoclint", "jsondoclint", Mode::ToolTarget;
     LintDocs, "src/tools/lint-docs", "lint-docs", Mode::ToolTarget;
     LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", Mode::ToolTarget;
-    Miri, "src/tools/miri", "miri", Mode::ToolRustc;
+    Miri, "src/tools/miri", "miri", Mode::ToolRustcPrivate;
     MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools", Mode::ToolTarget;
     OptDist, "src/tools/opt-dist", "opt-dist", Mode::ToolTarget;
     RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolTarget;
     RemoteTestServer, "src/tools/remote-test-server", "remote-test-server", Mode::ToolTarget;
-    RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustc;
-    Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustc;
-    Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustc;
+    RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer", Mode::ToolRustcPrivate;
+    Rustdoc, "src/librustdoc", "clippy", Mode::ToolRustcPrivate;
+    Rustfmt, "src/tools/rustfmt", "rustfmt", Mode::ToolRustcPrivate;
     RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::ToolTarget;
     Tidy, "src/tools/tidy", "tidy", Mode::ToolTarget;
     TestFloatParse, "src/tools/test-float-parse", "test-float-parse", Mode::ToolStd;
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index f113dd7683d..820dda5a652 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -823,6 +823,18 @@ pub struct RustcDev {
     target: TargetSelection,
 }
 
+impl RustcDev {
+    pub fn new(builder: &Builder<'_>, target: TargetSelection) -> Self {
+        Self {
+            // We currently always ship a stage 2 rustc-dev component, so we build it with the
+            // stage 1 compiler. This might change in the future.
+            // The precise stage used here is important, so we hard-code it.
+            build_compiler: builder.compiler(1, builder.config.host_target),
+            target,
+        }
+    }
+}
+
 impl Step for RustcDev {
     type Output = Option<GeneratedTarball>;
     const DEFAULT: bool = true;
@@ -833,13 +845,7 @@ impl Step for RustcDev {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(RustcDev {
-            // We currently always ship a stage 2 rustc-dev component, so we build it with the
-            // stage 1 compiler. This might change in the future.
-            // The precise stage used here is important, so we hard-code it.
-            build_compiler: run.builder.compiler(1, run.builder.config.host_target),
-            target: run.target,
-        });
+        run.builder.ensure(RustcDev::new(run.builder, run.target));
     }
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 0789eefa894..eb198a0051a 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -995,7 +995,7 @@ macro_rules! tool_doc {
                     // Build rustc docs so that we generate relative links.
                     run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
 
-                    (compilers.build_compiler(), Mode::ToolRustc)
+                    (compilers.build_compiler(), Mode::ToolRustcPrivate)
                 } else {
                     // bootstrap/host tools have to be documented with the stage 0 compiler
                     (prepare_doc_compiler(run.builder, run.builder.host_target, 1), Mode::ToolBootstrap)
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index d47c1495838..83ed7430c39 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -205,6 +205,7 @@ pub(crate) fn is_ci_llvm_available_for_target(
         // tier 1
         ("aarch64-unknown-linux-gnu", false),
         ("aarch64-apple-darwin", false),
+        ("aarch64-pc-windows-msvc", false),
         ("i686-pc-windows-gnu", false),
         ("i686-pc-windows-msvc", false),
         ("i686-unknown-linux-gnu", false),
@@ -213,7 +214,6 @@ pub(crate) fn is_ci_llvm_available_for_target(
         ("x86_64-pc-windows-gnu", true),
         ("x86_64-pc-windows-msvc", true),
         // tier 2 with host tools
-        ("aarch64-pc-windows-msvc", false),
         ("aarch64-unknown-linux-musl", false),
         ("arm-unknown-linux-gnueabi", false),
         ("arm-unknown-linux-gnueabihf", false),
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index d9de6b7ef96..9f7248b80f7 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -161,7 +161,7 @@ impl Step for Miri {
         let mut miri = tool::prepare_tool_cargo(
             builder,
             compilers.build_compiler(),
-            Mode::ToolRustc,
+            Mode::ToolRustcPrivate,
             host,
             Kind::Run,
             "src/tools/miri",
@@ -487,7 +487,7 @@ impl Step for Rustfmt {
         let mut rustfmt = tool::prepare_tool_cargo(
             builder,
             rustfmt_build.build_compiler,
-            Mode::ToolRustc,
+            Mode::ToolRustcPrivate,
             host,
             Kind::Run,
             "src/tools/rustfmt",
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index ee2cbe9385e..cb81d738666 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -455,7 +455,7 @@ impl Step for RustAnalyzer {
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             self.compilers.build_compiler(),
-            Mode::ToolRustc,
+            Mode::ToolRustcPrivate,
             host,
             Kind::Test,
             crate_path,
@@ -518,7 +518,7 @@ impl Step for Rustfmt {
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             build_compiler,
-            Mode::ToolRustc,
+            Mode::ToolRustcPrivate,
             target,
             Kind::Test,
             "src/tools/rustfmt",
@@ -571,7 +571,8 @@ impl Miri {
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
 
         let mut cargo = BootstrapCommand::from(cargo);
-        let _guard = builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustc, compiler, target);
+        let _guard =
+            builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustcPrivate, compiler, target);
         cargo.run(builder);
 
         // # Determine where Miri put its sysroot.
@@ -648,7 +649,7 @@ impl Step for Miri {
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             miri.build_compiler,
-            Mode::ToolRustc,
+            Mode::ToolRustcPrivate,
             host,
             Kind::Test,
             "src/tools/miri",
@@ -861,7 +862,7 @@ impl Step for Clippy {
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             build_compiler,
-            Mode::ToolRustc,
+            Mode::ToolRustcPrivate,
             target,
             Kind::Test,
             "src/tools/clippy",
@@ -872,7 +873,7 @@ impl Step for Clippy {
         cargo.env("RUSTC_TEST_SUITE", builder.rustc(build_compiler));
         cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(build_compiler));
         let host_libs =
-            builder.stage_out(build_compiler, Mode::ToolRustc).join(builder.cargo_dir());
+            builder.stage_out(build_compiler, Mode::ToolRustcPrivate).join(builder.cargo_dir());
         cargo.env("HOST_LIBS", host_libs);
 
         // Build the standard library that the tests can use.
@@ -2411,7 +2412,7 @@ impl BookTest {
         let libs = if !self.dependencies.is_empty() {
             let mut lib_paths = vec![];
             for dep in self.dependencies {
-                let mode = Mode::ToolRustc;
+                let mode = Mode::ToolRustcPrivate;
                 let target = builder.config.host_target;
                 let cargo = tool::prepare_tool_cargo(
                     builder,
@@ -2996,7 +2997,7 @@ impl Step for CrateRustdoc {
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             compiler,
-            Mode::ToolRustc,
+            Mode::ToolRustcPrivate,
             target,
             builder.kind,
             "src/tools/rustdoc",
@@ -3182,6 +3183,7 @@ impl Step for Distcheck {
     ///   check steps from those sources.
     /// - Check that selected dist components (`rust-src` only at the moment) at least have expected
     ///   directory shape and crate manifests that cargo can generate a lockfile from.
+    /// - Check that we can run `cargo metadata` on the workspace in the `rustc-dev` component
     ///
     /// FIXME(#136822): dist components are under-tested.
     fn run(self, builder: &Builder<'_>) {
@@ -3189,61 +3191,91 @@ impl Step for Distcheck {
         // local source code, built artifacts or configuration by accident
         let root_dir = std::env::temp_dir().join("distcheck");
 
-        // Check that we can build some basic things from the plain source tarball
-        builder.info("Distcheck plain source tarball");
-        let plain_src_tarball = builder.ensure(dist::PlainSourceTarball);
-        let plain_src_dir = root_dir.join("distcheck-plain-src");
-        builder.clear_dir(&plain_src_dir);
-
-        let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS")
-            .map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>())
-            .unwrap_or_default();
-
-        command("tar")
-            .arg("-xf")
-            .arg(plain_src_tarball.tarball())
-            .arg("--strip-components=1")
-            .current_dir(&plain_src_dir)
-            .run(builder);
-        command("./configure")
-            .arg("--set")
-            .arg("rust.omit-git-hash=false")
-            .args(&configure_args)
-            .arg("--enable-vendor")
-            .current_dir(&plain_src_dir)
-            .run(builder);
-        command(helpers::make(&builder.config.host_target.triple))
-            .arg("check")
-            // Do not run the build as if we were in CI, otherwise git would be assumed to be
-            // present, but we build from a tarball here
-            .env("GITHUB_ACTIONS", "0")
-            .current_dir(&plain_src_dir)
-            .run(builder);
-
-        // Now make sure that rust-src has all of libstd's dependencies
-        builder.info("Distcheck rust-src");
-        let src_tarball = builder.ensure(dist::Src);
-        let src_dir = root_dir.join("distcheck-src");
-        builder.clear_dir(&src_dir);
-
-        command("tar")
-            .arg("-xf")
-            .arg(src_tarball.tarball())
-            .arg("--strip-components=1")
-            .current_dir(&src_dir)
-            .run(builder);
-
-        let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
-        command(&builder.initial_cargo)
-            // Will read the libstd Cargo.toml
-            // which uses the unstable `public-dependency` feature.
-            .env("RUSTC_BOOTSTRAP", "1")
-            .arg("generate-lockfile")
-            .arg("--manifest-path")
-            .arg(&toml)
-            .current_dir(&src_dir)
-            .run(builder);
-    }
+        distcheck_plain_source_tarball(builder, &root_dir.join("distcheck-rustc-src"));
+        distcheck_rust_src(builder, &root_dir.join("distcheck-rust-src"));
+        distcheck_rustc_dev(builder, &root_dir.join("distcheck-rustc-dev"));
+    }
+}
+
+/// Check that we can build some basic things from the plain source tarball
+fn distcheck_plain_source_tarball(builder: &Builder<'_>, plain_src_dir: &Path) {
+    builder.info("Distcheck plain source tarball");
+    let plain_src_tarball = builder.ensure(dist::PlainSourceTarball);
+    builder.clear_dir(plain_src_dir);
+
+    let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS")
+        .map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>())
+        .unwrap_or_default();
+
+    command("tar")
+        .arg("-xf")
+        .arg(plain_src_tarball.tarball())
+        .arg("--strip-components=1")
+        .current_dir(plain_src_dir)
+        .run(builder);
+    command("./configure")
+        .arg("--set")
+        .arg("rust.omit-git-hash=false")
+        .args(&configure_args)
+        .arg("--enable-vendor")
+        .current_dir(plain_src_dir)
+        .run(builder);
+    command(helpers::make(&builder.config.host_target.triple))
+        .arg("check")
+        // Do not run the build as if we were in CI, otherwise git would be assumed to be
+        // present, but we build from a tarball here
+        .env("GITHUB_ACTIONS", "0")
+        .current_dir(plain_src_dir)
+        .run(builder);
+}
+
+/// Check that rust-src has all of libstd's dependencies
+fn distcheck_rust_src(builder: &Builder<'_>, src_dir: &Path) {
+    builder.info("Distcheck rust-src");
+    let src_tarball = builder.ensure(dist::Src);
+    builder.clear_dir(src_dir);
+
+    command("tar")
+        .arg("-xf")
+        .arg(src_tarball.tarball())
+        .arg("--strip-components=1")
+        .current_dir(src_dir)
+        .run(builder);
+
+    let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
+    command(&builder.initial_cargo)
+        // Will read the libstd Cargo.toml
+        // which uses the unstable `public-dependency` feature.
+        .env("RUSTC_BOOTSTRAP", "1")
+        .arg("generate-lockfile")
+        .arg("--manifest-path")
+        .arg(&toml)
+        .current_dir(src_dir)
+        .run(builder);
+}
+
+/// Check that rustc-dev's compiler crate source code can be loaded with `cargo metadata`
+fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) {
+    builder.info("Distcheck rustc-dev");
+    let tarball = builder.ensure(dist::RustcDev::new(builder, builder.host_target)).unwrap();
+    builder.clear_dir(dir);
+
+    command("tar")
+        .arg("-xf")
+        .arg(tarball.tarball())
+        .arg("--strip-components=1")
+        .current_dir(dir)
+        .run(builder);
+
+    command(&builder.initial_cargo)
+        .arg("metadata")
+        .arg("--manifest-path")
+        .arg("rustc-dev/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml")
+        .env("RUSTC_BOOTSTRAP", "1")
+        // We might not have a globally available `rustc` binary on CI
+        .env("RUSTC", &builder.initial_rustc)
+        .current_dir(dir)
+        .run(builder);
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -3294,9 +3326,7 @@ impl Step for Bootstrap {
             .env("INSTA_WORKSPACE_ROOT", &builder.src)
             .env("RUSTC_BOOTSTRAP", "1");
 
-        // bootstrap tests are racy on directory creation so just run them one at a time.
-        // Since there's not many this shouldn't be a problem.
-        run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder);
+        run_cargo_test(cargo, &[], &[], None, host, builder);
     }
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 65c4c499086..c5308034fe3 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -82,7 +82,7 @@ impl Step for ToolBuild {
         let path = self.path;
 
         match self.mode {
-            Mode::ToolRustc => {
+            Mode::ToolRustcPrivate => {
                 // FIXME: remove this, it's only needed for download-rustc...
                 if !self.build_compiler.is_forced_compiler() && builder.download_rustc() {
                     builder.std(self.build_compiler, self.build_compiler.host);
@@ -123,7 +123,7 @@ impl Step for ToolBuild {
 
         // Rustc tools (miri, clippy, cargo, rustfmt, rust-analyzer)
         // could use the additional optimizations.
-        if self.mode == Mode::ToolRustc && is_lto_stage(&self.build_compiler) {
+        if self.mode == Mode::ToolRustcPrivate && is_lto_stage(&self.build_compiler) {
             let lto = match builder.config.rust_lto {
                 RustcLto::Off => Some("off"),
                 RustcLto::Thin => Some("thin"),
@@ -607,7 +607,7 @@ impl Step for ErrorIndex {
             build_compiler: self.compilers.build_compiler,
             target: self.compilers.target(),
             tool: "error_index_generator",
-            mode: Mode::ToolRustc,
+            mode: Mode::ToolRustcPrivate,
             path: "src/tools/error_index_generator",
             source_type: SourceType::InTree,
             extra_features: Vec::new(),
@@ -671,7 +671,7 @@ impl Step for RemoteTestServer {
 /// Represents `Rustdoc` that either comes from the external stage0 sysroot or that is built
 /// locally.
 /// Rustdoc is special, because it both essentially corresponds to a `Compiler` (that can be
-/// externally provided), but also to a `ToolRustc` tool.
+/// externally provided), but also to a `ToolRustcPrivate` tool.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Rustdoc {
     /// If the stage of `target_compiler` is `0`, then rustdoc is externally provided.
@@ -759,7 +759,7 @@ impl Step for Rustdoc {
                 // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
                 // rustdoc a different name.
                 tool: "rustdoc_tool_binary",
-                mode: Mode::ToolRustc,
+                mode: Mode::ToolRustcPrivate,
                 path: "src/tools/rustdoc",
                 source_type: SourceType::InTree,
                 extra_features,
@@ -1048,7 +1048,7 @@ impl Step for RustAnalyzer {
             build_compiler,
             target,
             tool: "rust-analyzer",
-            mode: Mode::ToolRustc,
+            mode: Mode::ToolRustcPrivate,
             path: "src/tools/rust-analyzer",
             extra_features: vec!["in-rust-tree".to_owned()],
             source_type: SourceType::InTree,
@@ -1105,7 +1105,7 @@ impl Step for RustAnalyzerProcMacroSrv {
             build_compiler: self.compilers.build_compiler,
             target: self.compilers.target(),
             tool: "rust-analyzer-proc-macro-srv",
-            mode: Mode::ToolRustc,
+            mode: Mode::ToolRustcPrivate,
             path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli",
             extra_features: vec!["in-rust-tree".to_owned()],
             source_type: SourceType::InTree,
@@ -1352,7 +1352,7 @@ impl RustcPrivateCompilers {
     }
 }
 
-/// Creates a step that builds an extended `Mode::ToolRustc` tool
+/// Creates a step that builds an extended `Mode::ToolRustcPrivate` tool
 /// and installs it into the sysroot of a corresponding compiler.
 macro_rules! tool_rustc_extended {
     (
@@ -1466,7 +1466,7 @@ fn build_extended_rustc_tool(
         build_compiler,
         target,
         tool: tool_name,
-        mode: Mode::ToolRustc,
+        mode: Mode::ToolRustcPrivate,
         path,
         extra_features,
         source_type: SourceType::InTree,
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index cdf6fe573e5..cca0c803e63 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -533,7 +533,7 @@ impl Builder<'_> {
         if cmd_kind == Kind::Doc {
             let my_out = match mode {
                 // This is the intended out directory for compiler documentation.
-                Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap => {
+                Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap => {
                     self.compiler_doc_out(target)
                 }
                 Mode::Std => {
@@ -583,7 +583,7 @@ impl Builder<'_> {
 
         // We synthetically interpret a stage0 compiler used to build tools as a
         // "raw" compiler in that it's the exact snapshot we download. For things like
-        // ToolRustc, we would have to use the artificial stage0-sysroot compiler instead.
+        // ToolRustcPrivate, we would have to use the artificial stage0-sysroot compiler instead.
         let use_snapshot =
             mode == Mode::ToolBootstrap || (mode == Mode::ToolTarget && build_compiler_stage == 0);
         assert!(!use_snapshot || build_compiler_stage == 0 || self.local_rebuild);
@@ -643,7 +643,8 @@ impl Builder<'_> {
         // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
         // library unnecessary. This can be removed when windows-rs enables raw-dylib
         // unconditionally.
-        if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap | Mode::ToolTarget = mode {
+        if let Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap | Mode::ToolTarget = mode
+        {
             rustflags.arg("--cfg=windows_raw_dylib");
         }
 
@@ -657,7 +658,7 @@ impl Builder<'_> {
         // - rust-analyzer, due to the rowan crate
         // so we exclude an entire category of steps here due to lack of fine-grained control over
         // rustflags.
-        if self.config.rust_randomize_layout && mode != Mode::ToolRustc {
+        if self.config.rust_randomize_layout && mode != Mode::ToolRustcPrivate {
             rustflags.arg("-Zrandomize-layout");
         }
 
@@ -717,7 +718,7 @@ impl Builder<'_> {
 
         match mode {
             Mode::Std | Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTarget => {}
-            Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
+            Mode::Rustc | Mode::Codegen | Mode::ToolRustcPrivate => {
                 // Build proc macros both for the host and the target unless proc-macros are not
                 // supported by the target.
                 if target != compiler.host && cmd_kind != Kind::Check {
@@ -778,7 +779,7 @@ impl Builder<'_> {
                 "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
                     .to_string()
             }
-            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
+            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustcPrivate => String::new(),
         };
 
         cargo.arg("-j").arg(self.jobs().to_string());
@@ -825,7 +826,7 @@ impl Builder<'_> {
             // rustc step and one that we just built. This isn't always a
             // problem, somehow -- not really clear why -- but we know that this
             // fixes things.
-            Mode::ToolRustc => metadata.push_str("tool-rustc"),
+            Mode::ToolRustcPrivate => metadata.push_str("tool-rustc"),
             // Same for codegen backends.
             Mode::Codegen => metadata.push_str("codegen"),
             _ => {}
@@ -877,8 +878,11 @@ impl Builder<'_> {
             .env("RUSTC_LIBDIR", libdir)
             .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
             .env("RUSTDOC_REAL", rustdoc_path)
-            .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
-            .env("RUSTC_BREAK_ON_ICE", "1");
+            .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir());
+
+        if self.config.rust_break_on_ice {
+            cargo.env("RUSTC_BREAK_ON_ICE", "1");
+        }
 
         // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree
         // sysroot depending on whether we're building build scripts.
@@ -917,7 +921,7 @@ impl Builder<'_> {
         let debuginfo_level = match mode {
             Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
             Mode::Std => self.config.rust_debuginfo_level_std,
-            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
+            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustcPrivate | Mode::ToolTarget => {
                 self.config.rust_debuginfo_level_tools
             }
         };
@@ -930,7 +934,7 @@ impl Builder<'_> {
             match mode {
                 Mode::Std => self.config.std_debug_assertions,
                 Mode::Rustc | Mode::Codegen => self.config.rustc_debug_assertions,
-                Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
+                Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustcPrivate | Mode::ToolTarget => {
                     self.config.tools_debug_assertions
                 }
             }
@@ -1005,7 +1009,7 @@ impl Builder<'_> {
             }
             Mode::Std
             | Mode::ToolBootstrap
-            | Mode::ToolRustc
+            | Mode::ToolRustcPrivate
             | Mode::ToolStd
             | Mode::ToolTarget => {
                 if let Some(ref map_to) =
@@ -1078,7 +1082,7 @@ impl Builder<'_> {
         // requirement, but the `-L` library path is not propagated across
         // separate Cargo projects. We can add LLVM's library path to the
         // rustc args as a workaround.
-        if (mode == Mode::ToolRustc || mode == Mode::Codegen)
+        if (mode == Mode::ToolRustcPrivate || mode == Mode::Codegen)
             && let Some(llvm_config) = self.llvm_config(target)
         {
             let llvm_libdir =
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index ef01f14f5e9..9e8c13eb4de 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -10,8 +10,8 @@ use crate::core::build_steps::doc::DocumentationFormat;
 use crate::core::config::Config;
 use crate::utils::cache::ExecutedStep;
 use crate::utils::helpers::get_host_target;
-use crate::utils::tests::ConfigBuilder;
 use crate::utils::tests::git::{GitCtx, git_test};
+use crate::utils::tests::{ConfigBuilder, TestCtx};
 
 static TEST_TRIPLE_1: &str = "i686-unknown-haiku";
 static TEST_TRIPLE_2: &str = "i686-unknown-hurd-gnu";
@@ -22,38 +22,13 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
 }
 
 fn configure_with_args(cmd: &[&str], host: &[&str], target: &[&str]) -> Config {
-    let cmd = cmd.iter().copied().map(String::from).collect::<Vec<_>>();
-    let mut config = Config::parse(Flags::parse(&cmd));
-    // don't save toolstates
-    config.save_toolstates = None;
-    config.set_dry_run(DryRun::SelfCheck);
-
-    // Ignore most submodules, since we don't need them for a dry run, and the
-    // tests run much faster without them.
-    //
-    // The src/doc/book submodule is needed because TheBook step tries to
-    // access files even during a dry-run (may want to consider just skipping
-    // that in a dry run).
-    let submodule_build = Build::new(Config {
-        // don't include LLVM, so CI doesn't require ninja/cmake to be installed
-        rust_codegen_backends: vec![],
-        ..Config::parse(Flags::parse(&["check".to_owned()]))
-    });
-    submodule_build.require_submodule("src/doc/book", None);
-    config.submodules = Some(false);
-
-    config.ninja_in_file = false;
-    // try to avoid spurious failures in dist where we create/delete each others file
-    // HACK: rather than pull in `tempdir`, use the one that cargo has conveniently created for us
-    let dir = Path::new(env!("OUT_DIR"))
-        .join("tmp-rustbuild-tests")
-        .join(&thread::current().name().unwrap_or("unknown").replace(":", "-"));
-    t!(fs::create_dir_all(&dir));
-    config.out = dir;
-    config.host_target = TargetSelection::from_user(TEST_TRIPLE_1);
-    config.hosts = host.iter().map(|s| TargetSelection::from_user(s)).collect();
-    config.targets = target.iter().map(|s| TargetSelection::from_user(s)).collect();
-    config
+    TestCtx::new()
+        .config(cmd[0])
+        .args(&cmd[1..])
+        .hosts(host)
+        .targets(target)
+        .args(&["--build", TEST_TRIPLE_1])
+        .create_config()
 }
 
 fn first<A, B>(v: Vec<(A, B)>) -> Vec<A> {
@@ -547,8 +522,8 @@ mod snapshot {
 
     use crate::core::build_steps::{compile, dist, doc, test, tool};
     use crate::core::builder::tests::{
-        RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, configure_with_args,
-        first, host_target, render_steps, run_build,
+        RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, first, host_target,
+        render_steps, run_build,
     };
     use crate::core::builder::{Builder, Kind, StepDescription, StepMetadata};
     use crate::core::config::TargetSelection;
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index d12cc962187..678a9b63952 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -221,6 +221,7 @@ pub struct Config {
     pub rust_lto: RustcLto,
     pub rust_validate_mir_opts: Option<u32>,
     pub rust_std_features: BTreeSet<String>,
+    pub rust_break_on_ice: bool,
     pub llvm_profile_use: Option<String>,
     pub llvm_profile_generate: bool,
     pub llvm_libunwind_default: Option<LlvmLibunwind>,
@@ -550,6 +551,7 @@ impl Config {
             strip: rust_strip,
             lld_mode: rust_lld_mode,
             std_features: rust_std_features,
+            break_on_ice: rust_break_on_ice,
         } = toml.rust.unwrap_or_default();
 
         let Llvm {
@@ -1269,6 +1271,7 @@ impl Config {
             reproducible_artifacts: flags_reproducible_artifact,
             reuse: build_reuse.map(PathBuf::from),
             rust_analyzer_info,
+            rust_break_on_ice: rust_break_on_ice.unwrap_or(true),
             rust_codegen_backends: rust_codegen_backends
                 .map(|backends| parse_codegen_backends(backends, "rust"))
                 .unwrap_or(vec![CodegenBackendKind::Llvm]),
diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs
index c54df456d52..4832a1d37b7 100644
--- a/src/bootstrap/src/core/config/toml/rust.rs
+++ b/src/bootstrap/src/core/config/toml/rust.rs
@@ -65,6 +65,7 @@ define_config! {
         lto: Option<String> = "lto",
         validate_mir_opts: Option<u32> = "validate-mir-opts",
         std_features: Option<BTreeSet<String>> = "std-features",
+        break_on_ice: Option<bool> = "break-on-ice",
     }
 }
 
@@ -355,6 +356,7 @@ pub fn check_incompatible_options_for_ci_rustc(
         download_rustc: _,
         validate_mir_opts: _,
         frame_pointers: _,
+        break_on_ice: _,
     } = ci_rust_config;
 
     // There are two kinds of checks for CI rustc incompatible options:
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 59c0f9faaca..a2aeed20948 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -85,12 +85,12 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
 const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
     (Some(Mode::Rustc), "bootstrap", None),
     (Some(Mode::Codegen), "bootstrap", None),
-    (Some(Mode::ToolRustc), "bootstrap", None),
+    (Some(Mode::ToolRustcPrivate), "bootstrap", None),
     (Some(Mode::ToolStd), "bootstrap", None),
     (Some(Mode::Rustc), "llvm_enzyme", None),
     (Some(Mode::Codegen), "llvm_enzyme", None),
-    (Some(Mode::ToolRustc), "llvm_enzyme", None),
-    (Some(Mode::ToolRustc), "rust_analyzer", None),
+    (Some(Mode::ToolRustcPrivate), "llvm_enzyme", None),
+    (Some(Mode::ToolRustcPrivate), "rust_analyzer", None),
     (Some(Mode::ToolStd), "rust_analyzer", None),
     // Any library specific cfgs like `target_os`, `target_arch` should be put in
     // priority the `[lints.rust.unexpected_cfgs.check-cfg]` table
@@ -334,17 +334,18 @@ pub enum Mode {
     /// compiletest which needs libtest.
     ToolStd,
 
-    /// Build a tool which uses the locally built rustc and the target std,
+    /// Build a tool which uses the `rustc_private` mechanism, and thus
+    /// the locally built rustc rlib artifacts,
     /// placing the output in the "stageN-tools" directory. This is used for
-    /// anything that needs a fully functional rustc, such as rustdoc, clippy,
-    /// cargo, rustfmt, miri, etc.
-    ToolRustc,
+    /// everything that links to rustc as a library, such as rustdoc, clippy,
+    /// rustfmt, miri, etc.
+    ToolRustcPrivate,
 }
 
 impl Mode {
     pub fn is_tool(&self) -> bool {
         match self {
-            Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd | Mode::ToolTarget => true,
+            Mode::ToolBootstrap | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => true,
             Mode::Std | Mode::Codegen | Mode::Rustc => false,
         }
     }
@@ -353,7 +354,7 @@ impl Mode {
         match self {
             Mode::Std | Mode::Codegen => true,
             Mode::ToolBootstrap
-            | Mode::ToolRustc
+            | Mode::ToolRustcPrivate
             | Mode::ToolStd
             | Mode::ToolTarget
             | Mode::Rustc => false,
@@ -924,7 +925,7 @@ impl Build {
             Mode::Rustc => (Some(build_compiler.stage + 1), "rustc"),
             Mode::Codegen => (Some(build_compiler.stage + 1), "codegen"),
             Mode::ToolBootstrap => bootstrap_tool(),
-            Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage + 1), "tools"),
+            Mode::ToolStd | Mode::ToolRustcPrivate => (Some(build_compiler.stage + 1), "tools"),
             Mode::ToolTarget => {
                 // If we're not cross-compiling (the common case), share the target directory with
                 // bootstrap tools to reuse the build cache.
@@ -1145,7 +1146,7 @@ impl Build {
                 | Mode::ToolBootstrap
                 | Mode::ToolTarget
                 | Mode::ToolStd
-                | Mode::ToolRustc,
+                | Mode::ToolRustcPrivate,
             )
             | None => target_and_stage.stage + 1,
         };
diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs
index bed03c18aaa..a6233e6b61c 100644
--- a/src/bootstrap/src/utils/cc_detect/tests.rs
+++ b/src/bootstrap/src/utils/cc_detect/tests.rs
@@ -3,7 +3,8 @@ use std::{env, iter};
 
 use super::*;
 use crate::core::config::{Target, TargetSelection};
-use crate::{Build, Config, Flags};
+use crate::utils::tests::TestCtx;
+use crate::{Build, Config, Flags, t};
 
 #[test]
 fn test_ndk_compiler_c() {
@@ -68,7 +69,8 @@ fn test_language_clang() {
 
 #[test]
 fn test_new_cc_build() {
-    let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
+    let config = TestCtx::new().config("build").create_config();
+    let build = Build::new(config);
     let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
     let cfg = new_cc_build(&build, target.clone());
     let compiler = cfg.get_compiler();
@@ -77,7 +79,8 @@ fn test_new_cc_build() {
 
 #[test]
 fn test_default_compiler_wasi() {
-    let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
+    let config = TestCtx::new().config("build").create_config();
+    let mut build = Build::new(config);
     let target = TargetSelection::from_user("wasm32-wasi");
     let wasi_sdk = PathBuf::from("/wasi-sdk");
     build.wasi_sdk_path = Some(wasi_sdk.clone());
@@ -98,7 +101,8 @@ fn test_default_compiler_wasi() {
 
 #[test]
 fn test_default_compiler_fallback() {
-    let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
+    let config = TestCtx::new().config("build").create_config();
+    let build = Build::new(config);
     let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
     let mut cfg = cc::Build::new();
     let result = default_compiler(&mut cfg, Language::C, target, &build);
@@ -107,7 +111,8 @@ fn test_default_compiler_fallback() {
 
 #[test]
 fn test_find_target_with_config() {
-    let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
+    let config = TestCtx::new().config("build").create_config();
+    let mut build = Build::new(config);
     let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
     let mut target_config = Target::default();
     target_config.cc = Some(PathBuf::from("dummy-cc"));
@@ -128,7 +133,8 @@ fn test_find_target_with_config() {
 
 #[test]
 fn test_find_target_without_config() {
-    let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
+    let config = TestCtx::new().config("build").create_config();
+    let mut build = Build::new(config);
     let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
     build.config.target_config.clear();
     fill_target_compiler(&mut build, target.clone());
@@ -141,7 +147,8 @@ fn test_find_target_without_config() {
 
 #[test]
 fn test_find() {
-    let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
+    let config = TestCtx::new().config("build").create_config();
+    let mut build = Build::new(config);
     let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu");
     let target2 = TargetSelection::from_user("x86_64-unknown-openbsd");
     build.targets.push(target1.clone());
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 2cc2fb486fa..01309072927 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -536,4 +536,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Warning,
         summary: "It is no longer possible to `x test` with stage 0, except for running compiletest and opting into `build.compiletest-allow-stage0`.",
     },
+    ChangeInfo {
+        change_id: 145976,
+        severity: ChangeSeverity::Info,
+        summary: "Added a new option `rust.break-on-ice` to control if internal compiler errors cause a debug break on Windows.",
+    },
 ];