Skip to content

Commit

Permalink
mesonpy: include uncommited changes in sdist
Browse files Browse the repository at this point in the history
Fixes #53

Signed-off-by: Filipe Laíns <[email protected]>
  • Loading branch information
FFY00 committed May 26, 2022
1 parent 931a06f commit e4a9c3d
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 44 deletions.
47 changes: 35 additions & 12 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import collections
import contextlib
import functools
import io
import itertools
import json
import os
Expand All @@ -23,6 +24,7 @@
import subprocess
import sys
import sysconfig
import tarfile
import tempfile
import textwrap
import typing
Expand Down Expand Up @@ -538,7 +540,8 @@ def version(self) -> str:
assert isinstance(version, str)
return version

@property
@property # type: ignore[misc]
@functools.lru_cache(maxsize=1)
def metadata(self) -> bytes: # noqa: C901
"""Project metadata."""
# the rest of the keys are only available when using PEP 621 metadata
Expand Down Expand Up @@ -718,26 +721,46 @@ def _select_abi_tag(self) -> Optional[mesonpy._tags.Tag]: # noqa: C901
def sdist(self, directory: Path) -> pathlib.Path:
"""Generates a sdist (source distribution) in the specified directory."""
# generate meson dist file
self._meson('dist', '--no-tests', '--formats', 'gztar')
self._meson('dist', '--allow-dirty', '--no-tests', '--formats', 'gztar')

# move meson dist file to output path
dist_name = f'{self.name}-{self.version}'
meson_dist_name = f'{self._meson_name}-{self._meson_version}'
meson_dist = pathlib.Path(self._build_dir, 'meson-dist', f'{meson_dist_name}.tar.gz')
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')

with mesonpy._util.edit_targz(meson_dist, sdist) as content:
# rename from meson name to sdist name if necessary
if dist_name != meson_dist_name:
shutil.move(str(content / meson_dist_name), str(content / dist_name))
with tarfile.open(meson_dist_path, 'r:gz') as meson_dist, mesonpy._util.create_targz(sdist) as (tar, mtime):
for member in meson_dist.getmembers():
# skip the generated meson native file
if member.name == f'{meson_dist_name}/.mesonpy-native-file.ini':
continue

# 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 not path.is_file():
continue

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

# rewrite the size
member.size = os.path.getsize(path)

# remove .mesonpy-native-file.ini if it exists
native_file = content / meson_dist_name / '.mesonpy-native-file.ini'
if native_file.exists():
native_file.unlink()
with path.open('rb') as f:
tar.addfile(member, fileobj=f)

# add PKG-INFO to dist file to make it a sdist
content.joinpath(dist_name, 'PKG-INFO').write_bytes(self.metadata)
pkginfo_info = tarfile.TarInfo(f'{dist_name}/PKG-INFO')
if mtime:
pkginfo_info.mtime = mtime
pkginfo_info.size = len(self.metadata) # type: ignore[arg-type]
tar.addfile(pkginfo_info, fileobj=io.BytesIO(self.metadata)) # type: ignore[arg-type]

return sdist

Expand Down
51 changes: 20 additions & 31 deletions mesonpy/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
import contextlib
import gzip
import os
import pathlib
import sys
import tarfile
import tempfile
import typing

from typing import IO
from typing import IO, Optional, Tuple

from mesonpy._compat import Iterable, Iterator, Path

Expand Down Expand Up @@ -41,35 +39,26 @@ def add_ld_path(paths: Iterable[str]) -> Iterator[None]:


@contextlib.contextmanager
def edit_targz(path: Path, new_path: Path) -> Iterator[pathlib.Path]:
def create_targz(path: Path) -> Iterator[Tuple[tarfile.TarFile, Optional[int]]]:
"""Opens a .tar.gz file in the file system for edition.."""
with tempfile.TemporaryDirectory(prefix='mesonpy-') as tmpdir:
workdir = pathlib.Path(tmpdir)
with tarfile.open(path, 'r:gz') as tar:
tar.extractall(tmpdir)

yield workdir

# reproducibility
source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH')
mtime = int(source_date_epoch) if source_date_epoch else None

file = typing.cast(IO[bytes], gzip.GzipFile(
os.path.join(path, new_path),
mode='wb',
mtime=mtime,
))
with contextlib.closing(file), tarfile.TarFile(
mode='w',
fileobj=file,
format=tarfile.PAX_FORMAT, # changed in 3.8 to GNU
) as tar:
for path in workdir.rglob('*'):
if path.is_file():
tar.add(
name=path,
arcname=path.relative_to(workdir).as_posix(),
)

# reproducibility
source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH')
mtime = int(source_date_epoch) if source_date_epoch else None

file = typing.cast(IO[bytes], gzip.GzipFile(
os.path.join(path, path),
mode='wb',
mtime=mtime,
))
tar = tarfile.TarFile(
mode='w',
fileobj=file,
format=tarfile.PAX_FORMAT, # changed in 3.8 to GNU
)

with contextlib.closing(file), tar:
yield tar, mtime


class CLICounter:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
build-backend = 'mesonpy'
backend-path = ['.']
requires = [
'meson>=0.60.0',
'meson>=0.62.0',
'ninja',
'pep621>=0.3.0',
'tomli>=1.0.0',
Expand Down
14 changes: 14 additions & 0 deletions tests/packages/subdirs/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
project(
'subdirs',
version: '1.0.0',
)

py_mod = import('python')
py = py_mod.find_installation()

py.install_sources([
'subdirs' / '__init__.py',
'subdirs' / 'a' / '__init__.py',
'subdirs' / 'a' / 'b' / 'c.py',
'subdirs' / 'b' / 'c.py',
])
3 changes: 3 additions & 0 deletions tests/packages/subdirs/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']
Empty file.
Empty file.
Empty file.
Empty file.
51 changes: 51 additions & 0 deletions tests/test_sdist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# SPDX-License-Identifier: EUPL-1.2

import os
import tarfile
import textwrap

import mesonpy

from .conftest import in_git_repo_context


def test_contents(sdist_library):
Expand All @@ -14,3 +20,48 @@ def test_contents(sdist_library):
'library-1.0.0/pyproject.toml',
'library-1.0.0/PKG-INFO',
}


def test_contents_subdirs(sdist_subdirs):
sdist = tarfile.open(sdist_subdirs, 'r:gz')

assert set(sdist.getnames()) == {
'subdirs-1.0.0/PKG-INFO',
'subdirs-1.0.0/meson.build',
'subdirs-1.0.0/pyproject.toml',
'subdirs-1.0.0/subdirs/__init__.py',
'subdirs-1.0.0/subdirs/a/__init__.py',
'subdirs-1.0.0/subdirs/a/b/c.py',
'subdirs-1.0.0/subdirs/b/c.py',
}


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

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

try:
with in_git_repo_context(), open('pure.py', 'w') as f, open('crap', 'x'):
f.write(new_data)

sdist_path = mesonpy.build_sdist(os.fspath(tmpdir))
finally:
with open('pure.py', 'w') as f:
f.write(old_data)
os.unlink('crap')

sdist = tarfile.open(tmpdir / sdist_path, 'r:gz')

assert set(sdist.getnames()) == {
'pure-1.0.0/PKG-INFO',
'pure-1.0.0/meson.build',
'pure-1.0.0/pure.py',
'pure-1.0.0/pyproject.toml',
}
read_data = sdist.extractfile('pure-1.0.0/pure.py').read().replace(b'\r\n', b'\n')
assert read_data == new_data.encode()

0 comments on commit e4a9c3d

Please sign in to comment.