about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-02-11 19:58:12 +0000
committerbors <bors@rust-lang.org>2024-02-11 19:58:12 +0000
commit1a648b397dedc98ada3dd3360f6d661ec2436c56 (patch)
tree3db289d0c8e62851abd14834eac4e00084d139fd
parenta166af7729d6f4e6570918701f79dfae750a00c4 (diff)
parentff6d296589d959c82c2dcb17b0072209dbdb07d5 (diff)
downloadrust-1a648b397dedc98ada3dd3360f6d661ec2436c56.tar.gz
rust-1a648b397dedc98ada3dd3360f6d661ec2436c56.zip
Auto merge of #120803 - onur-ozkan:fix-compilation-cache, r=Mark-Simulacrum
always run `configure_linker` except for mir-opt tests

`configure_linker` now runs consistently unless it's for mir-opt tests. Previously `!= "check"` condition was causing dirt in the cargo cache between runs of `x anything-but-not-check` and `x check`.

 Fixes #120768

 cc `@saethlin`
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs19
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs38
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs34
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs4
-rw-r--r--src/bootstrap/src/core/builder.rs314
7 files changed, 250 insertions, 171 deletions
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 5f0afdb1b36..4a04dbf44aa 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -4,7 +4,9 @@ use crate::core::build_steps::compile::{
     add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo,
 };
 use crate::core::build_steps::tool::{prepare_tool_cargo, SourceType};
-use crate::core::builder::{crate_description, Alias, Builder, Kind, RunConfig, ShouldRun, Step};
+use crate::core::builder::{
+    self, crate_description, Alias, Builder, Kind, RunConfig, ShouldRun, Step,
+};
 use crate::core::config::TargetSelection;
 use crate::utils::cache::Interned;
 use crate::INTERNER;
