Skip to content
Merged
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
198 changes: 141 additions & 57 deletions .github/workflows/pixi.lock

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions .github/workflows/pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@ ninja = "*"
pkg-config = "*"
cmake = "*"
patchelf = "*"
c-compiler = "*"
cxx-compiler = "*"
geos = "*"
pip = "*"
python-build = "*"
cython = "*"
pytest = "*"
pytest-mock = "*"
wheel = "*"

[feature.compilers.dependencies]
c-compiler = "*"
cxx-compiler = "*"

[environments]
default = { features = ["compilers"], solve-group = "test" }
system-compilers = { features = [], solve-group = "test" }

[tasks]
test = { cmd = "pytest", cwd = "../.." }
12 changes: 11 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,16 @@ jobs:
run: mypy -p mesonpy

pixi:
name: ${{ matrix.name }}
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
include:
- name: pixi
environment: default
- name: pixi-system-compilers
environment: system-compilers
steps:
- name: Checkout
uses: actions/checkout@v7
Expand All @@ -347,11 +356,12 @@ jobs:
with:
pixi-version: v0.77.1
manifest-path: .github/workflows/pixi.toml
environments: ${{ matrix.environment }}
cache: false

- name: Run tests
working-directory: .github/workflows
run: pixi run test
run: pixi run --environment ${{ matrix.environment }} test

uv-sync:
runs-on: ubuntu-latest
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
Changelog
+++++++++

0.22.1
======

- Do not remove absolute build RPATH entries, unless pointing within the
build directory. Fixes an issue when using system compilers in a conda
environment.

Daniele Nicolodi, Ralf Gommers --- 01-10-2026.


0.22.0
======

Expand Down
28 changes: 25 additions & 3 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class InvalidLicenseExpression(Exception): # type: ignore[no-redef]
Path = Union[str, os.PathLike[str]]


__version__ = '0.22.0'
__version__ = '0.22.1'


_PYPROJECT_METADATA_VERSION = tuple(map(int, pyproject_metadata.__version__.split('.')[:2]))
Expand Down Expand Up @@ -143,12 +143,15 @@ class _Entry(typing.NamedTuple):


def _map_to_wheel(sources: Dict[str, Dict[str, Any]],
exclude: List[str], include: List[str]) -> DefaultDict[str, List[_Entry]]:
exclude: List[str], include: List[str],
build_dir: str) -> DefaultDict[str, List[_Entry]]:
"""Map files to the wheel, organized by wheel installation directory."""
wheel_files: DefaultDict[str, List[_Entry]] = collections.defaultdict(list)
packages: Dict[str, str] = {}
excluded = _compile_patterns(exclude)
included = _compile_patterns(include)
build_dir = os.path.normpath(build_dir)
assert os.path.isabs(build_dir)

for key, group in sources.items():
for src, target in group.items():
Expand Down Expand Up @@ -206,6 +209,25 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]],
if sys.platform == 'darwin':
install_rpath = [_translate_rpath(path, dst) for path in install_rpath]

# ``build_rpath`` lists the RPATH entries to remove from the
# binary. Meson automatically adds RPATH entries for shared
# libraries built as part of the project, using relative paths
# anchored at ``$ORIGIN`` or ``@loader_path``, and for shared
# libraries from dependencies installed in non-standard
# locations, using absolute paths. Installed binaries need
# the absolute paths to find their external dependencies, so
# we drop them from the list. The relative paths are only
# meaningful inside the build tree, so we leave them in the
# list to be removed. Additional entries can be added with
# the ``build_rpath`` argument passed to Meson functions. The
# most common use case for these is to point at directories
# inside the build directory. These may be absolute paths,
# but they are irrelevant for installed binaries, so we leave
# them too in the list to be removed.
build_rpath = [path for path in build_rpath
if not path.startswith('/')
or os.path.commonpath((os.path.normpath(path), build_dir)) == build_dir]

wheel_files[path].append(_Entry(dst, src, install_rpath, build_rpath))

return wheel_files
Expand Down Expand Up @@ -1015,7 +1037,7 @@ def _manifest(self) -> DefaultDict[str, List[_Entry]]:
sources[key][target] = details

