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

rpath doesn't get set with find_library('foo', dirs:'/path/to/lib') #314

Closed
rhd opened this issue Nov 18, 2015 · 56 comments
Closed

rpath doesn't get set with find_library('foo', dirs:'/path/to/lib') #314

rhd opened this issue Nov 18, 2015 · 56 comments

Comments

@rhd
Copy link
Contributor

rhd commented Nov 18, 2015

Hi Jussi,

If you build a shared object with meson, the rpath get sets correctly (or so the docs say), but when I use find_library() and give it a search path, the rpath doesn't get set. This is causing me lot of pain.

I'm currently doing stuff like this:

thirdparty_root = '/usr/local/thirdparty-1.6.0'
thirdparty_lib = [thirdparty_root + '/lib/linux-x64-all',
    thirdparty_root + '/lib/linux-x64-gcc4.8'
]
thirdparty_rpath = thirdparty_lib[0] + ':' + thirdparty_lib[1]

thirdparty_dep = declare_dependency(
    include_directories: thirdparty_inc,
    dependencies: [
        find_library('lib1', dirs: thirdparty_lib),
        find_library('lib2', dirs: thirdparty_lib),
        find_library('lib3', dirs: thirdparty_lib)
    ]
)

Then doing this:

executable('foo', 'foo.cpp',
   ...,
    link_args: '-Wl,-rpath,@0@:@1@'.format(thirdparty_rpath, boost_rpath),
)

Am I doing it wrong?

@jpakkane
Copy link
Member

You can not set rpath yourself for the build dir (you can set an rpath that will be set after 'ninja install'). This is because Meson needs to do some Magic to the object files while they are in the build tree.

The established Unix convention is that you should not use rpath for finding public libraries, only for your private ones (like if you have stuff in /usr/lib/foobarlib/libfoobarprivate.so). For libraries that are public but not in the standard location (this includes things like /usr/local/lib) you need to set the LD_LIBRARY_PATH environment variable.

@rhd
Copy link
Contributor Author

rhd commented Nov 19, 2015

Hi Jussi,

This is one area where cmake and meson differ and IMO cmake does the "better thing".

By default, cmake will jam in the rpath values and strip them out for an install while giving you the option to keep them. This prevents the need for LD_LIBRARY_PATH and just works.

Meson is the opposite - it doesn't provide the rpath by default and allows you to add them during the install. This would cause 'ninja test' to fail on a system that wasn't properly setup even though the shared objects were there and everything built fine.

Are there any downsides to the cmake way? I know you mention some "magic".

What I'm currently doing is putting in the rpath in the link_args - is this going to cause me problems?

My other issue is that meson will strip out the rpath values during install - I really wish there was an option to not do that.

Thanks for taking the time to talk about these issues with me!

@jpakkane
Copy link
Member

The last time I checked, CMake has two properties for rpath. One for build tree and one after install. CMake stores internal dirs in the rpath so binaries can be run directly from the build tree. They are removed upon ninja install. Meson does the same thing (I stole it directly).

To set the rpath to be used after install, just set install_rpath keyword argument on your targets.

Setting rpath yourself is very much undefined behaviour. It might work. It might not work. It might stop working at any time for any reason.

Setting builddir rpath has not been supported for a reason, which is that it is very easy to shoot yourself in the foot with it. At deployment time it can cause wacky stuff that is painful to debug. The simple and reliable ways to do dependencies is to either have a staging dir where you put your stuff (such as ~/devroot) and set LD_LIBRARY_PATH. The second is to embed your deps as Meson subprojects with wrap. That is obviously not possible for most deps.

If there is need, we could add a new keyword argument such as build_rpath that has a list of dirs that would be added to the list of rpath dirs. However I highly recommend you avoid rpaths if at all possible. It is one of those things that, unless you are extremely careful, will jump up and bite you in the ass when you least expect it.

@rhd
Copy link
Contributor Author

rhd commented Nov 19, 2015

Hi,

The last time I checked, CMake has two properties for rpath. One for build tree and one after install. CMake stores internal dirs in the rpath so binaries can be run directly from the build tree. They are removed upon ninja install. Meson does the same thing (I stole it directly).

Yes, it does. cmake goes one step further and links in rpath from external libs too. Meson doesn't. I'll follow up tomorrow with a specific example.

Setting rpath yourself is very much undefined behaviour. It might work. It might not work. It might stop working at any time for any reason.

:\

Setting builddir rpath has not been supported for a reason, which is that it is very easy to shoot yourself in the foot with it.

I agree - except I am on a system with a known configuration that must be setup the same way for things to work. In my specific case, the danger of shooting myself in the foot is limited...

If there is need, we could add a new keyword argument such as build_rpath that has a list of dirs that would be added to the list of rpath dirs.

The the project I'm working on, this would be pretty perfect. The code would look like:

lib1_lib_dir = '/path/to/lib1'
lib2_lib_dir = '/path/to/lib2'
exe = executable('blah', src, 
    build_rpath:[lib1_lib_dir, lib2_lib_dir], 
    install_rpath:[lib1_lib_dir, lib2_lib_dir], # would be nice if it could use the 'build_rpath' setting, but I'll live :)
)

However I highly recommend you avoid rpaths if at all possible.

Agreed, I wish I could avoid shared objects all together, but in this case, I can't.

I'll follow up again tomorrow with what I'm seeing cmake do.

Thanks!

@rhd
Copy link
Contributor Author

rhd commented Nov 19, 2015

Here are the bits from cmake files.

top level:

set(LIB_ROOT /usr/local/lib-3.6.0)
set(LIB_INCLUDE_DIRS ${LIB_ROOT}/include/common
 ${LIB_ROOT}/include/inc ${LIB_ROOT}/include/posix
 ${LIB_ROOT}/include/gcc)
set(LIB_LIBRARY_DIRS ${LIB_ROOT}/lib/linux-x64-all
 ${LIB_ROOT}/lib/linux-x64-gcc4.8)
set(LIB_LIBRARIES lib1 lib2)

sub folder

include_directories(${LIB_INCLUDE_DIRS}) 
link_directories(${LIB_LIBRARY_DIRS})
add_executable(foo main.cpp) 

And this outputs (during the link phase):

/usr/local/gcc-4.8.1/bin/g++    -std=c++11    
    CMakeFiles/foo.dir/main.cpp.o  -o foo  
    -L/usr/local/lib-3.6.0/lib/linux-x64-all  
    -L/usr/local/lib-3.6.0/lib/linux-x64-gcc4.8  
    -rdynamic -Wl,-Bstatic -Wl,-Bdynamic -llib1 -llib2 
    -Wl,-rpath,/usr/local/lib-3.6.0/lib/linux-x64-all:/usr/local/lib-3.6.0/lib/linux-x64-gcc4.8

This allows the unit tests to work without any extra work. The rpath gets stripped during install (which is changeable via an option)

@jpakkane
Copy link
Member

What does it do for libraries obtained via FindPackage or pkg-config?

@rhd
Copy link
Contributor Author

rhd commented Nov 20, 2015

The top level

set(BOOST_ROOT /usr/local/boost_1_58_0)
find_package(Boost COMPONENTS program_options iostreams filesystem system REQUIRED)

Generates this: -Wl,-rpath,/usr/local/boost_1_58_0/lib (other paths stripped off).

I'm not sure about pkg-config.

@shlevy
Copy link

shlevy commented Aug 28, 2017

Note that this issue makes meson-build binaries efffectively unusable without further wrapping in nixpkgs, since by design nix doesn't install all libraries into a common prefix and instead puts each library in its own prefix. Is there any workaround from the perspective of a package manager maintainer?

@nirbheek
Copy link
Member

nirbheek commented Sep 4, 2017

Maintainers can edit the build files and set the install_rpath: kwarg on the relevant binaries. Unfortunately that's the only workaround available right now.

For the general case I suppose we would want to set the RPATH when linking to any libraries that aren't already in the linker's search path. This would mean people wouldn't need to manually set install_rpath: when linking to libraries from the same project that are installed into locations outside the linker search path.

I can only see advantages for this, any reason why we shouldn't do this?

@rhd
Copy link
Contributor Author

rhd commented Sep 4, 2017

You'd also need to maintain the build_rpath otherwise ninja test won't work.

This would bring parity to that cmake does and would be a very welcome change.

shlevy referenced this issue in vcunat/nixpkgs Sep 7, 2017
@jtojnar
Copy link
Contributor

jtojnar commented Sep 26, 2017

What would be the implications of this patch:

NixOS/nixpkgs@259a74a

@nirbheek
Copy link
Member

