about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
authorBen Kimock <kimockb@gmail.com>2024-01-07 17:53:20 -0500
committerBen Kimock <kimockb@gmail.com>2024-02-06 23:36:05 -0500
commit4451bf4d678c67755c153995d638559a76469af2 (patch)
treeee2639c9bb1e6d5d5dd104989b64fd07151f0c2f /src/bootstrap
parent586893c7b0adabf5f0a4c155fd86e13cf470e74b (diff)
downloadrust-4451bf4d678c67755c153995d638559a76469af2.tar.gz
rust-4451bf4d678c67755c153995d638559a76469af2.zip
Use a better set of targets for blessing mir-opt tests
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs32
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs119
-rw-r--r--src/bootstrap/src/core/builder.rs256
3 files changed, 201 insertions, 206 deletions
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index ddbe18ab838..3c0d074807d 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -97,6 +97,20 @@ impl Std {
             is_for_mir_opt_tests: false,
         }
     }
+
+    fn copy_extra_objects(
+        &self,
+        builder: &Builder<'_>,
+        compiler: &Compiler,
+        target: TargetSelection,
+    ) -> Vec<(PathBuf, DependencyType)> {
+        let mut deps = Vec::new();
+        if !self.is_for_mir_opt_tests {
+            deps.extend(copy_third_party_objects(builder, &compiler, target));
+            deps.extend(copy_self_contained_objects(builder, &compiler, target));
+        }
+        deps
+    }
 }
 
 impl Step for Std {
@@ -159,8 +173,7 @@ impl Step for Std {
         {
             builder.info("WARNING: Using a potentially old libstd. This may not behave well.");
 
-            copy_third_party_objects(builder, &compiler, target);
-            copy_self_contained_objects(builder, &compiler, target);
+            self.copy_extra_objects(builder, &compiler, target);
 
             builder.ensure(StdLink::from_std(self, compiler));
             return;
@@ -193,15 +206,13 @@ impl Step for Std {
 
             // Even if we're not building std this stage, the new sysroot must
             // still contain the third party objects needed by various targets.
-            copy_third_party_objects(builder, &compiler, target);
-            copy_self_contained_objects(builder, &compiler, target);
+            self.copy_extra_objects(builder, &compiler, target);
 
             builder.ensure(StdLink::from_std(self, compiler_to_use));
             return;
         }
 
-        target_deps.extend(copy_third_party_objects(builder, &compiler, target));
-        target_deps.extend(copy_self_contained_objects(builder, &compiler, target));
+        target_deps.extend(self.copy_extra_objects(builder, &compiler, target));
 
         // The LLD wrappers and `rust-lld` are self-contained linking components that can be
         // necessary to link the stdlib on some targets. We'll also need to copy these binaries to
@@ -222,9 +233,12 @@ impl Step for Std {
             }
         }
 
+        // We build a sysroot for mir-opt tests using the same trick that Miri does: A check build
+        // 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, "rustc");
-            cargo.arg("-p").arg("std").arg("--crate-type=lib");
+            let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "check");
+            cargo.rustflag("-Zalways-encode-mir");
             std_cargo(builder, target, compiler.stage, &mut cargo);
             cargo
         } else {
@@ -257,7 +271,7 @@ impl Step for Std {
             vec![],
             &libstd_stamp(builder, compiler, target),
             target_deps,
-            false,
+            self.is_for_mir_opt_tests, // is_check
             false,
         );
 
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 04728e2e00d..e1a1c416ae9 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -25,7 +25,6 @@ use crate::core::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::flags::get_completion;
 use crate::core::config::flags::Subcommand;
 use crate::core::config::TargetSelection;
-use crate::utils;
 use crate::utils::cache::{Interned, INTERNER};
 use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::{
@@ -38,23 +37,6 @@ use crate::{envify, CLang, DocTests, GitRepo, Mode};
 
 const ADB_TEST_DIR: &str = "/data/local/tmp/work";
 
-// mir-opt tests have different variants depending on whether a target is 32bit or 64bit, and
-// blessing them requires blessing with each target. To aid developers, when blessing the mir-opt
-// test suite the corresponding target of the opposite pointer size is also blessed.
-//
-// This array serves as the known mappings between 32bit and 64bit targets. If you're developing on
-// a target where a target with the opposite pointer size exists, feel free to add it here.
-const MIR_OPT_BLESS_TARGET_MAPPING: &[(&str, &str)] = &[
-    // (32bit, 64bit)
-    ("i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu"),
-    ("i686-unknown-linux-musl", "x86_64-unknown-linux-musl"),
-    ("i686-pc-windows-msvc", "x86_64-pc-windows-msvc"),
-    ("i686-pc-windows-gnu", "x86_64-pc-windows-gnu"),
-    // ARM Macs don't have a corresponding 32-bit target that they can (easily)
-    // build for, so there is no entry for "aarch64-apple-darwin" here.
-    // Likewise, i686 for macOS is no longer possible to build.
-];
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct CrateBootstrap {
     path: Interned<PathBuf>,
@@ -1487,46 +1469,19 @@ impl Step for MirOpt {
             })
         };
 
-        // We use custom logic to bless the mir-opt suite: mir-opt tests have multiple variants
-        // (32bit vs 64bit, and panic=abort vs panic=unwind), and all of them needs to be blessed.
-        // When blessing, we try best-effort to also bless the other variants, to aid developers.
         if builder.config.cmd.bless() {
-            let targets = MIR_OPT_BLESS_TARGET_MAPPING
-                .iter()
-                .filter(|(target_32bit, target_64bit)| {
-                    *target_32bit == &*self.target.triple || *target_64bit == &*self.target.triple
-                })
-                .next()
-                .map(|(target_32bit, target_64bit)| {
-                    let target_32bit = TargetSelection::from_user(target_32bit);
-                    let target_64bit = TargetSelection::from_user(target_64bit);
-
-                    // Running compiletest requires a C compiler to be available, but it might not
-                    // have been detected by bootstrap if the target we're testing wasn't in the
-                    // --target flags.
-                    if !builder.cc.borrow().contains_key(&target_32bit) {
-                        utils::cc_detect::find_target(builder, target_32bit);
-                    }
-                    if !builder.cc.borrow().contains_key(&target_64bit) {
-                        utils::cc_detect::find_target(builder, target_64bit);
-                    }
-
-                    vec![target_32bit, target_64bit]
-                })
-                .unwrap_or_else(|| {
-                    eprintln!(
-                        "\
-Note that not all variants of mir-opt tests are going to be blessed, as no mapping between
-a 32bit and a 64bit target was found for {target}.
-You can add that mapping by changing MIR_OPT_BLESS_TARGET_MAPPING in src/bootstrap/test.rs",
-                        target = self.target,
-                    );
-                    vec![self.target]
-                });
-
-            for target in targets {
-                run(target);
+            crate::utils::cc_detect::find_target(builder, self.compiler.host);
+            // All that we really need to do is cover all combinations of 32/64-bit and unwind/abort,
+            // but while we're at it we might as well flex our cross-compilation support. This
+            // selection covers all our tier 1 operating systems and architectures using only tier
+            // 1 targets.
+
+            for target in ["aarch64-unknown-linux-gnu", "i686-pc-windows-msvc"] {
+                run(TargetSelection::from_user(target));
+            }
 
+            for target in ["x86_64-apple-darwin", "i686-unknown-linux-musl"] {
+                let target = TargetSelection::from_user(target);
                 let panic_abort_target = builder.ensure(MirOptPanicAbortSyntheticTarget {
                     compiler: self.compiler,
                     base: target,
@@ -1616,27 +1571,27 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
                 .ensure(dist::DebuggerScripts { sysroot: builder.sysroot(compiler), host: target });
         }
 
-        if suite == "mir-opt" {
-            builder.ensure(compile::Std::new_for_mir_opt_tests(compiler, target));
-        } else {
-            builder.ensure(compile::Std::new(compiler, target));
-        }
+        // Also provide `rust_test_helpers` for the host.
+        builder.ensure(TestHelpers { target: compiler.host });
 
         // ensure that `libproc_macro` is available on the host.
         builder.ensure(compile::Std::new(compiler, compiler.host));
 
-        // Also provide `rust_test_helpers` for the host.
-        builder.ensure(TestHelpers { target: compiler.host });
-
         // As well as the target, except for plain wasm32, which can't build it
         if suite != "mir-opt" && !target.contains("wasm") && !target.contains("emscripten") {
             builder.ensure(TestHelpers { target });
         }
 
-        builder.ensure(RemoteCopyLibs { compiler, target });
-
         let mut cmd = builder.tool_cmd(Tool::Compiletest);
 
+        if suite == "mir-opt" {
+            builder.ensure(compile::Std::new_for_mir_opt_tests(compiler, target));
+        } else {
+            builder.ensure(compile::Std::new(compiler, target));
+        }
+
+        builder.ensure(RemoteCopyLibs { compiler, target });
+
         // compiletest currently has... a lot of arguments, so let's just pass all
         // of them!
 
@@ -1745,11 +1700,13 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests));
         flags.extend(builder.config.cmd.rustc_args().iter().map(|s| s.to_string()));
 
-        if let Some(linker) = builder.linker(target) {
-            cmd.arg("--target-linker").arg(linker);
-        }
-        if let Some(linker) = builder.linker(compiler.host) {
-            cmd.arg("--host-linker").arg(linker);
+        if suite != "mir-opt" {
+            if let Some(linker) = builder.linker(target) {
+                cmd.arg("--target-linker").arg(linker);
+            }
+            if let Some(linker) = builder.linker(compiler.host) {
+                cmd.arg("--host-linker").arg(linker);
+            }
         }
 
         let mut hostflags = flags.clone();
@@ -1936,15 +1893,17 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             cmd.arg("--remote-test-client").arg(builder.tool_exe(Tool::RemoteTestClient));
         }
 
-        // Running a C compiler on MSVC requires a few env vars to be set, to be
-        // sure to set them here.
-        //
-        // Note that if we encounter `PATH` we make sure to append to our own `PATH`
-        // rather than stomp over it.
-        if !builder.config.dry_run() && target.is_msvc() {
-            for &(ref k, ref v) in builder.cc.borrow()[&target].env() {
-                if k != "PATH" {
-                    cmd.env(k, v);
+        if suite != "mir-opt" {
+            // Running a C compiler on MSVC requires a few env vars to be set, to be
+            // sure to set them here.
+            //
+            // Note that if we encounter `PATH` we make sure to append to our own `PATH`
+            // rather than stomp over it.
+            if !builder.config.dry_run() && target.is_msvc() {
+                for &(ref k, ref v) in builder.cc.borrow()[&target].env() {
+                    if k != "PATH" {
+                        cmd.env(k, v);
+                    }
                 }
             }
         }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index dac8393de6b..4bb8ed2fa67 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1646,76 +1646,12 @@ impl<'a> Builder<'a> {
             cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper);
         }
 
-        // 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}"));
-            }
-        }
-
         cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string());
 
         if let Some(stack_protector) = &self.config.rust_stack_protector {
             rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
         }
 
-        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 !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc {
             cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
         }
@@ -1731,10 +1667,6 @@ impl<'a> Builder<'a> {
         if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() {
             cargo.env(profile_var("OPT_LEVEL"), opt_level);
         }
-        if !self.config.dry_run() && self.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz")
-        {
-            rustflags.arg("-Clink-arg=-gz");
-        }
         cargo.env(
             profile_var("DEBUG_ASSERTIONS"),
             if mode == Mode::Std {
@@ -1940,55 +1872,15 @@ impl<'a> Builder<'a> {
             rustflags.arg("-Wrustc::internal");
         }
 
-        // 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);
-            }
+        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
@@ -2142,6 +2034,136 @@ impl<'a> Builder<'a> {
         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);
+            }
+        }
+    }
+
     /// Ensure that a given step is built, returning its output. This will
     /// cache the step, so it is safe (and good!) to call this as often as
     /// needed to ensure that all dependencies are built.