# Map Meson installation locations to wheel paths.
return _map_to_wheel(sources, self._excluded_files, self._included_files)
return _map_to_wheel(sources, self._excluded_files, self._included_files, str(self._build_dir))

@property
def _meson_name(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ requires = [

[project]
name = 'meson-python'
version = '0.22.0'
version = '0.22.1'
description = 'The Python build backend for Meson projects'
readme = 'README.rst'
requires-python = '>= 3.10'
Expand Down
23 changes: 23 additions & 0 deletions tests/packages/link-against-external-lib/external.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2026 The meson-python developers
//
// SPDX-License-Identifier: MIT

#include <Python.h>
#include <geos_c.h>

static PyObject *version(PyObject *self, PyObject *args) {
return PyUnicode_FromString(GEOSversion());
}

static PyMethodDef methods[] = {
{"version", version, METH_NOARGS, NULL},
{NULL, NULL, 0, NULL},
};

static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT, "external", NULL, -1, methods,
};

PyMODINIT_FUNC PyInit_external(void) {
return PyModule_Create(&module);
}
16 changes: 16 additions & 0 deletions tests/packages/link-against-external-lib/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2026 The meson-python developers
#
# SPDX-License-Identifier: MIT

project('link-against-external-lib', 'c', version: '1.0.0')

py = import('python').find_installation(pure: false)

external = dependency('geos', method: 'pkg-config')

py.extension_module(
'external',
'external.c',
dependencies: external,
install: true
)
7 changes: 7 additions & 0 deletions tests/packages/link-against-external-lib/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2026 The meson-python developers
#
# SPDX-License-Identifier: MIT

[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']
1 change: 1 addition & 0 deletions tests/packages/purelib-and-platlib/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ py.extension_module(
'plat.c',
install: true,
install_tag: 'platlib',
build_rpath: ':'.join(['/usr/local/lib', meson.current_build_dir() / 'private']),
)
23 changes: 21 additions & 2 deletions tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
import shutil
import stat
import subprocess
import sys
import textwrap

Expand Down Expand Up @@ -268,6 +269,22 @@ def check(rpath, *expected):
check(rpath, 'custom-rpath', f'{origin}/../.link_against_local_lib.mesonpy.libs')


@pytest.mark.skipif(sys.platform in {'win32', 'cygwin'}, reason='requires RPATH support')
@pytest.mark.skipif(shutil.which('pkg-config') is None, reason='requires pkg-config')
def test_link_against_external_lib_rpath(package_link_against_external_lib, tmp_path, venv):
if subprocess.run(['pkg-config', '--exists', 'geos']).returncode:
pytest.skip('requires GEOS')
version = subprocess.check_output(['pkg-config', '--modversion', 'geos'], text=True).strip()

# Use an installed dependency without supplying an explicit RPATH.
# Check its version too, to detect fallback to a different system copy.
filename = mesonpy.build_wheel(tmp_path)
venv.pip('install', tmp_path / filename)
output = venv.python('-c', 'import external; print(external.version())')
# GEOSversion() appends the C API version after "-CAPI-".
assert output.strip().split('-CAPI-')[0] == version


@pytest.mark.skipif(sys.platform in {'win32', 'cygwin'}, reason='requires RPATH support')
def test_link_against_local_lib_rpath_ldflags(package_link_against_local_lib, tmp_path, monkeypatch):
origin = '@loader_path' if sys.platform == 'darwin' else '$ORIGIN'
Expand All @@ -290,8 +307,10 @@ def test_uneeded_rpath(wheel_purelib_and_platlib, tmp_path):

origin = '@loader_path' if sys.platform == 'darwin' else '$ORIGIN'
rpath = mesonpy._rpath.get_rpath(tmp_path / f'plat{EXT_SUFFIX}')
for path in rpath:
assert origin not in path

assert not any(path.startswith(origin) for path in rpath)
assert '/usr/local/lib' in rpath
assert not any(path.endswith('private') for path in rpath) or not BUILD_RPATH_SUPPORT


@pytest.mark.skipif(sys.platform in {'win32', 'cygwin'}, reason='requires RPATH support')
Expand Down
Loading