That patch would result in the build-time rpaths being kept in the final binary. You can check by looking at the output of readelf -d <binary>.

@nirbheek
Copy link
Member

That patch would result in the build-time rpaths being kept in the final binary; including those created by meson for running binaries uninstalled. You can check by looking at the output of readelf -d <binary>.

I think we should definitely auto-set RPATH when find_library contains dirs:, otherwise we're building a binary that cannot be run.

@jtojnar
Copy link
Contributor

jtojnar commented Sep 26, 2017

I mean, are there any downsides? Other than more directories to crawl through when loading a library. cc NixOS/nixpkgs#29784 (comment)

@nirbheek
Copy link
Member

nirbheek commented Sep 26, 2017

No real downsides (besides affecting build reproducibility), as long as the build directory is truly temporary and will be gone afterwards.

@lukateras
Copy link

lukateras commented Sep 26, 2017

There is a downside as far as I understand. One could use install_rpath to supply a custom rpath to a directory, for example:

executable('app', 'main.cpp',
    dependencies: [ Xrandr ],
    install: true,
    install_rpath: '../custom-lib-path',
)

With patch @yorickvP proposed that I want(ed) to incorporate in NixOS/nixpkgs#29784, install_rpath would never be actually installed (or rather, appended) to rpath.

I still believe that with this patch, Meson behavior is much more sane (and no project I've seen in the wild actually uses install_rpath this way).

@jpakkane
Copy link
Member

install_rpath: '../custom-lib-path',

This should be $ORIGIN/../custom-libpath. Rpath handling is special in many ways.

@yorickvP
Copy link

I don't remember patches. Leaving the meson build dir in the rpath is a security problem if other people can write to them or change environment variables in them (don't get sanitized by sudo like LD_LIBRARY_PATH). Ideally, meson would only remove it's own rpaths and not the ones added by our gcc wrapper(?).

@morrisonlevi
Copy link

morrisonlevi commented Oct 26, 2017

Chiming in a bit late, but regarding:

For libraries that are public but not in the standard location (this includes things like /usr/local/lib) you need to set the LD_LIBRARY_PATH environment variable.

I'd like to invoke CITATION NEEDED. Everything I've read from knowledgeable sources says that LD_LIBRARY_PATH is designed for temporary usage, not permanent usage. Here are some that explicitly call LD_LIBRARY_PATH bad:

Typically on gcc you would add non-standard software locations to -L and append it to the -Wl,-rpath, preferably with -Wl,--enable-new-dtags. I definitely think meson should make this simple to get right.

Edit: and, incidentally, using LD_LIBRARY_PATH temporarily in the build directory is appropriate and using rpath there would be inappropriate, at least as far as I understand the meson-specific issue.

@NickeZ
Copy link
Contributor

NickeZ commented Oct 27, 2017

If you have public libraries in non-standard locations (like /usr/local/lib) you should probably do echo /usr/local/lib > /etc/ld.so.conf.d/local.conf (at least on debian-based systems). This is the whole purpose of having ldconfig read files in a special .d folder.

@jtojnar
Copy link
Contributor

jtojnar commented Oct 27, 2017

If you have public libraries in non-standard locations (like /usr/local/lib) you should probably do echo /usr/local/lib > /etc/ld.so.conf.d/local.conf (at least on debian-based systems). This is the whole purpose of having ldconfig read files in a special .d folder.

This is not a possibility on NixOS, where every library is stored in its own directory like /nix/store/hash-pkgname. We could add a wrapper script that would set the LD_LIBRARY_PATH variable but handling this on meson level would be much more practical.

@jtojnar
Copy link
Contributor

jtojnar commented Nov 11, 2017

Would there be any disadvantage to doing the following? As I understand it, the whole X business is to ensure the RPATH is not contaminated with the install_rpath when running the app from build directory but I do not see what harm it could do – even if the install_rpath existed on the file system (e.g. /usr/local/lib/deja-dup) the $ORIGIN paths would precede it.

diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 3f088b0f..71a5fe8e 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -834,12 +834,10 @@ class Compiler:
         # Build_rpath is used as-is (it is usually absolute).
         if build_rpath != '':
             paths += ':' + build_rpath
