Skip to content

Commit 49dd86e

Browse files
committed
BUG: fix mtime of sdist archive members
Set the mtime of all archive members to the mtime set by meson dist, also for files that may have been locally modified. Use the current time for the PKG-INFO file. Drop support for the $SOURCE_DATE_EPOCH environment variable when creating the sdist. This environment variable is for meant to be used as a work-around for build tools that produce non-reproducible binary packages. It does not have a standardized meaning for tools producing source distributions. See https://reproducible-builds.org/docs/source-date-epoch/ Fixes mesonbuild#450.
1 parent 2e99c7b commit 49dd86e

File tree

8 files changed

+100
-16
lines changed

8 files changed

+100
-16
lines changed

mesonpy/__init__.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import tarfile
3232
import tempfile
3333
import textwrap
34+
import time
3435
import typing
3536
import warnings
3637

@@ -919,7 +920,7 @@ def sdist(self, directory: Path) -> pathlib.Path:
919920
meson_dist_path = pathlib.Path(self._build_dir, 'meson-dist', f'{meson_dist_name}.tar.gz')
920921
sdist = pathlib.Path(directory, f'{dist_name}.tar.gz')
921922

922-
with tarfile.open(meson_dist_path, 'r:gz') as meson_dist, mesonpy._util.create_targz(sdist) as (tar, mtime):
923+
with tarfile.open(meson_dist_path, 'r:gz') as meson_dist, mesonpy._util.create_targz(sdist) as tar:
923924
for member in meson_dist.getmembers():
924925
# calculate the file path in the source directory
925926
assert member.name, member.name
@@ -946,6 +947,7 @@ def sdist(self, directory: Path) -> pathlib.Path:
946947

947948
info = tarfile.TarInfo(member.name)
948949
file_stat = os.stat(path)
950+
info.mtime = member.mtime
949951
info.size = file_stat.st_size
950952
info.mode = int(oct(file_stat.st_mode)[-3:], 8)
951953

@@ -961,8 +963,7 @@ def sdist(self, directory: Path) -> pathlib.Path:
961963

962964
# add PKG-INFO to dist file to make it a sdist
963965
pkginfo_info = tarfile.TarInfo(f'{dist_name}/PKG-INFO')
964-
if mtime:
965-
pkginfo_info.mtime = mtime
966+
pkginfo_info.mtime = time.time() # type: ignore[assignment]
966967
pkginfo_info.size = len(self.metadata)
967968
tar.addfile(pkginfo_info, fileobj=io.BytesIO(self.metadata))
968969

mesonpy/_util.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,13 @@ def add_ld_path(paths: Iterable[str]) -> Iterator[None]:
4848

4949

5050
@contextlib.contextmanager
51-
def create_targz(path: Path) -> Iterator[Tuple[tarfile.TarFile, Optional[int]]]:
51+
def create_targz(path: Path) -> Iterator[tarfile.TarFile]:
5252
"""Opens a .tar.gz file in the file system for edition.."""
5353

54-
# reproducibility
55-
source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH')
56-
mtime = int(source_date_epoch) if source_date_epoch else None
57-
5854
os.makedirs(os.path.dirname(path), exist_ok=True)
5955
file = typing.cast(IO[bytes], gzip.GzipFile(
6056
path,
6157
mode='wb',
62-
mtime=mtime,
6358
))
6459
tar = tarfile.TarFile(
6560
mode='w',
@@ -68,7 +63,7 @@ def create_targz(path: Path) -> Iterator[Tuple[tarfile.TarFile, Optional[int]]]:
6863
)
6964

7065
with contextlib.closing(file), tar:
71-
yield tar, mtime
66+
yield tar
7267

7368

7469
class CLICounter:
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/Users/daniele/src/meson-python/tests/packages/limited-api:
2+
drwxr-xr-x 10 daniele staff 320 Aug 15 20:19 .
3+
drwxr-xr-x 33 daniele staff 1056 Aug 15 19:03 ..
4+
-rw-r--r-- 1 daniele staff 283 Aug 15 20:19 meson.build
5+
-rw-r--r-- 1 daniele staff 42 Aug 15 19:03 meson.build~
6+
-rw-r--r-- 1 daniele staff 583 Aug 15 20:16 module.c
7+
-rw-r--r-- 1 daniele staff 500 Aug 15 20:06 module.c~
8+
-rw-r--r-- 1 daniele staff 44 Aug 15 20:19 meson.options
9+
-rw-r--r-- 1 daniele staff 27 Aug 15 20:19 options.meson~
10+
-rw-r--r-- 1 daniele staff 202 Aug 15 19:04 pyproject.toml
11+
-rw-r--r-- 1 daniele staff 202 Aug 15 19:04 pyproject.toml~
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
project('limoted-api', 'c', version: '1.0.0')
2+
3+
py = import('python').find_installation()
4+
5+
py.extension_module(
6+
'module',
7+
'module.c',
8+
limited_api: '3.7',
9+
install: true,
10+
)
11+
12+
if get_option('extra')
13+
py.extension_module(
14+
'extra',
15+
'module.c',
16+
install: true,
17+
)
18+
endif
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
option('extra', type: 'boolean', value: false)