@@ -110,13 +112,15 @@ impl Step for Std {
         let target = self.target;
         let compiler = builder.compiler(builder.top_stage, builder.config.build);
 
-        let mut cargo = builder.cargo(
+        let mut cargo = builder::Cargo::new(
+            builder,
             compiler,
             Mode::Std,
             SourceType::InTree,
             target,
             cargo_subcommand(builder.kind),
         );
+
         std_cargo(builder, target, compiler.stage, &mut cargo);
         if matches!(builder.config.cmd, Subcommand::Fix { .. }) {
             // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot.
@@ -162,7 +166,8 @@ impl Step for Std {
         // since we initialize with an empty sysroot.
         //
         // Currently only the "libtest" tree of crates does this.
-        let mut cargo = builder.cargo(
+        let mut cargo = builder::Cargo::new(
+            builder,
             compiler,
             Mode::Std,
             SourceType::InTree,
@@ -256,13 +261,15 @@ impl Step for Rustc {
             builder.ensure(Std::new(target));
         }
 
-        let mut cargo = builder.cargo(
+        let mut cargo = builder::Cargo::new(
+            builder,
             compiler,
             Mode::Rustc,
             SourceType::InTree,
             target,
             cargo_subcommand(builder.kind),
         );
+
         rustc_cargo(builder, &mut cargo, target, compiler.stage);
 
         // For ./x.py clippy, don't run with --all-targets because
@@ -332,13 +339,15 @@ impl Step for CodegenBackend {
 
         builder.ensure(Rustc::new(target, builder));
 
-        let mut cargo = builder.cargo(
+        let mut cargo = builder::Cargo::new(
+            builder,
             compiler,
             Mode::Codegen,
             SourceType::InTree,
             target,
             cargo_subcommand(builder.kind),
         );
+
         cargo
             .arg("--manifest-path")
             .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml")));
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 27b1d311864..64bef2d3015 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -22,6 +22,7 @@ use serde_derive::Deserialize;
 use crate::core::build_steps::dist;
 use crate::core::build_steps::llvm;
 use crate::core::build_steps::tool::SourceType;
+use crate::core::builder;
 use crate::core::builder::crate_description;
 use crate::core::builder::Cargo;
 use crate::core::builder::{Builder, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath};
@@ -239,12 +240,26 @@ impl Step for Std {
         // with -Zalways-encode-mir. This frees us from the need to have a target linker, and the
         // fact that this is a check build integrates nicely with run_cargo.
         let mut cargo = if self.is_for_mir_opt_tests {
-            let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "check");
+            let mut cargo = builder::Cargo::new_for_mir_opt_tests(
+                builder,
+                compiler,
+                Mode::Std,
+                SourceType::InTree,
+                target,
+                "check",
+            );
             cargo.rustflag("-Zalways-encode-mir");
             cargo.arg("--manifest-path").arg(builder.src.join("library/sysroot/Cargo.toml"));
             cargo
         } else {
-            let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build");
+            let mut cargo = builder::Cargo::new(
+                builder,
+                compiler,
+                Mode::Std,
+                SourceType::InTree,
+                target,
+                "build",
+            );
             std_cargo(builder, target, compiler.stage, &mut cargo);
             for krate in &*self.crates {
                 cargo.arg("-p").arg(krate);
@@ -913,7 +928,15 @@ impl Step for Rustc {
             builder.config.build,
         ));
 
-        let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "build");
+        let mut cargo = builder::Cargo::new(
+            builder,
+            compiler,
+            Mode::Rustc,
+            SourceType::InTree,
+            target,
+            "build",
+        );
+
         rustc_cargo(builder, &mut cargo, target, compiler.stage);
 
         if builder.config.rust_profile_use.is_some()
@@ -1333,7 +1356,14 @@ impl Step for CodegenBackend {
 
         let out_dir = builder.cargo_out(compiler, Mode::Codegen, target);
 
-        let mut cargo = builder.cargo(compiler, Mode::Codegen, SourceType::InTree, target, "build");
+        let mut cargo = builder::Cargo::new(
+            builder,
+            compiler,
+            Mode::Codegen,
+            SourceType::InTree,
+            target,
+            "build",
+        );
         cargo
             .arg("--manifest-path")
             .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml")));
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 57e63927c95..fdb099e4ab1 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -13,7 +13,7 @@ use std::{fs, mem};
 
 use crate::core::build_steps::compile;
 use crate::core::build_steps::tool::{self, prepare_tool_cargo, SourceType, Tool};
-use crate::core::builder::crate_description;
+use crate::core::builder::{self, crate_description};
 use crate::core::builder::{Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::{Config, TargetSelection};
 use crate::utils::cache::{Interned, INTERNER};
@@ -684,7 +684,9 @@ fn doc_std(
     // as a function parameter.
     let out_dir = target_dir.join(target.triple).join("doc");
 
-    let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "doc");
+    let mut cargo =
+        builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, "doc");
+
     compile::std_cargo(builder, target, compiler.stage, &mut cargo);
     cargo
         .arg("--no-deps")
@@ -785,7 +787,9 @@ impl Step for Rustc {
         );
 
         // Build cargo command.
-        let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc");
+        let mut cargo =
+            builder::Cargo::new(builder, compiler, Mode::Rustc, SourceType::InTree, target, "doc");
+
         cargo.rustdocflag("--document-private-items");
         // Since we always pass --document-private-items, there's no need to warn about linking to private items.
         cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index d9e0da14a70..5fa5f2d4794 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -165,7 +165,7 @@ impl Step for Miri {
             SourceType::InTree,
             &[],
         );
-        miri.add_rustc_lib_path(builder, compiler);
+        miri.add_rustc_lib_path(builder);
         // Forward arguments.
         miri.arg("--").arg("--target").arg(target.rustc_target_arg());
         miri.args(builder.config.args());
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index baf1b5a4a1a..1dbda405128 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -20,6 +20,7 @@ use crate::core::build_steps::llvm;
 use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
 use crate::core::build_steps::tool::{self, SourceType, Tool};
 use crate::core::build_steps::toolstate::ToolState;
+use crate::core::builder;
 use crate::core::builder::crate_description;
 use crate::core::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::flags::get_completion;
@@ -380,7 +381,7 @@ impl Step for RustAnalyzer {
         // work in Rust CI
         cargo.env("SKIP_SLOW_TESTS", "1");
 
-        cargo.add_rustc_lib_path(builder, compiler);
+        cargo.add_rustc_lib_path(builder);
         run_cargo_test(cargo, &[], &[], "rust-analyzer", "rust-analyzer", compiler, host, builder);
     }
 }
@@ -426,7 +427,7 @@ impl Step for Rustfmt {
         t!(fs::create_dir_all(&dir));
         cargo.env("RUSTFMT_TEST_DIR", dir);
 
-        cargo.add_rustc_lib_path(builder, compiler);
+        cargo.add_rustc_lib_path(builder);
 
         run_cargo_test(cargo, &[], &[], "rustfmt", "rustfmt", compiler, host, builder);
     }
@@ -476,7 +477,7 @@ impl Step for RustDemangler {
         t!(fs::create_dir_all(&dir));
 
         cargo.env("RUST_DEMANGLER_DRIVER_PATH", rust_demangler);
-        cargo.add_rustc_lib_path(builder, compiler);
+        cargo.add_rustc_lib_path(builder);
 
         run_cargo_test(
             cargo,
@@ -517,7 +518,7 @@ impl Miri {
             SourceType::InTree,
             &[],
         );
-        cargo.add_rustc_lib_path(builder, compiler);
+        cargo.add_rustc_lib_path(builder);
         cargo.arg("--").arg("miri").arg("setup");
         cargo.arg("--target").arg(target.rustc_target_arg());
 
@@ -618,7 +619,7 @@ impl Step for Miri {
         );
         let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target);
 
-        cargo.add_rustc_lib_path(builder, compiler);
+        cargo.add_rustc_lib_path(builder);
 
         // miri tests need to know about the stage sysroot
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
@@ -671,7 +672,7 @@ impl Step for Miri {
             SourceType::Submodule,
             &[],
         );
-        cargo.add_rustc_lib_path(builder, compiler);
+        cargo.add_rustc_lib_path(builder);
         cargo.arg("--").arg("miri").arg("test");
         if builder.config.locked_deps {
             cargo.arg("--locked");
@@ -788,7 +789,7 @@ impl Step for Clippy {
         let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir());
         cargo.env("HOST_LIBS", host_libs);
 
-        cargo.add_rustc_lib_path(builder, compiler);
+        cargo.add_rustc_lib_path(builder);
         let mut cargo = prepare_cargo_test(cargo, &[], &[], "clippy", compiler, host, builder);
 
         let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host);
@@ -2499,8 +2500,15 @@ impl Step for Crate {
         // we're working with automatically.
         let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
 
-        let mut cargo =
-            builder.cargo(compiler, mode, SourceType::InTree, target, builder.kind.as_str());
+        let mut cargo = builder::Cargo::new(
+            builder,
+            compiler,
+            mode,
+            SourceType::InTree,
+            target,
+            builder.kind.as_str(),
+        );
+
         match mode {
             Mode::Std => {
                 compile::std_cargo(builder, target, compiler.stage, &mut cargo);
@@ -3134,13 +3142,15 @@ impl Step for CodegenCranelift {
         let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
 
         let build_cargo = || {
-            let mut cargo = builder.cargo(
+            let mut cargo = builder::Cargo::new(
+                builder,
                 compiler,
                 Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
                 SourceType::InTree,
                 target,
                 "run",
             );
+
             cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
             cargo
                 .arg("--manifest-path")
@@ -3260,13 +3270,15 @@ impl Step for CodegenGCC {
         let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
 
         let build_cargo = || {
-            let mut cargo = builder.cargo(
+            let mut cargo = builder::Cargo::new(
+                builder,
                 compiler,
                 Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
                 SourceType::InTree,
                 target,
                 "run",
             );
+
             cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
             cargo
                 .arg("--manifest-path")
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 5d8d10a7deb..a3daf22c9a9 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -5,6 +5,7 @@ use std::process::Command;
 
 use crate::core::build_steps::compile;
 use crate::core::build_steps::toolstate::ToolState;
+use crate::core::builder;
 use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::channel::GitInfo;
@@ -142,7 +143,8 @@ pub fn prepare_tool_cargo(
     source_type: SourceType,
     extra_features: &[String],
 ) -> CargoCommand {
-    let mut cargo = builder.cargo(compiler, mode, source_type, target, command);
+    let mut cargo = builder::Cargo::new(builder, compiler, mode, source_type, target, command);
+
     let dir = builder.src.join(path);
     cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
 
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 83485abfa56..b21ffe868e1 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1299,14 +1299,12 @@ impl<'a> Builder<'a> {
         cargo
     }
 
-    /// Prepares an invocation of `cargo` to be run.
-    ///
     /// This will create a `Command` that represents a pending execution of
     /// Cargo. This cargo will be configured to use `compiler` as the actual
     /// rustc compiler, its output will be scoped by `mode`'s output directory,
     /// it will pass the `--target` flag for the specified `target`, and will be
     /// executing the Cargo command `cmd`.
-    pub fn cargo(
+    fn cargo(
         &self,
         compiler: Compiler,
         mode: Mode,
@@ -1872,17 +1870,6 @@ impl<'a> Builder<'a> {
             rustflags.arg("-Wrustc::internal");
         }
 
-        if cmd != "check" {
-            self.configure_linker(
-                compiler,
-                target,
-                &mut cargo,
-                &mut rustflags,
-                &mut rustdocflags,
-                &mut hostflags,
-            );
-        }
-
         // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc
         // when compiling the standard library, since this might be linked into the final outputs
         // produced by rustc. Since this mitigation is only available on Windows, only enable it
@@ -2031,136 +2018,14 @@ impl<'a> Builder<'a> {
             cargo.env("RUSTFLAGS", &rustc_args.join(" "));
         }
 
-        Cargo { command: cargo, rustflags, rustdocflags, hostflags, allow_features }
-    }
-
-    fn configure_linker(
-        &self,
-        compiler: Compiler,
-        target: TargetSelection,
-        cargo: &mut Command,
-        rustflags: &mut Rustflags,
-        rustdocflags: &mut Rustflags,
-        hostflags: &mut HostFlags,
-    ) {
-        // Dealing with rpath here is a little special, so let's go into some
-        // detail. First off, `-rpath` is a linker option on Unix platforms
-        // which adds to the runtime dynamic loader path when looking for
-        // dynamic libraries. We use this by default on Unix platforms to ensure
-        // that our nightlies behave the same on Windows, that is they work out
-        // of the box. This can be disabled by setting `rpath = false` in `[rust]`
-        // table of `config.toml`
-        //
-        // Ok, so the astute might be wondering "why isn't `-C rpath` used
-        // here?" and that is indeed a good question to ask. This codegen
-        // option is the compiler's current interface to generating an rpath.
-        // Unfortunately it doesn't quite suffice for us. The flag currently
-        // takes no value as an argument, so the compiler calculates what it
-        // should pass to the linker as `-rpath`. This unfortunately is based on
-        // the **compile time** directory structure which when building with
-        // Cargo will be very different than the runtime directory structure.
-        //
-        // All that's a really long winded way of saying that if we use
-        // `-Crpath` then the executables generated have the wrong rpath of
-        // something like `$ORIGIN/deps` when in fact the way we distribute
-        // rustc requires the rpath to be `$ORIGIN/../lib`.
-        //
-        // So, all in all, to set up the correct rpath we pass the linker
-        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
-        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
-        // to change a flag in a binary?
-        if self.config.rpath_enabled(target) && helpers::use_host_linker(target) {
-            let libdir = self.sysroot_libdir_relative(compiler).to_str().unwrap();
-            let rpath = if target.contains("apple") {
-                // Note that we need to take one extra step on macOS to also pass
-                // `-Wl,-instal_name,@rpath/...` to get things to work right. To
-                // do that we pass a weird flag to the compiler to get it to do
-                // so. Note that this is definitely a hack, and we should likely
-                // flesh out rpath support more fully in the future.
-                rustflags.arg("-Zosx-rpath-install-name");
-                Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
-            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
-                rustflags.arg("-Clink-args=-Wl,-z,origin");
-                Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
-            } else {
-                None
-            };
-            if let Some(rpath) = rpath {
-                rustflags.arg(&format!("-Clink-args={rpath}"));
-            }
-        }
-
-        for arg in linker_args(self, compiler.host, LldThreads::Yes) {
-            hostflags.arg(&arg);
-        }
-
-        if let Some(target_linker) = self.linker(target) {
-            let target = crate::envify(&target.triple);
-            cargo.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker);
-        }
-        // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
-        // `linker_args` here.
-        for flag in linker_flags(self, target, LldThreads::Yes) {
-            rustflags.arg(&flag);
-        }
-        for arg in linker_args(self, target, LldThreads::Yes) {
-            rustdocflags.arg(&arg);
-        }
-
-        if !self.config.dry_run() && self.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
-        {
-            rustflags.arg("-Clink-arg=-gz");
-        }
-
-        // Throughout the build Cargo can execute a number of build scripts
-        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
-        // obtained previously to those build scripts.
-        // Build scripts use either the `cc` crate or `configure/make` so we pass
-        // the options through environment variables that are fetched and understood by both.
-        //
-        // FIXME: the guard against msvc shouldn't need to be here
-        if target.is_msvc() {
-            if let Some(ref cl) = self.config.llvm_clang_cl {
-                cargo.env("CC", cl).env("CXX", cl);
-            }
-        } else {
-            let ccache = self.config.ccache.as_ref();
-            let ccacheify = |s: &Path| {
-                let ccache = match ccache {
-                    Some(ref s) => s,
-                    None => return s.display().to_string(),
-                };
-                // FIXME: the cc-rs crate only recognizes the literal strings
-                // `ccache` and `sccache` when doing caching compilations, so we
-                // mirror that here. It should probably be fixed upstream to
-                // accept a new env var or otherwise work with custom ccache
-                // vars.
-                match &ccache[..] {
-                    "ccache" | "sccache" => format!("{} {}", ccache, s.display()),
-                    _ => s.display().to_string(),
-                }
-            };
-            let triple_underscored = target.triple.replace("-", "_");
-            let cc = ccacheify(&self.cc(target));
-            cargo.env(format!("CC_{triple_underscored}"), &cc);
-
-            let cflags = self.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
-            cargo.env(format!("CFLAGS_{triple_underscored}"), &cflags);
-
-            if let Some(ar) = self.ar(target) {
-                let ranlib = format!("{} s", ar.display());
-                cargo
-                    .env(format!("AR_{triple_underscored}"), ar)
-                    .env(format!("RANLIB_{triple_underscored}"), ranlib);
-            }
-
-            if let Ok(cxx) = self.cxx(target) {
-                let cxx = ccacheify(&cxx);
-                let cxxflags = self.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
-                cargo
-                    .env(format!("CXX_{triple_underscored}"), &cxx)
-                    .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags);
-            }
+        Cargo {
+            command: cargo,
+            compiler,
+            target,
+            rustflags,
+            rustdocflags,
+            hostflags,
+            allow_features,
         }
     }
 
@@ -2363,6 +2228,8 @@ impl HostFlags {
 #[derive(Debug)]
 pub struct Cargo {
     command: Command,
+    compiler: Compiler,
+    target: TargetSelection,
     rustflags: Rustflags,
     rustdocflags: Rustflags,
     hostflags: HostFlags,
@@ -2370,10 +2237,37 @@ pub struct Cargo {
 }
 
 impl Cargo {
+    /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run.
+    pub fn new(
+        builder: &Builder<'_>,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd: &str,
+    ) -> Cargo {
+        let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd);
+        cargo.configure_linker(builder);
+        cargo
+    }
+
+    /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker`
+    pub fn new_for_mir_opt_tests(
+        builder: &Builder<'_>,
+        compiler: Compiler,
+        mode: Mode,
+        source_type: SourceType,
+        target: TargetSelection,
+        cmd: &str,
+    ) -> Cargo {
+        builder.cargo(compiler, mode, source_type, target, cmd)
+    }
+
     pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo {
         self.rustdocflags.arg(arg);
         self
     }
+
     pub fn rustflag(&mut self, arg: &str) -> &mut Cargo {
         self.rustflags.arg(arg);
         self
@@ -2403,8 +2297,8 @@ impl Cargo {
         self
     }
 
-    pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>, compiler: Compiler) {
-        builder.add_rustc_lib_path(compiler, &mut self.command);
+    pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) {
+        builder.add_rustc_lib_path(self.compiler, &mut self.command);
     }
 
     pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo {
@@ -2423,6 +2317,134 @@ impl Cargo {
         self.allow_features.push_str(features);
         self
     }
+
+    fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo {
+        let target = self.target;
+        let compiler = self.compiler;
+
+        // Dealing with rpath here is a little special, so let's go into some
+        // detail. First off, `-rpath` is a linker option on Unix platforms
+        // which adds to the runtime dynamic loader path when looking for
+        // dynamic libraries. We use this by default on Unix platforms to ensure
+        // that our nightlies behave the same on Windows, that is they work out
+        // of the box. This can be disabled by setting `rpath = false` in `[rust]`
+        // table of `config.toml`
+        //
+        // Ok, so the astute might be wondering "why isn't `-C rpath` used
+        // here?" and that is indeed a good question to ask. This codegen
+        // option is the compiler's current interface to generating an rpath.
+        // Unfortunately it doesn't quite suffice for us. The flag currently
+        // takes no value as an argument, so the compiler calculates what it
+        // should pass to the linker as `-rpath`. This unfortunately is based on
+        // the **compile time** directory structure which when building with
+        // Cargo will be very different than the runtime directory structure.
+        //
+        // All that's a really long winded way of saying that if we use
+        // `-Crpath` then the executables generated have the wrong rpath of
+        // something like `$ORIGIN/deps` when in fact the way we distribute
+        // rustc requires the rpath to be `$ORIGIN/../lib`.
+        //
+        // So, all in all, to set up the correct rpath we pass the linker
+        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
+        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
+        // to change a flag in a binary?
+        if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) {
+            let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap();
+            let rpath = if target.contains("apple") {
+                // Note that we need to take one extra step on macOS to also pass
+                // `-Wl,-instal_name,@rpath/...` to get things to work right. To
+                // do that we pass a weird flag to the compiler to get it to do
+                // so. Note that this is definitely a hack, and we should likely
+                // flesh out rpath support more fully in the future.
+                self.rustflags.arg("-Zosx-rpath-install-name");
+                Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
+            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
+                self.rustflags.arg("-Clink-args=-Wl,-z,origin");
+                Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
+            } else {
+                None
+            };
+            if let Some(rpath) = rpath {
+                self.rustflags.arg(&format!("-Clink-args={rpath}"));
+            }
+        }
+
+        for arg in linker_args(builder, compiler.host, LldThreads::Yes) {
+            self.hostflags.arg(&arg);
+        }
+
+        if let Some(target_linker) = builder.linker(target) {
+            let target = crate::envify(&target.triple);
+            self.command.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker);
+        }
+        // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
+        // `linker_args` here.
+        for flag in linker_flags(builder, target, LldThreads::Yes) {
+            self.rustflags.arg(&flag);
+        }
+        for arg in linker_args(builder, target, LldThreads::Yes) {
+            self.rustdocflags.arg(&arg);
+        }
+
+        if !builder.config.dry_run()
+            && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
+        {
+            self.rustflags.arg("-Clink-arg=-gz");
+        }
+
+        // Throughout the build Cargo can execute a number of build scripts
+        // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
+        // obtained previously to those build scripts.
+        // Build scripts use either the `cc` crate or `configure/make` so we pass
+        // the options through environment variables that are fetched and understood by both.
+        //
+        // FIXME: the guard against msvc shouldn't need to be here
+        if target.is_msvc() {
+            if let Some(ref cl) = builder.config.llvm_clang_cl {
+                self.command.env("CC", cl).env("CXX", cl);
+            }
+        } else {
+            let ccache = builder.config.ccache.as_ref();
+            let ccacheify = |s: &Path| {
+                let ccache = match ccache {
+                    Some(ref s) => s,
+                    None => return s.display().to_string(),
+                };
+                // FIXME: the cc-rs crate only recognizes the literal strings
+                // `ccache` and `sccache` when doing caching compilations, so we
+                // mirror that here. It should probably be fixed upstream to
+                // accept a new env var or otherwise work with custom ccache
+                // vars.
+                match &ccache[..] {
+                    "ccache" | "sccache" => format!("{} {}", ccache, s.display()),
+                    _ => s.display().to_string(),
+                }
+            };
+            let triple_underscored = target.triple.replace("-", "_");
+            let cc = ccacheify(&builder.cc(target));
+            self.command.env(format!("CC_{triple_underscored}"), &cc);
+
+            let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ");
+            self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags);
+
+            if let Some(ar) = builder.ar(target) {
+                let ranlib = format!("{} s", ar.display());
+                self.command
+                    .env(format!("AR_{triple_underscored}"), ar)
+                    .env(format!("RANLIB_{triple_underscored}"), ranlib);
+            }
+
+            if let Ok(cxx) = builder.cxx(target) {
+                let cxx = ccacheify(&cxx);
+                let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ");
+                self.command
+                    .env(format!("CXX_{triple_underscored}"), &cxx)
+                    .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags);
+            }
+        }
+
+        self
+    }
 }
 
 impl From<Cargo> for Command {