Skip to content

Commit

Permalink
Auto merge of rust-lang#3887 - luser:rustc-wrapper, r=alexcrichton
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Apr 19, 2017
2 parents 89ca25e + 20f23d9 commit 8326a36
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 10 deletions.
20 changes: 15 additions & 5 deletions src/cargo/util/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ impl Config {
}

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

pub fn cargo_exe(&self) -> CargoResult<&Path> {
Expand Down Expand Up @@ -415,18 +416,27 @@ impl Config {
}
}

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

let var = format!("build.{}", tool);
if let Some(tool_path) = self.get_path(&var)? {
return Ok(tool_path.val);
return Ok(Some(tool_path.val));
}

Ok(PathBuf::from(tool))
Ok(None)
}

/// Look for a path for `tool` in an environment variable or config path, defaulting to `tool`
/// as a path.
fn get_tool(&self, tool: &str) -> CargoResult<PathBuf> {
self.maybe_get_tool(tool)
.map(|t| t.unwrap_or(PathBuf::from(tool)))
}
}

Expand Down
18 changes: 14 additions & 4 deletions src/cargo/util/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use util::{self, CargoResult, internal, ChainError, ProcessBuilder};

pub struct Rustc {
pub path: PathBuf,
pub wrapper: Option<PathBuf>,
pub verbose_version: String,
pub host: String,
}
Expand All @@ -14,12 +15,12 @@ impl Rustc {
///
/// If successful this function returns a description of the compiler along
/// with a list of its capabilities.
pub fn new(path: PathBuf) -> CargoResult<Rustc> {
pub fn new(path: PathBuf, wrapper: Option<PathBuf>) -> CargoResult<Rustc> {
let mut cmd = util::process(&path);
cmd.arg("-vV");

let output = cmd.exec_with_output()?;

let verbose_version = String::from_utf8(output.stdout).map_err(|_| {
internal("rustc -v didn't return utf8 output")
})?;
Expand All @@ -36,12 +37,21 @@ impl Rustc {

Ok(Rustc {
path: path,
wrapper: wrapper,
verbose_version: verbose_version,
host: host,
})
}

pub fn process(&self) -> ProcessBuilder {
util::process(&self.path)
if let Some(ref wrapper) = self.wrapper {
let mut cmd = util::process(wrapper);
{
cmd.arg(&self.path);
}
cmd
} else {
util::process(&self.path)
}
}
}
3 changes: 3 additions & 0 deletions src/doc/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ system:
relative to the current working directory.
* `RUSTC` - Instead of running `rustc`, Cargo will execute this specified
compiler instead.
* `RUSTC_WRAPPER` - Instead of simply running `rustc`, Cargo will execute this
specified wrapper instead, passing as its commandline arguments the rustc
invocation, with the first argument being rustc.
* `RUSTDOC` - Instead of running `rustdoc`, Cargo will execute this specified
`rustdoc` instance instead.
* `RUSTFLAGS` - A space-separated list of custom flags to pass to all compiler
Expand Down
15 changes: 15 additions & 0 deletions tests/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2894,3 +2894,18 @@ fn run_proper_binary_main_rs_as_foo() {
assert_that(p.cargo_process("run").arg("--bin").arg("foo"),
execs().with_status(0));
}

#[test]
fn rustc_wrapper() {
// We don't have /usr/bin/env on Windows.
if cfg!(windows) { return }

let p = project("foo")
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

assert_that(p.cargo_process("build").arg("-v").env("RUSTC_WRAPPER", "/usr/bin/env"),
execs().with_stderr_contains(
"[RUNNING] `/usr/bin/env rustc --crate-name foo [..]")
.with_status(0));
}
3 changes: 2 additions & 1 deletion tests/cargotest/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use std::env;
pub mod support;
pub mod install;

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

pub fn rustc_host() -> String {
RUSTC.with(|r| r.host.clone())
Expand All @@ -54,6 +54,7 @@ fn _process(t: &OsStr) -> cargo::util::ProcessBuilder {
.env_remove("__CARGO_DEFAULT_LIB_METADATA")
.env_remove("RUSTC")
.env_remove("RUSTDOC")
.env_remove("RUSTC_WRAPPER")
.env_remove("RUSTFLAGS")
.env_remove("CARGO_INCREMENTAL")
.env_remove("XDG_CONFIG_HOME") // see #2345
Expand Down

0 comments on commit 8326a36

Please sign in to comment.