Skip to content

Commit

Permalink
BUG: fix handling of files installed via install_subdir
Browse files Browse the repository at this point in the history
Install files in the right location in case of nested directory
structures, and implement handling of exclude_directories and
exclude_files. The latter requires Meson 1.1.0 or later.

Fixes mesonbuild#317.
  • Loading branch information
dnicolodi committed Sep 1, 2023
1 parent 7aea380 commit 216632e
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 48 deletions.
106 changes: 58 additions & 48 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,11 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[T
wheel_files: DefaultDict[str, List[Tuple[pathlib.Path, str]]] = collections.defaultdict(list)
packages: Dict[str, str] = {}

for group in sources.values():
for key, group in sources.items():
for src, target in group.items():
destination = pathlib.Path(target['destination'])
anchor = destination.parts[0]
dst = pathlib.Path(*destination.parts[1:])

path = _INSTALLATION_PATH_MAP.get(anchor)
if path is None:
Expand All @@ -170,7 +171,28 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[T
f'{this!r} and {that!r}, a "pure: false" argument may be missing in meson.build. '
f'It is recommended to set it in "import(\'python\').find_installation()"')

wheel_files[path].append((pathlib.Path(*destination.parts[1:]), src))
if key == 'install_subdirs':
assert os.path.isdir(src)
exclude_files = {os.path.normpath(x) for x in target.get('exclude_files', [])}
exclude_dirs = {os.path.normpath(x) for x in target.get('exclude_dirs', [])}
for root, dirnames, filenames in os.walk(src):
for name in dirnames.copy():
dirsrc = os.path.join(root, name)
relpath = os.path.relpath(dirsrc, src)
if relpath in exclude_dirs:
dirnames.remove(name)
# sort to process directories determninistically
dirnames.sort()
for name in sorted(filenames):
filesrc = os.path.join(root, name)
relpath = os.path.relpath(filesrc, src)
if relpath in exclude_files:
continue
filedst = dst / relpath
wheel_files[path].append((filedst, filesrc))
else:
wheel_files[path].append((dst, src))

return wheel_files


Expand Down Expand Up @@ -422,7 +444,7 @@ def _is_native(self, file: Union[str, pathlib.Path]) -> bool:
return True
return False

def _install_path( # noqa: C901
def _install_path(
self,
wheel_file: mesonpy._wheelfile.WheelFile,
origin: Path,
Expand All @@ -436,51 +458,39 @@ def _install_path( # noqa: C901
"""
location = destination.as_posix()

# fix file
if os.path.isdir(origin):
for root, dirnames, filenames in os.walk(str(origin)):
# Sort the directory names so that `os.walk` will walk them in a
# defined order on the next iteration.
dirnames.sort()
for name in sorted(filenames):
path = os.path.normpath(os.path.join(root, name))
if os.path.isfile(path):
arcname = os.path.join(destination, os.path.relpath(path, origin).replace(os.path.sep, '/'))
wheel_file.write(path, arcname)
else:
if self._has_internal_libs:
if platform.system() == 'Linux' or platform.system() == 'Darwin':
# add .mesonpy.libs to the RPATH of ELF files
if self._is_native(os.fspath(origin)):
# copy ELF to our working directory to avoid Meson having to regenerate the file
new_origin = self._libs_build_dir / pathlib.Path(origin).relative_to(self._build_dir)
os.makedirs(new_origin.parent, exist_ok=True)
shutil.copy2(origin, new_origin)
origin = new_origin
# add our in-wheel libs folder to the RPATH
if platform.system() == 'Linux':
elf = mesonpy._elf.ELF(origin)
libdir_path = \
f'$ORIGIN/{os.path.relpath(f".{self._project.name}.mesonpy.libs", destination.parent)}'
if libdir_path not in elf.rpath:
elf.rpath = [*elf.rpath, libdir_path]
elif platform.system() == 'Darwin':
dylib = mesonpy._dylib.Dylib(origin)
libdir_path = \
f'@loader_path/{os.path.relpath(f".{self._project.name}.mesonpy.libs", destination.parent)}'
if libdir_path not in dylib.rpath:
dylib.rpath = [*dylib.rpath, libdir_path]
else:
# Internal libraries are currently unsupported on this platform
raise NotImplementedError("Bundling libraries in wheel is not supported on platform '{}'"
.format(platform.system()))

try:
wheel_file.write(origin, location)
except FileNotFoundError:
# work around for Meson bug, see https://github.com/mesonbuild/meson/pull/11655
if not os.fspath(origin).endswith('.pdb'):
raise
if self._has_internal_libs:
if platform.system() == 'Linux' or platform.system() == 'Darwin':
# add .mesonpy.libs to the RPATH of ELF files
if self._is_native(os.fspath(origin)):
# copy ELF to our working directory to avoid Meson having to regenerate the file
new_origin = self._libs_build_dir / pathlib.Path(origin).relative_to(self._build_dir)
os.makedirs(new_origin.parent, exist_ok=True)
shutil.copy2(origin, new_origin)
origin = new_origin
# add our in-wheel libs folder to the RPATH
if platform.system() == 'Linux':
elf = mesonpy._elf.ELF(origin)
libdir_path = \
f'$ORIGIN/{os.path.relpath(f".{self._project.name}.mesonpy.libs", destination.parent)}'
if libdir_path not in elf.rpath:
elf.rpath = [*elf.rpath, libdir_path]
elif platform.system() == 'Darwin':
dylib = mesonpy._dylib.Dylib(origin)
libdir_path = \
f'@loader_path/{os.path.relpath(f".{self._project.name}.mesonpy.libs", destination.parent)}'
if libdir_path not in dylib.rpath:
dylib.rpath = [*dylib.rpath, libdir_path]
else:
# Internal libraries are currently unsupported on this platform
raise NotImplementedError("Bundling libraries in wheel is not supported on platform '{}'"
.format(platform.system()))

try:
wheel_file.write(origin, location)
except FileNotFoundError:
# work around for Meson bug, see https://github.com/mesonbuild/meson/pull/11655
if not os.fspath(origin).endswith('.pdb'):
raise

def _wheel_write_metadata(self, whl: mesonpy._wheelfile.WheelFile) -> None:
# add metadata
Expand Down
27 changes: 27 additions & 0 deletions tests/packages/install-subdir/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT

project('install-subdir', version: '1.0.0')

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

install_subdir(
'subdir',
exclude_files: 'excluded.py',
exclude_directories: 'excluded',
install_dir: py.get_install_dir(pure: false),
)

install_subdir(
'strip',
strip_directory: true,
exclude_files: 'excluded.py',
install_dir: py.get_install_dir(pure: false) / 'test',
)

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

[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']
3 changes: 3 additions & 0 deletions tests/packages/install-subdir/strip/excluded.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT
3 changes: 3 additions & 0 deletions tests/packages/install-subdir/strip/module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT
3 changes: 3 additions & 0 deletions tests/packages/install-subdir/subdir/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT
3 changes: 3 additions & 0 deletions tests/packages/install-subdir/subdir/excluded.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT
3 changes: 3 additions & 0 deletions tests/packages/install-subdir/subdir/excluded/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT
3 changes: 3 additions & 0 deletions tests/packages/install-subdir/subdir/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT
19 changes: 19 additions & 0 deletions tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,22 @@ def test_limited_api_disabled(package_limited_api, tmp_path):
assert name.group('pyver') == INTERPRETER
assert name.group('abi') == ABI
assert name.group('plat') == PLATFORM


def test_install_subdir(wheel_install_subdir):
artifact = wheel.wheelfile.WheelFile(wheel_install_subdir)
# 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) == {
'install_subdir-1.0.0.dist-info/METADATA',
'install_subdir-1.0.0.dist-info/RECORD',
'install_subdir-1.0.0.dist-info/WHEEL',
'subdir/__init__.py',
'subdir/test.py',
'test/module.py',
'nested/deep/deep.py',
'nested/nested.py',
}

0 comments on commit 216632e

Please sign in to comment.