Description
If a build script changes what values it prints for "rerun-if" directives between runs, then the rebuild detection doesn't work correctly. An example of what happens:
- Perform a build, which emits a certain set of rerun-if directives.
- Change something that invalidates a rerun-if directive, and build. During this step, the build script should emit a different set of rerun-if directives to trigger the bug.
- Build again with nothing changed from step 2, causes a rebuild when it shouldn't.
A real-world example is the llvm and sanitizer build scripts in rust-lang/rust (like this). I suspect this is a relatively common idiom.
There is also a problem, when running a debug build of cargo, it causes cargo to panic and hangs. In non-debug builds, it hits this unhelpful line because it has become confused.
The problem is this line. When the local
value is updated, the cached hash is invalidated. However, the invalidation needs to propagate to all parent units as well. Otherwise they write out their fingerprint with the deps
value using the old hash.
The panics in debug mode are due to the json validations. The round-trip fails because the cached hash values are incorrect. It hangs because of #6433.
I've been trying to think of some way to work around this, but haven't come up with any good ideas. @alexcrichton do you have any ideas how to invalidate the memoized hashes? Or maybe a completely different approach?
Note that this is similar in spirit to #6779, but I think the solution may be different.
Here is a sample test. Similar problems occur with rerun-if-changed
, but env vars are a little easier to use. Each of the "FIXME" lines fails with current cargo.
#[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();
// Stays fresh.
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();
// Stays fresh.
// FIXME: Error here.
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();
// Stays fresh.
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();
// Ignores BAR.
// FIXME: Error here.
p.cargo("build")
.env("BAR", "2")
.with_stderr("[FINISHED] [..]")
.run();
}