-        if len(paths) < len(install_rpath):
-            padding = 'X' * (len(install_rpath) - len(paths))
-            if not paths:
-                paths = padding
-            else:
-                paths = paths + ':' + padding
+        if not paths:
+            paths = install_rpath
+        else:
+            paths = paths + ':' + install_rpath
         args = ['-Wl,-rpath,' + paths]
         if get_compiler_is_linuxlike(self):
             # Rpaths to use while linking must be absolute. These are not
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
index 985b0e94..665f523b 100644
--- a/mesonbuild/scripts/meson_install.py
+++ b/mesonbuild/scripts/meson_install.py
@@ -353,7 +353,6 @@ def install_targets(d):
         if is_elf_platform() and os.path.isfile(outname):
             try:
                 e = depfixer.Elf(outname, False)
-                e.fix_rpath(install_rpath)
             except SystemExit as e:
                 if isinstance(e.code, int) and e.code == 0:
                     pass

@jpakkane
Copy link
Member

Would there be any disadvantage to doing the following? As I understand it, the whole X business is to ensure the RPATH is not contaminated with the install_rpath when running the app from build directory

The disadvantage would be that it would break everything. The X business is not there to prevent install rpath from being in the build dir, it is to ensure the build dir rpath is not there after an install.

The final product must not contain any traces of the build dir. This is a hard, non-negotiable requirement from Linux distros. If rpath on binaries is not perfectly clean, the package will get autorejected.

netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this issue Feb 25, 2018
By default, meson strips all rpaths, see
mesonbuild/meson#314

Remove the stripping (which might leave build rpaths inside installed binaries
but at least gives us runnable binaries) until meson fixes this properly.

Bump PKGREVISION.
elebihan added a commit to elebihan/buildroot that referenced this issue May 10, 2018
By default, Meson strips RPATH from the executable it builds [1,2]. This will
make support/scripts/check-host-rpath fail.

So add a patch to prevent RPATH from being stripped.

[1] mesonbuild/meson#2567
[2] mesonbuild/meson#314 (comment)
buildroot-auto-update pushed a commit to buildroot/buildroot that referenced this issue May 30, 2018
By default, Meson strips RPATH from the executable it builds [1,2],
unless explicitly set via install_rpath.

This will make support/scripts/check-host-rpath fail when building the
host variant of a Meson-based package.

So add a patch to prevent RPATH from being stripped if install_rpath is
not set and notify user about it.

[1] mesonbuild/meson#2567
[2] mesonbuild/meson#314 (comment)

Signed-off-by: Eric Le Bihan <eric.le.bihan.dev@free.fr>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
nirbheek added a commit that referenced this issue Jun 5, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes #2150
Fixes #2118

RPATHs:
Fixes #314
Fixes #2881

Also fixes the uninstalled usage portions of:
#3038
#3077
nirbheek added a commit that referenced this issue Jun 5, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes #2150
Fixes #2118

RPATHs:
Fixes #314
Fixes #2881

Also fixes the uninstalled usage portions of:
#3038
#3077
nirbheek added a commit that referenced this issue Jun 7, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes #2150
Fixes #2118

RPATHs:
Fixes #314
Fixes #2881

Also fixes the uninstalled usage portions of:
#3038
#3077
nirbheek added a commit that referenced this issue Jun 7, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes #2150
Fixes #2118
Fixes #3071

RPATHs:
Fixes #314
Fixes #2881

Also fixes the uninstalled usage portions of:
#3038
#3077
nirbheek added a commit that referenced this issue Jun 8, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes #2150
Fixes #2118
Fixes #3071

RPATHs:
Fixes #314
Fixes #2881

Also fixes the uninstalled usage portions of:
#3038
#3077
nirbheek added a commit that referenced this issue Jun 10, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes #2150
Fixes #2118
Fixes #3071

RPATHs:
Fixes #314
Fixes #2881

Also fixes the uninstalled usage portions of:
#3038
#3077
nirbheek added a commit that referenced this issue Jun 10, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes #2150
Fixes #2118
Fixes #3071

RPATHs:
Fixes #314
Fixes #2881

Also fixes the uninstalled usage portions of:
#3038
#3077
ananos pushed a commit to ananos/buildroot that referenced this issue Jun 15, 2018
By default, Meson strips RPATH from the executable it builds [1,2],
unless explicitly set via install_rpath.

This will make support/scripts/check-host-rpath fail when building the
host variant of a Meson-based package.

So add a patch to prevent RPATH from being stripped if install_rpath is
not set and notify user about it.

