Repository navigation
RPATH goes missing when using both install_rpath and an internal shared library dependency #711
Description
This is a case that probably hasn't been exercised before: a Python extension module that links against both a shared library that's part of the package and a shared library built in a subproject that's folded into the wheel by meson-python.
I ran into this in SciPy when adding a subproject. This worked as advertised, except for this scipy.special._ufuncs extension module, which depends on this libsf_error shared library as well as on the library in the subproject.
What one sees then is that the installed wheel can no longer find libsf_error.so, because the RPATH entry for install_rpath: '$ORIGIN' goes missing.
With a default SciPy build (no subproject), the Library rpath|runpath entries on the _ufuncs target in the build and install directories looks like this (has $ORIGIN as it should):
$ readelf -d build/scipy/special/_ufuncs.cpython-312-x86_64-linux-gnu.so | rg "Library r"
0x000000000000000f (RPATH) Library rpath: [/home/rgommers/mambaforge/envs/scipy-dev-py312/lib:$ORIGIN/]
$ readelf -d build-install/lib/python3.12/site-packages/scipy/special/_ufuncs.cpython-312-x86_64-linux-gnu.so | rg "Library r"
0x000000000000000f (RPATH) Library rpath: [$ORIGIN:/home/rgommers/mambaforge/envs/scipy-dev-py312/lib]On the branch with the subproject, we see this instead:
$ readelf -d build-whl/scipy/special/_ufuncs.cpython-312-x86_64-linux-gnu.so | rg Library
0x000000000000001d (RUNPATH) Library runpath: [/home/rgommers/code/pixi-dev-scipystack/scipy/.pixi/envs/openblas-src/lib:$ORIGIN/../../.scipy.mesonpy.libs:$ORIGIN/../../.scipy.mesonpy.libs:$ORIGIN/../../.scipy.mesonpy.libs:$ORIGIN/../../.scipy.mesonpy.libs]
$ readelf -d site-packages/scipy/special/_ufuncs.cpython-312-x86_64-linux-gnu.so | rg "Library r"
0x000000000000001d (RUNPATH) Library runpath: [/home/rgommers/code/pixi-dev-scipystack/scipy/.pixi/envs/openblas-src/lib:$ORIGIN/../../.scipy.mesonpy.libs:$ORIGIN/../../.scipy.mesonpy.libs:$ORIGIN/../../.scipy.mesonpy.libs:$ORIGIN/../../.scipy.mesonpy.libs]The duplication of the .scipy.mesonpy.libs entries is a minor stylistic issue, we may be able to de-duplicate but it doesn't matter. What does matter is that $ORIGIN is no longer present.
This looks like a bug in the fix_rpath implementation at:
meson-python/mesonpy/_rpath.py
Lines 28 to 36 in 91d64d1
| 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) |
The problem seems clear: all RPATH entries are cleared, and the ones that are added back all get libs_relative_path appended (i.e. to the subproject-relocated location). Any existing RPATHs within the package, like '$ORIGIN', will get lost.
Activity
rgommers commentedon Dec 6, 2024
Adding the relevant content from the
build-whl/meson-info/intro-install_plan.jsoninstall metadata file:{ "targets": { "/home/.../build-whl/subprojects/openblas/lapack-netlib/SRC/libscipy_lapack.so": { "destination": "{libdir_shared}/libscipy_lapack.so", "tag": "runtime", "subproject": "openblas", "install_rpath": null }, "/home/.../build-whl/scipy/special/libsf_error_state.so": { "destination": "{py_platlib}/scipy/special/libsf_error_state.so", "tag": "runtime", "subproject": null, "install_rpath": null }, "/home/.../build-whl/scipy/special/_ufuncs.cpython-312-x86_64-linux-gnu.so": { "destination": "{py_platlib}/scipy/special/_ufuncs.cpython-312-x86_64-linux-gnu.so", "tag": "runtime", "subproject": null, "install_rpath": "$ORIGIN" },rgommers commentedon Dec 6, 2024
I think the correct behavior for
fix_rpathis:If the install target has no RPATH entries, do nothing
If the install target has >=1 RPATH entries, then:
.<project-name>.mesonpy.libs$ORIGINbut pointing outside of the package's own tree and into subprojects (e.g.,$ORIGIN/../../subprojects). Those are the ones being relocated.$ORIGINunchanged$ORIGINalso unchanged (that is already the case)dnicolodi commentedon Feb 13, 2025
I've tried to brush up on the reason why the RPATH adjustment code does what it does. IIRC, what it needs to do is to remove RPATH entries added by meson that point to the build tree and add an RPATH entry that points to
.<project-name>.mesonpy.libs. Removing all RPATH entries that start with$ORIGINwas an attempt for an heuristic to determine which RPATH entries have been added by meson. We would need to find a better one. I'm not sure what that could be. meson keeps a list of the RPATH entries it added, but meson-python does not have access to it.rgommers commentedon Feb 13, 2025
intro-install_plan.jsondoes contain theinstall_rpathentries originating frommeson.build, so I think we can process those. Snippet from a SciPy build:This metadata matches with https://github.com/scipy/scipy/blob/1336806190a3810342a6cd74e3aee49d49e3ce20/scipy/special/meson.build#L83. All extension modules and shared libraries have the
install_rpathmetadata, it's eithernullor a string.dnicolodi commentedon Feb 13, 2025
Oh, right, I added that to the meson introspection data exactly for this purpose mesonbuild/meson#13625 🙂 This does not solve the issue for RPATH entries added passing linker arguments, but meson already emits a warning for these and we can say that these are not supported. I guess that what we should do is thus to remove all RPATH entries that are not listed in the introspection data. I'll have a look at this later today.
rgommers commentedon Feb 13, 2025
Thanks! Always nice to discover you solved the problem you think you're having yourself months ago:)
eli-schwartz commentedon Feb 13, 2025
There are reasons why someone might want to pass rpath via LDFLAGS but none of them are applicable to internal dependencies. They will always be for directories that aren't inside the meson source/build trees.
ENH: correctly handle install_rpath when installing into wheels
ENH: correctly handle install_rpath when installing into wheels
ENH: correctly handle install_rpath when installing into wheels
ENH: correctly handle install_rpath when installing into wheels
ENH: correctly handle install_rpath when installing into wheels
ENH: correctly handle install_rpath when installing into wheels
16 remaining items
ENH: improve RPATH handling
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN
stephanlachnit commentedon Aug 6, 2026
Also ran into this - this was quite though to figure out, would be good if this would be fixed.
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN
BUG: do not remove RPATH entries relative to $ORIGIN