From 33e18907bf67f723b809ea7d6368cb5f3bac3cd7 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 23 Jun 2026 16:50:54 +0200 Subject: [PATCH 1/2] fix(rust): backdate extracted CLI mtime to stop self-invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `bundled-cli` is off, `build.rs` watches the extracted CLI binary via `cargo:rerun-if-changed` so a deleted cache binary forces a re-extract. On a cold cache the same build script *creates* that binary mid-build, after the download — so its mtime ends up newer than cargo's build-script `output` reference (stamped when the script was spawned). The next identical `cargo` invocation then sees the watched file as "changed", reruns build.rs, recompiles the SDK crate, and relinks every downstream crate (observed ~9 min of wasted CI on a second cargo invocation in the same job). Backdate the staged binary's mtime to the Unix epoch before the atomic rename (rename preserves mtime), so it lands unambiguously older than any real build reference and a no-change rebuild stays a true no-op. Best-effort: on error we emit a `cargo:warning` and continue, reverting to the prior redundant-rebuild behaviour rather than breaking the build. The deleted-file recovery contract is untouched — a missing file still can't be stat'd, so cargo still reruns. Embed mode (`bundled-cli` on) is unaffected: it emits no `rerun-if-changed` on any build-created file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- rust/build.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/rust/build.rs b/rust/build.rs index bc944a318..5bdc86de4 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -378,6 +378,35 @@ fn extract_to_cache(archive: &[u8], install_dir: &Path, platform: Platform) -> P } } + // Backdate the staged binary to the Unix epoch before it lands. We emit + // `cargo:rerun-if-changed` on `final_path` (see caller) so a *deleted* + // cache binary forces a re-extract — but cargo stamps the build-script + // `output` reference when the script is spawned, seconds before this + // freshly-downloaded binary is written. A current mtime would therefore + // be *newer* than that reference, so the next identical `cargo` + // invocation would see the watched file as "changed" and pointlessly + // rerun build.rs + recompile the crate + relink every downstream crate. + // Pinning to the epoch keeps the file unambiguously older than any real + // build reference; `rename` preserves mtime (same inode), so it lands + // already-backdated and a no-change rebuild stays a true no-op. The + // deleted-file recovery contract is untouched: a missing file can't be + // stat'd, so cargo still treats it as stale and reruns regardless. + // + // Best-effort: a filesystem that refuses the epoch (e.g. FAT's 1980 floor + // clamps it — still older than any real reference) or rejects the call + // just reverts to the pre-fix redundant-rebuild behaviour, never a broken + // build. + if let Err(e) = std::fs::File::options() + .write(true) + .open(&staging_path) + .and_then(|f| f.set_modified(std::time::SystemTime::UNIX_EPOCH)) + { + println!( + "cargo:warning=Could not backdate {} (a redundant rebuild may occur): {e}", + staging_path.display() + ); + } + // Atomic file-replace on both Unix and Windows. If a concurrent build // already produced the same file the rename overwrites it; the bytes // are SHA-verified-identical so replacement is safe. From 4ebae5e5f6c4372575180554a67e2bacb3f66909 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 23 Jun 2026 17:45:20 +0200 Subject: [PATCH 2/2] refactor(rust): write staging binary through a single file handle Address review: open the staging file once and perform the write, permission-set, and mtime-backdate on one handle instead of reopening it for each step. Write and chmod failures stay fatal; the epoch backdate stays best-effort (warn and continue). Behavior is otherwise unchanged: the extracted binary still lands epoch-dated so a no-change rebuild is a true no-op. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- rust/build.rs | 90 +++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/rust/build.rs b/rust/build.rs index 5bdc86de4..66d1de7bc 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -1,4 +1,4 @@ -use std::io::Read; +use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -359,52 +359,56 @@ fn extract_to_cache(archive: &[u8], install_dir: &Path, platform: Platform) -> P std::process::id(), )); - if let Err(e) = std::fs::write(&staging_path, &bytes) { - let _ = std::fs::remove_file(&staging_path); - panic!( - "failed to write staging file {}: {e}", - staging_path.display() - ); - } - - #[cfg(unix)] { - use std::os::unix::fs::PermissionsExt; - if let Err(e) = - std::fs::set_permissions(&staging_path, std::fs::Permissions::from_mode(0o755)) - { + let mut f = std::fs::File::create(&staging_path).unwrap_or_else(|e| { let _ = std::fs::remove_file(&staging_path); - panic!("failed to chmod {}: {e}", staging_path.display()); + panic!( + "failed to create staging file {}: {e}", + staging_path.display() + ); + }); + + if let Err(e) = f.write_all(&bytes) { + let _ = std::fs::remove_file(&staging_path); + panic!( + "failed to write staging file {}: {e}", + staging_path.display() + ); } - } - // Backdate the staged binary to the Unix epoch before it lands. We emit - // `cargo:rerun-if-changed` on `final_path` (see caller) so a *deleted* - // cache binary forces a re-extract — but cargo stamps the build-script - // `output` reference when the script is spawned, seconds before this - // freshly-downloaded binary is written. A current mtime would therefore - // be *newer* than that reference, so the next identical `cargo` - // invocation would see the watched file as "changed" and pointlessly - // rerun build.rs + recompile the crate + relink every downstream crate. - // Pinning to the epoch keeps the file unambiguously older than any real - // build reference; `rename` preserves mtime (same inode), so it lands - // already-backdated and a no-change rebuild stays a true no-op. The - // deleted-file recovery contract is untouched: a missing file can't be - // stat'd, so cargo still treats it as stale and reruns regardless. - // - // Best-effort: a filesystem that refuses the epoch (e.g. FAT's 1980 floor - // clamps it — still older than any real reference) or rejects the call - // just reverts to the pre-fix redundant-rebuild behaviour, never a broken - // build. - if let Err(e) = std::fs::File::options() - .write(true) - .open(&staging_path) - .and_then(|f| f.set_modified(std::time::SystemTime::UNIX_EPOCH)) - { - println!( - "cargo:warning=Could not backdate {} (a redundant rebuild may occur): {e}", - staging_path.display() - ); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if let Err(e) = f.set_permissions(std::fs::Permissions::from_mode(0o755)) { + let _ = std::fs::remove_file(&staging_path); + panic!("failed to chmod {}: {e}", staging_path.display()); + } + } + + // Backdate the staged binary to the Unix epoch before it lands. We emit + // `cargo:rerun-if-changed` on `final_path` (see caller) so a *deleted* + // cache binary forces a re-extract — but cargo stamps the build-script + // `output` reference when the script is spawned, seconds before this + // freshly-downloaded binary is written. A current mtime would therefore + // be *newer* than that reference, so the next identical `cargo` + // invocation would see the watched file as "changed" and pointlessly + // rerun build.rs + recompile the crate + relink every downstream crate. + // Pinning to the epoch keeps the file unambiguously older than any real + // build reference; `rename` preserves mtime (same inode), so it lands + // already-backdated and a no-change rebuild stays a true no-op. The + // deleted-file recovery contract is untouched: a missing file can't be + // stat'd, so cargo still treats it as stale and reruns regardless. + // + // Best-effort: a filesystem that refuses the epoch (e.g. FAT's 1980 floor + // clamps it — still older than any real reference) or rejects the call + // just reverts to the pre-fix redundant-rebuild behaviour, never a broken + // build. + if let Err(e) = f.set_modified(std::time::SystemTime::UNIX_EPOCH) { + println!( + "cargo:warning=Could not backdate {} (a redundant rebuild may occur): {e}", + staging_path.display() + ); + } } // Atomic file-replace on both Unix and Windows. If a concurrent build