Skip to content

Build script rerun-if changes break rebuild detection. #7362

Closed
@ehuss

Description

@ehuss

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:

  1. Perform a build, which emits a certain set of rerun-if directives.
  2. 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.
  3. 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();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions