Add an option to have @rpath install_name instead of an absolute path
@nirbheek is already working on this.
Since Jun 18, 2018.
- Dominant language
- Python
- Stars
- 6.6k
- Forks
- 1.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 33
Description
Description
Meson's RPATH and install_name handling are currently broken on macOS. Here's a toy example. Let foo be the library (aka the provider) and let bar be the executable (aka the consumer). For foo we have:
foo.cpp:
#include <iostream>
void foo_func()
{
std::cout << "Hello World!\n";
}
foo.hpp:
void foo_func();
meson.build:
project('libfoo', 'cpp')
foo_lib = library('foo', 'foo.cpp', install : true)
install_headers('foo.hpp', subdir : 'foo')
import('pkgconfig').generate(
libraries : foo_lib,
version : '1.0',
name : 'foo',
filebase : 'foo',
subdirs : 'foo',
description : 'A foo library')
For bar we have:
bar.cpp:
#include <foo.hpp>
int main()
{
foo_func();
return 0;
}
meson.build:
project('barapp', 'cpp')
foo_dep = dependency('foo')
# build_rpath is used here for test dependencies
executable('bar', 'bar.cpp', dependencies : foo_dep, build_rpath : foo_dep.get_pkgconfig_variable('libdir'))
How to reproduce
in foo (the rm -r build at the end is crucial to reproduce the issue):
meson.py --prefix /Users/Soap/Desktop/PREFIX/ build . && ninja -C build -v install && rm -r build
in bar:
export PKG_CONFIG_PATH=/Users/Soap/Desktop/PREFIX/lib/pkgconfig
meson.py --prefix /Users/Soap/Desktop/PREFIX/ build . && ninja -C build -v
./build/bar
which then greets you with a message:
dyld: Library not loaded: /Users/Soap/Desktop/foo/build/libfoo.dylib
Referenced from: /Users/Soap/Desktop/bar/./build/bar
Reason: image not found
The actual problem
The culprit is how meson builds libraries on macOS. The linking line is currently:
c++ -o libfoo.dylib 'foo@sha/foo.cpp.o' -shared -install_name /Users/Soap/Desktop/foo/build/libfoo.dylib '-Wl,-rpath,$ORIGIN/'
which is incorrect. MacOS has a peculiar and very unusual way of linking, namely that consumers of a library inherit a provider's install_name. If you run otool -l on the library you get
cmd LC_ID_DYLIB
cmdsize 72
name /Users/Soap/Desktop/foo/build/libfoo.dylib (offset 24)
where the name is clearly wrong, as it uses the builddir name. As an absolute minimum this should have been /Users/Soap/Desktop/PREFIX/lib/libfoo.dylib (but hold on, you still don't want this). This is why, when not running rm -r build in foo, the linking seems to work, because bar is still using the build dir lib, not the installed one.
Furthermore, another peculiarity of macOS is the fact that for RPATH of consumers to work, the provider has to play along. This is different from Linux, where RPATH/RUNPATH are purely a consumer problem, the provider does not have to know about consumers' RPATH behaviour. Thus, in order for RPATH to work for consumers, the install_name has to be @rpath/<libname>.dylib. In a quick and hacky way, doing this in foo:
c++ -o libfoo.dylib 'foo@sha/foo.cpp.o' -shared -install_name @rpath/libfoo.dylib '-Wl,-rpath,$ORIGIN/'
And then in bar (notice the missing colon : after -Wl,-rpath,):
c++ -o bar 'bar@exe/bar.cpp.o' -L/Users/Soap/Desktop/PREFIX/lib -lfoo -Wl,-rpath,/Users/Soap/Desktop/PREFIX/lib
and then ./bar gives me
Hello World!
Conclusion
This issue is caused by macOS' unconventional linking behaviour, and the fact that library providers have to be RPATH aware, which is unduly burdensome and complicates things. I believe the following to be the best line of operation:
- all (shared) libraries would use
-install_name @rpath/<libname>.dylibunconditionally, purely to be consumed in anRPATH-capable way. While using an absoluteinstall_namewould make things a bit easier in the short run, this is short-sighted and a path full of pain. There are two reasons to avoid absoluteinstall_names:- reproducible builds, as this would code path/layout-dependent information into libraries
- it makes libraries non-relocatable, whereas relative
RPATH-based builds are relocatable.
- all consumers (that is, other libraries and executables) should specify
RPATHs in a relative way, where@loader_pathis the proper analogue to$ORIGINon Linux.
References
https://github.com/conda/conda-build/issues/279
https://www.mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html
https://wincent.com/wiki/%40executable_path%2C_%40load_path_and_%40rpath
http://jorgen.tjer.no/post/2014/05/20/dt-rpath-ld-and-at-rpath-dyld/
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.