From 2a07c68eef1d9658bd1f429b8176aa0f3514e8cb Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 14 Nov 2025 04:21:21 -0500 Subject: [PATCH 1/2] test: cargo-check stuck due to build script mtime too new See rust-lang/cargo 16104 for what we're testing against --- tests/testsuite/freshness.rs | 64 +++++++++++++++++++++++++++ tests/testsuite/freshness_checksum.rs | 64 +++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index 0cf673e4f7a..4fd18076b69 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -3184,3 +3184,67 @@ fn use_mtime_cache_in_cargo_home() { "#]]) .run(); } + +#[cargo_test] +fn incremental_build_script_execution_got_new_mtime_and_cargo_check() { + // See https://github.com/rust-lang/cargo/issues/16104 + let p = project() + .file("src/lib.rs", "") + .file("touch-me", "") + .file( + "build.rs", + r#"fn main() { println!("cargo::rerun-if-changed=touch-me") }"#, + ) + .build(); + + p.cargo("check") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + if is_coarse_mtime() { + sleep_ms(1000); + } + + p.change_file("touch-me", "oops"); + + // The first one is expected to rerun build script + p.cargo("check -v") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[DIRTY] foo v0.0.1 ([ROOT]/foo): the file `touch-me` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + // subsequent cargo check gets stuck... + p.cargo("check -v") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -v") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} diff --git a/tests/testsuite/freshness_checksum.rs b/tests/testsuite/freshness_checksum.rs index ffc94abb78c..c5025af3553 100644 --- a/tests/testsuite/freshness_checksum.rs +++ b/tests/testsuite/freshness_checksum.rs @@ -2963,3 +2963,67 @@ fn use_checksum_cache_in_cargo_home() { "#]]) .run(); } + +#[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] +fn incremental_build_script_execution_got_new_mtime_and_cargo_check() { + // See https://github.com/rust-lang/cargo/issues/16104 + let p = project() + .file("src/lib.rs", "") + .file("touch-me", "") + .file( + "build.rs", + r#"fn main() { println!("cargo::rerun-if-changed=touch-me") }"#, + ) + .build(); + + p.cargo("check -Zchecksum-freshness") + .masquerade_as_nightly_cargo(&["checksum-freshness"]) + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.change_file("touch-me", "oops"); + + // The first one is expected to rerun build script + p.cargo("check -Zchecksum-freshness -v") + .masquerade_as_nightly_cargo(&["checksum-freshness"]) + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[DIRTY] foo v0.0.1 ([ROOT]/foo): the file `touch-me` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + // subsequent cargo check gets stuck... + p.cargo("check -Zchecksum-freshness -v") + .masquerade_as_nightly_cargo(&["checksum-freshness"]) + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo("check -Zchecksum-freshness -v") + .masquerade_as_nightly_cargo(&["checksum-freshness"]) + .env("CARGO_INCREMENTAL", "1") + .with_stderr_data(str![[r#" +[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} From 9e7e29e56da473c60d821764dea57fbca5ca18df Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 14 Nov 2025 04:22:10 -0500 Subject: [PATCH 2/2] fix(fingerprint): force update mtime of cargo-check artifacts 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. --- src/cargo/core/compiler/mod.rs | 19 +++++++++++++++++++ tests/testsuite/freshness.rs | 8 ++------ tests/testsuite/freshness_checksum.rs | 8 ++------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index a9b588c016c..a425a3bda26 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -477,6 +477,25 @@ fn rustc( paths::set_file_time_no_err(dep_info_loc, timestamp); } + // 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. + if mode.is_check() { + for output in outputs.iter() { + paths::set_file_time_no_err(&output.path, timestamp); + } + } + Ok(()) })); diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index 4fd18076b69..7d626bbb559 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -3229,9 +3229,7 @@ fn incremental_build_script_execution_got_new_mtime_and_cargo_check() { p.cargo("check -v") .env("CARGO_INCREMENTAL", "1") .with_stderr_data(str![[r#" -[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) -[CHECKING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustc --crate-name foo [..]` +[FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -3240,9 +3238,7 @@ fn incremental_build_script_execution_got_new_mtime_and_cargo_check() { p.cargo("check -v") .env("CARGO_INCREMENTAL", "1") .with_stderr_data(str![[r#" -[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) -[CHECKING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustc --crate-name foo [..]` +[FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) diff --git a/tests/testsuite/freshness_checksum.rs b/tests/testsuite/freshness_checksum.rs index c5025af3553..eedc1f43de9 100644 --- a/tests/testsuite/freshness_checksum.rs +++ b/tests/testsuite/freshness_checksum.rs @@ -3007,9 +3007,7 @@ fn incremental_build_script_execution_got_new_mtime_and_cargo_check() { .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("CARGO_INCREMENTAL", "1") .with_stderr_data(str![[r#" -[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) -[CHECKING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustc --crate-name foo [..]` +[FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -3019,9 +3017,7 @@ fn incremental_build_script_execution_got_new_mtime_and_cargo_check() { .masquerade_as_nightly_cargo(&["checksum-freshness"]) .env("CARGO_INCREMENTAL", "1") .with_stderr_data(str![[r#" -[DIRTY] foo v0.0.1 ([ROOT]/foo): the dependency build_script_build was rebuilt ([TIME_DIFF_AFTER_LAST_BUILD]) -[CHECKING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustc --crate-name foo [..]` +[FRESH] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]])