tests/packages/limited-api/module.c

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-FileCopyrightText: 2021 The meson-python developers
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include <Python.h>
6+
7+
static PyObject* add(PyObject *self, PyObject *args) {
8+
int a, b;
9+
10+
if (!PyArg_ParseTuple(args, "ii", &a, &b))
11+
return NULL;
12+
13+
return PyLong_FromLong(a + b);
14+
}
15+
16+
static PyMethodDef methods[] = {
17+
{"add", add, METH_VARARGS, NULL},
18+
{NULL, NULL, 0, NULL},
19+
};
20+
21+
static struct PyModuleDef module = {
22+
PyModuleDef_HEAD_INIT,
23+
"plat",
24+
NULL,
25+
-1,
26+
methods,
27+
};
28+
29+
PyMODINIT_FUNC PyInit_module(void) {
30+
return PyModule_Create(&module);
31+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-FileCopyrightText: 2023 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
[build-system]
6+
build-backend = 'mesonpy'
7+
requires = ['meson-python']
8+
9+
[tool.meson-python]
10+
limited-api = true

tests/test_sdist.py

+23-6
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
def test_contents(sdist_library):
1919
with tarfile.open(sdist_library, 'r:gz') as sdist:
20-
names = set(sdist.getnames())
20+
names = {member.name for member in sdist.getmembers()}
21+
mtimes = {member.mtime for member in sdist.getmembers()}
2122

2223
assert names == {
2324
'library-1.0.0/example.c',
@@ -28,10 +29,14 @@ def test_contents(sdist_library):
2829
'library-1.0.0/PKG-INFO',
2930
}
3031

32+
# All the archive members have a valid mtime.
33+
assert 0 not in mtimes
34+
3135

3236
def test_contents_subdirs(sdist_subdirs):
3337
with tarfile.open(sdist_subdirs, 'r:gz') as sdist:
34-
names = set(sdist.getnames())
38+
names = {member.name for member in sdist.getmembers()}
39+
mtimes = {member.mtime for member in sdist.getmembers()}
3540

3641
assert names == {
3742
'subdirs-1.0.0/PKG-INFO',
@@ -43,6 +48,9 @@ def test_contents_subdirs(sdist_subdirs):
4348
'subdirs-1.0.0/subdirs/b/c.py',
4449
}
4550

51+
# All the archive members have a valid mtime.
52+
assert 0 not in mtimes
53+
4654

4755
def test_contents_unstaged(package_pure, tmp_path):
4856
new_data = textwrap.dedent('''
@@ -65,7 +73,8 @@ def bar():
6573
os.unlink('crap')
6674

6775
with tarfile.open(tmp_path / sdist_path, 'r:gz') as sdist:
68-
names = set(sdist.getnames())
76+
names = {member.name for member in sdist.getmembers()}
77+
mtimes = {member.mtime for member in sdist.getmembers()}
6978
read_data = sdist.extractfile('pure-1.0.0/pure.py').read().replace(b'\r\n', b'\n')
7079

7180
assert names == {
@@ -76,6 +85,9 @@ def bar():
7685
}
7786
assert read_data == new_data.encode()
7887

88+
# All the archive members have a valid mtime.
89+
assert 0 not in mtimes
90+
7991

8092
@pytest.mark.skipif(sys.platform in {'win32', 'cygwin'}, reason='Platform does not support executable bit')
8193
def test_executable_bit(sdist_executable_bit):
@@ -94,7 +106,11 @@ def test_executable_bit(sdist_executable_bit):
94106

95107

96108
def test_generated_files(sdist_generated_files):
97-
expected = {
109+
with tarfile.open(sdist_generated_files, 'r:gz') as sdist:
110+
names = {member.name for member in sdist.getmembers()}
111+
mtimes = {member.mtime for member in sdist.getmembers()}
112+
113+
assert names == {
98114
'executable_bit-1.0.0/PKG-INFO',
99115
'executable_bit-1.0.0/example-script.py',
100116
'executable_bit-1.0.0/example.c',
@@ -104,5 +120,6 @@ def test_generated_files(sdist_generated_files):
104120
'executable_bit-1.0.0/_version_meson.py',
105121
'executable_bit-1.0.0/generate_version.py',
106122
}
107-
with tarfile.open(sdist_generated_files, 'r:gz') as sdist:
108-
assert {tar.name for tar in sdist.getmembers()} == expected
123+
124+
# All the archive members have a valid mtime.
125+
assert 0 not in mtimes

0 commit comments

Comments
 (0)