diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cc48c21c..ba7b2214 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -91,6 +91,9 @@ jobs: - os: ubuntu-latest python: '3.12' meson: '~=1.5.0' + - os: ubuntu-latest + python: '3.13' + meson: '~=1.6.0' # Test with Meson master branch. - os: ubuntu-latest python: '3.12' diff --git a/docs/reference/meson-compatibility.rst b/docs/reference/meson-compatibility.rst index 6b339d60..dfa96432 100644 --- a/docs/reference/meson-compatibility.rst +++ b/docs/reference/meson-compatibility.rst @@ -53,6 +53,10 @@ versions. declared via the ``project()`` call in ``meson.build``. This also requires ``pyproject-metadata`` version 0.9.0 or later. + Meson 1.6.0 or later is also required for support for the + ``install_rpath`` argument to Meson functions declaring build rules + for object files. + Build front-ends by default build packages in an isolated Python environment where build dependencies are installed. Most often, unless a package or its build dependencies declare explicitly a version diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 51c6f0fb..c01ab1cd 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -14,6 +14,7 @@ import argparse import collections import contextlib +import dataclasses import difflib import functools import importlib.machinery @@ -109,9 +110,15 @@ class InvalidLicenseExpression(Exception): # type: ignore[no-redef] } -def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[Tuple[pathlib.Path, str]]]: +class Entry(typing.NamedTuple): + dst: pathlib.Path + src: str + rpath: Optional[str] = None + + +def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[Entry]]: """Map files to the wheel, organized by wheel installation directory.""" - wheel_files: DefaultDict[str, List[Tuple[pathlib.Path, str]]] = collections.defaultdict(list) + wheel_files: DefaultDict[str, List[Entry]] = collections.defaultdict(list) packages: Dict[str, str] = {} for key, group in sources.items(): @@ -129,7 +136,8 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[T other = packages.setdefault(package, path) if other != path: this = os.fspath(pathlib.Path(path, *destination.parts[1:])) - that = os.fspath(other / next(d for d, s in wheel_files[other] if d.parts[0] == destination.parts[1])) + module = next(entry.dst for entry in wheel_files[other] if entry.dst.parts[0] == destination.parts[1]) + that = os.fspath(other / module) raise BuildError( f'The {package} package is split between {path} and {other}: ' f'{this!r} and {that!r}, a "pure: false" argument may be missing in meson.build. ' @@ -152,9 +160,9 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[T if relpath in exclude_files: continue filedst = dst / relpath - wheel_files[path].append((filedst, filesrc)) + wheel_files[path].append(Entry(filedst, filesrc)) else: - wheel_files[path].append((dst, src)) + wheel_files[path].append(Entry(dst, src, target.get('install_rpath'))) return wheel_files @@ -301,20 +309,14 @@ def _is_native(file: Path) -> bool: return f.read(4) == b'\x7fELF' # ELF +@dataclasses.dataclass class _WheelBuilder(): """Helper class to build wheels from projects.""" - def __init__( - self, - metadata: Metadata, - manifest: Dict[str, List[Tuple[pathlib.Path, str]]], - limited_api: bool, - allow_windows_shared_libs: bool, - ) -> None: - self._metadata = metadata - self._manifest = manifest - self._limited_api = limited_api - self._allow_windows_shared_libs = allow_windows_shared_libs + _metadata: Metadata + _manifest: Dict[str, List[Entry]] + _limited_api: bool + _allow_windows_shared_libs: bool @property def _has_internal_libs(self) -> bool: @@ -330,8 +332,8 @@ def _pure(self) -> bool: """Whether the wheel is architecture independent""" if self._manifest['platlib'] or self._manifest['mesonpy-libs']: return False - for _, file in self._manifest['scripts']: - if _is_native(file): + for entry in self._manifest['scripts']: + if _is_native(entry.src): return False return True @@ -408,36 +410,36 @@ def _stable_abi(self) -> Optional[str]: # in {platlib} that look like extension modules, and raise # an exception if any of them has a Python version # specific extension filename suffix ABI tag. - for path, _ in self._manifest['platlib']: - match = _EXTENSION_SUFFIX_REGEX.match(path.name) + for entry in self._manifest['platlib']: + match = _EXTENSION_SUFFIX_REGEX.match(entry.dst.name) if match: abi = match.group('abi') if abi is not None and abi != 'abi3': raise BuildError( f'The package declares compatibility with Python limited API but extension ' - f'module {os.fspath(path)!r} is tagged for a specific Python version.') + f'module {os.fspath(entry.dst)!r} is tagged for a specific Python version.') return 'abi3' return None - def _install_path(self, wheel_file: mesonpy._wheelfile.WheelFile, origin: Path, destination: pathlib.Path) -> None: + def _install_path(self, wheel_file: mesonpy._wheelfile.WheelFile, + origin: Path, destination: pathlib.Path, rpath: Optional[str]) -> None: """Add a file to the wheel.""" - if self._has_internal_libs: - if _is_native(origin): - if sys.platform == 'win32' and not self._allow_windows_shared_libs: - raise NotImplementedError( - 'Loading shared libraries bundled in the Python wheel on Windows requires ' - 'setting the DLL load path or preloading. See the documentation for ' - 'the "tool.meson-python.allow-windows-internal-shared-libs" option.') - - # When an executable, libray, or Python extension module is + if _is_native(origin): + libspath = None + if self._has_internal_libs: + # When an executable, library, or Python extension module is # dynamically linked to a library built as part of the project, # Meson adds a library load path to it pointing to the build # directory, in the form of a relative RPATH entry. meson-python - # relocates the shared libraries to the $project.mesonpy.libs + # relocates the shared libraries to the ``..mesonpy.libs`` # folder. Rewrite the RPATH to point to that folder instead. libspath = os.path.relpath(self._libs_dir, destination.parent) - mesonpy._rpath.fix_rpath(origin, libspath) + + # Adjust RPATH: remove build RPATH added by meson, add an RPATH + # entries as per above, and add any ``install_rpath`` specified in + # meson.build + mesonpy._rpath.fix_rpath(origin, rpath, libspath) try: wheel_file.write(origin, destination.as_posix()) @@ -466,6 +468,13 @@ def _wheel_write_metadata(self, whl: mesonpy._wheelfile.WheelFile) -> None: whl.write(f, f'{self._distinfo_dir}/licenses/{pathlib.Path(f).as_posix()}') def build(self, directory: Path) -> pathlib.Path: + + if sys.platform == 'win32' and self._has_internal_libs and not self._allow_windows_shared_libs: + raise ConfigError( + 'Loading shared libraries bundled in the Python wheel on Windows requires ' + 'setting the DLL load path or preloading. See the documentation for ' + 'the "tool.meson-python.allow-windows-internal-shared-libs" option.') + wheel_file = pathlib.Path(directory, f'{self.name}.whl') with mesonpy._wheelfile.WheelFile(wheel_file, 'w') as whl: self._wheel_write_metadata(whl) @@ -475,7 +484,7 @@ def build(self, directory: Path) -> pathlib.Path: root = 'purelib' if self._pure else 'platlib' for path, entries in self._manifest.items(): - for dst, src in entries: + for dst, src, rpath in entries: counter.update(src) if path == root: @@ -486,7 +495,7 @@ def build(self, directory: Path) -> pathlib.Path: else: dst = pathlib.Path(self._data_dir, path, dst) - self._install_path(whl, src, dst) + self._install_path(whl, src, dst, rpath) return wheel_file @@ -497,8 +506,8 @@ class _EditableWheelBuilder(_WheelBuilder): def _top_level_modules(self) -> Collection[str]: modules = set() for type_ in self._manifest: - for path, _ in self._manifest[type_]: - name, dot, ext = path.parts[0].partition('.') + for entry in self._manifest[type_]: + name, dot, ext = entry.dst.parts[0].partition('.') if dot: # module suffix = dot + ext @@ -854,7 +863,7 @@ def _info(self, name: str) -> Any: return json.loads(info.read_text(encoding='utf-8')) @property - def _manifest(self) -> DefaultDict[str, List[Tuple[pathlib.Path, str]]]: + def _manifest(self) -> DefaultDict[str, List[Entry]]: """The files to be added to the wheel, organized by wheel path.""" # Obtain the list of files Meson would install. diff --git a/mesonpy/_rpath.py b/mesonpy/_rpath.py index a7cbbb92..69103e64 100644 --- a/mesonpy/_rpath.py +++ b/mesonpy/_rpath.py @@ -11,19 +11,81 @@ if typing.TYPE_CHECKING: - from typing import List + from typing import List, Optional, TypeVar from mesonpy._compat import Iterable, Path + T = TypeVar('T') -if sys.platform == 'win32' or sys.platform == 'cygwin': - def fix_rpath(filepath: Path, libs_relative_path: str) -> None: +def unique(values: List[T]) -> List[T]: + r = [] + for value in values: + if value not in r: + r.append(value) + return r + + +class _Windows: + + @staticmethod + def get_rpath(filepath: Path) -> List[str]: + return [] + + @classmethod + def fix_rpath(cls, filepath: Path, install_rpath: Optional[str], libs_rpath: Optional[str]) -> None: pass -elif sys.platform == 'darwin': - def _get_rpath(filepath: Path) -> List[str]: +class RPATH: + origin = '$ORIGIN' + + @staticmethod + def get_rpath(filepath: Path) -> List[str]: + raise NotImplementedError + + @staticmethod + def set_rpath(filepath: Path, old: List[str], rpath: List[str]) -> None: + raise NotImplementedError + + @classmethod + def fix_rpath(cls, filepath: Path, install_rpath: Optional[str], libs_rpath: Optional[str]) -> None: + old_rpath = cls.get_rpath(filepath) + new_rpath = [] + if libs_rpath is not None: + if libs_rpath == '.': + libs_rpath = '' + for path in old_rpath: + if path.split('/', 1)[0] == cls.origin: + # Any RPATH entry relative to ``$ORIGIN`` is interpreted as + # pointing to a location in the build directory added by + # Meson. These need to be removed. Their presence indicates + # that the executable, shared library, or Python module + # depends on libraries build as part of the package. These + # entries are thus replaced with entries pointing to the + # ``..mesonpy.libs`` folder where meson-python + # relocates shared libraries distributed with the package. + # The package may however explicitly install these in a + # different location, thus this is not a perfect heuristic + # and may add not required RPATH entries. These are however + # harmless. + path = f'{cls.origin}/{libs_rpath}' + # Any other RPATH entry is preserved. + new_rpath.append(path) + if install_rpath: + # Add the RPATH entry spcified with the ``install_rpath`` argument. + new_rpath.append(install_rpath) + # Make the RPATH entries unique. + new_rpath = unique(new_rpath) + if new_rpath != old_rpath: + cls.set_rpath(filepath, old_rpath, new_rpath) + + +class _MacOS(RPATH): + origin = '@loader_path' + + @staticmethod + def get_rpath(filepath: Path) -> List[str]: rpath = [] r = subprocess.run(['otool', '-l', os.fspath(filepath)], capture_output=True, text=True) rpath_tag = False @@ -35,17 +97,31 @@ def _get_rpath(filepath: Path) -> List[str]: rpath_tag = False return rpath - def _replace_rpath(filepath: Path, old: str, new: str) -> None: - subprocess.run(['install_name_tool', '-rpath', old, new, os.fspath(filepath)], check=True) - - def fix_rpath(filepath: Path, libs_relative_path: str) -> None: - for path in _get_rpath(filepath): - if path.startswith('@loader_path/'): - _replace_rpath(filepath, path, '@loader_path/' + libs_relative_path) - -elif sys.platform == 'sunos5': - - def _get_rpath(filepath: Path) -> List[str]: + @staticmethod + def set_rpath(filepath: Path, old: List[str], rpath: List[str]) -> None: + args: List[str] = [] + for path in rpath: + if path not in old: + args += ['-add_rpath', path] + for path in old: + if path not in rpath: + args += ['-delete_rpath', path] + subprocess.run(['install_name_tool', *args, os.fspath(filepath)], check=True) + + @classmethod + def fix_rpath(cls, filepath: Path, install_rpath: Optional[str], libs_rpath: Optional[str]) -> None: + if install_rpath is not None: + root, sep, stem = install_rpath.partition('/') + if root == '$ORIGIN': + install_rpath = f'{cls.origin}{sep}{stem}' + # warnings.warn('...') + super().fix_rpath(filepath, install_rpath, libs_rpath) + + +class _SunOS(RPATH): + + @staticmethod + def get_rpath(filepath: Path) -> List[str]: rpath = [] r = subprocess.run(['/usr/bin/elfedit', '-r', '-e', 'dyn:rpath', os.fspath(filepath)], capture_output=True, check=True, text=True) @@ -56,35 +132,31 @@ def _get_rpath(filepath: Path) -> List[str]: rpath.append(path) return rpath - def _set_rpath(filepath: Path, rpath: Iterable[str]) -> None: + @staticmethod + def set_rpath(filepath: Path, old: Iterable[str], rpath: Iterable[str]) -> None: subprocess.run(['/usr/bin/elfedit', '-e', 'dyn:rpath ' + ':'.join(rpath), os.fspath(filepath)], check=True) - def fix_rpath(filepath: Path, libs_relative_path: str) -> None: - old_rpath = _get_rpath(filepath) - new_rpath = [] - for path in old_rpath: - if path.startswith('$ORIGIN/'): - path = '$ORIGIN/' + libs_relative_path - new_rpath.append(path) - if new_rpath != old_rpath: - _set_rpath(filepath, new_rpath) -else: - # Assume that any other platform uses ELF binaries. +class _ELF(RPATH): - def _get_rpath(filepath: Path) -> List[str]: + @staticmethod + def get_rpath(filepath: Path) -> List[str]: r = subprocess.run(['patchelf', '--print-rpath', os.fspath(filepath)], capture_output=True, text=True) return r.stdout.strip().split(':') - def _set_rpath(filepath: Path, rpath: Iterable[str]) -> None: + @staticmethod + def set_rpath(filepath: Path, old: Iterable[str], rpath: Iterable[str]) -> None: subprocess.run(['patchelf','--set-rpath', ':'.join(rpath), os.fspath(filepath)], check=True) - def fix_rpath(filepath: Path, libs_relative_path: str) -> None: - old_rpath = _get_rpath(filepath) - new_rpath = [] - for path in old_rpath: - if path.startswith('$ORIGIN/'): - path = '$ORIGIN/' + libs_relative_path - new_rpath.append(path) - if new_rpath != old_rpath: - _set_rpath(filepath, new_rpath) + +if sys.platform == 'win32' or sys.platform == 'cygwin': + _cls = _Windows +elif sys.platform == 'darwin': + _cls = _MacOS +elif sys.platform == 'sunos5': + _cls = _SunOS +else: + _cls = _ELF + +_get_rpath = _cls.get_rpath +fix_rpath = _cls.fix_rpath diff --git a/tests/packages/link-against-local-lib/meson.build b/tests/packages/link-against-local-lib/meson.build index e8cd2830..ff041820 100644 --- a/tests/packages/link-against-local-lib/meson.build +++ b/tests/packages/link-against-local-lib/meson.build @@ -9,7 +9,7 @@ if meson.get_compiler('c').get_id() in ['msvc', 'clang-cl', 'intel-cl'] link_args = ['-DEXAMPLE_DLL_IMPORTS'] else lib_compile_args = [] - link_args = ['-Wl,-rpath,custom-rpath'] + link_args = ['-Wl,-rpath,rpath-from-linker-arguments'] endif subdir('lib') @@ -26,6 +26,7 @@ py.extension_module( 'examplemod.c', link_with: example_lib, link_args: link_args, + install_rpath: 'custom-rpath', install: true, subdir: 'example', ) diff --git a/tests/packages/sharedlib-in-package/mypkg/__init__.py b/tests/packages/sharedlib-in-package/mypkg/__init__.py index 857d2e90..e4fbc2a9 100644 --- a/tests/packages/sharedlib-in-package/mypkg/__init__.py +++ b/tests/packages/sharedlib-in-package/mypkg/__init__.py @@ -45,7 +45,7 @@ def _append_to_sharedlib_load_path(): # end-literalinclude -from ._example import example_prod, example_sum #noqa: E402 +from ._example import prodsum # noqa: E402 -__all__ = ['example_prod', 'example_sum'] +__all__ = ['prodsum'] diff --git a/tests/packages/sharedlib-in-package/mypkg/_examplemod.c b/tests/packages/sharedlib-in-package/mypkg/_examplemod.c index 080e03c1..b4cc3f0c 100644 --- a/tests/packages/sharedlib-in-package/mypkg/_examplemod.c +++ b/tests/packages/sharedlib-in-package/mypkg/_examplemod.c @@ -4,36 +4,23 @@ #include -#include "examplelib.h" -#include "examplelib2.h" +#include "lib.h" -static PyObject* example_sum(PyObject* self, PyObject *args) +static PyObject* example_prodsum(PyObject* self, PyObject *args) { - int a, b; - if (!PyArg_ParseTuple(args, "ii", &a, &b)) { - return NULL; - } + int a, b, x; - long result = sum(a, b); - - return PyLong_FromLong(result); -} - -static PyObject* example_prod(PyObject* self, PyObject *args) -{ - int a, b; - if (!PyArg_ParseTuple(args, "ii", &a, &b)) { + if (!PyArg_ParseTuple(args, "iii", &a, &b, &x)) { return NULL; } - long result = prod(a, b); + long result = prodsum(a, b, x); return PyLong_FromLong(result); } static PyMethodDef methods[] = { - {"example_prod", (PyCFunction)example_prod, METH_VARARGS, NULL}, - {"example_sum", (PyCFunction)example_sum, METH_VARARGS, NULL}, + {"prodsum", (PyCFunction)example_prodsum, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL}, }; diff --git a/tests/packages/sharedlib-in-package/mypkg/examplelib.c b/tests/packages/sharedlib-in-package/mypkg/examplelib.c deleted file mode 100644 index f486bd7f..00000000 --- a/tests/packages/sharedlib-in-package/mypkg/examplelib.c +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The meson-python developers -// -// SPDX-License-Identifier: MIT - -#include "sub/mypkg_dll.h" - -MYPKG_DLL int sum(int a, int b) { - return a + b; -} diff --git a/tests/packages/sharedlib-in-package/mypkg/examplelib.h b/tests/packages/sharedlib-in-package/mypkg/examplelib.h deleted file mode 100644 index c09f4f78..00000000 --- a/tests/packages/sharedlib-in-package/mypkg/examplelib.h +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The meson-python developers -// -// SPDX-License-Identifier: MIT - -#include "sub/mypkg_dll.h" - -MYPKG_DLL int sum(int a, int b); diff --git a/tests/packages/sharedlib-in-package/mypkg/lib.c b/tests/packages/sharedlib-in-package/mypkg/lib.c new file mode 100644 index 00000000..e4fe1478 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/lib.c @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include "lib.h" +#include "sublib.h" + +int prodsum(int a, int b, int x) { + return prod(a, x) + b; +} diff --git a/tests/packages/sharedlib-in-package/mypkg/lib.h b/tests/packages/sharedlib-in-package/mypkg/lib.h new file mode 100644 index 00000000..fb6a02d8 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/lib.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#if defined(MYPKG_DLL_EXPORTS) + #define EXPORT __declspec(dllexport) +#elif defined(MYPKG_DLL_IMPORTS) + #define EXPORT __declspec(dllimport) +#else + #define EXPORT +#endif + +EXPORT int prodsum(int a, int b, int x); diff --git a/tests/packages/sharedlib-in-package/mypkg/meson.build b/tests/packages/sharedlib-in-package/mypkg/meson.build index 75904bed..47dc1c40 100644 --- a/tests/packages/sharedlib-in-package/mypkg/meson.build +++ b/tests/packages/sharedlib-in-package/mypkg/meson.build @@ -10,29 +10,42 @@ else import_dll_args = [] endif -example_lib = shared_library( - 'examplelib', - 'examplelib.c', +sublib = shared_library( + 'sublib', + 'sublib.c', c_args: export_dll_args, install: true, - install_dir: py.get_install_dir() / 'mypkg', + install_dir: py.get_install_dir() / 'mypkg/sub', ) -example_lib_dep = declare_dependency( +sublib_dep = declare_dependency( compile_args: import_dll_args, - link_with: example_lib, + link_with: sublib, +) + +lib = shared_library( + 'lib', + 'lib.c', + dependencies: sublib_dep, + c_args: export_dll_args, + install_rpath: '$ORIGIN/sub', + install: true, + install_dir: py.get_install_dir() / 'mypkg', ) -subdir('sub') +lib_dep = declare_dependency( + compile_args: import_dll_args, + link_with: lib, +) py.extension_module( '_example', '_examplemod.c', - dependencies: [example_lib_dep, example_lib2_dep], + dependencies: lib_dep, include_directories: 'sub', install: true, subdir: 'mypkg', - install_rpath: '$ORIGIN', + install_rpath: build_machine.system() == 'darwin' ? '@loader_path' : '$ORIGIN', ) py.install_sources( diff --git a/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.h b/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.h deleted file mode 100644 index 64b6a907..00000000 --- a/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.h +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The meson-python developers -// -// SPDX-License-Identifier: MIT - -#include "mypkg_dll.h" - -MYPKG_DLL int prod(int a, int b); diff --git a/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.c b/tests/packages/sharedlib-in-package/mypkg/sublib.c similarity index 66% rename from tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.c rename to tests/packages/sharedlib-in-package/mypkg/sublib.c index 12f5b87a..facfdf2e 100644 --- a/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.c +++ b/tests/packages/sharedlib-in-package/mypkg/sublib.c @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: MIT -#include "mypkg_dll.h" +#include "sublib.h" -MYPKG_DLL int prod(int a, int b) { +int prod(int a, int b) { return a * b; } diff --git a/tests/packages/sharedlib-in-package/mypkg/sublib.h b/tests/packages/sharedlib-in-package/mypkg/sublib.h new file mode 100644 index 00000000..9fc7ae51 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/sublib.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#if defined(MYPKG_DLL_EXPORTS) + #define EXPORT __declspec(dllexport) +#elif defined(MYPKG_DLL_IMPORTS) + #define EXPORT __declspec(dllimport) +#else + #define EXPORT +#endif + +EXPORT int prod(int a, int b); diff --git a/tests/test_tags.py b/tests/test_tags.py index 433628b8..0a8d975a 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -76,7 +76,8 @@ def test_python_host_platform(monkeypatch): def wheel_builder_test_factory(content, pure=True, limited_api=False): manifest = defaultdict(list) - manifest.update({key: [(pathlib.Path(x), os.path.join('build', x)) for x in value] for key, value in content.items()}) + for key, value in content.items(): + manifest[key] = [mesonpy.Entry(pathlib.Path(x), os.path.join('build', x)) for x in value] return mesonpy._WheelBuilder(None, manifest, limited_api, False) diff --git a/tests/test_wheel.py b/tests/test_wheel.py index b6f64cee..426221d7 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -178,15 +178,14 @@ def test_local_lib(venv, wheel_link_against_local_lib): assert int(output) == 3 +@pytest.mark.skipif(MESON_VERSION < (1, 6, 0), reason='meson too old') def test_sharedlib_in_package(venv, wheel_sharedlib_in_package): venv.pip('install', wheel_sharedlib_in_package) - output = venv.python('-c', 'import mypkg; print(mypkg.example_sum(2, 5))') - assert int(output) == 7 - output = venv.python('-c', 'import mypkg; print(mypkg.example_prod(6, 7))') - assert int(output) == 42 + output = venv.python('-c', 'import mypkg; print(mypkg.prodsum(2, 3, 4))') + assert int(output) == 11 -@pytest.mark.skipif(MESON_VERSION < (1, 3, 0), reason='Meson version too old') +@pytest.mark.skipif(MESON_VERSION < (1, 3, 0), reason='meson too old') def test_link_library_in_subproject(venv, wheel_link_library_in_subproject): venv.pip('install', wheel_link_library_in_subproject) output = venv.python('-c', 'import foo; print(foo.example_sum(3, 6))') @@ -199,7 +198,11 @@ def test_rpath(wheel_link_against_local_lib, tmp_path): artifact.extractall(tmp_path) origin = '@loader_path' if sys.platform == 'darwin' else '$ORIGIN' - expected = {f'{origin}/../.link_against_local_lib.mesonpy.libs', 'custom-rpath',} + expected = {f'{origin}/../.link_against_local_lib.mesonpy.libs', 'rpath-from-linker-arguments',} + + # ``install_rpath`` is supported starting with meson 1.6.0 + if MESON_VERSION >= (1, 6, 0): + expected.add('custom-rpath') rpath = set(mesonpy._rpath._get_rpath(tmp_path / 'example' / f'_example{EXT_SUFFIX}')) # Verify that rpath is a superset of the expected one: linking to