From 1778ab62cb28c702161576016b1d84761412b64b Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Sat, 9 Dec 2023 20:33:44 +0100 Subject: [PATCH] ENH: support shared libraries on Windows when explicitly enabled Fixes #525. --- docs/reference/pyproject-settings.rst | 11 +++++++++++ mesonpy/__init__.py | 14 ++++++++++++-- mesonpy/_rpath.py | 2 +- tests/test_tags.py | 2 +- tests/test_wheel.py | 3 ++- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/reference/pyproject-settings.rst b/docs/reference/pyproject-settings.rst index 3beb47c61..1473ad6ee 100644 --- a/docs/reference/pyproject-settings.rst +++ b/docs/reference/pyproject-settings.rst @@ -34,6 +34,17 @@ use them and examples. ``meson-python`` itself. It can be overrridden by the :envvar:`MESON` environment variable. +.. option:: tool.meson-python.shared-libs-win32 + + A boolean indicating whether shared libraries should be supported on + Windows. ``meson-python`` installs shared libraries in a dedicated location + and uses RPATH or equivalent mechanisms to have Python modules and native + executables load them form there. Windows does not have an equivalent + mechanism to set the DLL load path. Supporting shared libraries on Windows + requires collaboration from the package. To make sure that package authors + are aware of this requirement, ``meson-python`` raises an error if a + package contains DLLs and this option is not set. + .. option:: tool.meson-python.args.dist Extra arguments to be passed to the ``meson dist`` command. diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 05016a1c3..b3db7799e 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -311,10 +311,12 @@ def __init__( metadata: Metadata, manifest: Dict[str, List[Tuple[pathlib.Path, str]]], limited_api: bool, + shared_libs_win32: bool, ) -> None: self._metadata = metadata self._manifest = manifest self._limited_api = limited_api + self._shared_libs_win32 = shared_libs_win32 @property def _has_internal_libs(self) -> bool: @@ -430,6 +432,9 @@ def _install_path(self, wheel_file: mesonpy._wheelfile.WheelFile, origin: Path, if self._has_internal_libs: if _is_native(origin): + if sys.platform == 'win32' and not self._shared_libs_win32: + raise NotImplementedError(f'Bundling libraries in wheel is not supported on {sys.platform}') + # When an executable, libray, 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 @@ -566,6 +571,7 @@ def _string_or_path(value: Any, name: str) -> str: scheme = _table({ 'meson': _string_or_path, 'limited-api': _bool, + 'shared-libs-win32': _bool, 'args': _table({ name: _strings for name in _MESON_ARGS_KEYS }), @@ -757,6 +763,10 @@ def __init__( if not value: self._limited_api = False + # Shared library support on Windows requires collaboration + # from the package, make sure the developpers aknowledge this. + self._shared_libs_win32 = pyproject_config.get('shared-libs-win32', False) + def _run(self, cmd: Sequence[str]) -> None: """Invoke a subprocess.""" # Flush the line to ensure that the log line with the executed @@ -920,13 +930,13 @@ def sdist(self, directory: Path) -> pathlib.Path: def wheel(self, directory: Path) -> pathlib.Path: """Generates a wheel in the specified directory.""" self.build() - builder = _WheelBuilder(self._metadata, self._manifest, self._limited_api) + builder = _WheelBuilder(self._metadata, self._manifest, self._limited_api, self._shared_libs_win32) return builder.build(directory) def editable(self, directory: Path) -> pathlib.Path: """Generates an editable wheel in the specified directory.""" self.build() - builder = _EditableWheelBuilder(self._metadata, self._manifest, self._limited_api) + builder = _EditableWheelBuilder(self._metadata, self._manifest, self._limited_api, self._shared_libs_win32) return builder.build(directory, self._source_dir, self._build_dir, self._build_command, self._editable_verbose) diff --git a/mesonpy/_rpath.py b/mesonpy/_rpath.py index 6c0016b34..e618fbb76 100644 --- a/mesonpy/_rpath.py +++ b/mesonpy/_rpath.py @@ -19,7 +19,7 @@ if sys.platform == 'win32' or sys.platform == 'cygwin': def fix_rpath(filepath: Path, libs_relative_path: str) -> None: - raise NotImplementedError(f'Bundling libraries in wheel is not supported on {sys.platform}') + pass elif sys.platform == 'darwin': diff --git a/tests/test_tags.py b/tests/test_tags.py index c55d69739..73d1187f0 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -77,7 +77,7 @@ 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()}) - return mesonpy._WheelBuilder(None, manifest, limited_api) + return mesonpy._WheelBuilder(None, manifest, limited_api, False) def test_tag_empty_wheel(): diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 33a974c41..e8ece6868 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -231,7 +231,8 @@ def test_entrypoints(wheel_full_metadata): def test_top_level_modules(package_module_types): with mesonpy._project() as project: - builder = mesonpy._EditableWheelBuilder(project._metadata, project._manifest, project._limited_api) + builder = mesonpy._EditableWheelBuilder( + project._metadata, project._manifest, project._limited_api, project._shared_libs_win32) assert set(builder._top_level_modules) == { 'file', 'package',