Skip to content

Commit

Permalink
ENH: do not include uncommitted changes in the sdist
Browse files Browse the repository at this point in the history
Including uncommitted changes in the sdist was implemented in mesonbuild#58
after the discussion in mesonbuild#53. However, the current behavior for which
uncommitted changes to files under version control are included but
not other files, is a hard to justify surprising half measure.

After careful analysis, it has been determined that none of the use
cases for this feature is still valid. Removing it makes the
implementation easier, and the behavior less surprising and easier to
document and explain.

While at it, modernize the associated test.
  • Loading branch information
dnicolodi committed Apr 23, 2024
1 parent d940353 commit 3334d6c
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 60 deletions.
67 changes: 22 additions & 45 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -860,64 +860,41 @@ def _meson_version(self) -> str:

def sdist(self, directory: Path) -> pathlib.Path:
"""Generates a sdist (source distribution) in the specified directory."""
# generate meson dist file
# Generate meson dist file.
self._run(self._meson + ['dist', '--allow-dirty', '--no-tests', '--formats', 'gztar', *self._meson_args['dist']])

# move meson dist file to output path
dist_name = f'{self._metadata.distribution_name}-{self._metadata.version}'
meson_dist_name = f'{self._meson_name}-{self._meson_version}'
meson_dist_path = pathlib.Path(self._build_dir, 'meson-dist', f'{meson_dist_name}.tar.gz')
sdist = pathlib.Path(directory, f'{dist_name}.tar.gz')
sdist_path = pathlib.Path(directory, f'{dist_name}.tar.gz')

with tarfile.open(meson_dist_path, 'r:gz') as meson_dist, mesonpy._util.create_targz(sdist) as tar:
with tarfile.open(meson_dist_path, 'r:gz') as meson_dist, mesonpy._util.create_targz(sdist_path) as sdist:
for member in meson_dist.getmembers():
# calculate the file path in the source directory
assert member.name, member.name
member_parts = member.name.split('/')
if len(member_parts) <= 1:
continue
path = self._source_dir.joinpath(*member_parts[1:])
if member.isfile():
file = meson_dist.extractfile(member.name)

if not path.exists() and member.isfile():
# File doesn't exists on the source directory but exists on
# the Meson dist, so it is generated file, which we need to
# include.
# See https://mesonbuild.com/Reference-manual_builtin_meson.html#mesonadd_dist_script
# Rewrite the path to match the sdist distribution name.
stem = member.name.split('/', 1)[1]
member.name = '/'.join((dist_name, stem))

# MESON_DIST_ROOT could have a different base name
# than the actual sdist basename, so we need to rename here
file = meson_dist.extractfile(member.name)
member.name = str(pathlib.Path(dist_name, *member_parts[1:]).as_posix())
tar.addfile(member, file)
continue
# Reset owner and group to root:root. This mimics what
# 'git archive' does and makes the sdist reproducible upon
# being built by different users.
member.uname = member.gname = 'root'
member.uid = member.gid = 0

if not path.is_file():
continue
sdist.addfile(member, file)

info = tarfile.TarInfo(member.name)
file_stat = os.stat(path)
info.mtime = member.mtime
info.size = file_stat.st_size
info.mode = int(oct(file_stat.st_mode)[-3:], 8)

# rewrite the path if necessary, to match the sdist distribution name
if dist_name != meson_dist_name:
info.name = pathlib.Path(
dist_name,
path.relative_to(self._source_dir)
).as_posix()

with path.open('rb') as f:
tar.addfile(info, fileobj=f)

# add PKG-INFO to dist file to make it a sdist
pkginfo_info = tarfile.TarInfo(f'{dist_name}/PKG-INFO')
pkginfo_info.mtime = time.time() # type: ignore[assignment]
# Add 'PKG-INFO'.
member = tarfile.TarInfo(f'{dist_name}/PKG-INFO')
member.uid = member.gid = 0
member.uname = member.gname = 'root'
member.mtime = time.time()
metadata = bytes(self._metadata.as_rfc822())
pkginfo_info.size = len(metadata)
tar.addfile(pkginfo_info, fileobj=io.BytesIO(metadata))
member.size = len(metadata)
sdist.addfile(member, io.BytesIO(metadata))

return sdist
return sdist_path

def wheel(self, directory: Path) -> pathlib.Path:
"""Generates a wheel in the specified directory."""
Expand Down
29 changes: 14 additions & 15 deletions tests/test_sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# SPDX-License-Identifier: MIT

import os
import pathlib
import re
import stat
import sys
Expand Down Expand Up @@ -122,37 +123,35 @@ def test_contents_subdirs(sdist_subdirs):


def test_contents_unstaged(package_pure, tmp_path):
new_data = textwrap.dedent('''
def bar():
new = textwrap.dedent('''
def bar():
return 'foo'
''').strip()

with open('pure.py', 'r') as f:
old_data = f.read()

try:
with in_git_repo_context():
with open('pure.py', 'w') as f, open('crap', 'x'):
f.write(new_data)
old = pathlib.Path('pure.py').read_text()

with in_git_repo_context():
try:
pathlib.Path('pure.py').write_text(new)
pathlib.Path('other.py').touch()
sdist_path = mesonpy.build_sdist(os.fspath(tmp_path))
finally:
with open('pure.py', 'w') as f:
f.write(old_data)
os.unlink('crap')
finally:
pathlib.Path('pure.py').write_text(old)
pathlib.Path('other.py').unlink()

with tarfile.open(tmp_path / sdist_path, 'r:gz') as sdist:
names = {member.name for member in sdist.getmembers()}
mtimes = {member.mtime for member in sdist.getmembers()}
read_data = sdist.extractfile('pure-1.0.0/pure.py').read().replace(b'\r\n', b'\n')
data = sdist.extractfile('pure-1.0.0/pure.py').read().replace(b'\r\n', b'\n')

# Verify that uncommitted changes are not included in the sdist.
assert names == {
'pure-1.0.0/PKG-INFO',
'pure-1.0.0/meson.build',
'pure-1.0.0/pure.py',
'pure-1.0.0/pyproject.toml',
}
assert read_data == new_data.encode()
assert data == old.encode()

# All the archive members have a valid mtime.
assert 0 not in mtimes
Expand Down

0 comments on commit 3334d6c

Please sign in to comment.