Skip to content

Commit

Permalink
Add support for SOURCE_DATE_EPOCH
Browse files Browse the repository at this point in the history
Setting `SOURCE_DATE_EPOCH` in the environment will set the time stamps
of all files and directories in the produced wheels to the specified
value.
  • Loading branch information
lkollar committed Nov 29, 2021
1 parent b1d354a commit 8e111ef
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 6 deletions.
22 changes: 17 additions & 5 deletions src/auditwheel/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import os
import subprocess
import zipfile
from typing import Any, Iterable, List
from datetime import datetime, timezone
from typing import Any, Iterable, List, Optional


def unique_by_index(sequence: Iterable[Any]) -> List[Any]:
Expand Down Expand Up @@ -49,7 +50,7 @@ def zip2dir(zip_fname: str, out_dir: str) -> None:
os.chmod(extracted_path, attr)


def dir2zip(in_dir: str, zip_fname: str) -> None:
def dir2zip(in_dir: str, zip_fname: str, date_time: Optional[datetime] = None) -> None:
"""Make a zip file `zip_fname` with contents of directory `in_dir`
The recorded filenames are relative to `in_dir`, so doing a standard zip
Expand All @@ -62,17 +63,28 @@ def dir2zip(in_dir: str, zip_fname: str) -> None:
Directory path containing files to go in the zip archive
zip_fname : str
Filename of zip archive to write
date_time : Optional[datetime]
Time stamp to set on each file in the archive
"""
if date_time is None:
st = os.stat(in_dir)
date_time = datetime.fromtimestamp(st.st_mtime, tz=timezone.utc)
date_time_args = date_time.timetuple()[:6]
with zipfile.ZipFile(zip_fname, "w", compression=zipfile.ZIP_DEFLATED) as z:
for root, dirs, files in os.walk(in_dir):
for dir in dirs:
dname = os.path.join(root, dir)
out_dname = os.path.relpath(dname, in_dir)
z.write(dname, out_dname)
out_dname = os.path.relpath(dname, in_dir) + "/"
zinfo = zipfile.ZipInfo(out_dname, date_time=date_time_args)
zinfo.external_attr = os.stat(dname).st_mode << 16
z.writestr(zinfo, "")
for file in files:
fname = os.path.join(root, file)
out_fname = os.path.relpath(fname, in_dir)
z.write(fname, out_fname)
zinfo = zipfile.ZipInfo(out_fname, date_time=date_time_args)
zinfo.external_attr = os.stat(fname).st_mode << 16
with open(fname, "rb") as fp:
z.writestr(zinfo, fp.read())


def tarbz2todir(tarbz2_fname: str, out_dir: str) -> None:
Expand Down
7 changes: 6 additions & 1 deletion src/auditwheel/wheeltools.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import logging
import os
from base64 import urlsafe_b64encode
from datetime import datetime, timezone
from itertools import product
from os.path import abspath, basename, dirname, exists
from os.path import join as pjoin
Expand Down Expand Up @@ -127,7 +128,11 @@ def __exit__(
) -> None:
if self.out_wheel is not None:
rewrite_record(self.name)
dir2zip(self.name, self.out_wheel)
date_time = None
timestamp = os.environ.get("SOURCE_DATE_EPOCH")
if timestamp:
date_time = datetime.fromtimestamp(int(timestamp), tz=timezone.utc)
dir2zip(self.name, self.out_wheel, date_time)
return super().__exit__(exc, value, tb)


Expand Down
47 changes: 47 additions & 0 deletions tests/integration/test_bundled_wheels.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import platform
import subprocess
import sys
import zipfile
from datetime import datetime, timezone
from pathlib import Path

import pytest
Expand Down Expand Up @@ -30,3 +34,46 @@ def test_analyze_wheel_abi_pyfpe():
assert (
winfo.pyfpe_tag == "linux_x86_64"
) # but for having the pyfpe reference, it gets just linux


@pytest.mark.skipif(platform.machine() != "x86_64", reason="only checked on x86_64")
def test_wheel_source_date_epoch(tmp_path):
wheel_build_path = tmp_path / "wheel"
subprocess.run(
[
sys.executable,
"-m",
"pip",
"wheel",
"--no-deps",
"-w",
wheel_build_path,
HERE / "sample_extension",
],
check=True,
)

wheel_path, *_ = list(wheel_build_path.glob("*.whl"))
wheel_output_path = tmp_path / "out"
subprocess.run(
[
sys.executable,
"-m",
"auditwheel",
"repair",
str(wheel_path),
"-w",
str(wheel_output_path),
],
env={
"SOURCE_DATE_EPOCH": "650203200",
},
check=True,
)

output_wheel, *_ = list(wheel_output_path.glob("*.whl"))
with zipfile.ZipFile(output_wheel) as wheel_file:
for file in wheel_file.infolist():
assert (
datetime(*file.date_time, tzinfo=timezone.utc).timestamp() == 650203200
)

0 comments on commit 8e111ef

Please sign in to comment.