Skip to content

Commit

Permalink
Preserve symlink contents in sdists
Browse files Browse the repository at this point in the history
Wheels do not support symlinks (as they are ZIP files), but tarballs do.
For consistent results however, expand those symlinks to the files they
reference.

This is consistent with 0.16.0, and fixes this regression in 0.17.0.
  • Loading branch information
QuLogic committed Dec 13, 2024
1 parent 91d64d1 commit b1eb285
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 0 deletions.
12 changes: 12 additions & 0 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import argparse
import collections
import contextlib
import copy
import difflib
import functools
import importlib.machinery
Expand Down Expand Up @@ -854,6 +855,17 @@ def sdist(self, directory: Path) -> pathlib.Path:

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():
# Wheels do not support links, though sdist tarballs could. For portability, reduce these to regular files.
if member.islnk() or member.issym():
# Symlinks are relative to member directory, but hard links are relative to tarball root.
path = member.name.rsplit('/', 1)[0] + '/' if member.issym() else ''
orig = meson_dist.getmember(path + member.linkname)
member = copy.copy(member)
member.mode = orig.mode
member.mtime = orig.mtime
member.size = orig.size
member.type = tarfile.REGTYPE

if member.isfile():
file = meson_dist.extractfile(member.name)

Expand Down
12 changes: 12 additions & 0 deletions tests/packages/symlinks/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: 2024 The meson-python developers
#
# SPDX-License-Identifier: MIT

project('symlinks', version: '1.0.0')

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

install_subdir(
'subdir',
install_dir: py.get_install_dir(pure: false),
)
7 changes: 7 additions & 0 deletions tests/packages/symlinks/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2024 The meson-python developers
#
# SPDX-License-Identifier: MIT

[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']
3 changes: 3 additions & 0 deletions tests/packages/symlinks/subdir/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2024 The meson-python developers
#
# SPDX-License-Identifier: MIT
1 change: 1 addition & 0 deletions tests/packages/symlinks/subdir/symlink.py
3 changes: 3 additions & 0 deletions tests/packages/symlinks/subdir/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2024 The meson-python developers
#
# SPDX-License-Identifier: MIT
27 changes: 27 additions & 0 deletions tests/test_sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,33 @@ def test_contents_subdirs(sdist_subdirs):
assert 0 not in mtimes


def test_contents_symlinks(sdist_symlinks):
with tarfile.open(sdist_symlinks, 'r:gz') as sdist:
names = {member.name for member in sdist.getmembers()}
mtimes = {member.mtime for member in sdist.getmembers()}

orig_info = sdist.getmember('symlinks-1.0.0/subdir/test.py')
symlink_info = sdist.getmember('symlinks-1.0.0/subdir/symlink.py')
assert orig_info.mode == symlink_info.mode
assert orig_info.mtime == symlink_info.mtime
assert orig_info.size == symlink_info.size
orig = sdist.extractfile('symlinks-1.0.0/subdir/test.py')
symlink = sdist.extractfile('symlinks-1.0.0/subdir/symlink.py')
assert orig.read() == symlink.read()

assert names == {
'symlinks-1.0.0/PKG-INFO',
'symlinks-1.0.0/meson.build',
'symlinks-1.0.0/pyproject.toml',
'symlinks-1.0.0/subdir/__init__.py',
'symlinks-1.0.0/subdir/test.py',
'symlinks-1.0.0/subdir/symlink.py',
}

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


def test_contents_unstaged(package_pure, tmp_path):
new = textwrap.dedent('''
def bar():
Expand Down
17 changes: 17 additions & 0 deletions tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,23 @@ def test_install_subdir(wheel_install_subdir):
}


def test_install_symlink(wheel_symlinks):
artifact = wheel.wheelfile.WheelFile(wheel_symlinks)
# Handling of the exclude_files and exclude_directories requires
# Meson 1.1.0, see https://github.com/mesonbuild/meson/pull/11432.
# Run the test anyway to ensure that meson-python can produce a
# wheel also for older versions of Meson.
if MESON_VERSION >= (1, 1, 99):
assert wheel_contents(artifact) == {
'symlinks-1.0.0.dist-info/METADATA',
'symlinks-1.0.0.dist-info/RECORD',
'symlinks-1.0.0.dist-info/WHEEL',
'subdir/__init__.py',
'subdir/test.py',
'subdir/symlink.py',
}


def test_vendored_meson(wheel_vendored_meson):
# This test will error if the vendored meson.py wrapper script in
# the test package isn't used.
Expand Down

0 comments on commit b1eb285

Please sign in to comment.