Skip to content

Commit da54d6b

Browse files
committed
Auto merge of #8073 - ehuss:hash-channel, r=alexcrichton
Use the same filename hash for pre-release channels. This changes it so that filenames do not hash the entire verbose version from rustc if they are a pre-release version. The intent is to avoid leaving stale artifacts in the target directory whenever someone updates a nightly or beta release. This should help reduce disk space usage for someone who updates these toolchains frequently. I tested with the rustc repo, and it seems to be OK. It keeps everything in separate target directories, so I think it should be generally safe. This should only affect someone switching between different nightlies and wanting to avoid recompiling when switching back. I suspect that is a rare use case, though if there are complaints this can be easily reverted (or made a config option). cargo-bisect-rustc should also be safe since it uses a different target directory for each toolchain. One concern here for me was incremental support. It looks like ([src](https://github.com/rust-lang/rust/blob/6387b09153939b2a104cd63148598a5f458de2c2/src/librustc_incremental/persist/file_format.rs#L88-L100)) the incremental cache includes the detailed rustc version, so I think that is safe. It also looks like it is [smart enough](https://github.com/rust-lang/rust/blob/6387b09153939b2a104cd63148598a5f458de2c2/src/librustc_incremental/persist/load.rs#L40-L49) to delete stale files. We will need to be more careful in the future when changing the target directory structure or the format of files. We previously relied on the fact that each new nightly will use different filenames. If we change the structure in a backwards-incompatible way, we will need to be careful to update the version (`1.hash` in `compute_metadata`).
2 parents fd25241 + fdfdb3d commit da54d6b

File tree

4 files changed

+228
-10
lines changed

4 files changed

+228
-10
lines changed

src/cargo/core/compiler/context/compilation_files.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ fn compute_metadata<'a, 'cfg>(
614614
unit.target.name().hash(&mut hasher);
615615
unit.target.kind().hash(&mut hasher);
616616

617-
bcx.rustc().verbose_version.hash(&mut hasher);
617+
hash_rustc_version(bcx, &mut hasher);
618618

619619
if cx.bcx.ws.is_member(unit.pkg) {
620620
// This is primarily here for clippy. This ensures that the clippy
@@ -641,3 +641,35 @@ fn compute_metadata<'a, 'cfg>(
641641

642642
Some(Metadata(hasher.finish()))
643643
}
644+
645+
fn hash_rustc_version(bcx: &BuildContext<'_, '_>, hasher: &mut SipHasher) {
646+
let vers = &bcx.rustc().version;
647+
if vers.pre.is_empty() || bcx.config.cli_unstable().separate_nightlies {
648+
// For stable, keep the artifacts separate. This helps if someone is
649+
// testing multiple versions, to avoid recompiles.
650+
bcx.rustc().verbose_version.hash(hasher);
651+
return;
652+
}
653+
// On "nightly"/"beta"/"dev"/etc, keep each "channel" separate. Don't hash
654+
// the date/git information, so that whenever someone updates "nightly",
655+
// they won't have a bunch of stale artifacts in the target directory.
656+
//
657+
// This assumes that the first segment is the important bit ("nightly",
658+
// "beta", "dev", etc.). Skip other parts like the `.3` in `-beta.3`.
659+
vers.pre[0].hash(hasher);
660+
// Keep "host" since some people switch hosts to implicitly change
661+
// targets, (like gnu vs musl or gnu vs msvc). In the future, we may want
662+
// to consider hashing `unit.kind.short_name()` instead.
663+
bcx.rustc().host.hash(hasher);
664+
// None of the other lines are important. Currently they are:
665+
// binary: rustc <-- or "rustdoc"
666+
// commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a
667+
// commit-date: 2020-03-21
668+
// host: x86_64-apple-darwin
669+
// release: 1.44.0-nightly
670+
// LLVM version: 9.0
671+
//
672+
// The backend version ("LLVM version") might become more relevant in
673+
// the future when cranelift sees more use, and people want to switch
674+
// between different backends without recompiling.
675+
}

src/cargo/core/features.rs

+2
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ pub struct CliUnstable {
343343
pub jobserver_per_rustc: bool,
344344
pub features: Option<Vec<String>>,
345345
pub crate_versions: bool,
346+
pub separate_nightlies: bool,
346347
}
347348

348349
impl CliUnstable {
@@ -420,6 +421,7 @@ impl CliUnstable {
420421
"jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
421422
"features" => self.features = Some(parse_features(v)),
422423
"crate-versions" => self.crate_versions = parse_empty(k, v)?,
424+
"separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
423425
_ => bail!("unknown `-Z` flag specified: {}", k),
424426
}
425427

src/cargo/util/rustc.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub struct Rustc {
2525
pub workspace_wrapper: Option<PathBuf>,
2626
/// Verbose version information (the output of `rustc -vV`)
2727
pub verbose_version: String,
28+
/// The rustc version (`1.23.4-beta.2`), this comes from verbose_version.
29+
pub version: semver::Version,
2830
/// The host triple (arch-platform-OS), this comes from verbose_version.
2931
pub host: InternedString,
3032
cache: Mutex<Cache>,
@@ -51,25 +53,34 @@ impl Rustc {
5153
cmd.arg("-vV");
5254
let verbose_version = cache.cached_output(&cmd)?.0;
5355

54-
let host = {
55-
let triple = verbose_version
56+
let extract = |field: &str| -> CargoResult<&str> {
57+
verbose_version
5658
.lines()
57-
.find(|l| l.starts_with("host: "))
58-
.map(|l| &l[6..])
59+
.find(|l| l.starts_with(field))
60+
.map(|l| &l[field.len()..])
5961
.ok_or_else(|| {
6062
anyhow::format_err!(
61-
"`rustc -vV` didn't have a line for `host:`, got:\n{}",
63+
"`rustc -vV` didn't have a line for `{}`, got:\n{}",
64+
field.trim(),
6265
verbose_version
6366
)
64-
})?;
65-
InternedString::new(triple)
67+
})
6668
};
6769

70+
let host = InternedString::new(extract("host: ")?);
71+
let version = semver::Version::parse(extract("release: ")?).chain_err(|| {
72+
format!(
73+
"rustc version does not appear to be a valid semver version, from:\n{}",
74+
verbose_version
75+
)
76+
})?;
77+
6878
Ok(Rustc {
6979
path,
7080
wrapper,
7181
workspace_wrapper,
7282
verbose_version,
83+
version,
7384
host,
7485
cache: Mutex::new(cache),
7586
})

tests/testsuite/freshness.rs

+175-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ use std::time::SystemTime;
1111

1212
use cargo_test_support::paths::{self, CargoPathExt};
1313
use cargo_test_support::registry::Package;
14-
use cargo_test_support::sleep_ms;
15-
use cargo_test_support::{basic_manifest, is_coarse_mtime, project};
14+
use cargo_test_support::{basic_manifest, is_coarse_mtime, project, rustc_host, sleep_ms};
1615

1716
#[cargo_test]
1817
fn modifying_and_moving() {
@@ -2150,3 +2149,177 @@ fn rerun_if_changes() {
21502149
.with_stderr("[FINISHED] [..]")
21512150
.run();
21522151
}
2152+
2153+
#[cargo_test]
2154+
fn channel_shares_filenames() {
2155+
// Test that different "nightly" releases use the same output filename.
2156+
2157+
// Create separate rustc binaries to emulate running different toolchains.
2158+
let nightly1 = format!(
2159+
"\
2160+
rustc 1.44.0-nightly (38114ff16 2020-03-21)
2161+
binary: rustc
2162+
commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a
2163+
commit-date: 2020-03-21
2164+
host: {}
2165+
release: 1.44.0-nightly
2166+
LLVM version: 9.0
2167+
",
2168+
rustc_host()
2169+
);
2170+
2171+
let nightly2 = format!(
2172+
"\
2173+
rustc 1.44.0-nightly (a5b09d354 2020-03-31)
2174+
binary: rustc
2175+
commit-hash: a5b09d35473615e7142f5570f5c5fad0caf68bd2
2176+
commit-date: 2020-03-31
2177+
host: {}
2178+
release: 1.44.0-nightly
2179+
LLVM version: 9.0
2180+
",
2181+
rustc_host()
2182+
);
2183+
2184+
let beta1 = format!(
2185+
"\
2186+
rustc 1.43.0-beta.3 (4c587bbda 2020-03-25)
2187+
binary: rustc
2188+
commit-hash: 4c587bbda04ab55aaf56feab11dfdfe387a85d7a
2189+
commit-date: 2020-03-25
2190+
host: {}
2191+
release: 1.43.0-beta.3
2192+
LLVM version: 9.0
2193+
",
2194+
rustc_host()
2195+
);
2196+
2197+
let beta2 = format!(
2198+
"\
2199+
rustc 1.42.0-beta.5 (4e1c5f0e9 2020-02-28)
2200+
binary: rustc
2201+
commit-hash: 4e1c5f0e9769a588b91c977e3d81e140209ef3a2
2202+
commit-date: 2020-02-28
2203+
host: {}
2204+
release: 1.42.0-beta.5
2205+
LLVM version: 9.0
2206+
",
2207+
rustc_host()
2208+
);
2209+
2210+
let stable1 = format!(
2211+
"\
2212+
rustc 1.42.0 (b8cedc004 2020-03-09)
2213+
binary: rustc
2214+
commit-hash: b8cedc00407a4c56a3bda1ed605c6fc166655447
2215+
commit-date: 2020-03-09
2216+
host: {}
2217+
release: 1.42.0
2218+
LLVM version: 9.0
2219+
",
2220+
rustc_host()
2221+
);
2222+
2223+
let stable2 = format!(
2224+
"\
2225+
rustc 1.41.1 (f3e1a954d 2020-02-24)
2226+
binary: rustc
2227+
commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196
2228+
commit-date: 2020-02-24
2229+
host: {}
2230+
release: 1.41.1
2231+
LLVM version: 9.0
2232+
",
2233+
rustc_host()
2234+
);
2235+
2236+
let compiler = project()
2237+
.at("compiler")
2238+
.file("Cargo.toml", &basic_manifest("compiler", "0.1.0"))
2239+
.file(
2240+
"src/main.rs",
2241+
r#"
2242+
fn main() {
2243+
if std::env::args_os().any(|a| a == "-vV") {
2244+
print!("{}", env!("FUNKY_VERSION_TEST"));
2245+
return;
2246+
}
2247+
let mut cmd = std::process::Command::new("rustc");
2248+
cmd.args(std::env::args_os().skip(1));
2249+
assert!(cmd.status().unwrap().success());
2250+
}
2251+
"#,
2252+
)
2253+
.build();
2254+
2255+
let makeit = |version, vv| {
2256+
// Force a rebuild.
2257+
compiler.target_debug_dir().join("deps").rm_rf();
2258+
compiler.cargo("build").env("FUNKY_VERSION_TEST", vv).run();
2259+
fs::rename(compiler.bin("compiler"), compiler.bin(version)).unwrap();
2260+
};
2261+
makeit("nightly1", nightly1);
2262+
makeit("nightly2", nightly2);
2263+
makeit("beta1", beta1);
2264+
makeit("beta2", beta2);
2265+
makeit("stable1", stable1);
2266+
makeit("stable2", stable2);
2267+
2268+
// Run `cargo check` with different rustc versions to observe its behavior.
2269+
let p = project().file("src/lib.rs", "").build();
2270+
2271+
// Runs `cargo check` and returns the rmeta filename created.
2272+
// Checks that the freshness matches the given value.
2273+
let check = |version, fresh| -> String {
2274+
let output = p
2275+
.cargo("check --message-format=json")
2276+
.env("RUSTC", compiler.bin(version))
2277+
.exec_with_output()
2278+
.unwrap();
2279+
// Collect the filenames generated.
2280+
let mut artifacts: Vec<_> = std::str::from_utf8(&output.stdout)
2281+
.unwrap()
2282+
.lines()
2283+
.filter_map(|line| {
2284+
let value: serde_json::Value = serde_json::from_str(line).unwrap();
2285+
if value["reason"].as_str().unwrap() == "compiler-artifact" {
2286+
assert_eq!(value["fresh"].as_bool().unwrap(), fresh);
2287+
let filenames = value["filenames"].as_array().unwrap();
2288+
assert_eq!(filenames.len(), 1);
2289+
Some(filenames[0].to_string())
2290+
} else {
2291+
None
2292+
}
2293+
})
2294+
.collect();
2295+
// Should only generate one rmeta file.
2296+
assert_eq!(artifacts.len(), 1);
2297+
artifacts.pop().unwrap()
2298+
};
2299+
2300+
let nightly1_name = check("nightly1", false);
2301+
assert_eq!(check("nightly1", true), nightly1_name);
2302+
assert_eq!(check("nightly2", false), nightly1_name); // same as before
2303+
assert_eq!(check("nightly2", true), nightly1_name);
2304+
// Should rebuild going back to nightly1.
2305+
assert_eq!(check("nightly1", false), nightly1_name);
2306+
2307+
let beta1_name = check("beta1", false);
2308+
assert_ne!(beta1_name, nightly1_name);
2309+
assert_eq!(check("beta1", true), beta1_name);
2310+
assert_eq!(check("beta2", false), beta1_name); // same as before
2311+
assert_eq!(check("beta2", true), beta1_name);
2312+
// Should rebuild going back to beta1.
2313+
assert_eq!(check("beta1", false), beta1_name);
2314+
2315+
let stable1_name = check("stable1", false);
2316+
assert_ne!(stable1_name, nightly1_name);
2317+
assert_ne!(stable1_name, beta1_name);
2318+
let stable2_name = check("stable2", false);
2319+
assert_ne!(stable1_name, stable2_name);
2320+
// Check everything is fresh.
2321+
assert_eq!(check("stable1", true), stable1_name);
2322+
assert_eq!(check("stable2", true), stable2_name);
2323+
assert_eq!(check("beta1", true), beta1_name);
2324+
assert_eq!(check("nightly1", true), nightly1_name);
2325+
}

0 commit comments

Comments
 (0)