diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index fd85650bc5620..0e44bd287c06d 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -703,9 +703,7 @@ download-rustc = false let file_times = fs::FileTimes::new().set_accessed(now).set_modified(now); let llvm_config = llvm_root.join("bin").join(exe("llvm-config", self.build)); - let llvm_config_file = t!(File::options().write(true).open(llvm_config)); - - t!(llvm_config_file.set_times(file_times)); + t!(crate::utils::helpers::set_file_times(llvm_config, file_times)); if self.should_fix_bins_and_dylibs() { let llvm_lib = llvm_root.join("lib"); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index bfd0e42acfd32..bad3b80427e5f 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -37,7 +37,9 @@ use crate::core::builder; use crate::core::builder::{Builder, Kind}; use crate::core::config::{flags, DryRun, LldMode, LlvmLibunwind, Target, TargetSelection}; use crate::utils::exec::{command, BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode}; -use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir}; +use crate::utils::helpers::{ + self, dir_is_empty, exe, libdir, mtime, output, set_file_times, symlink_dir, +}; mod core; mod utils; @@ -1774,21 +1776,20 @@ Executed at: {executed_at}"#, } } if let Ok(()) = fs::hard_link(&src, dst) { - // Attempt to "easy copy" by creating a hard link - // (symlinks don't work on windows), but if that fails - // just fall back to a slow `copy` operation. + // Attempt to "easy copy" by creating a hard link (symlinks are priviledged on windows), + // but if that fails just fall back to a slow `copy` operation. } else { if let Err(e) = fs::copy(&src, dst) { panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e) } t!(fs::set_permissions(dst, metadata.permissions())); + // Restore file times because changing permissions on e.g. Linux using `chmod` can cause + // file access time to change. let file_times = fs::FileTimes::new() .set_accessed(t!(metadata.accessed())) .set_modified(t!(metadata.modified())); - - let dst_file = t!(fs::File::open(dst)); - t!(dst_file.set_times(file_times)); + t!(set_file_times(dst, file_times)); } } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 65e75f114bbeb..a856c99ff55a5 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -544,3 +544,15 @@ pub fn get_closest_merge_base_commit( Ok(output_result(git.as_command_mut())?.trim().to_owned()) } + +/// Sets the file times for a given file at `path`. +pub fn set_file_times>(path: P, times: fs::FileTimes) -> io::Result<()> { + // Windows requires file to be writable to modify file times. But on Linux CI the file does not + // need to be writable to modify file times and might be read-only. + let f = if cfg!(windows) { + fs::File::options().write(true).open(path)? + } else { + fs::File::open(path)? + }; + f.set_times(times) +} diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 103c4d26a1854..12719861f974b 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -3,7 +3,8 @@ use std::io::Write; use std::path::PathBuf; use crate::utils::helpers::{ - check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, symlink_dir, + check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, set_file_times, + symlink_dir, }; use crate::{Config, Flags}; @@ -92,3 +93,24 @@ fn test_symlink_dir() { #[cfg(not(windows))] fs::remove_file(link_path).unwrap(); } + +#[test] +fn test_set_file_times_sanity_check() { + let config = Config::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]); + let tempfile = config.tempdir().join(".tmp-file"); + + { + File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); + assert!(tempfile.exists()); + } + + // This might only fail on Windows (if file is default read-only then we try to modify file + // times). + let unix_epoch = std::time::SystemTime::UNIX_EPOCH; + let target_time = fs::FileTimes::new().set_accessed(unix_epoch).set_modified(unix_epoch); + set_file_times(&tempfile, target_time).unwrap(); + + let found_metadata = fs::metadata(tempfile).unwrap(); + assert_eq!(found_metadata.accessed().unwrap(), unix_epoch); + assert_eq!(found_metadata.modified().unwrap(), unix_epoch) +}