Skip to content

Commit 8326a36

Browse files
committed
Auto merge of #3887 - luser:rustc-wrapper, r=alexcrichton
Add support for wrapping cargo's rustc invocations by setting RUSTC_WRAPPER To use sccache for cargo builds we need a simple way to get sccache into the rustc commandline when cargo invokes rustc. Currently this is only possible by hard-linking or copying the `sccache` binary to be named `rustc` and then either setting `RUSTC` to its path or putting it first in `$PATH`, both of which are sort of clunky and require manual steps even if installing sccache via `cargo install`. This patch adds support for a `RUSTC_WRAPPER` environment variable which, if set, will simply be inserted as the actual binary for all rustc process execution, with rustc and all other rustc arguments following. I didn't add any tests for this, I couldn't figure out the right place to put them, and presumably we'd need to build a helper binary of some sort to use as the wrapper. If you've got suggestions for how to do that properly I'd be happy to write tests. This works well in my local testing: ``` luser@eye7:/build/read-process-memory$ /build/cargo/target/release/cargo clean; time RUSTC_WRAPPER=/build/sccache2/target/release/sccache RUSTC=/home/luser/.rustup/toolchains/beta-x86_64-unknown-linux-gnu/bin/rustc /build/cargo/target/release/cargo build Compiling getopts v0.2.14 Compiling log v0.3.6 Compiling libc v0.2.16 Compiling rand v0.3.14 Compiling pulldown-cmark v0.0.3 Compiling tempdir v0.3.5 Compiling skeptic v0.5.0 Compiling read-process-memory v0.1.2-pre (file:///build/read-process-memory) Finished dev [unoptimized + debuginfo] target(s) in 7.31 secs real 0m7.733s user 0m0.060s sys 0m0.036s luser@eye7:/build/read-process-memory$ /build/cargo/target/release/cargo clean; time RUSTC_WRAPPER=/build/sccache2/target/release/sccache RUSTC=/home/luser/.rustup/toolchains/beta-x86_64-unknown-linux-gnu/bin/rustc /build/cargo/target/release/cargo build Compiling getopts v0.2.14 Compiling libc v0.2.16 Compiling log v0.3.6 Compiling pulldown-cmark v0.0.3 Compiling rand v0.3.14 Compiling tempdir v0.3.5 Compiling skeptic v0.5.0 Compiling read-process-memory v0.1.2-pre (file:///build/read-process-memory) Finished dev [unoptimized + debuginfo] target(s) in 0.97 secs real 0m1.049s user 0m0.060s sys 0m0.036s ``` The use of beta rustc is just to pick up the fix for making `--emit=dep-info` faster (which should ship in 1.17). If this patch ships in cargo then in the future developers should simply be able to `cargo install sccache; export RUSTC_WRAPPER=sccache` and `cargo build` as normal, but benefit from local sccache caching.
2 parents 89ca25e + 20f23d9 commit 8326a36

File tree

5 files changed

+49
-10
lines changed

5 files changed

+49
-10
lines changed

src/cargo/util/config.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ impl Config {
9393
}
9494

9595
pub fn rustc(&self) -> CargoResult<&Rustc> {
96-
self.rustc.get_or_try_init(|| Rustc::new(self.get_tool("rustc")?))
96+
self.rustc.get_or_try_init(|| Rustc::new(self.get_tool("rustc")?,
97+
self.maybe_get_tool("rustc_wrapper")?))
9798
}
9899

99100
pub fn cargo_exe(&self) -> CargoResult<&Path> {
@@ -415,18 +416,27 @@ impl Config {
415416
}
416417
}
417418

418-
fn get_tool(&self, tool: &str) -> CargoResult<PathBuf> {
419+
/// Look for a path for `tool` in an environment variable or config path, but return `None`
420+
/// if it's not present.
421+
fn maybe_get_tool(&self, tool: &str) -> CargoResult<Option<PathBuf>> {
419422
let var = tool.chars().flat_map(|c| c.to_uppercase()).collect::<String>();
420423
if let Some(tool_path) = env::var_os(&var) {
421-
return Ok(PathBuf::from(tool_path));
424+
return Ok(Some(PathBuf::from(tool_path)));
422425
}
423426

424427
let var = format!("build.{}", tool);
425428
if let Some(tool_path) = self.get_path(&var)? {
426-
return Ok(tool_path.val);
429+
return Ok(Some(tool_path.val));
427430
}
428431

429-
Ok(PathBuf::from(tool))
432+
Ok(None)
433+
}
434+
435+
/// Look for a path for `tool` in an environment variable or config path, defaulting to `tool`
436+
/// as a path.
437+
fn get_tool(&self, tool: &str) -> CargoResult<PathBuf> {
438+
self.maybe_get_tool(tool)
439+
.map(|t| t.unwrap_or(PathBuf::from(tool)))
430440
}
431441
}
432442

