diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index d4acfa73779..50f11a4d900 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -147,6 +147,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> { super::compile(&mut self, &mut queue, &mut plan, unit, exec, force_rebuild)?; } + // Now that we've got the full job queue and we've done all our + // fingerprint analysis to determine what to run, bust all the memoized + // fingerprint hashes to ensure that during the build they all get the + // most up-to-date values. In theory we only need to bust hashes that + // transitively depend on a dirty build script, but it shouldn't matter + // that much for performance anyway. + for fingerprint in self.fingerprints.values() { + fingerprint.clear_memoized(); + } + // Now that we've figured out everything that we're going to do, do it! queue.execute(&mut self, &mut plan)?; diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 27392ac7cad..3db1c9602ef 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -301,7 +301,6 @@ pub fn prepare_target<'a, 'cfg>( // hobble along if it happens to return `Some`. if let Some(new_local) = (gen_local)(&deps, None)? { *fingerprint.local.lock().unwrap() = new_local; - *fingerprint.memoized_hash.lock().unwrap() = None; } write_fingerprint(&loc, &fingerprint) @@ -604,6 +603,16 @@ impl Fingerprint { } } + /// For performance reasons fingerprints will memoize their own hash, but + /// there's also internal mutability with its `local` field which can + /// change, for example with build scripts, during a build. + /// + /// This method can be used to bust all memoized hashes just before a build + /// to ensure that after a build completes everything is up-to-date. + pub fn clear_memoized(&self) { + *self.memoized_hash.lock().unwrap() = None; + } + fn hash(&self) -> u64 { if let Some(s) = *self.memoized_hash.lock().unwrap() { return s; diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index 1b4527c3905..7b4d0631a9f 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -2049,3 +2049,74 @@ fn move_target_directory_with_path_deps() { .with_stderr("[FINISHED] [..]") .run(); } + +#[cargo_test] +fn rerun_if_changes() { + let p = project() + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-env-changed=FOO"); + if std::env::var("FOO").is_ok() { + println!("cargo:rerun-if-env-changed=BAR"); + } + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); + + p.cargo("build -v") + .env("FOO", "1") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("build") + .env("FOO", "1") + .with_stderr("[FINISHED] [..]") + .run(); + + p.cargo("build -v") + .env("FOO", "1") + .env("BAR", "1") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("build") + .env("FOO", "1") + .env("BAR", "1") + .with_stderr("[FINISHED] [..]") + .run(); + + p.cargo("build -v") + .env("BAR", "2") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("build") + .env("BAR", "2") + .with_stderr("[FINISHED] [..]") + .run(); +}