Skip to content

Commit c54a2a5

Browse files
committed
Make mtime of reproducible tarball dependent on git commit
1 parent be99243 commit c54a2a5

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

src/bootstrap/src/utils/tarball.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use std::path::{Path, PathBuf};
99

1010
use crate::core::builder::Builder;
1111
use crate::core::{build_steps::dist::distdir, builder::Kind};
12-
use crate::utils::channel;
1312
use crate::utils::exec::BootstrapCommand;
1413
use crate::utils::helpers::{move_file, t};
14+
use crate::utils::{channel, helpers};
1515

1616
#[derive(Copy, Clone)]
1717
pub(crate) enum OverlayKind {
@@ -351,6 +351,30 @@ impl<'a> Tarball<'a> {
351351
};
352352

353353
cmd.args(["--compression-profile", compression_profile]);
354+
355+
// We want to use a pinned modification time for files in the archive
356+
// to achieve better reproducibility. However, using the same mtime for all
357+
// releases is not ideal, because it can break e.g. Cargo mtime checking
358+
// (https://github.com/rust-lang/rust/issues/125578).
359+
// Therefore, we set mtime to the date of the latest commit (if we're managed
360+
// by git). In this way, the archive will still be always the same for a given commit
361+
// (achieving reproducibility), but it will also change between different commits and
362+
// Rust versions, so that it won't break mtime-based caches.
363+
//
364+
// Note that this only overrides the mtime of files, not directories, due to the
365+
// limitations of the tarballer tool. Directories will have their mtime set to 2006.
366+
367+
// Get the UTC timestamp of the last git commit, if we're under git.
368+
// We need to use UTC, so that anyone who tries to rebuild from the same commit
369+
// gets the same timestamp.
370+
if self.builder.rust_info().is_managed_git_subrepository() {
371+
// %ct means committer date
372+
let timestamp = helpers::output(
373+
helpers::git(Some(&self.builder.src)).arg("log").arg("-1").arg("--format=%ct"),
374+
);
375+
cmd.args(["--override-file-mtime", timestamp.trim()]);
376+
}
377+
354378
self.builder.run(cmd);
355379

356380
// Ensure there are no symbolic links in the tarball. In particular,

src/tools/rust-installer/src/tarballer.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ actor! {
3232
/// The formats used to compress the tarball.
3333
#[arg(value_name = "FORMAT", default_value_t)]
3434
compression_formats: CompressionFormats,
35+
36+
/// Modification time that will be set for all files added to the archive.
37+
/// The default is the date of the first Rust commit from 2006.
38+
/// This serves for better reproducibility of the archives.
39+
#[arg(value_name = "FILE_MTIME", default_value_t = 1153704088)]
40+
override_file_mtime: u64,
3541
}
3642
}
3743

@@ -65,6 +71,8 @@ impl Tarballer {
6571
let buf = BufWriter::with_capacity(1024 * 1024, encoder);
6672
let mut builder = Builder::new(buf);
6773
// Make uid, gid and mtime deterministic to improve reproducibility
74+
// The modification time of directories will be set to the date of the first Rust commit.
75+
// The modification time of files will be set to `override_file_mtime` (see `append_path`).
6876
builder.mode(HeaderMode::Deterministic);
6977

7078
let pool = rayon::ThreadPoolBuilder::new().num_threads(2).build().unwrap();
@@ -77,7 +85,7 @@ impl Tarballer {
7785
}
7886
for path in files {
7987
let src = Path::new(&self.work_dir).join(&path);
80-
append_path(&mut builder, &src, &path)
88+
append_path(&mut builder, &src, &path, self.override_file_mtime)
8189
.with_context(|| format!("failed to tar file '{}'", src.display()))?;
8290
}
8391
builder
@@ -93,10 +101,16 @@ impl Tarballer {
93101
}
94102
}
95103

96-
fn append_path<W: Write>(builder: &mut Builder<W>, src: &Path, path: &String) -> Result<()> {
104+
fn append_path<W: Write>(
105+
builder: &mut Builder<W>,
106+
src: &Path,
107+
path: &String,
108+
override_file_mtime: u64,
109+
) -> Result<()> {
97110
let stat = symlink_metadata(src)?;
98111
let mut header = Header::new_gnu();
99112
header.set_metadata_in_mode(&stat, HeaderMode::Deterministic);
113+
header.set_mtime(override_file_mtime);
100114

101115
if stat.file_type().is_symlink() {
102116
let link = read_link(src)?;

0 commit comments

Comments
 (0)