src/cargo/util/rustc.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use util::{self, CargoResult, internal, ChainError, ProcessBuilder};
44

55
pub struct Rustc {
66
pub path: PathBuf,
7+
pub wrapper: Option<PathBuf>,
78
pub verbose_version: String,
89
pub host: String,
910
}
@@ -14,12 +15,12 @@ impl Rustc {
1415
///
1516
/// If successful this function returns a description of the compiler along
1617
/// with a list of its capabilities.
17-
pub fn new(path: PathBuf) -> CargoResult<Rustc> {
18+
pub fn new(path: PathBuf, wrapper: Option<PathBuf>) -> CargoResult<Rustc> {
1819
let mut cmd = util::process(&path);
1920
cmd.arg("-vV");
20-
21+
2122
let output = cmd.exec_with_output()?;
22-
23+
2324
let verbose_version = String::from_utf8(output.stdout).map_err(|_| {
2425
internal("rustc -v didn't return utf8 output")
2526
})?;
@@ -36,12 +37,21 @@ impl Rustc {
3637

3738
Ok(Rustc {
3839
path: path,
40+
wrapper: wrapper,
3941
verbose_version: verbose_version,
4042
host: host,
4143
})
4244
}
4345

4446
pub fn process(&self) -> ProcessBuilder {
45-
util::process(&self.path)
47+
if let Some(ref wrapper) = self.wrapper {
48+
let mut cmd = util::process(wrapper);
49+
{
50+
cmd.arg(&self.path);
51+
}
52+
cmd
53+
} else {
54+
util::process(&self.path)
55+
}
4656
}
4757
}

src/doc/environment-variables.md

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ system:
1717
relative to the current working directory.
1818
* `RUSTC` - Instead of running `rustc`, Cargo will execute this specified
1919
compiler instead.
20+
* `RUSTC_WRAPPER` - Instead of simply running `rustc`, Cargo will execute this
21+
specified wrapper instead, passing as its commandline arguments the rustc
22+
invocation, with the first argument being rustc.
2023
* `RUSTDOC` - Instead of running `rustdoc`, Cargo will execute this specified
2124
`rustdoc` instance instead.
2225
* `RUSTFLAGS` - A space-separated list of custom flags to pass to all compiler

tests/build.rs

+15
Original file line numberDiff line numberDiff line change
@@ -2894,3 +2894,18 @@ fn run_proper_binary_main_rs_as_foo() {
28942894
assert_that(p.cargo_process("run").arg("--bin").arg("foo"),
28952895
execs().with_status(0));
28962896
}
2897+
2898+
#[test]
2899+
fn rustc_wrapper() {
2900+
// We don't have /usr/bin/env on Windows.
2901+
if cfg!(windows) { return }
2902+
2903+
let p = project("foo")
2904+
.file("Cargo.toml", &basic_bin_manifest("foo"))
2905+
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]));
2906+
2907+
assert_that(p.cargo_process("build").arg("-v").env("RUSTC_WRAPPER", "/usr/bin/env"),
2908+
execs().with_stderr_contains(
2909+
"[RUNNING] `/usr/bin/env rustc --crate-name foo [..]")
2910+
.with_status(0));
2911+
}

tests/cargotest/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use std::env;
2727
pub mod support;
2828
pub mod install;
2929

30-
thread_local!(pub static RUSTC: Rustc = Rustc::new(PathBuf::from("rustc")).unwrap());
30+
thread_local!(pub static RUSTC: Rustc = Rustc::new(PathBuf::from("rustc"), None).unwrap());
3131

3232
pub fn rustc_host() -> String {
3333
RUSTC.with(|r| r.host.clone())
@@ -54,6 +54,7 @@ fn _process(t: &OsStr) -> cargo::util::ProcessBuilder {
5454
.env_remove("__CARGO_DEFAULT_LIB_METADATA")
5555
.env_remove("RUSTC")
5656
.env_remove("RUSTDOC")
57+
.env_remove("RUSTC_WRAPPER")
5758
.env_remove("RUSTFLAGS")
5859
.env_remove("CARGO_INCREMENTAL")
5960
.env_remove("XDG_CONFIG_HOME") // see #2345

0 commit comments

Comments
 (0)