Include the linker in the fingerprint.

This commit is contained in:
Eric Huss 2021-07-01 20:33:48 -07:00
parent 4952979031
commit 28c3bef71a
4 changed files with 60 additions and 42 deletions

View File

@ -1035,6 +1035,11 @@ pub fn rustc_host() -> &'static str {
&RUSTC_INFO.host &RUSTC_INFO.host
} }
/// The host triple suitable for use in a cargo environment variable (uppercased).
pub fn rustc_host_env() -> String {
rustc_host().to_uppercase().replace('-', "_")
}
pub fn is_nightly() -> bool { pub fn is_nightly() -> bool {
let vv = &RUSTC_INFO.verbose_version; let vv = &RUSTC_INFO.verbose_version;
env::var("CARGO_TEST_DISABLE_NIGHTLY").is_err() env::var("CARGO_TEST_DISABLE_NIGHTLY").is_err()

View File

@ -315,7 +315,7 @@
use std::collections::hash_map::{Entry, HashMap}; use std::collections::hash_map::{Entry, HashMap};
use std::convert::TryInto; use std::convert::TryInto;
use std::env; use std::env;
use std::hash::{self, Hasher}; use std::hash::{self, Hash, Hasher};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str; use std::str;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -334,7 +334,7 @@ use crate::core::Package;
use crate::util; use crate::util;
use crate::util::errors::CargoResult; use crate::util::errors::CargoResult;
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::{internal, path_args, profile}; use crate::util::{internal, path_args, profile, StableHasher};
use crate::CARGO_ENV; use crate::CARGO_ENV;
use super::custom_build::BuildDeps; use super::custom_build::BuildDeps;
@ -502,7 +502,7 @@ struct DepFingerprint {
/// as a fingerprint (all source files must be modified *before* this mtime). /// as a fingerprint (all source files must be modified *before* this mtime).
/// This dep-info file is not generated, however, until after the crate is /// This dep-info file is not generated, however, until after the crate is
/// compiled. As a result, this structure can be thought of as a fingerprint /// compiled. As a result, this structure can be thought of as a fingerprint
/// to-be. The actual value can be calculated via `hash()`, but the operation /// to-be. The actual value can be calculated via `hash_u64()`, but the operation
/// may fail as some files may not have been generated. /// may fail as some files may not have been generated.
/// ///
/// Note that dependencies are taken into account for fingerprints because rustc /// Note that dependencies are taken into account for fingerprints because rustc
@ -594,7 +594,7 @@ impl Serialize for DepFingerprint {
&self.pkg_id, &self.pkg_id,
&self.name, &self.name,
&self.public, &self.public,
&self.fingerprint.hash(), &self.fingerprint.hash_u64(),
) )
.serialize(ser) .serialize(ser)
} }
@ -812,7 +812,7 @@ impl Fingerprint {
*self.memoized_hash.lock().unwrap() = None; *self.memoized_hash.lock().unwrap() = None;
} }
fn hash(&self) -> u64 { fn hash_u64(&self) -> u64 {
if let Some(s) = *self.memoized_hash.lock().unwrap() { if let Some(s) = *self.memoized_hash.lock().unwrap() {
return s; return s;
} }
@ -956,13 +956,13 @@ impl Fingerprint {
return Err(e); return Err(e);
} }
if a.fingerprint.hash() != b.fingerprint.hash() { if a.fingerprint.hash_u64() != b.fingerprint.hash_u64() {
let e = format_err!( let e = format_err!(
"new ({}/{:x}) != old ({}/{:x})", "new ({}/{:x}) != old ({}/{:x})",
a.name, a.name,
a.fingerprint.hash(), a.fingerprint.hash_u64(),
b.name, b.name,
b.fingerprint.hash() b.fingerprint.hash_u64()
) )
.context("unit dependency information changed"); .context("unit dependency information changed");
return Err(e); return Err(e);
@ -1145,7 +1145,7 @@ impl hash::Hash for Fingerprint {
name.hash(h); name.hash(h);
public.hash(h); public.hash(h);
// use memoized dep hashes to avoid exponential blowup // use memoized dep hashes to avoid exponential blowup
h.write_u64(Fingerprint::hash(fingerprint)); h.write_u64(fingerprint.hash_u64());
} }
} }
} }
@ -1318,12 +1318,17 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
// Include metadata since it is exposed as environment variables. // Include metadata since it is exposed as environment variables.
let m = unit.pkg.manifest().metadata(); let m = unit.pkg.manifest().metadata();
let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository)); let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository));
let mut config = 0u64; let mut config = StableHasher::new();
if let Some(linker) = cx.bcx.linker(unit.kind) {
linker.hash(&mut config);
}
if unit.mode.is_doc() && cx.bcx.config.cli_unstable().rustdoc_map { if unit.mode.is_doc() && cx.bcx.config.cli_unstable().rustdoc_map {
config = config.wrapping_add(cx.bcx.config.doc_extern_map().map_or(0, util::hash_u64)); if let Ok(map) = cx.bcx.config.doc_extern_map() {
map.hash(&mut config);
}
} }
if let Some(allow_features) = &cx.bcx.config.cli_unstable().allow_features { if let Some(allow_features) = &cx.bcx.config.cli_unstable().allow_features {
config = config.wrapping_add(util::hash_u64(allow_features)); allow_features.hash(&mut config);
} }
let compile_kind = unit.kind.fingerprint_hash(); let compile_kind = unit.kind.fingerprint_hash();
Ok(Fingerprint { Ok(Fingerprint {
@ -1338,7 +1343,7 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
local: Mutex::new(local), local: Mutex::new(local),
memoized_hash: Mutex::new(None), memoized_hash: Mutex::new(None),
metadata, metadata,
config, config: config.finish(),
compile_kind, compile_kind,
rustflags: extra_flags, rustflags: extra_flags,
fs_status: FsStatus::Stale, fs_status: FsStatus::Stale,
@ -1570,14 +1575,14 @@ fn write_fingerprint(loc: &Path, fingerprint: &Fingerprint) -> CargoResult<()> {
// fingerprint::new().rustc == 0, make sure it doesn't make it to the file system. // fingerprint::new().rustc == 0, make sure it doesn't make it to the file system.
// This is mostly so outside tools can reliably find out what rust version this file is for, // This is mostly so outside tools can reliably find out what rust version this file is for,
// as we can use the full hash. // as we can use the full hash.
let hash = fingerprint.hash(); let hash = fingerprint.hash_u64();
debug!("write fingerprint ({:x}) : {}", hash, loc.display()); debug!("write fingerprint ({:x}) : {}", hash, loc.display());
paths::write(loc, util::to_hex(hash).as_bytes())?; paths::write(loc, util::to_hex(hash).as_bytes())?;
let json = serde_json::to_string(fingerprint).unwrap(); let json = serde_json::to_string(fingerprint).unwrap();
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
let f: Fingerprint = serde_json::from_str(&json).unwrap(); let f: Fingerprint = serde_json::from_str(&json).unwrap();
assert_eq!(f.hash(), hash); assert_eq!(f.hash_u64(), hash);
} }
paths::write(&loc.with_extension("json"), json.as_bytes())?; paths::write(&loc.with_extension("json"), json.as_bytes())?;
Ok(()) Ok(())
@ -1621,7 +1626,7 @@ fn compare_old_fingerprint(
paths::set_file_time_no_err(loc, t); paths::set_file_time_no_err(loc, t);
} }
let new_hash = new_fingerprint.hash(); let new_hash = new_fingerprint.hash_u64();
if util::to_hex(new_hash) == old_fingerprint_short && new_fingerprint.fs_status.up_to_date() { if util::to_hex(new_hash) == old_fingerprint_short && new_fingerprint.fs_status.up_to_date() {
return Ok(()); return Ok(());
@ -1632,7 +1637,10 @@ fn compare_old_fingerprint(
.with_context(|| internal("failed to deserialize json"))?; .with_context(|| internal("failed to deserialize json"))?;
// Fingerprint can be empty after a failed rebuild (see comment in prepare_target). // Fingerprint can be empty after a failed rebuild (see comment in prepare_target).
if !old_fingerprint_short.is_empty() { if !old_fingerprint_short.is_empty() {
debug_assert_eq!(util::to_hex(old_fingerprint.hash()), old_fingerprint_short); debug_assert_eq!(
util::to_hex(old_fingerprint.hash_u64()),
old_fingerprint_short
);
} }
let result = new_fingerprint.compare(&old_fingerprint); let result = new_fingerprint.compare(&old_fingerprint);
assert!(result.is_err()); assert!(result.is_err());

View File

@ -13,7 +13,9 @@ use std::time::SystemTime;
use super::death; use super::death;
use cargo_test_support::paths::{self, CargoPathExt}; use cargo_test_support::paths::{self, CargoPathExt};
use cargo_test_support::registry::Package; use cargo_test_support::registry::Package;
use cargo_test_support::{basic_manifest, is_coarse_mtime, project, rustc_host, sleep_ms}; use cargo_test_support::{
basic_manifest, is_coarse_mtime, project, rustc_host, rustc_host_env, sleep_ms,
};
#[cargo_test] #[cargo_test]
fn modifying_and_moving() { fn modifying_and_moving() {
@ -2405,10 +2407,7 @@ fn linking_interrupted() {
// Make a change, start a build, then interrupt it. // Make a change, start a build, then interrupt it.
p.change_file("src/lib.rs", "// modified"); p.change_file("src/lib.rs", "// modified");
let linker_env = format!( let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env());
"CARGO_TARGET_{}_LINKER",
rustc_host().to_uppercase().replace('-', "_")
);
// NOTE: This assumes that the paths to the linker or rustc are not in the // NOTE: This assumes that the paths to the linker or rustc are not in the
// fingerprint. But maybe they should be? // fingerprint. But maybe they should be?
let mut cmd = p let mut cmd = p
@ -2641,3 +2640,22 @@ fn cargo_env_changes() {
) )
.run(); .run();
} }
#[cargo_test]
fn changing_linker() {
// Changing linker should rebuild.
let p = project().file("src/main.rs", "fn main() {}").build();
p.cargo("build").run();
let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env());
p.cargo("build --verbose")
.env(&linker_env, "nonexistent-linker")
.with_status(101)
.with_stderr_contains(
"\
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]`
[ERROR] [..]linker[..]
",
)
.run();
}

View File

@ -1,6 +1,8 @@
//! Tests for configuration values that point to programs. //! Tests for configuration values that point to programs.
use cargo_test_support::{basic_lib_manifest, no_such_file_err_msg, project, rustc_host}; use cargo_test_support::{
basic_lib_manifest, no_such_file_err_msg, project, rustc_host, rustc_host_env,
};
#[cargo_test] #[cargo_test]
fn pathless_tools() { fn pathless_tools() {
@ -262,13 +264,9 @@ second match `cfg(not(target_os = \"none\"))` located in [..]/foo/.cargo/config
#[cargo_test] #[cargo_test]
fn custom_runner_env() { fn custom_runner_env() {
let target = rustc_host();
let p = project().file("src/main.rs", "fn main() {}").build(); let p = project().file("src/main.rs", "fn main() {}").build();
let key = format!( let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env());
"CARGO_TARGET_{}_RUNNER",
target.to_uppercase().replace('-', "_")
);
p.cargo("run") p.cargo("run")
.env(&key, "nonexistent-runner --foo") .env(&key, "nonexistent-runner --foo")
@ -305,10 +303,7 @@ fn custom_runner_env_overrides_config() {
) )
.build(); .build();
let key = format!( let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env());
"CARGO_TARGET_{}_RUNNER",
target.to_uppercase().replace('-', "_")
);
p.cargo("run") p.cargo("run")
.env(&key, "should-run --foo") .env(&key, "should-run --foo")
@ -322,13 +317,9 @@ fn custom_runner_env_overrides_config() {
fn custom_runner_env_true() { fn custom_runner_env_true() {
// Check for a bug where "true" was interpreted as a boolean instead of // Check for a bug where "true" was interpreted as a boolean instead of
// the executable. // the executable.
let target = rustc_host();
let p = project().file("src/main.rs", "fn main() {}").build(); let p = project().file("src/main.rs", "fn main() {}").build();
let key = format!( let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env());
"CARGO_TARGET_{}_RUNNER",
target.to_uppercase().replace('-', "_")
);
p.cargo("run") p.cargo("run")
.env(&key, "true") .env(&key, "true")
@ -338,13 +329,9 @@ fn custom_runner_env_true() {
#[cargo_test] #[cargo_test]
fn custom_linker_env() { fn custom_linker_env() {
let target = rustc_host();
let p = project().file("src/main.rs", "fn main() {}").build(); let p = project().file("src/main.rs", "fn main() {}").build();
let key = format!( let key = format!("CARGO_TARGET_{}_LINKER", rustc_host_env());
"CARGO_TARGET_{}_LINKER",
target.to_uppercase().replace('-', "_")
);
p.cargo("build -v") p.cargo("build -v")
.env(&key, "nonexistent-linker") .env(&key, "nonexistent-linker")