Don't re-use rustc cache when RUSTC_WRAPPER changes

We cache initial `rustc --version` invocations, to speed up noop builds.

We check the mtime of `rustc` to bust the cache if the complier changed.
However, before this PR, we didn't look at mtimes of `RUSTC_WRAPPER` /
`RUSTC_WORKSPACE_WRAPPER`, so we could've re-use old cache with new
wrapper.
This commit is contained in:
Aleksey Kladov 2021-04-12 13:07:04 +03:00
parent 57c512b3df
commit f6070b3e3f
3 changed files with 146 additions and 33 deletions

View file

@ -139,7 +139,7 @@ impl TargetInfo {
"RUSTFLAGS",
)?;
let extra_fingerprint = kind.fingerprint_hash();
let mut process = rustc.process();
let mut process = rustc.workspace_process();
process
.arg("-")
.arg("--crate-name")

View file

@ -45,7 +45,13 @@ impl Rustc {
) -> CargoResult<Rustc> {
let _p = profile::start("Rustc::new");
let mut cache = Cache::load(&path, rustup_rustc, cache_location);
let mut cache = Cache::load(
wrapper.as_deref(),
workspace_wrapper.as_deref(),
&path,
rustup_rustc,
cache_location,
);
let mut cmd = ProcessBuilder::new(&path);
cmd.arg("-vV");
@ -154,8 +160,17 @@ struct Output {
}
impl Cache {
fn load(rustc: &Path, rustup_rustc: &Path, cache_location: Option<PathBuf>) -> Cache {
match (cache_location, rustc_fingerprint(rustc, rustup_rustc)) {
fn load(
wrapper: Option<&Path>,
workspace_wrapper: Option<&Path>,
rustc: &Path,
rustup_rustc: &Path,
cache_location: Option<PathBuf>,
) -> Cache {
match (
cache_location,
rustc_fingerprint(wrapper, workspace_wrapper, rustc, rustup_rustc),
) {
(Some(cache_location), Ok(rustc_fingerprint)) => {
let empty = CacheData {
rustc_fingerprint,
@ -272,13 +287,29 @@ impl Drop for Cache {
}
}
fn rustc_fingerprint(path: &Path, rustup_rustc: &Path) -> CargoResult<u64> {
fn rustc_fingerprint(
wrapper: Option<&Path>,
workspace_wrapper: Option<&Path>,
rustc: &Path,
rustup_rustc: &Path,
) -> CargoResult<u64> {
let mut hasher = StableHasher::new();
let path = paths::resolve_executable(path)?;
path.hash(&mut hasher);
let hash_exe = |hasher: &mut _, path| -> CargoResult<()> {
let path = paths::resolve_executable(path)?;
path.hash(hasher);
paths::mtime(&path)?.hash(&mut hasher);
paths::mtime(&path)?.hash(hasher);
Ok(())
};
hash_exe(&mut hasher, rustc)?;
if let Some(wrapper) = wrapper {
hash_exe(&mut hasher, wrapper)?;
}
if let Some(workspace_wrapper) = workspace_wrapper {
hash_exe(&mut hasher, workspace_wrapper)?;
}
// Rustup can change the effective compiler without touching
// the `rustc` binary, so we try to account for this here.
@ -291,7 +322,7 @@ fn rustc_fingerprint(path: &Path, rustup_rustc: &Path) -> CargoResult<u64> {
//
// If we don't see rustup env vars, but it looks like the compiler
// is managed by rustup, we conservatively bail out.
let maybe_rustup = rustup_rustc == path;
let maybe_rustup = rustup_rustc == rustc;
match (
maybe_rustup,
env::var("RUSTUP_HOME"),

View file

@ -1,40 +1,40 @@
//! Tests for the cache file for the rustc version info.
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::{basic_bin_manifest, paths::CargoPathExt};
use cargo_test_support::{basic_manifest, project};
use std::env;
const MISS: &str = "[..] rustc info cache miss[..]";
const HIT: &str = "[..]rustc info cache hit[..]";
const UPDATE: &str = "[..]updated rustc info cache[..]";
#[cargo_test]
fn rustc_info_cache() {
let p = project()
.file("src/main.rs", r#"fn main() { println!("hello"); }"#)
.build();
let miss = "[..] rustc info cache miss[..]";
let hit = "[..]rustc info cache hit[..]";
let update = "[..]updated rustc info cache[..]";
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.with_stderr_contains("[..]failed to read rustc info cache[..]")
.with_stderr_contains(miss)
.with_stderr_does_not_contain(hit)
.with_stderr_contains(update)
.with_stderr_contains(MISS)
.with_stderr_does_not_contain(HIT)
.with_stderr_contains(UPDATE)
.run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.with_stderr_contains("[..]reusing existing rustc info cache[..]")
.with_stderr_contains(hit)
.with_stderr_does_not_contain(miss)
.with_stderr_does_not_contain(update)
.with_stderr_contains(HIT)
.with_stderr_does_not_contain(MISS)
.with_stderr_does_not_contain(UPDATE)
.run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env("CARGO_CACHE_RUSTC_INFO", "0")
.with_stderr_contains("[..]rustc info cache disabled[..]")
.with_stderr_does_not_contain(update)
.with_stderr_does_not_contain(UPDATE)
.run();
let other_rustc = {
@ -68,18 +68,18 @@ fn rustc_info_cache() {
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env("RUSTC", other_rustc.display().to_string())
.with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
.with_stderr_contains(miss)
.with_stderr_does_not_contain(hit)
.with_stderr_contains(update)
.with_stderr_contains(MISS)
.with_stderr_does_not_contain(HIT)
.with_stderr_contains(UPDATE)
.run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env("RUSTC", other_rustc.display().to_string())
.with_stderr_contains("[..]reusing existing rustc info cache[..]")
.with_stderr_contains(hit)
.with_stderr_does_not_contain(miss)
.with_stderr_does_not_contain(update)
.with_stderr_contains(HIT)
.with_stderr_does_not_contain(MISS)
.with_stderr_does_not_contain(UPDATE)
.run();
other_rustc.move_into_the_future();
@ -88,17 +88,99 @@ fn rustc_info_cache() {
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env("RUSTC", other_rustc.display().to_string())
.with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
.with_stderr_contains(miss)
.with_stderr_does_not_contain(hit)
.with_stderr_contains(update)
.with_stderr_contains(MISS)
.with_stderr_does_not_contain(HIT)
.with_stderr_contains(UPDATE)
.run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env("RUSTC", other_rustc.display().to_string())
.with_stderr_contains("[..]reusing existing rustc info cache[..]")
.with_stderr_contains(hit)
.with_stderr_does_not_contain(miss)
.with_stderr_does_not_contain(update)
.with_stderr_contains(HIT)
.with_stderr_does_not_contain(MISS)
.with_stderr_does_not_contain(UPDATE)
.run();
}
#[cargo_test]
fn rustc_info_cache_with_wrappers() {
let wrapper_project = project()
.at("wrapper")
.file("Cargo.toml", &basic_bin_manifest("wrapper"))
.file("src/main.rs", r#"fn main() { }"#)
.build();
let wrapper = wrapper_project.bin("wrapper");
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "test"
version = "0.0.0"
authors = []
[workspace]
"#,
)
.file("src/main.rs", r#"fn main() { println!("hello"); }"#)
.build();
for &wrapper_env in ["RUSTC_WRAPPER", "RUSTC_WORKSPACE_WRAPPER"].iter() {
p.cargo("clean").with_status(0).run();
wrapper_project.change_file(
"src/main.rs",
r#"
fn main() {
let mut args = std::env::args_os();
let _me = args.next().unwrap();
let rustc = args.next().unwrap();
let status = std::process::Command::new(rustc).args(args).status().unwrap();
std::process::exit(if status.success() { 0 } else { 1 })
}
"#,
);
wrapper_project.cargo("build").with_status(0).run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env(wrapper_env, &wrapper)
.with_stderr_contains("[..]failed to read rustc info cache[..]")
.with_stderr_contains(MISS)
.with_stderr_contains(UPDATE)
.with_stderr_does_not_contain(HIT)
.with_status(0)
.run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env(wrapper_env, &wrapper)
.with_stderr_contains("[..]reusing existing rustc info cache[..]")
.with_stderr_contains(HIT)
.with_stderr_does_not_contain(UPDATE)
.with_stderr_does_not_contain(MISS)
.with_status(0)
.run();
wrapper_project.change_file("src/main.rs", r#"fn main() { panic!() }"#);
wrapper_project.cargo("build").with_status(0).run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env(wrapper_env, &wrapper)
.with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
.with_stderr_contains(MISS)
.with_stderr_contains(UPDATE)
.with_stderr_does_not_contain(HIT)
.with_status(101)
.run();
p.cargo("build")
.env("CARGO_LOG", "cargo::util::rustc=debug")
.env(wrapper_env, &wrapper)
.with_stderr_contains("[..]reusing existing rustc info cache[..]")
.with_stderr_contains(HIT)
.with_stderr_does_not_contain(UPDATE)
.with_stderr_does_not_contain(MISS)
.with_status(101)
.run();
}
}