Skip to content

Commit 16deff3

Browse files
committed
Auto merge of #127050 - Kobzol:reproducibility-git, r=<try>
Make mtime of reproducible tarballs dependent on git commit Since #123246, our tarballs should be fully reproducible. That means that the mtime of all files and directories in the tarballs is set to the date of the first Rust commit (from 2006). However, this is causing some mtime invalidation issues (#125578 (comment)). Ideally, we would like to keep the mtime reproducible, but still update it with new versions of Rust. That's what this PR does. It modifies the tarball installer bootstrap invocation so that if the current rustc directory is managed by git, we will set the UTC timestamp of the latest commit as the mtime for all files in the archive. This means that the archive should be still fully reproducible from a given commit SHA, but it will also be changed with new beta bumps and `download-rustc` versions. Note that only files are set to this mtime, directories are still set to the year 2006, because the `tar` library used by `rust-installer` doesn't allow us to selectively override mtime for directories (or at least I haven't found it). We could work around that by doing all the mtime modifications in bootstrap, but that would require more changes. I think/hope that just modifying the file mtimes should be enough. It should at least fix cargo `rustc` mtime invalidation. Fixes: #125578 r? `@onur-ozkan` try-job: x86_64-gnu-distcheck
2 parents d1b7355 + e1999f9 commit 16deff3

File tree

4 files changed

+57
-5
lines changed

4 files changed

+57
-5
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/combiner.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ actor! {
5555
/// The formats used to compress the tarball
5656
#[arg(value_name = "FORMAT", default_value_t)]
5757
compression_formats: CompressionFormats,
58+
59+
/// Modification time that will be set for all files added to the archive.
60+
/// The default is the date of the first Rust commit from 2006.
61+
/// This serves for better reproducibility of the archives.
62+
#[arg(value_name = "FILE_MTIME", default_value_t = 1153704088)]
63+
override_file_mtime: u64,
5864
}
5965
}
6066

@@ -145,7 +151,8 @@ impl Combiner {
145151
.input(self.package_name)
146152
.output(path_to_str(&output)?.into())
147153
.compression_profile(self.compression_profile)
148-
.compression_formats(self.compression_formats);
154+
.compression_formats(self.compression_formats)
155+
.override_file_mtime(self.override_file_mtime);
149156
tarballer.run()?;
150157

151158
Ok(())

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ actor! {
6161
/// The formats used to compress the tarball
6262
#[arg(value_name = "FORMAT", default_value_t)]
6363
compression_formats: CompressionFormats,
64+
65+
/// Modification time that will be set for all files added to the archive.
66+
/// The default is the date of the first Rust commit from 2006.
67+
/// This serves for better reproducibility of the archives.
68+
#[arg(value_name = "FILE_MTIME", default_value_t = 1153704088)]
69+
override_file_mtime: u64,
6470
}
6571
}
6672

@@ -114,7 +120,8 @@ impl Generator {
114120
.input(self.package_name)
115121
.output(path_to_str(&output)?.into())
116122
.compression_profile(self.compression_profile)
117-
.compression_formats(self.compression_formats);
123+
.compression_formats(self.compression_formats)
124+
.override_file_mtime(self.override_file_mtime);
118125
tarballer.run()?;
119126

120127
Ok(())

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)