diff --git a/src/build_context.rs b/src/build_context.rs index 717dcc73c..a42a66050 100644 --- a/src/build_context.rs +++ b/src/build_context.rs @@ -33,6 +33,7 @@ use std::io; use std::path::{Path, PathBuf}; use std::str::FromStr; use tracing::instrument; +use zip::DateTime; /// Insert wasm launcher scripts as entrypoints and the wasmtime dependency fn bin_wasi_helper( @@ -752,6 +753,10 @@ impl BuildContext { let platform = self.get_platform_tag(platform_tags)?; let tag = format!("cp{major}{min_minor}-abi3-{platform}"); + let file_options = self + .compression + .get_file_options() + .last_modified_time(zip_mtime()); let mut writer = WheelWriter::new( &tag, &self.out, @@ -759,7 +764,7 @@ impl BuildContext { &self.metadata24, std::slice::from_ref(&tag), self.excludes(Format::Wheel)?, - self.compression, + file_options, )?; self.add_external_libs(&mut writer, &[&artifact], &[ext_libs])?; @@ -831,6 +836,10 @@ impl BuildContext { ) -> Result { let tag = python_interpreter.get_tag(self, platform_tags)?; + let file_options = self + .compression + .get_file_options() + .last_modified_time(zip_mtime()); let mut writer = WheelWriter::new( &tag, &self.out, @@ -838,7 +847,7 @@ impl BuildContext { &self.metadata24, std::slice::from_ref(&tag), self.excludes(Format::Wheel)?, - self.compression, + file_options, )?; self.add_external_libs(&mut writer, &[&artifact], &[ext_libs])?; @@ -955,6 +964,10 @@ impl BuildContext { ) -> Result { let (tag, tags) = self.get_universal_tags(platform_tags)?; + let file_options = self + .compression + .get_file_options() + .last_modified_time(zip_mtime()); let mut writer = WheelWriter::new( &tag, &self.out, @@ -962,7 +975,7 @@ impl BuildContext { &self.metadata24, &tags, self.excludes(Format::Wheel)?, - self.compression, + file_options, )?; self.add_external_libs(&mut writer, &[&artifact], &[ext_libs])?; @@ -1028,6 +1041,10 @@ impl BuildContext { ) -> Result { let (tag, tags) = self.get_universal_tags(platform_tags)?; + let file_options = self + .compression + .get_file_options() + .last_modified_time(zip_mtime()); let mut writer = WheelWriter::new( &tag, &self.out, @@ -1035,7 +1052,7 @@ impl BuildContext { &self.metadata24, &tags, self.excludes(Format::Wheel)?, - self.compression, + file_options, )?; self.add_external_libs(&mut writer, &[&artifact], &[ext_libs])?; @@ -1128,6 +1145,10 @@ impl BuildContext { self.metadata24.clone() }; + let file_options = self + .compression + .get_file_options() + .last_modified_time(zip_mtime()); let mut writer = WheelWriter::new( &tag, &self.out, @@ -1135,7 +1156,7 @@ impl BuildContext { &metadata24, &tags, self.excludes(Format::Wheel)?, - self.compression, + file_options, )?; if self.project_layout.python_module.is_some() && self.target.is_wasi() { @@ -1396,6 +1417,21 @@ fn emcc_version() -> Result { Ok(trimmed.into()) } +/// 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 zip_mtime() -> DateTime { + let res = env::var("SOURCE_DATE_EPOCH") + .context("") // Only using context() to unify the error types + .and_then(|epoch| { + let epoch: i64 = epoch.parse()?; + let dt = time::OffsetDateTime::from_unix_timestamp(epoch)?; + let dt = DateTime::try_from(dt)?; + Ok(dt) + }); + + res.unwrap_or_default() +} + #[cfg(test)] mod tests { use super::{iphoneos_deployment_target, macosx_deployment_target}; diff --git a/src/compression.rs b/src/compression.rs index 5e179b76e..2afc09168 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -86,7 +86,7 @@ impl CompressionOptions { } } - pub(crate) fn get_file_options(&self) -> zip::write::FileOptions<'_, ()> { + pub(crate) fn get_file_options(&self) -> zip::write::FileOptions<'static, ()> { let method = if cfg!(feature = "faster-tests") { // Unlike users which can use the develop subcommand, the tests have to go through // packing a zip which pip than has to unpack. This makes this 2-3 times faster diff --git a/src/module_writer/wheel_writer.rs b/src/module_writer/wheel_writer.rs index 040baf37d..277f2e681 100644 --- a/src/module_writer/wheel_writer.rs +++ b/src/module_writer/wheel_writer.rs @@ -1,4 +1,3 @@ -use std::env; use std::io; use std::io::Read; use std::io::Write as _; @@ -7,15 +6,13 @@ use std::path::PathBuf; use anyhow::Context as _; use anyhow::Result; -use anyhow::anyhow; use fs_err::File; use ignore::overrides::Override; use normpath::PathExt as _; use tracing::debug; -use zip::DateTime; use zip::ZipWriter; +use zip::write::SimpleFileOptions; -use crate::CompressionOptions; use crate::Metadata24; use crate::project_layout::ProjectLayout; @@ -33,7 +30,7 @@ pub struct WheelWriter { wheel_path: PathBuf, file_tracker: FileTracker, excludes: Override, - compression: CompressionOptions, + file_options: SimpleFileOptions, } impl ModuleWriter for WheelWriter { @@ -57,15 +54,9 @@ impl ModuleWriter for WheelWriter { // The zip standard mandates using unix style paths let target = target.to_str().unwrap().replace('\\', "/"); - let mut options = self - .compression - .get_file_options() + let options = self + .file_options .unix_permissions(default_permission(executable)); - - if let Ok(mtime) = self.mtime() { - options = options.last_modified_time(mtime); - } - self.zip.start_file(target.clone(), options)?; let mut writer = StreamSha256::new(&mut self.zip); @@ -90,7 +81,7 @@ impl WheelWriter { metadata24: &Metadata24, tags: &[String], excludes: Override, - compression: CompressionOptions, + file_options: SimpleFileOptions, ) -> Result { let wheel_path = wheel_dir.join(format!( "{}-{}-{}.whl", @@ -108,7 +99,7 @@ impl WheelWriter { wheel_path, file_tracker: FileTracker::default(), excludes, - compression, + file_options, }; write_dist_info(&mut builder, pyproject_dir, metadata24, tags)?; @@ -152,29 +143,11 @@ impl WheelWriter { self.excludes.matched(path.as_ref(), false).is_whitelist() } - /// 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 { - let epoch: i64 = env::var("SOURCE_DATE_EPOCH")?.parse()?; - let dt = time::OffsetDateTime::from_unix_timestamp(epoch)?; - let min_dt = time::Date::from_calendar_date(1980, time::Month::January, 1) - .unwrap() - .midnight() - .assume_offset(time::UtcOffset::UTC); - let dt = dt.max(min_dt); - - let dt = DateTime::try_from(dt).map_err(|_| anyhow!("Failed to build zip DateTime"))?; - Ok(dt) - } - /// Creates the record file and finishes the zip pub fn finish(mut self) -> Result { - let mut options = self.compression.get_file_options(); - let mtime = self.mtime().ok(); - if let Some(mtime) = mtime { - options = options.last_modified_time(mtime); - } - + let options = self + .file_options + .unix_permissions(default_permission(false)); let record_filename = self.record_file.to_str().unwrap().replace('\\', "/"); debug!("Adding {}", record_filename); self.zip.start_file(&record_filename, options)?; @@ -212,6 +185,10 @@ mod tests { fn wheel_writer_no_compression() -> Result<(), Box> { let metadata = Metadata24::new("dummy".to_string(), Version::new([1, 0])); let tmp_dir = TempDir::new()?; + let compression_options = CompressionOptions { + compression_method: CompressionMethod::Stored, + ..Default::default() + }; let writer = WheelWriter::new( "no compression", @@ -220,10 +197,7 @@ mod tests { &metadata, &[], Override::empty(), - CompressionOptions { - compression_method: CompressionMethod::Stored, - ..Default::default() - }, + compression_options.get_file_options(), )?; writer.finish()?;