-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Open
Labels
A-build-scriptsArea: build.rs scriptsArea: build.rs scriptsA-rebuild-detectionArea: rebuild detection and fingerprintingArea: rebuild detection and fingerprintingC-bugCategory: bugCategory: bugS-triageStatus: This issue is waiting on initial triage.Status: This issue is waiting on initial triage.
Description
Problem
Using rerun-if-changed
to a file that changes, but changes with old timestamps, Cargo won't detect that it needs to run. For example:
- Extract a tarball with file
foo
with timestamp 1. cargo build
withrerun-if-changed
offoo
. This marks the build script as timestamp 5.- Extract a different tarball with file
foo
with timestamp 2. - Run
cargo build
again, it doesn't pick up the change from timestamp 1 to 2 because both values are less than 5.
I would expect any change to the file to cause it to re-run the build script.
The real-world use case is in the rust LLVM build system. It extracts LLVM from a downloaded tarball. The rustc_llvm
build script has a rerun-if-changed=/path/to/llvm-config
. When rustbuild determines a new tarball needs to be downloaded, the rustc_llvm
build script doesn't get the memo and doesn't get rerun. This can cause it to link against the wrong file.
Steps
The following is a test using Cargo's testsuite:
#[cargo_test]
fn past_rerun_if_changed() {
// Tests that a file that changes with an old timestamp triggers a build
// script to rerun. This can happen when extracting files from a tarball
// which preserves the timestamps.
let p = project()
.file("src/lib.rs", "")
.file(
"build.rs",
r#"
fn main() {
println!("cargo:rerun-if-changed=somefile");
}
"#,
)
.file("somefile", "")
.build();
// Simulate extracting some tarball.
let now = std::time::SystemTime::now();
let time_a = FileTime::from_system_time(now - Duration::new(60 * 10, 0));
let time_b = FileTime::from_system_time(now - Duration::new(60 * 5, 0));
let somefile = p.root().join("somefile");
filetime::set_file_times(&somefile, time_a, time_a).unwrap();
p.cargo("check").run();
// Simulate extracting a different tarball, but whose timestamp is still
// in the past.
p.change_file("somefile", "123");
filetime::set_file_times(&somefile, time_b, time_b).unwrap();
// FIXME: This should trigger a rebuild, but it currently does not.
p.cargo("check -v")
.with_stderr(
"\
[COMPILING] foo [..]
[RUNNING] `[ROOT]/foo/target/debug/build/foo-[..]/build-script-build`
[RUNNING] `rustc --crate-name foo [..]
[FINISHED] [..]
",
)
.run();
}
Possible Solution(s)
A few possible options:
- Hash the timestamp and file size of all rerun-if-changed files, and include that in the final hash (instead of just comparing the timestamps). This could potentially trigger rebuilds more often, though offhand I can't think of how it could happen.
- Hash the contents of the files (such as (Option to) Fingerprint by file contents instead of mtime #6529). This might be very expensive so I'm not sure if it would be a good idea to do this (possibly make this optional?).
Notes
No response
Version
cargo 1.63.0-nightly (03a849043 2022-06-19)
Metadata
Metadata
Assignees
Labels
A-build-scriptsArea: build.rs scriptsArea: build.rs scriptsA-rebuild-detectionArea: rebuild detection and fingerprintingArea: rebuild detection and fingerprintingC-bugCategory: bugCategory: bugS-triageStatus: This issue is waiting on initial triage.Status: This issue is waiting on initial triage.