Skip to content

Commit

Permalink
Merge #1334
Browse files Browse the repository at this point in the history
1334: Support SOURCE_DATE_EPOCH environment variable in wheel building r=messense a=zmanji

This adds support for the `SOURCE_DATE_EPOCH` environment variable like wheel does when building wheels. If this environment variable is set, the `mtime` of the files in the wheel are set to this value to ensure reproducible output.

Fixes #1320

Co-authored-by: Zameer Manji <[email protected]>
  • Loading branch information
bors[bot] and zmanji authored Dec 6, 2022
2 parents 1e09e2f + 15f8045 commit 37c62f6
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ rpassword = { version = "7.0.0", optional = true }
ureq = { version = "2.3.1", features = ["gzip", "socks-proxy"], default-features = false, optional = true }
native-tls-crate = { package = "native-tls", version = "0.2.8", optional = true }
keyring = { version = "1.1.1", optional = true }
time = "0.3.17"

[dev-dependencies]
indoc = "1.0.3"
Expand Down
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

* **Breaking Change**: Build with `--no-default-features` by default when bootstrapping from sdist in [#1333](https://github.com/PyO3/maturin/pull/1333)
* Support `SOURCE_DATE_EPOCH` when building wheels in [#1334](https://github.com/PyO3/maturin/pull/1334)

## [0.14.4] - 2022-12-05

Expand Down
37 changes: 34 additions & 3 deletions src/module_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use ignore::WalkBuilder;
use normpath::PathExt as _;
use sha2::{Digest, Sha256};
use std::collections::{HashMap, HashSet};
use std::env;
use std::ffi::OsStr;
use std::fmt::Write as _;
#[cfg(target_family = "unix")]
Expand All @@ -26,8 +27,10 @@ use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::str;
use tempfile::{tempdir, TempDir};
use time::macros::datetime;
use time::OffsetDateTime;
use tracing::debug;
use zip::{self, ZipWriter};
use zip::{self, DateTime, ZipWriter};

/// Allows writing the module to a wheel or add it directly to the virtualenv
pub trait ModuleWriter {
Expand Down Expand Up @@ -234,9 +237,15 @@ impl ModuleWriter for WheelWriter {
} else {
zip::CompressionMethod::Deflated
};
let options = zip::write::FileOptions::default()

let mut options = zip::write::FileOptions::default()
.unix_permissions(permissions)
.compression_method(compression_method);
let mtime = self.mtime().ok();
if let Some(mtime) = mtime {
options = options.last_modified_time(mtime);
}

self.zip.start_file(target.clone(), options)?;
self.zip.write_all(bytes)?;

Expand Down Expand Up @@ -309,14 +318,36 @@ impl WheelWriter {
}
}

/// Returns a DateTime representing the value SOURCE_DATE_EPOCH environment variable
/// Note that the earliest timestamp a zip file can represent is 1980-01-01
fn mtime(&self) -> Result<DateTime> {
let epoch: i64 = env::var("SOURCE_DATE_EPOCH")?.parse()?;
let dt = OffsetDateTime::from_unix_timestamp(epoch)?;
let min_dt = datetime!(1980-01-01 0:00 UTC);
let dt = dt.max(min_dt);

let dt = DateTime::from_time(dt);

match dt {
Ok(dt) => Ok(dt),
Err(_) => Err(anyhow!("failed to build zip DateTime")),
}
}

/// Creates the record file and finishes the zip
pub fn finish(mut self) -> Result<PathBuf, io::Error> {
let compression_method = if cfg!(feature = "faster-tests") {
zip::CompressionMethod::Stored
} else {
zip::CompressionMethod::Deflated
};
let options = zip::write::FileOptions::default().compression_method(compression_method);

let mut options = zip::write::FileOptions::default().compression_method(compression_method);
let mtime = self.mtime().ok();
if let Some(mtime) = mtime {
options = options.last_modified_time(mtime);
}

let record_filename = self.record_file.to_str().unwrap().replace('\\', "/");
debug!("Adding {}", record_filename);
self.zip.start_file(&record_filename, options)?;
Expand Down
34 changes: 29 additions & 5 deletions tests/common/other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::io::Read;
use std::iter::FromIterator;
use std::path::{Path, PathBuf};
use tar::Archive;
use time::OffsetDateTime;
use zip::ZipArchive;

/// Tries to compile a sample crate (pyo3-pure) for musl,
Expand Down Expand Up @@ -171,11 +172,7 @@ pub fn test_source_distribution(
Ok(())
}

pub fn check_wheel_files(
package: impl AsRef<Path>,
expected_files: Vec<&str>,
unique_name: &str,
) -> Result<()> {
fn build_wheel_files(package: impl AsRef<Path>, unique_name: &str) -> Result<ZipArchive<File>> {
let manifest_path = package.as_ref().join("Cargo.toml");
let wheel_directory = Path::new("test-crates").join("wheels").join(unique_name);

Expand All @@ -202,6 +199,33 @@ pub fn check_wheel_files(
let (wheel_path, _) = &wheels[0];

let wheel = ZipArchive::new(File::open(wheel_path)?)?;
Ok(wheel)
}

pub fn check_wheel_mtimes(
package: impl AsRef<Path>,
expected_mtime: Vec<OffsetDateTime>,
unique_name: &str,
) -> Result<()> {
let mut wheel = build_wheel_files(package, unique_name)?;
let mut mtimes = BTreeSet::<OffsetDateTime>::new();

for idx in 0..wheel.len() {
let mtime = wheel.by_index(idx)?.last_modified().to_time()?;
mtimes.insert(mtime);
}

assert_eq!(mtimes, expected_mtime.into_iter().collect::<BTreeSet<_>>());

Ok(())
}

pub fn check_wheel_files(
package: impl AsRef<Path>,
expected_files: Vec<&str>,
unique_name: &str,
) -> Result<()> {
let wheel = build_wheel_files(package, unique_name)?;
let drop_platform_specific_files = |file: &&str| -> bool {
!matches!(Path::new(file).extension(), Some(ext) if ext == "pyc" || ext == "pyd" || ext == "so")
};
Expand Down
12 changes: 12 additions & 0 deletions tests/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use common::{
};
use indoc::indoc;
use maturin::Target;
use std::env;
use std::path::{Path, PathBuf};
use time::macros::datetime;

mod common;

Expand Down Expand Up @@ -576,3 +578,13 @@ fn workspace_inheritance_sdist() {
fn abi3_python_interpreter_args() {
handle_result(other::abi3_python_interpreter_args());
}

#[test]
fn pyo3_source_date_epoch() {
env::set_var("SOURCE_DATE_EPOCH", "0");
handle_result(other::check_wheel_mtimes(
"test-crates/pyo3-mixed-include-exclude",
vec![datetime!(1980-01-01 0:00 UTC)],
"pyo3_source_date_epoch",
))
}

0 comments on commit 37c62f6

Please sign in to comment.