about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/src/bin/rustc.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs352
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs33
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs132
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs105
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs8
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs18
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs4
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs30
-rw-r--r--src/bootstrap/src/core/builder/tests.rs2
-rw-r--r--src/bootstrap/src/core/config/mod.rs2
-rw-r--r--src/bootstrap/src/core/config/target_selection.rs2
-rw-r--r--src/bootstrap/src/lib.rs24
-rw-r--r--src/bootstrap/src/utils/tracing.rs93
14 files changed, 591 insertions, 224 deletions
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 5865df67b66..f15b76fa85c 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -179,6 +179,16 @@ fn main() {
         }
     }
 
+    // Here we pass additional paths that essentially act as a sysroot.
+    // These are used to load rustc crates (e.g. `extern crate rustc_ast;`)
+    // for rustc_private tools, so that we do not have to copy them into the
+    // actual sysroot of the compiler that builds the tool.
+    if let Ok(dirs) = env::var("RUSTC_ADDITIONAL_SYSROOT_PATHS") {
+        for dir in dirs.split(",") {
+            cmd.arg(format!("-L{dir}"));
+        }
+    }
+
     // Force all crates compiled by this compiler to (a) be unstable and (b)
     // allow the `rustc_private` feature to link to other unstable crates
     // also in the sysroot. We also do this for host crates, since those
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 4a110b733e1..bebae893ee7 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -1,5 +1,8 @@
 //! Implementation of compiling the compiler and standard library, in "check"-based modes.
 
+use std::fs;
+use std::path::{Path, PathBuf};
+
 use crate::core::build_steps::compile::{
     add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
 };
@@ -9,11 +12,11 @@ use crate::core::build_steps::tool::{
     prepare_tool_cargo,
 };
 use crate::core::builder::{
-    self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
+    self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
 };
 use crate::core::config::TargetSelection;
 use crate::utils::build_stamp::{self, BuildStamp};
-use crate::{CodegenBackendKind, Compiler, Mode, Subcommand};
+use crate::{CodegenBackendKind, Compiler, Mode, Subcommand, t};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Std {
@@ -33,7 +36,7 @@ impl Std {
 }
 
 impl Step for Std {
-    type Output = ();
+    type Output = BuildStamp;
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -60,13 +63,14 @@ impl Step for Std {
 
         let crates = std_crates_for_run_make(&run);
         run.builder.ensure(Std {
-            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std),
+            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std)
+                .build_compiler(),
             target: run.target,
             crates,
         });
     }
 
-    fn run(self, builder: &Builder<'_>) {
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
         let build_compiler = self.build_compiler;
         let target = self.target;
 
@@ -93,18 +97,27 @@ impl Step for Std {
             Kind::Check,
             format_args!("library artifacts{}", crate_description(&self.crates)),
             Mode::Std,
-            self.build_compiler,
+            build_compiler,
             target,
         );
 
-        let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check");
-        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
+        let check_stamp =
+            build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check");
+        run_cargo(
+            builder,
+            cargo,
+            builder.config.free_args.clone(),
+            &check_stamp,
+            vec![],
+            true,
+            false,
+        );
 
         drop(_guard);
 
         // don't check test dependencies if we haven't built libtest
         if !self.crates.iter().any(|krate| krate == "test") {
-            return;
+            return check_stamp;
         }
 
         // Then run cargo again, once we've put the rmeta files for the library
@@ -137,10 +150,11 @@ impl Step for Std {
             Kind::Check,
             "library test/bench/example targets",
             Mode::Std,
-            self.build_compiler,
+            build_compiler,
             target,
         );
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
+        check_stamp
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
@@ -148,12 +162,135 @@ impl Step for Std {
     }
 }
 
-/// Checks rustc using `build_compiler` and copies the built
-/// .rmeta files into the sysroot of `build_compiler`.
+/// Represents a proof that rustc was **checked**.
+/// Contains directories with .rmeta files generated by checking rustc for a specific
+/// target.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+struct RmetaSysroot {
+    host_dir: PathBuf,
+    target_dir: PathBuf,
+}
+
+impl RmetaSysroot {
+    /// Copy rmeta artifacts from the given `stamp` into a sysroot located at `directory`.
+    fn from_stamp(
+        builder: &Builder<'_>,
+        stamp: BuildStamp,
+        target: TargetSelection,
+        directory: &Path,
+    ) -> Self {
+        let host_dir = directory.join("host");
+        let target_dir = directory.join(target);
+        let _ = fs::remove_dir_all(directory);
+        t!(fs::create_dir_all(directory));
+        add_to_sysroot(builder, &target_dir, &host_dir, &stamp);
+
+        Self { host_dir, target_dir }
+    }
+
+    /// Configure the given cargo invocation so that the compiled crate will be able to use
+    /// rustc .rmeta artifacts that were previously generated.
+    fn configure_cargo(&self, cargo: &mut Cargo) {
+        cargo.append_to_env(
+            "RUSTC_ADDITIONAL_SYSROOT_PATHS",
+            format!("{},{}", self.host_dir.to_str().unwrap(), self.target_dir.to_str().unwrap()),
+            ",",
+        );
+    }
+}
+
+/// Checks rustc using the given `build_compiler` for the given `target`, and produces
+/// a sysroot in the build directory that stores the generated .rmeta files.
+///
+/// This step exists so that we can store the generated .rmeta artifacts into a separate
+/// directory, instead of copying them into the sysroot of `build_compiler`, which would
+/// "pollute" it (that is especially problematic for the external stage0 rustc).
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+struct PrepareRustcRmetaSysroot {
+    build_compiler: CompilerForCheck,
+    target: TargetSelection,
+}
+
+impl PrepareRustcRmetaSysroot {
+    fn new(build_compiler: CompilerForCheck, target: TargetSelection) -> Self {
+        Self { build_compiler, target }
+    }
+}
+
+impl Step for PrepareRustcRmetaSysroot {
+    type Output = RmetaSysroot;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.never()
+    }
+
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        // Check rustc
+        let stamp = builder.ensure(Rustc::from_build_compiler(
+            self.build_compiler.clone(),
+            self.target,
+            vec![],
+        ));
+
+        let build_compiler = self.build_compiler.build_compiler();
+
+        // Copy the generated rmeta artifacts to a separate directory
+        let dir = builder
+            .out
+            .join(build_compiler.host)
+            .join(format!("stage{}-rustc-rmeta-artifacts", build_compiler.stage + 1));
+        RmetaSysroot::from_stamp(builder, stamp, self.target, &dir)
+    }
+}
+
+/// Checks std using the given `build_compiler` for the given `target`, and produces
+/// a sysroot in the build directory that stores the generated .rmeta files.
+///
+/// This step exists so that we can store the generated .rmeta artifacts into a separate
+/// directory, instead of copying them into the sysroot of `build_compiler`, which would
+/// "pollute" it (that is especially problematic for the external stage0 rustc).
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+struct PrepareStdRmetaSysroot {
+    build_compiler: Compiler,
+    target: TargetSelection,
+}
+
+impl PrepareStdRmetaSysroot {
+    fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
+        Self { build_compiler, target }
+    }
+}
+
+impl Step for PrepareStdRmetaSysroot {
+    type Output = RmetaSysroot;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.never()
+    }
+
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        // Check std
+        let stamp = builder.ensure(Std {
+            build_compiler: self.build_compiler,
+            target: self.target,
+            crates: vec![],
+        });
+
+        // Copy the generated rmeta artifacts to a separate directory
+        let dir = builder
+            .out
+            .join(self.build_compiler.host)
+            .join(format!("stage{}-std-rmeta-artifacts", self.build_compiler.stage));
+
+        RmetaSysroot::from_stamp(builder, stamp, self.target, &dir)
+    }
+}
+
+/// Checks rustc using `build_compiler`.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
     /// Compiler that will check this rustc.
