Skip to content

Commit 745aae6

Browse files
authored
fix(fingerprint): force update mtime of cargo-check artifacts (#16262)
### What does this PR try to resolve? This mtime shift for .rmeta is a workaround as rustc incremental build since rust-lang/rust#114669 (1.90.0) skips unnecessary rmeta generation. The situation is like this: 1. When build script execution's external dependendies (rerun-if-changed, rerun-if-env-changed) got updated, the execution unit reran and got a newer mtime. 2. rustc type-checked the associated crate, though with incremental compilation, no rmeta regeneration. Its `.rmeta` stays old. 3. Run `cargo check` again. Cargo found build script execution had a new mtime than existing crate rmeta, so re-checking the crate. However the check is a no-op (input has no change), so stuck. Fixes #16104 ### How to test and review this PR? Tests pass. There is a reproduction in <https://github.com/rust-lang/cargo/issues/16104#issuecomment-3530739039>,but actually a minimal one is touching a file of build script dependency, than `cargo check`. It'll easily get stuck. This is an ugly workaround. And `-Zchecksum-freshness` doesn't help because build script is involved.
2 parents 3885b78 + 9e7e29e commit 745aae6

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

src/cargo/core/compiler/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,25 @@ fn rustc(
477477
paths::set_file_time_no_err(dep_info_loc, timestamp);
478478
}
479479

480+
// This mtime shift for .rmeta is a workaround as rustc incremental build
481+
// since rust-lang/rust#114669 (1.90.0) skips unnecessary rmeta generation.
482+
//
483+
// The situation is like this:
484+
//
485+
// 1. When build script execution's external dependendies
486+
// (rerun-if-changed, rerun-if-env-changed) got updated,
487+
// the execution unit reran and got a newer mtime.
488+
// 2. rustc type-checked the associated crate, though with incremental
489+
// compilation, no rmeta regeneration. Its `.rmeta` stays old.
490+
// 3. Run `cargo check` again. Cargo found build script execution had
491+
// a new mtime than existing crate rmeta, so re-checking the crate.
492+
// However the check is a no-op (input has no change), so stuck.
493+
if mode.is_check() {
494+
for output in outputs.iter() {
495+
paths::set_file_time_no_err(&output.path, timestamp);
496+
}
497+
}
498+
480499
Ok(())
481500
}));
482501

tests/testsuite/freshness.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3184,3 +3184,63 @@ fn use_mtime_cache_in_cargo_home() {
31843184
"#]])
31853185
.run();
31863186
}
3187+
3188+
#[cargo_test]
3189+
fn incremental_build_script_execution_got_new_mtime_and_cargo_check() {
3190+
// See https://github.com/rust-lang/cargo/issues/16104
3191+
let p = project()
3192+
.file("src/lib.rs", "")
3193+
.file("touch-me", "")
3194+
.file(
3195+
"build.rs",
3196+
r#"fn main() { println!("cargo::rerun-if-changed=touch-me") }"#,
3197+
)
3198+
.build();
3199+
3200+
p.cargo("check")
3201+
.env("CARGO_INCREMENTAL", "1")
3202+
.with_stderr_data(str![[r#"
3203+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
3204+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3205+
3206+
"#]])
3207+
.run();
3208+
3209+
if is_coarse_mtime() {
3210+
sleep_ms(1000);
3211+
}
3212+
3213+
p.change_file("touch-me", "oops");
3214+
3215+
// The first one is expected to rerun build script
3216+
p.cargo("check -v")
3217+
.env("CARGO_INCREMENTAL", "1")
3218+
.with_stderr_data(str![[r#"
3219+
[DIRTY] foo v0.0.1 ([ROOT]/foo): the file `touch-me` has changed ([TIME_DIFF_AFTER_LAST_BUILD])
3220+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
3221+
[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build`
3222+
[RUNNING] `rustc --crate-name foo [..]`
3223+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3224+
3225+
"#]])
3226+
.run();
3227+
3228+
// subsequent cargo check gets stuck...
3229+
p.cargo("check -v")
3230+
.env("CARGO_INCREMENTAL", "1")
3231+
.with_stderr_data(str![[r#"
3232+
[FRESH] foo v0.0.1 ([ROOT]/foo)
3233+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3234+
3235+
"#]])
3236+
.run();
3237+
3238+
p.cargo("check -v")
3239+
.env("CARGO_INCREMENTAL", "1")
3240+
.with_stderr_data(str![[r#"
3241+
[FRESH] foo v0.0.1 ([ROOT]/foo)
3242+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3243+
3244+
"#]])
3245+
.run();
3246+
}

tests/testsuite/freshness_checksum.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2963,3 +2963,63 @@ fn use_checksum_cache_in_cargo_home() {
29632963
"#]])
29642964
.run();
29652965
}
2966+
2967+
#[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")]
2968+
fn incremental_build_script_execution_got_new_mtime_and_cargo_check() {
2969+
// See https://github.com/rust-lang/cargo/issues/16104
2970+
let p = project()
2971+
.file("src/lib.rs", "")
2972+
.file("touch-me", "")
2973+
.file(
2974+
"build.rs",
2975+
r#"fn main() { println!("cargo::rerun-if-changed=touch-me") }"#,
2976+
)
2977+
.build();
2978+
2979+
p.cargo("check -Zchecksum-freshness")
2980+
.masquerade_as_nightly_cargo(&["checksum-freshness"])
2981+
.env("CARGO_INCREMENTAL", "1")
2982+
.with_stderr_data(str![[r#"
2983+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
2984+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
2985+
2986+
"#]])
2987+
.run();
2988+
2989+
p.change_file("touch-me", "oops");
2990+
2991+
// The first one is expected to rerun build script
2992+
p.cargo("check -Zchecksum-freshness -v")
2993+
.masquerade_as_nightly_cargo(&["checksum-freshness"])
2994+
.env("CARGO_INCREMENTAL", "1")
2995+
.with_stderr_data(str![[r#"
2996+
[DIRTY] foo v0.0.1 ([ROOT]/foo): the file `touch-me` has changed ([TIME_DIFF_AFTER_LAST_BUILD])
2997+
[COMPILING] foo v0.0.1 ([ROOT]/foo)
2998+
[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build`
2999+
[RUNNING] `rustc --crate-name foo [..]`
3000+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3001+
3002+
"#]])
3003+
.run();
3004+
3005+
// subsequent cargo check gets stuck...
3006+
p.cargo("check -Zchecksum-freshness -v")
3007+
.masquerade_as_nightly_cargo(&["checksum-freshness"])
3008+
.env("CARGO_INCREMENTAL", "1")
3009+
.with_stderr_data(str![[r#"
3010+
[FRESH] foo v0.0.1 ([ROOT]/foo)
3011+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3012+
3013+
"#]])
3014+
.run();
3015+
3016+
p.cargo("check -Zchecksum-freshness -v")
3017+
.masquerade_as_nightly_cargo(&["checksum-freshness"])
3018+
.env("CARGO_INCREMENTAL", "1")
3019+
.with_stderr_data(str![[r#"
3020+
[FRESH] foo v0.0.1 ([ROOT]/foo)
3021+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3022+
3023+
"#]])
3024+
.run();
3025+
}

0 commit comments

Comments
 (0)