[1] mesonbuild/meson#2567
[2] mesonbuild/meson#314 (comment)

Signed-off-by: Eric Le Bihan <eric.le.bihan.dev@free.fr>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
noverby pushed a commit to noverby/meson that referenced this issue Jun 20, 2018
This allows us to more aggressively de-dup them, and also sets RPATHs
to all libraries that are not in the system linker paths so that
binaries can be run uninstalled without any special steps.

These RPATHs will be wiped on install, so they do not affect
reproducible builds.

De-duping:
Fixes mesonbuild#2150
Fixes mesonbuild#2118
Fixes mesonbuild#3071

RPATHs:
Fixes mesonbuild#314
Fixes mesonbuild#2881

Also fixes the uninstalled usage portions of:
mesonbuild#3038
mesonbuild#3077
@arnout
Copy link

arnout commented Oct 11, 2018

I don't think this is fixed.

Here is a reproduction scenario:

wget http://downloads.sourceforge.net/project/libpng/zlib/1.2.8/zlib-1.2.8.tar.xz
tar xf zlib-1.2.8.tar.xz
cd zlib-1.2.8
./configure --prefix=/tmp/mesonfail
make
make install

cd ..

cat > fail.c <<EOF
#include <zlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
   printf("%s = %s\n", zlibVersion(), ZLIB_VERSION);
   return !!strcmp(zlibVersion(), ZLIB_VERSION);
}

EOF

cat > meson.build <<EOF
project('mesonfail', ['c'])
zdep = dependency('zlib', version : '=1.2.8')
exe = executable('fail', 'fail.c', dependencies: zdep, install: true)
EOF

rm -rf build
mkdir build
cd build
PKG_CONFIG_LIBDIR=/tmp/mesonfail/lib/pkgconfig meson --prefix=/tmp/mesonfail ..
ninja -v install
./fail && echo OK # OK
/tmp/mesonfail/bin/fail || echo FAIL # FAIL unless zlib 1.2.8 is installed in /usr/lib

@xclaesse
Copy link
Member

xclaesse commented Oct 11, 2018

I'm my pr #4321 i use find_library() with 'dirs:' and the rpath seems to work on Linux but not on Mac OSX.

@aduskett
Copy link
Contributor

aduskett commented Nov 30, 2019

Buildroot has had a workaround for over a year now in regards to the fix_rpath on install bug:
https://gitlab.com/buildroot.org/buildroot/blob/master/package/meson/0001-Only-fix-RPATH-if-install_rpath-is-not-empty.patch

It seems like a trivial fix to implement, and I am not sure why it hasn't been done yet:

  1. It's simple.
  2. It works properly.
  3. It's only 8 lines of code, 3 of which are comments, one of which is a print statement.

@dankegel
Copy link
Contributor

dankegel commented May 12, 2020

@arnout, thank you for the easy to reproduce test case.

My attempt at a fix is #7103, now pending review.

You can rescue your test case by applying #7103
and then supplying the right rpath in one of two ways:

  1. by adding LDFLAGS when running ninja, e.g.
 rm -rf build
 mkdir build
 cd build
+LDFLAGS="-Wl,-rpath=/tmp/mesonfail/lib" \
 PKG_CONFIG_LIBDIR=/tmp/mesonfail/lib/pkgconfig ~/src/meson/meson.py --prefix=/tmp/mesonfail ..
 ninja -v install
 ./fail && echo OK # OK
  1. if you don't want to do that, you could supply the rpath by editing zlib.pc, e.g.
 Version: 1.2.8
 
 Requires:
-Libs: -L${libdir} -L${sharedlibdir} -lz
+Libs: -L${libdir} -L${sharedlibdir} -lz -Wl,-rpath=${libdir}
 Cflags: -I${includedir}

AndreRH pushed a commit to AndreRH/buildroot that referenced this issue Jul 8, 2023
By default, Meson strips RPATH from the executable it builds [1,2],
unless explicitly set via install_rpath.

This will make support/scripts/check-host-rpath fail when building the
host variant of a Meson-based package.

So add a patch to prevent RPATH from being stripped if install_rpath is
not set and notify user about it.

[1] mesonbuild/meson#2567
[2] mesonbuild/meson#314 (comment)

Signed-off-by: Eric Le Bihan <eric.le.bihan.dev@free.fr>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests