pull,context: preserve file mtimes when copying sources
CI / build (push) Successful in 8m11s
CI / snap (push) Successful in 6m9s

std::fs::copy resets the destination mtime to "now", which breaks
timestamp-based build systems (autotools/gnulib). Packages like
'hello' from Debian sid ship pre-generated files alongside their
prerequisites; when the copy flattens all mtimes, make considers the
generated targets out-of-date and tries to regenerate them with tools
(e.g. gperf) that are not declared build-dependencies, failing the
build.

Restore the source modification and access times after every file copy
in pull::copy_dir_all, pull::fetch_archive_sources merge step, and
context::unshare copy_dir_recursive/ensure_available.
This commit is contained in:
2026-07-24 14:14:18 +02:00
parent e1668d5d80
commit a0d35bb18e
2 changed files with 93 additions and 13 deletions
+34
View File
@@ -113,13 +113,46 @@ fn copy_dir_all(src: &Path, dst: &Path) -> Result<(), Box<dyn Error>> {
} else if src_path.is_dir() {
copy_dir_all(&src_path, &dst_path)?;
} else {
// Copy the file, preserving the source modification and access
// times. This is important for autotools/gnulib-based packages
// (e.g. 'hello' from Debian sid) that ship pre-generated files
// alongside their prerequisites: if the copy resets the mtime to
// "now", the prerequisites appear as new as the generated targets
// and `make` tries to regenerate them using tools (like gperf)
// that are not declared build-dependencies.
std::fs::copy(&src_path, &dst_path)?;
copy_file_times(&src_path, &dst_path)?;
}
}
Ok(())
}
/// Restore the modification and access times of `dest` to match `src`.
///
/// `std::fs::copy` resets the destination mtime to "now", which breaks
/// timestamp-based build systems (autotools/gnulib) that ship pre-generated
/// files alongside their prerequisites. Restoring the original timestamps
/// prevents `make` from needlessly regenerating those files with tools that
/// may not be installed (e.g. `gperf`).
fn copy_file_times(src: &Path, dest: &Path) -> Result<(), Box<dyn Error>> {
let metadata = std::fs::metadata(src)?;
let mut times = std::fs::FileTimes::new();
let mut have_times = false;
if let Ok(mtime) = metadata.modified() {
times = times.set_modified(mtime);
have_times = true;
}
if let Ok(atime) = metadata.accessed() {
times = times.set_accessed(atime);
have_times = true;
}
if have_times && let Ok(dest_file) = std::fs::File::open(dest) {
let _ = dest_file.set_times(times);
}
Ok(())
}
/// Helper function to extract tar archive with progress tracking
fn extract_tar_archive<D, F>(
file_path: &Path,
@@ -467,6 +500,7 @@ async fn fetch_archive_sources(
copy_dir_all(&sub_path, &target_path)?;
} else {
std::fs::copy(&sub_path, &target_path)?;
copy_file_times(&sub_path, &target_path)?;
}
}
std::fs::remove_dir_all(&src_dir)?;