-    pub build_compiler: Compiler,
+    pub build_compiler: CompilerForCheck,
     pub target: TargetSelection,
     /// Whether to build only a subset of crates.
     ///
@@ -166,12 +303,20 @@ pub struct Rustc {
 impl Rustc {
     pub fn new(builder: &Builder<'_>, target: TargetSelection, crates: Vec<String>) -> Self {
         let build_compiler = prepare_compiler_for_check(builder, target, Mode::Rustc);
+        Self::from_build_compiler(build_compiler, target, crates)
+    }
+
+    fn from_build_compiler(
+        build_compiler: CompilerForCheck,
+        target: TargetSelection,
+        crates: Vec<String>,
+    ) -> Self {
         Self { build_compiler, target, crates }
     }
 }
 
 impl Step for Rustc {
-    type Output = ();
+    type Output = BuildStamp;
     const IS_HOST: bool = true;
     const DEFAULT: bool = true;
 
@@ -191,8 +336,8 @@ impl Step for Rustc {
     /// created will also be linked into the sysroot directory.
     ///
     /// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it.
-    fn run(self, builder: &Builder<'_>) {
-        let build_compiler = self.build_compiler;
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let build_compiler = self.build_compiler.build_compiler;
         let target = self.target;
 
         let mut cargo = builder::Cargo::new(
@@ -205,6 +350,7 @@ impl Step for Rustc {
         );
 
         rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
+        self.build_compiler.configure_cargo(&mut cargo);
 
         // Explicitly pass -p for all compiler crates -- this will force cargo
         // to also check the tests/benches/examples for these crates, rather
@@ -217,7 +363,7 @@ impl Step for Rustc {
             Kind::Check,
             format_args!("compiler artifacts{}", crate_description(&self.crates)),
             Mode::Rustc,
-            self.build_compiler,
+            self.build_compiler.build_compiler(),
             target,
         );
 
@@ -226,13 +372,12 @@ impl Step for Rustc {
 
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
 
-        let libdir = builder.sysroot_target_libdir(build_compiler, target);
-        let hostdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host);
-        add_to_sysroot(builder, &libdir, &hostdir, &stamp);
+        stamp
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        let metadata = StepMetadata::check("rustc", self.target).built_by(self.build_compiler);
+        let metadata = StepMetadata::check("rustc", self.target)
+            .built_by(self.build_compiler.build_compiler());
         let metadata = if self.crates.is_empty() {
             metadata
         } else {
@@ -242,45 +387,101 @@ 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
+/// .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
+/// thing is being built) should call `configure_cargo` to ensure that the rmeta artifacts are
+/// properly linked, if present.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CompilerForCheck {
+    build_compiler: Compiler,
+    rustc_rmeta_sysroot: Option<RmetaSysroot>,
+    std_rmeta_sysroot: Option<RmetaSysroot>,
+}
+
+impl CompilerForCheck {
+    pub fn build_compiler(&self) -> Compiler {
+        self.build_compiler
+    }
+
+    /// If there are any rustc rmeta artifacts available, configure the Cargo invocation
+    /// so that the artifact being built can find them.
+    pub fn configure_cargo(&self, cargo: &mut Cargo) {
+        if let Some(sysroot) = &self.rustc_rmeta_sysroot {
+            sysroot.configure_cargo(cargo);
+        }
+        if let Some(sysroot) = &self.std_rmeta_sysroot {
+            sysroot.configure_cargo(cargo);
+        }
+    }
+}
+
+/// Prepare the standard library for checking something (that requires stdlib) using
+/// `build_compiler`.
+fn prepare_std(
+    builder: &Builder<'_>,
+    build_compiler: Compiler,
+    target: TargetSelection,
+) -> Option<RmetaSysroot> {
+    // We need to build the host stdlib even if we only check, to compile build scripts and proc
+    // macros
+    builder.std(build_compiler, builder.host_target);
+
+    // If we're cross-compiling, we generate the rmeta files for the given target
+    // This check has to be here, because if we generate both .so and .rmeta files, rustc will fail,
+    // as it will have multiple candidates for linking.
+    if builder.host_target != target {
+        Some(builder.ensure(PrepareStdRmetaSysroot::new(build_compiler, target)))
+    } else {
+        None
+    }
+}
+
 /// Prepares a compiler that will check something with the given `mode`.
 pub fn prepare_compiler_for_check(
     builder: &Builder<'_>,
     target: TargetSelection,
     mode: Mode,
-) -> Compiler {
+) -> CompilerForCheck {
     let host = builder.host_target;
 
-    match mode {
+    let mut rustc_rmeta_sysroot = None;
+    let mut std_rmeta_sysroot = None;
+    let build_compiler = match mode {
         Mode::ToolBootstrap => builder.compiler(0, host),
+        // We could also only check std here and use `prepare_std`, but `ToolTarget` is currently
+        // only used for running in-tree Clippy on bootstrap tools, so it does not seem worth it to
+        // optimize it. Therefore, here we build std for the target, instead of just checking it.
         Mode::ToolTarget => get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target)),
         Mode::ToolStd => {
             if builder.config.compile_time_deps {
                 // When --compile-time-deps is passed, we can't use any rustc
                 // other than the bootstrap compiler. Luckily build scripts and
                 // proc macros for tools are unlikely to need nightly.
-                return builder.compiler(0, host);
+                builder.compiler(0, host)
+            } else {
+                // These tools require the local standard library to be checked
+                let build_compiler = builder.compiler(builder.top_stage, host);
+                std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
+                build_compiler
             }
-
-            // These tools require the local standard library to be checked
-            let build_compiler = builder.compiler(builder.top_stage, host);
-
-            // We need to build the host stdlib to check the tool itself.
-            // We need to build the target stdlib so that the tool can link to it.
-            builder.std(build_compiler, host);
-            // We could only check this library in theory, but `check::Std` doesn't copy rmetas
-            // into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it
-            // instead.
-            builder.std(build_compiler, target);
-            build_compiler
         }
         Mode::ToolRustc | 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
             // an empty set of crates, which will avoid using `cargo -p`.
-            let check = Rustc::new(builder, target, vec![]);
-            let build_compiler = check.build_compiler;
-            builder.ensure(check);
+            let compiler_for_rustc = prepare_compiler_for_check(builder, target, Mode::Rustc);
+            rustc_rmeta_sysroot = Some(
+                builder.ensure(PrepareRustcRmetaSysroot::new(compiler_for_rustc.clone(), target)),
+            );
+            let build_compiler = compiler_for_rustc.build_compiler();
+
+            // To check a rustc_private tool, we also need to check std that it will link to
+            std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
             build_compiler
         }
         Mode::Rustc => {
@@ -294,15 +495,8 @@ pub fn prepare_compiler_for_check(
             let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
             let build_compiler = builder.compiler(stage, host);
 
-            // Build host std for compiling build scripts
-            builder.std(build_compiler, build_compiler.host);
-
-            // Build target std so that the checked rustc can link to it during the check
-            // FIXME: maybe we can a way to only do a check of std here?
-            // But for that we would have to copy the stdlib rmetas to the sysroot of the build
-            // compiler, which conflicts with std rlibs, if we also build std.
-            builder.std(build_compiler, target);
-
+            // To check rustc, we need to check std that it will link to
+            std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
             build_compiler
         }
         Mode::Std => {
@@ -311,13 +505,14 @@ pub fn prepare_compiler_for_check(
             // stage 0 stdlib is used to compile build scripts and proc macros.
             builder.compiler(builder.top_stage, host)
         }
-    }
+    };
+    CompilerForCheck { build_compiler, rustc_rmeta_sysroot, std_rmeta_sysroot }
 }
 
 /// Check the Cranelift codegen backend.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CraneliftCodegenBackend {
-    build_compiler: Compiler,
+    build_compiler: CompilerForCheck,
     target: TargetSelection,
 }
 
@@ -332,12 +527,14 @@ impl Step for CraneliftCodegenBackend {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen);
-        run.builder.ensure(CraneliftCodegenBackend { build_compiler, target: run.target });
+        run.builder.ensure(CraneliftCodegenBackend {
+            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Codegen),
+            target: run.target,
+        });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let build_compiler = self.build_compiler;
+        let build_compiler = self.build_compiler.build_compiler();
         let target = self.target;
 
         let mut cargo = builder::Cargo::new(
@@ -353,12 +550,13 @@ impl Step for CraneliftCodegenBackend {
             .arg("--manifest-path")
             .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml"));
         rustc_cargo_env(builder, &mut cargo, target);
+        self.build_compiler.configure_cargo(&mut cargo);
 
         let _guard = builder.msg(
             Kind::Check,
             "rustc_codegen_cranelift",
             Mode::Codegen,
-            self.build_compiler,
+            build_compiler,
             target,
         );
 
@@ -376,7 +574,7 @@ impl Step for CraneliftCodegenBackend {
     fn metadata(&self) -> Option<StepMetadata> {
         Some(
             StepMetadata::check("rustc_codegen_cranelift", self.target)
-                .built_by(self.build_compiler),
+                .built_by(self.build_compiler.build_compiler()),
         )
     }
 }
@@ -384,7 +582,7 @@ impl Step for CraneliftCodegenBackend {
 /// Check the GCC codegen backend.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct GccCodegenBackend {
-    build_compiler: Compiler,
+    build_compiler: CompilerForCheck,
     target: TargetSelection,
 }
 
@@ -399,8 +597,10 @@ impl Step for GccCodegenBackend {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen);
-        run.builder.ensure(GccCodegenBackend { build_compiler, target: run.target });
+        run.builder.ensure(GccCodegenBackend {
+            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Codegen),
+            target: run.target,
+        });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -410,7 +610,7 @@ impl Step for GccCodegenBackend {
             return;
         }
 
-        let build_compiler = self.build_compiler;
+        let build_compiler = self.build_compiler.build_compiler();
         let target = self.target;
 
         let mut cargo = builder::Cargo::new(
@@ -424,14 +624,10 @@ impl Step for GccCodegenBackend {
 
         cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
         rustc_cargo_env(builder, &mut cargo, target);
+        self.build_compiler.configure_cargo(&mut cargo);
 
-        let _guard = builder.msg(
-            Kind::Check,
-            "rustc_codegen_gcc",
-            Mode::Codegen,
-            self.build_compiler,
-            target,
-        );
+        let _guard =
+            builder.msg(Kind::Check, "rustc_codegen_gcc", Mode::Codegen, build_compiler, target);
 
         let stamp = build_stamp::codegen_backend_stamp(
             builder,
@@ -445,7 +641,10 @@ impl Step for GccCodegenBackend {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::check("rustc_codegen_gcc", self.target).built_by(self.build_compiler))
+        Some(
+            StepMetadata::check("rustc_codegen_gcc", self.target)
+                .built_by(self.build_compiler.build_compiler()),
+        )
     }
 }
 
@@ -467,8 +666,8 @@ macro_rules! tool_check_step {
     ) => {
         #[derive(Debug, Clone, PartialEq, Eq, Hash)]
         pub struct $name {
-            pub build_compiler: Compiler,
-            pub target: TargetSelection,
+            compiler: CompilerForCheck,
+            target: TargetSelection,
         }
 
         impl Step for $name {
@@ -486,7 +685,7 @@ macro_rules! tool_check_step {
                 let builder = run.builder;
                 let mode = $mode(builder);
 
-                let build_compiler = prepare_compiler_for_check(run.builder, target, mode);
+                let compiler = prepare_compiler_for_check(run.builder, target, mode);
 
                 // It doesn't make sense to cross-check bootstrap tools
                 if mode == Mode::ToolBootstrap && target != run.builder.host_target {
@@ -494,11 +693,11 @@ macro_rules! tool_check_step {
                     return;
                 };
 
-                run.builder.ensure($name { target, build_compiler });
+                run.builder.ensure($name { target, compiler });
             }
 
             fn run(self, builder: &Builder<'_>) {
-                let Self { target, build_compiler } = self;
+                let Self { target, compiler } = self;
                 let allow_features = {
                     let mut _value = "";
                     $( _value = $allow_features; )?
@@ -506,11 +705,11 @@ macro_rules! tool_check_step {
                 };
                 let extra_features: &[&str] = &[$($($enable_features),*)?];
                 let mode = $mode(builder);
-                run_tool_check_step(builder, build_compiler, target, $path, mode, allow_features, extra_features);
+                run_tool_check_step(builder, compiler, target, $path, mode, allow_features, extra_features);
             }
 
             fn metadata(&self) -> Option<StepMetadata> {
-                Some(StepMetadata::check(stringify!($name), self.target).built_by(self.build_compiler))
+                Some(StepMetadata::check(stringify!($name), self.target).built_by(self.compiler.build_compiler))
             }
         }
     }
@@ -519,7 +718,7 @@ macro_rules! tool_check_step {
 /// Used by the implementation of `Step::run` in `tool_check_step!`.
 fn run_tool_check_step(
     builder: &Builder<'_>,
-    build_compiler: Compiler,
+    compiler: CompilerForCheck,
     target: TargetSelection,
     path: &str,
     mode: Mode,
@@ -528,6 +727,8 @@ fn run_tool_check_step(
 ) {
     let display_name = path.rsplit('/').next().unwrap();
 
+    let build_compiler = compiler.build_compiler();
+
     let extra_features = extra_features.iter().map(|f| f.to_string()).collect::<Vec<String>>();
     let mut cargo = prepare_tool_cargo(
         builder,
@@ -544,6 +745,7 @@ fn run_tool_check_step(
         &extra_features,
     );
     cargo.allow_features(allow_features);
+    compiler.configure_cargo(&mut cargo);
 
     // FIXME: check bootstrap doesn't currently work when multiple targets are checked
     // FIXME: rust-analyzer does not work with --all-targets
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index 3c4aa0886c2..05f8b240291 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -18,7 +18,7 @@ use build_helper::exit;
 use super::compile::{run_cargo, rustc_cargo, std_cargo};
 use super::tool::{SourceType, prepare_tool_cargo};
 use crate::builder::{Builder, ShouldRun};
-use crate::core::build_steps::check::prepare_compiler_for_check;
+use crate::core::build_steps::check::{CompilerForCheck, prepare_compiler_for_check};
 use crate::core::build_steps::compile::std_crates_for_run_make;
 use crate::core::builder;
 use crate::core::builder::{Alias, Kind, RunConfig, Step, StepMetadata, crate_description};
@@ -231,7 +231,7 @@ impl Step for Std {
 /// in-tree rustc.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
-    build_compiler: Compiler,
+    build_compiler: CompilerForCheck,
     target: TargetSelection,
     config: LintConfig,
     /// Whether to lint only a subset of crates.
@@ -271,7 +271,7 @@ impl Step for Rustc {
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let build_compiler = self.build_compiler;
+        let build_compiler = self.build_compiler.build_compiler();
         let target = self.target;
 
         let mut cargo = builder::Cargo::new(
@@ -284,6 +284,7 @@ impl Step for Rustc {
         );
 
         rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
+        self.build_compiler.configure_cargo(&mut cargo);
 
         // Explicitly pass -p for all compiler crates -- this will force cargo
         // to also lint the tests/benches/examples for these crates, rather
@@ -312,13 +313,16 @@ impl Step for Rustc {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::clippy("rustc", self.target).built_by(self.build_compiler))
+        Some(
+            StepMetadata::clippy("rustc", self.target)
+                .built_by(self.build_compiler.build_compiler()),
+        )
     }
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct CodegenGcc {
-    build_compiler: Compiler,
+    build_compiler: CompilerForCheck,
     target: TargetSelection,
     config: LintConfig,
 }
@@ -347,10 +351,10 @@ impl Step for CodegenGcc {
     }
 
     fn run(self, builder: &Builder<'_>) -> Self::Output {
-        let build_compiler = self.build_compiler;
+        let build_compiler = self.build_compiler.build_compiler();
         let target = self.target;
 
-        let cargo = prepare_tool_cargo(
+        let mut cargo = prepare_tool_cargo(
             builder,
             build_compiler,
             Mode::Codegen,
@@ -360,6 +364,7 @@ impl Step for CodegenGcc {
             SourceType::InTree,
             &[],
         );
+        self.build_compiler.configure_cargo(&mut cargo);
 
         let _guard =
             builder.msg(Kind::Clippy, "rustc_codegen_gcc", Mode::ToolRustc, build_compiler, target);
@@ -379,7 +384,10 @@ impl Step for CodegenGcc {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::clippy("rustc_codegen_gcc", self.target).built_by(self.build_compiler))
+        Some(
+            StepMetadata::clippy("rustc_codegen_gcc", self.target)
+                .built_by(self.build_compiler.build_compiler()),
+        )
     }
 }
 
@@ -396,7 +404,7 @@ macro_rules! lint_any {
 
         #[derive(Debug, Clone, Hash, PartialEq, Eq)]
         pub struct $name {
-            build_compiler: Compiler,
+            build_compiler: CompilerForCheck,
             target: TargetSelection,
             config: LintConfig,
         }
@@ -419,9 +427,9 @@ macro_rules! lint_any {
             }
 
             fn run(self, builder: &Builder<'_>) -> Self::Output {
-                let build_compiler = self.build_compiler;
+                let build_compiler = self.build_compiler.build_compiler();
                 let target = self.target;
-                let cargo = prepare_tool_cargo(
+                let mut cargo = prepare_tool_cargo(
                     builder,
                     build_compiler,
                     $mode,
@@ -431,6 +439,7 @@ macro_rules! lint_any {
                     SourceType::InTree,
                     &[],
                 );
+                self.build_compiler.configure_cargo(&mut cargo);
 
                 let _guard = builder.msg(
                     Kind::Clippy,
@@ -456,7 +465,7 @@ macro_rules! lint_any {
             }
 
             fn metadata(&self) -> Option<StepMetadata> {
-                Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler))
+                Some(StepMetadata::clippy($readable_name, self.target).built_by(self.build_compiler.build_compiler()))
             }
         }
         )+
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 997a152a31f..004b132f28f 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -12,6 +12,7 @@ use std::ffi::OsStr;
 use std::io::BufReader;
 use std::io::prelude::*;
 use std::path::{Path, PathBuf};
+use std::time::SystemTime;
 use std::{env, fs, str};
 
 use serde_derive::Deserialize;
@@ -38,7 +39,7 @@ use crate::{
 };
 
 /// Build a standard library for the given `target` using the given `build_compiler`.
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Std {
     pub target: TargetSelection,
     /// Compiler that builds the standard library.
@@ -933,13 +934,22 @@ fn cp_rustc_component_to_ci_sysroot(builder: &Builder<'_>, sysroot: &Path, conte
     }
 }
 
+/// Represents information about a built rustc.
+#[derive(Clone, Debug)]
+pub struct BuiltRustc {
+    /// The compiler that actually built this *rustc*.
+    /// This can be different from the *build_compiler* passed to the `Rustc` step because of
+    /// uplifting.
+    pub build_compiler: Compiler,
+}
+
 /// Build rustc using the passed `build_compiler`.
 ///
 /// - Makes sure that `build_compiler` has a standard library prepared for its host target,
 ///   so that it can compile build scripts and proc macros when building this `rustc`.
 /// - Makes sure that `build_compiler` has a standard library prepared for `target`,
 ///   so that the built `rustc` can *link to it* and use it at runtime.
-#[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
     /// The target on which rustc will run (its host).
     pub target: TargetSelection,
@@ -960,7 +970,7 @@ impl Rustc {
 }
 
 impl Step for Rustc {
-    type Output = ();
+    type Output = BuiltRustc;
 
     const IS_HOST: bool = true;
     const DEFAULT: bool = false;
@@ -1000,7 +1010,7 @@ impl Step for Rustc {
     /// This will build the compiler for a particular stage of the build using
     /// the `build_compiler` targeting the `target` architecture. The artifacts
     /// created will also be linked into the sysroot directory.
-    fn run(self, builder: &Builder<'_>) {
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
         let build_compiler = self.build_compiler;
         let target = self.target;
 
@@ -1016,7 +1026,7 @@ impl Step for Rustc {
                 &sysroot,
                 builder.config.ci_rustc_dev_contents(),
             );
-            return;
+            return BuiltRustc { build_compiler };
         }
 
         // Build a standard library for `target` using the `build_compiler`.
@@ -1028,9 +1038,9 @@ impl Step for Rustc {
 
             builder.info("WARNING: Using a potentially old librustc. This may not behave well.");
             builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes");
-            builder.ensure(RustcLink::from_rustc(self, build_compiler));
+            builder.ensure(RustcLink::from_rustc(self));
 
-            return;
+            return BuiltRustc { build_compiler };
         }
 
         // The stage of the compiler that we're building
@@ -1042,21 +1052,35 @@ impl Step for Rustc {
             && !builder.config.full_bootstrap
             && (target == builder.host_target || builder.hosts.contains(&target))
         {
-            // If we're cross-compiling, the earliest rustc that we could have is stage 2.
-            // If we're not cross-compiling, then we should have rustc stage 1.
-            let stage_to_uplift = if target == builder.host_target { 1 } else { 2 };
-            let rustc_to_uplift = builder.compiler(stage_to_uplift, target);
-            let msg = if rustc_to_uplift.host == target {
-                format!("Uplifting rustc (stage{} -> stage{stage})", rustc_to_uplift.stage,)
+            // Here we need to determine the **build compiler** that built the stage that we will
+            // be uplifting. We cannot uplift stage 1, as it has a different ABI than stage 2+,
+            // so we always uplift the stage2 compiler (compiled with stage 1).
+            let uplift_build_compiler = builder.compiler(1, build_compiler.host);
+            let msg = if uplift_build_compiler.host == target {
+                format!("Uplifting rustc (stage2 -> stage{stage})")
             } else {
                 format!(
-                    "Uplifting rustc (stage{}:{} -> stage{stage}:{target})",
-                    rustc_to_uplift.stage, rustc_to_uplift.host,
+                    "Uplifting rustc (stage2:{} -> stage{stage}:{target})",
+                    uplift_build_compiler.host
                 )
             };
             builder.info(&msg);
-            builder.ensure(RustcLink::from_rustc(self, rustc_to_uplift));
-            return;
+
+            // Here the compiler that built the rlibs (`uplift_build_compiler`) can be different
+            // from the compiler whose sysroot should be modified in this step. So we need to copy
+            // the (previously built) rlibs into the correct sysroot.
+            builder.ensure(RustcLink::from_build_compiler_and_sysroot(
+                // This is the compiler that actually built the rustc rlibs
+                uplift_build_compiler,
+                // We copy the rlibs into the sysroot of `build_compiler`
+                build_compiler,
+                target,
+                self.crates,
+            ));
+
+            // Here we have performed an uplift, so we return the actual build compiler that "built"
+            // this rustc.
+            return BuiltRustc { build_compiler: uplift_build_compiler };
         }
 
         // Build a standard library for the current host target using the `build_compiler`.
@@ -1129,10 +1153,8 @@ impl Step for Rustc {
             strip_debug(builder, target, &target_root_dir.join("rustc-main"));
         }
 
-        builder.ensure(RustcLink::from_rustc(
-            self,
-            builder.compiler(build_compiler.stage, builder.config.host_target),
-        ));
+        builder.ensure(RustcLink::from_rustc(self));
+        BuiltRustc { build_compiler }
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
@@ -1441,31 +1463,51 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
     }
 }
 
-/// `RustcLink` copies all of the rlibs from the rustc build into the previous stage's sysroot.
+/// `RustcLink` copies compiler rlibs from a rustc build into a compiler sysroot.
+/// It works with (potentially up to) three compilers:
+/// - `build_compiler` is a compiler that built rustc rlibs
+/// - `sysroot_compiler` is a compiler into whose sysroot we will copy the rlibs
+///   - In most situations, `build_compiler` == `sysroot_compiler`
+/// - `target_compiler` is the compiler whose rlibs were built. It is not represented explicitly
+///   in this step, rather we just read the rlibs from a rustc build stamp of `build_compiler`.
+///
 /// This is necessary for tools using `rustc_private`, where the previous compiler will build
 /// a tool against the next compiler.
 /// To build a tool against a compiler, the rlibs of that compiler that it links against
 /// must be in the sysroot of the compiler that's doing the compiling.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 struct RustcLink {
-    /// The compiler whose rlibs we are copying around.
-    pub compiler: Compiler,
-    /// This is the compiler into whose sysroot we want to copy the rlibs into.
-    pub previous_stage_compiler: Compiler,
-    pub target: TargetSelection,
+    /// This compiler **built** some rustc, whose rlibs we will copy into a sysroot.
+    build_compiler: Compiler,
+    /// This is the compiler into whose sysroot we want to copy the built rlibs.
+    /// In most cases, it will correspond to `build_compiler`.
+    sysroot_compiler: Compiler,
+    target: TargetSelection,
     /// Not actually used; only present to make sure the cache invalidation is correct.
     crates: Vec<String>,
 }
 
 impl RustcLink {
-    fn from_rustc(rustc: Rustc, host_compiler: Compiler) -> Self {
+    /// Copy rlibs from the build compiler that build this `rustc` into the sysroot of that
+    /// build compiler.
+    fn from_rustc(rustc: Rustc) -> Self {
         Self {
-            compiler: host_compiler,
-            previous_stage_compiler: rustc.build_compiler,
+            build_compiler: rustc.build_compiler,
+            sysroot_compiler: rustc.build_compiler,
             target: rustc.target,
             crates: rustc.crates,
         }
     }
+
+    /// Copy rlibs **built** by `build_compiler` into the sysroot of `sysroot_compiler`.
+    fn from_build_compiler_and_sysroot(
+        build_compiler: Compiler,
+        sysroot_compiler: Compiler,
+        target: TargetSelection,
+        crates: Vec<String>,
+    ) -> Self {
+        Self { build_compiler, sysroot_compiler, target, crates }
+    }
 }
 
 impl Step for RustcLink {
@@ -1477,14 +1519,14 @@ impl Step for RustcLink {
 
     /// Same as `std_link`, only for librustc
     fn run(self, builder: &Builder<'_>) {
-        let compiler = self.compiler;
-        let previous_stage_compiler = self.previous_stage_compiler;
+        let build_compiler = self.build_compiler;
+        let sysroot_compiler = self.sysroot_compiler;
         let target = self.target;
         add_to_sysroot(
             builder,
-            &builder.sysroot_target_libdir(previous_stage_compiler, target),
-            &builder.sysroot_target_libdir(previous_stage_compiler, compiler.host),
-            &build_stamp::librustc_stamp(builder, compiler, target),
+            &builder.sysroot_target_libdir(sysroot_compiler, target),
+            &builder.sysroot_target_libdir(sysroot_compiler, sysroot_compiler.host),
+            &build_stamp::librustc_stamp(builder, build_compiler, target),
         );
     }
 }
@@ -1918,7 +1960,7 @@ impl Step for Sysroot {
 /// linker wrappers (LLD, LLVM bitcode linker, etc.).
 ///
 /// This will assemble a compiler in `build/$target/stage$stage`.
-#[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Assemble {
     /// The compiler which we will produce in this step. Assemble itself will
     /// take care of ensuring that the necessary prerequisites to do so exist,
@@ -2099,7 +2141,10 @@ impl Step for Assemble {
             "target_compiler.host" = ?target_compiler.host,
             "building compiler libraries to link to"
         );
-        builder.ensure(Rustc::new(build_compiler, target_compiler.host));
+
+        // It is possible that an uplift has happened, so we override build_compiler here.
+        let BuiltRustc { build_compiler } =
+            builder.ensure(Rustc::new(build_compiler, target_compiler.host));
 
         let stage = target_compiler.stage;
         let host = target_compiler.host;
@@ -2286,6 +2331,7 @@ impl Step for Assemble {
 ///
 /// For a particular stage this will link the file listed in `stamp` into the
 /// `sysroot_dst` provided.
+#[track_caller]
 pub fn add_to_sysroot(
     builder: &Builder<'_>,
     sysroot_dst: &Path,
@@ -2568,7 +2614,17 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path)
     }
 
     let previous_mtime = t!(t!(path.metadata()).modified());
-    command("strip").arg("--strip-debug").arg(path).run_capture(builder);
+    let stamp = BuildStamp::new(path.parent().unwrap())
+        .with_prefix(path.file_name().unwrap().to_str().unwrap())
+        .with_prefix("strip")
+        .add_stamp(previous_mtime.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_nanos());
+
+    // Running strip can be relatively expensive (~1s on librustc_driver.so), so we don't rerun it
+    // if the file is unchanged.
+    if !stamp.is_up_to_date() {
+        command("strip").arg("--strip-debug").arg(path).run_capture(builder);
+    }
+    t!(stamp.write());
 
     let file = t!(fs::File::open(path));
 
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 414f4464d1e..beb71e70035 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -54,7 +54,7 @@ fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool {
     builder.config.tools.as_ref().is_none_or(|tools| tools.contains(tool))
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Docs {
     pub host: TargetSelection,
 }
@@ -91,7 +91,7 @@ impl Step for Docs {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct JsonDocs {
     build_compiler: Compiler,
     target: TargetSelection,
@@ -354,7 +354,7 @@ fn get_cc_search_dirs(
     (bin_path, lib_path)
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Mingw {
     pub host: TargetSelection,
 }
@@ -394,7 +394,7 @@ impl Step for Mingw {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Rustc {
     pub compiler: Compiler,
 }
@@ -730,7 +730,7 @@ fn copy_target_libs(
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Std {
     pub compiler: Compiler,
     pub target: TargetSelection,
@@ -785,7 +785,7 @@ impl Step for Std {
 /// `rust.download-rustc`.
 ///
 /// (Don't confuse this with [`RustDev`], without the `c`!)
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustcDev {
     pub compiler: Compiler,
     pub target: TargetSelection,
@@ -916,6 +916,12 @@ fn copy_src_dirs(
     exclude_dirs: &[&str],
     dst_dir: &Path,
 ) {
+    // The src directories should be relative to `base`, we depend on them not being absolute
+    // paths below.
+    for src_dir in src_dirs {
+        assert!(Path::new(src_dir).is_relative());
+    }
+
     // Iterating, filtering and copying a large number of directories can be quite slow.
     // Avoid doing it in dry run (and thus also tests).
     if builder.config.dry_run() {
@@ -923,6 +929,7 @@ fn copy_src_dirs(
     }
 
     fn filter_fn(exclude_dirs: &[&str], dir: &str, path: &Path) -> bool {
+        // The paths are relative, e.g. `llvm-project/...`.
         let spath = match path.to_str() {
             Some(path) => path,
             None => return false,
@@ -930,65 +937,53 @@ fn copy_src_dirs(
         if spath.ends_with('~') || spath.ends_with(".pyc") {
             return false;
         }
+        // Normalize slashes
+        let spath = spath.replace("\\", "/");
 
-        const LLVM_PROJECTS: &[&str] = &[
+        static LLVM_PROJECTS: &[&str] = &[
             "llvm-project/clang",
-            "llvm-project\\clang",
             "llvm-project/libunwind",
-            "llvm-project\\libunwind",
             "llvm-project/lld",
-            "llvm-project\\lld",
             "llvm-project/lldb",
-            "llvm-project\\lldb",
             "llvm-project/llvm",
-            "llvm-project\\llvm",
             "llvm-project/compiler-rt",
-            "llvm-project\\compiler-rt",
             "llvm-project/cmake",
-            "llvm-project\\cmake",
             "llvm-project/runtimes",
-            "llvm-project\\runtimes",
             "llvm-project/third-party",
-            "llvm-project\\third-party",
         ];
-        if spath.contains("llvm-project")
-            && !spath.ends_with("llvm-project")
-            && !LLVM_PROJECTS.iter().any(|path| spath.contains(path))
-        {
-            return false;
-        }
+        if spath.starts_with("llvm-project") && spath != "llvm-project" {
+            if !LLVM_PROJECTS.iter().any(|path| spath.starts_with(path)) {
+                return false;
+            }
 
-        // Keep only these third party libraries
-        const LLVM_THIRD_PARTY: &[&str] =
-            &["llvm-project/third-party/siphash", "llvm-project\\third-party\\siphash"];
-        if (spath.starts_with("llvm-project/third-party")
-            || spath.starts_with("llvm-project\\third-party"))
-            && !(spath.ends_with("llvm-project/third-party")
-                || spath.ends_with("llvm-project\\third-party"))
-            && !LLVM_THIRD_PARTY.iter().any(|path| spath.contains(path))
-        {
-            return false;
-        }
+            // Keep siphash third-party dependency
+            if spath.starts_with("llvm-project/third-party")
+                && spath != "llvm-project/third-party"
+                && !spath.starts_with("llvm-project/third-party/siphash")
+            {
+                return false;
+            }
 
-        const LLVM_TEST: &[&str] = &["llvm-project/llvm/test", "llvm-project\\llvm\\test"];
-        if LLVM_TEST.iter().any(|path| spath.contains(path))
-            && (spath.ends_with(".ll") || spath.ends_with(".td") || spath.ends_with(".s"))
-        {
-            return false;
+            if spath.starts_with("llvm-project/llvm/test")
+                && (spath.ends_with(".ll") || spath.ends_with(".td") || spath.ends_with(".s"))
+            {
+                return false;
+            }
         }
 
         // Cargo tests use some files like `.gitignore` that we would otherwise exclude.
-        const CARGO_TESTS: &[&str] = &["tools/cargo/tests", "tools\\cargo\\tests"];
-        if CARGO_TESTS.iter().any(|path| spath.contains(path)) {
+        if spath.starts_with("tools/cargo/tests") {
             return true;
         }
 
-        let full_path = Path::new(dir).join(path);
-        if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) {
-            return false;
+        if !exclude_dirs.is_empty() {
+            let full_path = Path::new(dir).join(path);
+            if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) {
+                return false;
+            }
         }
 
-        let excludes = [
+        static EXCLUDES: &[&str] = &[
             "CVS",
             "RCS",
             "SCCS",
@@ -1011,7 +1006,15 @@ fn copy_src_dirs(
             ".hgrags",
             "_darcs",
         ];
-        !path.iter().map(|s| s.to_str().unwrap()).any(|s| excludes.contains(&s))
+
+        // We want to check if any component of `path` doesn't contain the strings in `EXCLUDES`.
+        // However, since we traverse directories top-down in `Builder::cp_link_filtered`,
+        // it is enough to always check only the last component:
+        // - If the path is a file, we will iterate to it and then check it's filename
+        // - If the path is a dir, if it's dir name contains an excluded string, we will not even
+        //   recurse into it.
+        let last_component = path.iter().next_back().map(|s| s.to_str().unwrap()).unwrap();
+        !EXCLUDES.contains(&last_component)
     }
 
     // Copy the directories using our filter
@@ -1023,7 +1026,7 @@ fn copy_src_dirs(
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Src;
 
 impl Step for Src {
@@ -1084,7 +1087,7 @@ impl Step for Src {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct PlainSourceTarball;
 
 impl Step for PlainSourceTarball {
@@ -1230,7 +1233,7 @@ impl Step for PlainSourceTarball {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Cargo {
     pub build_compiler: Compiler,
     pub target: TargetSelection,
@@ -1284,7 +1287,7 @@ impl Step for Cargo {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustAnalyzer {
     pub build_compiler: Compiler,
     pub target: TargetSelection,
@@ -1560,7 +1563,7 @@ impl Step for Rustfmt {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Extended {
     stage: u32,
     host: TargetSelection,
@@ -2401,7 +2404,7 @@ impl Step for LlvmTools {
 
 /// Distributes the `llvm-bitcode-linker` tool so that it can be used by a compiler whose host
 /// is `target`.
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct LlvmBitcodeLinker {
     /// The linker will be compiled by this compiler.
     pub build_compiler: Compiler,
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index f6b27d83120..7fe19c00ef5 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -580,7 +580,7 @@ impl Step for SharedAssets {
 }
 
 /// Document the standard library using `build_compiler`.
-#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Std {
     build_compiler: Compiler,
     target: TargetSelection,
@@ -715,7 +715,7 @@ impl Step for Std {
 /// or remote link.
 const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
 
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub enum DocumentationFormat {
     Html,
     Json,
@@ -1230,7 +1230,7 @@ fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
 }
 
 /// Builds the Rust compiler book.
-#[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RustcBook {
     build_compiler: Compiler,
     target: TargetSelection,
@@ -1334,7 +1334,7 @@ impl Step for RustcBook {
 /// Documents the reference.
 /// It has to always be done using a stage 1+ compiler, because it references in-tree
 /// compiler/stdlib concepts.
-#[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct Reference {
     build_compiler: Compiler,
     target: TargetSelection,
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index 7f1a7d00257..c6288f63847 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -17,7 +17,7 @@ use crate::core::config::flags::get_completion;
 use crate::utils::exec::command;
 use crate::{Mode, t};
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct BuildManifest;
 
 impl Step for BuildManifest {
@@ -56,7 +56,7 @@ impl Step for BuildManifest {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct BumpStage0;
 
 impl Step for BumpStage0 {
@@ -78,7 +78,7 @@ impl Step for BumpStage0 {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct ReplaceVersionPlaceholder;
 
 impl Step for ReplaceVersionPlaceholder {
@@ -169,7 +169,7 @@ impl Step for Miri {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct CollectLicenseMetadata;
 
 impl Step for CollectLicenseMetadata {
@@ -200,7 +200,7 @@ impl Step for CollectLicenseMetadata {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct GenerateCopyright;
 
 impl Step for GenerateCopyright {
@@ -264,7 +264,7 @@ impl Step for GenerateCopyright {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct GenerateWindowsSys;
 
 impl Step for GenerateWindowsSys {
@@ -326,7 +326,7 @@ impl Step for GenerateCompletions {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct UnicodeTableGenerator;
 
 impl Step for UnicodeTableGenerator {
@@ -348,7 +348,7 @@ impl Step for UnicodeTableGenerator {
     }
 }
 
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct FeaturesStatusDump;
 
 impl Step for FeaturesStatusDump {
@@ -408,7 +408,7 @@ impl Step for CyclicStep {
 ///
 /// The coverage-dump tool is an internal detail of coverage tests, so this run
 /// step is only needed when testing coverage-dump manually.
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct CoverageDump;
 
 impl Step for CoverageDump {
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 4006bed4ac5..e7a57a7f375 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -2732,7 +2732,7 @@ fn prepare_cargo_test(
 /// FIXME(Zalathar): Try to split this into two separate steps: a user-visible
 /// step for testing standard library crates, and an internal step used for both
 /// library crates and compiler crates.
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Crate {
     pub compiler: Compiler,
     pub target: TargetSelection,
@@ -3747,7 +3747,7 @@ impl Step for TestFloatParse {
 /// Runs the tool `src/tools/collect-license-metadata` in `ONLY_CHECK=1` mode,
 /// which verifies that `license-metadata.json` is up-to-date and therefore
 /// running the tool normally would not update anything.
-#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct CollectLicenseMetadata;
 
 impl Step for CollectLicenseMetadata {
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 3ce21eb151c..69a744a86cb 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -101,6 +101,7 @@ pub struct Cargo {
 impl Cargo {
     /// Calls [`Builder::cargo`] and [`Cargo::configure_linker`] to prepare an invocation of `cargo`
     /// to be run.
+    #[track_caller]
     pub fn new(
         builder: &Builder<'_>,
         compiler: Compiler,
@@ -139,6 +140,7 @@ impl Cargo {
 
     /// Same as [`Cargo::new`] except this one doesn't configure the linker with
     /// [`Cargo::configure_linker`].
+    #[track_caller]
     pub fn new_for_mir_opt_tests(
         builder: &Builder<'_>,
         compiler: Compiler,
@@ -186,6 +188,32 @@ impl Cargo {
         self
     }
 
+    /// Append a value to an env var of the cargo command instance.
+    /// If the variable was unset previously, this is equivalent to [`Cargo::env`].
+    /// If the variable was already set, this will append `delimiter` and then `value` to it.
+    ///
+    /// Note that this only considers the existence of the env. var. configured on this `Cargo`
+    /// instance. It does not look at the environment of this process.
+    pub fn append_to_env(
+        &mut self,
+        key: impl AsRef<OsStr>,
+        value: impl AsRef<OsStr>,
+        delimiter: impl AsRef<OsStr>,
+    ) -> &mut Cargo {
+        assert_ne!(key.as_ref(), "RUSTFLAGS");
+        assert_ne!(key.as_ref(), "RUSTDOCFLAGS");
+
+        let key = key.as_ref();
+        if let Some((_, Some(previous_value))) = self.command.get_envs().find(|(k, _)| *k == key) {
+            let mut combined: OsString = previous_value.to_os_string();
+            combined.push(delimiter.as_ref());
+            combined.push(value.as_ref());
+            self.env(key, combined)
+        } else {
+            self.env(key, value)
+        }
+    }
+
     pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) {
         builder.add_rustc_lib_path(self.compiler, &mut self.command);
     }
@@ -396,6 +424,7 @@ impl From<Cargo> for BootstrapCommand {
 
 impl Builder<'_> {
     /// Like [`Builder::cargo`], but only passes flags that are valid for all commands.
+    #[track_caller]
     pub fn bare_cargo(
         &self,
         compiler: Compiler,
@@ -480,6 +509,7 @@ impl Builder<'_> {
     /// scoped by `mode`'s output directory, it will pass the `--target` flag for the specified
     /// `target`, and will be executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for
     /// commands to be run with Miri.
+    #[track_caller]
     fn cargo(
         &self,
         compiler: Compiler,
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index a9398a654e9..f4266a6085b 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -1569,7 +1569,7 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
-        [build] rustc 1 <host> -> std 1 <target1>
+        [check] rustc 1 <host> -> std 1 <target1>
         [check] rustc 1 <host> -> rustc 2 <target1> (73 crates)
         [check] rustc 1 <host> -> rustc 2 <target1>
         [check] rustc 1 <host> -> Rustdoc 2 <target1>
diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs
index 285d20917e7..8c5f9037251 100644
--- a/src/bootstrap/src/core/config/mod.rs
+++ b/src/bootstrap/src/core/config/mod.rs
@@ -324,7 +324,7 @@ impl FromStr for LlvmLibunwind {
     }
 }
 
-#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum SplitDebuginfo {
     Packed,
     Unpacked,
diff --git a/src/bootstrap/src/core/config/target_selection.rs b/src/bootstrap/src/core/config/target_selection.rs
index ebd3fe7a8c6..40b63a7f9c7 100644
--- a/src/bootstrap/src/core/config/target_selection.rs
+++ b/src/bootstrap/src/core/config/target_selection.rs
@@ -14,7 +14,7 @@ pub struct TargetSelection {
 }
 
 /// Newtype over `Vec<TargetSelection>` so we can implement custom parsing logic
-#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Clone, Default, PartialEq, Eq, Hash, Debug)]
 pub struct TargetSelectionList(pub Vec<TargetSelection>);
 
 pub fn target_selection_list(s: &str) -> Result<TargetSelectionList, String> {
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 706a3cbb210..8f766ed00a5 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -279,7 +279,7 @@ pub enum DependencyType {
 ///
 /// These entries currently correspond to the various output directories of the
 /// build system, with each mod generating output in a different directory.
-#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
 pub enum Mode {
     /// Build the standard library, placing output in the "stageN-std" directory.
     Std,
@@ -357,7 +357,7 @@ pub enum RemapScheme {
     NonCompiler,
 }
 
-#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
 pub enum CLang {
     C,
     Cxx,
@@ -1743,6 +1743,7 @@ impl Build {
     ///
     /// If `src` is a symlink, `src` will be resolved to the actual path
     /// and copied to `dst` instead of the symlink itself.
+    #[track_caller]
     pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) {
         self.copy_link_internal(src, dst, true);
     }
@@ -1751,6 +1752,7 @@ impl Build {
     /// Attempts to use hard links if possible, falling back to copying.
     /// You can neither rely on this being a copy nor it being a link,
     /// so do not write to dst.
+    #[track_caller]
     pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
         self.copy_link_internal(src, dst, false);
 
@@ -1765,6 +1767,7 @@ impl Build {
         }
     }
 
+    #[track_caller]
     fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
         if self.config.dry_run() {
             return;
@@ -1773,6 +1776,10 @@ impl Build {
         if src == dst {
             return;
         }
+
+        #[cfg(feature = "tracing")]
+        let _span = trace_io!("file-copy-link", ?src, ?dst);
+
         if let Err(e) = fs::remove_file(dst)
             && cfg!(windows)
             && e.kind() != io::ErrorKind::NotFound
@@ -1815,6 +1822,7 @@ impl Build {
     /// Links the `src` directory recursively to `dst`. Both are assumed to exist
     /// when this function is called.
     /// Will attempt to use hard links if possible and fall back to copying.
+    #[track_caller]
     pub fn cp_link_r(&self, src: &Path, dst: &Path) {
         if self.config.dry_run() {
             return;
@@ -1837,12 +1845,14 @@ impl Build {
     /// Will attempt to use hard links if possible and fall back to copying.
     /// Unwanted files or directories can be skipped
     /// by returning `false` from the filter function.
+    #[track_caller]
     pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
         // Immediately recurse with an empty relative path
         self.cp_link_filtered_recurse(src, dst, Path::new(""), filter)
     }
 
     // Inner function does the actual work
+    #[track_caller]
     fn cp_link_filtered_recurse(
         &self,
         src: &Path,
@@ -1862,7 +1872,6 @@ impl Build {
                     self.create_dir(&dst);
                     self.cp_link_filtered_recurse(&path, &dst, &relative, filter);
                 } else {
-                    let _ = fs::remove_file(&dst);
                     self.copy_link(&path, &dst, FileType::Regular);
                 }
             }
@@ -1904,10 +1913,15 @@ impl Build {
         t!(fs::read_to_string(path))
     }
 
+    #[track_caller]
     fn create_dir(&self, dir: &Path) {
         if self.config.dry_run() {
             return;
         }
+
+        #[cfg(feature = "tracing")]
+        let _span = trace_io!("dir-create", ?dir);
+
         t!(fs::create_dir_all(dir))
     }
 
@@ -1915,6 +1929,10 @@ impl Build {
         if self.config.dry_run() {
             return;
         }
+
+        #[cfg(feature = "tracing")]
+        let _span = trace_io!("dir-remove", ?dir);
+
         t!(fs::remove_dir_all(dir))
     }
 
diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs
index 428ba013c98..472781ffa73 100644
--- a/src/bootstrap/src/utils/tracing.rs
+++ b/src/bootstrap/src/utils/tracing.rs
@@ -49,13 +49,36 @@ macro_rules! error {
 }
 
 #[cfg(feature = "tracing")]
+pub const IO_SPAN_TARGET: &str = "IO";
+
+/// Create a tracing span around an I/O operation, if tracing is enabled.
+/// Note that at least one tracing value field has to be passed to this macro, otherwise it will not
+/// compile.
+#[macro_export]
+macro_rules! trace_io {
+    ($name:expr, $($args:tt)*) => {
+        ::tracing::trace_span!(
+            target: $crate::utils::tracing::IO_SPAN_TARGET,
+            $name,
+            $($args)*,
+            location = $crate::utils::tracing::format_location(*::std::panic::Location::caller())
+        ).entered()
+    }
+}
+
+#[cfg(feature = "tracing")]
+pub fn format_location(location: std::panic::Location<'static>) -> String {
+    format!("{}:{}", location.file(), location.line())
+}
+
+#[cfg(feature = "tracing")]
 const COMMAND_SPAN_TARGET: &str = "COMMAND";
 
 #[cfg(feature = "tracing")]
 pub fn trace_cmd(command: &crate::BootstrapCommand) -> tracing::span::EnteredSpan {
     let fingerprint = command.fingerprint();
     let location = command.get_created_location();
-    let location = format!("{}:{}", location.file(), location.line());
+    let location = format_location(location);
 
     tracing::span!(
         target: COMMAND_SPAN_TARGET,
@@ -84,6 +107,7 @@ mod inner {
     use std::fmt::Debug;
     use std::fs::File;
     use std::io::Write;
+    use std::path::{Path, PathBuf};
     use std::sync::atomic::Ordering;
 
     use chrono::{DateTime, Utc};
@@ -93,8 +117,8 @@ mod inner {
     use tracing_subscriber::registry::{LookupSpan, SpanRef};
     use tracing_subscriber::{EnvFilter, Layer};
 
+    use super::{COMMAND_SPAN_TARGET, IO_SPAN_TARGET};
     use crate::STEP_SPAN_TARGET;
-    use crate::utils::tracing::COMMAND_SPAN_TARGET;
 
     pub fn setup_tracing(env_name: &str) -> TracingGuard {
         let filter = EnvFilter::from_env(env_name);
@@ -291,6 +315,23 @@ mod inner {
                 Ok(())
             }
 
+            // Write fields while treating the "location" field specially, and assuming that it
+            // contains the source file location relevant to the span.
+            let write_with_location = |writer: &mut W| -> std::io::Result<()> {
+                if let Some(values) = field_values {
+                    write_fields(
+                        writer,
+                        values.fields.iter().filter(|(name, _)| *name != "location"),
+                    )?;
+                    let location =
+                        &values.fields.iter().find(|(name, _)| *name == "location").unwrap().1;
+                    let (filename, line) = location.rsplit_once(':').unwrap();
+                    let filename = shorten_filename(filename);
+                    write!(writer, " ({filename}:{line})",)?;
+                }
+                Ok(())
+            };
+
             // We handle steps specially. We instrument them dynamically in `Builder::ensure`,
             // and we want to have custom name for each step span. But tracing doesn't allow setting
             // dynamic span names. So we detect step spans here and override their name.
@@ -311,17 +352,11 @@ mod inner {
                 // Executed command
                 COMMAND_SPAN_TARGET => {
                     write!(writer, "{}", span.name())?;
-                    if let Some(values) = field_values {
-                        write_fields(
-                            writer,
-                            values.fields.iter().filter(|(name, _)| *name != "location"),
-                        )?;
-                        write!(
-                            writer,
-                            " ({})",
-                            values.fields.iter().find(|(name, _)| *name == "location").unwrap().1
-                        )?;
-                    }
+                    write_with_location(writer)?;
+                }
+                IO_SPAN_TARGET => {
+                    write!(writer, "{}", span.name())?;
+                    write_with_location(writer)?;
                 }
                 // Other span
                 _ => {
@@ -342,21 +377,10 @@ mod inner {
         writer: &mut W,
         metadata: &'static tracing::Metadata<'static>,
     ) -> std::io::Result<()> {
-        use std::path::{Path, PathBuf};
-
         if let Some(filename) = metadata.file() {
-            // Keep only the module name and file name to make it shorter
-            let filename: PathBuf = Path::new(filename)
-                .components()
-                // Take last two path components
-                .rev()
-                .take(2)
-                .collect::<Vec<_>>()
-                .into_iter()
-                .rev()
-                .collect();
-
-            write!(writer, " ({}", filename.display())?;
+            let filename = shorten_filename(filename);
+
+            write!(writer, " ({filename}")?;
             if let Some(line) = metadata.line() {
                 write!(writer, ":{line}")?;
             }
@@ -365,6 +389,21 @@ mod inner {
         Ok(())
     }
 
+    /// Keep only the module name and file name to make it shorter
+    fn shorten_filename(filename: &str) -> String {
+        Path::new(filename)
+            .components()
+            // Take last two path components
+            .rev()
+            .take(2)
+            .collect::<Vec<_>>()
+            .into_iter()
+            .rev()
+            .collect::<PathBuf>()
+            .display()
+            .to_string()
+    }
+
     impl<S> Layer<S> for TracingPrinter
     where
         S: Subscriber,