Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: support shared libraries on Windows when explicitly enabled #551

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/reference/pyproject-settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to explain (or link to something that explains) what sort of "collaboration" is needed.

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.
Expand Down
14 changes: 12 additions & 2 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,12 @@
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:
Expand Down Expand Up @@ -430,6 +432,9 @@

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}')

Check warning on line 436 in mesonpy/__init__.py

View check run for this annotation

Codecov / codecov/patch

mesonpy/__init__.py#L436

Added line #L436 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should mention the configuration option?


# 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
Expand Down Expand Up @@ -566,6 +571,7 @@
scheme = _table({
'meson': _string_or_path,
'limited-api': _bool,
'shared-libs-win32': _bool,
'args': _table({
name: _strings for name in _MESON_ARGS_KEYS
}),
Expand Down Expand Up @@ -757,6 +763,10 @@
if not value:
self._limited_api = False

# Shared library support on Windows requires collaboration
# from the package, make sure the developpers aknowledge this.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# from the package, make sure the developpers aknowledge this.
# from the package; make sure the developers acknowledge 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
Expand Down Expand Up @@ -920,13 +930,13 @@
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)


Expand Down
36 changes: 18 additions & 18 deletions mesonpy/_rpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,10 @@
from mesonpy._compat import Iterable, Path


if sys.platform == 'linux':

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:
subprocess.run(['patchelf','--set-rpath', ':'.join(rpath), os.fspath(filepath)], check=True)
if sys.platform == 'win32' or sys.platform == 'cygwin':

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)

pass

Check warning on line 22 in mesonpy/_rpath.py

View check run for this annotation

Codecov / codecov/patch

mesonpy/_rpath.py#L22

Added line #L22 was not covered by tests

elif sys.platform == 'darwin':

Expand All @@ -59,6 +44,21 @@
_replace_rpath(filepath, path, '@loader_path/' + libs_relative_path)

else:
# Assume that any other platform uses ELF binaries.

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:
subprocess.run(['patchelf','--set-rpath', ':'.join(rpath), os.fspath(filepath)], check=True)

def fix_rpath(filepath: Path, libs_relative_path: str) -> None:
raise NotImplementedError(f'Bundling libraries in wheel is not supported on {sys.platform}')
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)
2 changes: 1 addition & 1 deletion tests/test_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
3 changes: 2 additions & 1 deletion tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading