Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/cargo-test-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1735,3 +1735,21 @@ pub fn assert_deps_contains(project: &Project, fingerprint: &str, expected: &[(u
}
})
}

#[track_caller]
pub fn assert_deterministic_mtime(path: impl AsRef<Path>) {
// Hardcoded value be removed once alexcrichton/tar-rs#420 is merged and released.
// See also rust-lang/cargo#16237
const DETERMINISTIC_TIMESTAMP: u64 = 1153704088;

let path = path.as_ref();
let mtime = path.metadata().unwrap().modified().unwrap();
let timestamp = mtime
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
assert_eq!(
timestamp, DETERMINISTIC_TIMESTAMP,
"expected deterministic mtime for {path:?}, got {timestamp}"
);
}
24 changes: 24 additions & 0 deletions src/cargo/sources/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ impl<'gctx> RegistrySource<'gctx> {
dst.create_dir()?;

let bytes_written = unpack(self.gctx, tarball, unpack_dir, &|_| true)?;
update_mtime_for_generated_files(unpack_dir);

// Now that we've finished unpacking, create and write to the lock file to indicate that
// unpacking was successful.
Expand Down Expand Up @@ -679,6 +680,7 @@ impl<'gctx> RegistrySource<'gctx> {
let tarball =
File::open(path).with_context(|| format!("failed to open {}", path.display()))?;
unpack(self.gctx, &tarball, &dst, include)?;
update_mtime_for_generated_files(&dst);
Ok(dst)
}

Expand Down Expand Up @@ -1104,3 +1106,25 @@ fn unpack(

Ok(bytes_written)
}

/// Workaround for rust-lang/cargo#16237
///
/// Generated files should have the same deterministic mtime as other files.
/// However, since we forgot to set mtime for those files when uploading, they
/// always have older mtime (1973-11-29) that prevents zip from packing (requiring >1980)
///
/// This workaround updates mtime after we unpack the tarball at the destination.
fn update_mtime_for_generated_files(pkg_root: &Path) {
const GENERATED_FILES: &[&str] = &["Cargo.lock", "Cargo.toml", ".cargo_vcs_info.json"];
// Hardcoded value be removed once alexcrichton/tar-rs#420 is merged and released.
// See also rust-lang/cargo#16237
const DETERMINISTIC_TIMESTAMP: i64 = 1153704088;

for file in GENERATED_FILES {
let path = pkg_root.join(file);
let mtime = filetime::FileTime::from_unix_time(DETERMINISTIC_TIMESTAMP, 0);
if let Err(e) = filetime::set_file_mtime(&path, mtime) {
tracing::trace!("failed to set deterministic mtime for {path:?}: {e}");
}
}
}
42 changes: 42 additions & 0 deletions tests/testsuite/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::sync::Mutex;
use crate::prelude::*;
use crate::utils::cargo_process;
use cargo::core::SourceId;
use cargo_test_support::assert_deterministic_mtime;
use cargo_test_support::paths;
use cargo_test_support::registry::{
self, Dependency, Package, RegistryBuilder, Response, TestRegistry, registry_path,
Expand Down Expand Up @@ -4653,3 +4654,44 @@ required by package `foo v0.0.1 ([ROOT]/foo)`
"#]])
.run();
}

#[cargo_test]
fn deterministic_mtime() {
let registry = registry::init();
Package::new("foo", "0.1.0")
// content doesn't matter, we just want to check mtime
.file("Cargo.lock", "")
.file(".cargo_vcs_info.json", "")
.file("src/lib.rs", "")
.publish();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "a"
edition = "2015"

[dependencies]
foo = '0.1.0'
"#,
)
.file("src/lib.rs", "")
.build();

p.cargo("fetch").run();

let id = SourceId::for_registry(registry.index_url()).unwrap();
let hash = cargo::util::hex::short_hash(&id);
let pkg_root = paths::cargo_home()
.join("registry")
.join("src")
.join(format!("-{hash}"))
.join("foo-0.1.0");

// Generated files should have deterministic mtime after unpacking.
assert_deterministic_mtime(pkg_root.join("Cargo.lock"));
assert_deterministic_mtime(pkg_root.join("Cargo.toml"));
assert_deterministic_mtime(pkg_root.join(".cargo_vcs_info.json"));
}
33 changes: 33 additions & 0 deletions tests/testsuite/vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::fs;

use crate::prelude::*;
use cargo_test_support::assert_deterministic_mtime;
use cargo_test_support::compare::assert_e2e;
use cargo_test_support::git;
use cargo_test_support::registry::{self, Package, RegistryBuilder};
Expand Down Expand Up @@ -2119,3 +2120,35 @@ fn vendor_rename_fallback() {

assert!(p.root().join("vendor/log/Cargo.toml").exists());
}

#[cargo_test]
fn deterministic_mtime() {
Package::new("foo", "0.1.0")
// content doesn't matter, we just want to check mtime
.file("Cargo.lock", "")
.file(".cargo_vcs_info.json", "")
.file("src/lib.rs", "")
.publish();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "a"
edition = "2015"

[dependencies]
foo = '0.1.0'
"#,
)
.file("src/lib.rs", "")
.build();

p.cargo("vendor --respect-source-config").run();

// Generated files should have deterministic mtime after unpacking.
assert_deterministic_mtime(p.root().join("vendor/foo/Cargo.lock"));
assert_deterministic_mtime(p.root().join("vendor/foo/Cargo.toml"));
assert_deterministic_mtime(p.root().join("vendor/foo/.cargo_vcs_info.json"));
}