From 84d7f867db667a368b8cfc45f681aafe4b709b8c Mon Sep 17 00:00:00 2001 From: Dan Kegel Date: Tue, 12 May 2020 09:28:01 -0700 Subject: [PATCH] Let LDFLAGS specify rpath. Fixes #2567 --- mesonbuild/backend/backends.py | 18 +++++++++++ run_unittests.py | 30 +++++++++++++++++++ test cases/unit/77 global-rpath/meson.build | 3 ++ .../unit/77 global-rpath/rpathified.cpp | 6 ++++ .../unit/77 global-rpath/yonder/meson.build | 5 ++++ .../unit/77 global-rpath/yonder/yonder.cpp | 3 ++ .../unit/77 global-rpath/yonder/yonder.h | 1 + 7 files changed, 66 insertions(+) create mode 100644 test cases/unit/77 global-rpath/meson.build create mode 100644 test cases/unit/77 global-rpath/rpathified.cpp create mode 100644 test cases/unit/77 global-rpath/yonder/meson.build create mode 100644 test cases/unit/77 global-rpath/yonder/yonder.cpp create mode 100644 test cases/unit/77 global-rpath/yonder/yonder.h diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 45313a7689a0..480d909bfb5d 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -444,6 +444,21 @@ def _libdir_is_system(libdir, compilers, env): return True return False + def get_external_rpath_dirs(self, target): + dirs = set() + args = [] + # FIXME: is there a better way? + for lang in ['c', 'cpp']: + try: + args.extend(self.environment.coredata.get_external_link_args(target.for_machine, lang)) + except: + pass + for arg in args: + if arg.startswith('-Wl,-rpath='): + for dir in arg.replace('-Wl,-rpath=','').split(':'): + dirs.add(dir) + return dirs + def rpaths_for_bundled_shared_libraries(self, target, exclude_system=True): paths = [] for dep in target.external_deps: @@ -458,6 +473,9 @@ def rpaths_for_bundled_shared_libraries(self, target, exclude_system=True): if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment): # No point in adding system paths. continue + # Don't remove rpaths specified in LDFLAGS. + if libdir in self.get_external_rpath_dirs(target): + continue # Windows doesn't support rpaths, but we use this function to # emulate rpaths by setting PATH, so also accept DLLs here if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']: diff --git a/run_unittests.py b/run_unittests.py index d7fc221dcde1..201deb9db3ce 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -6079,6 +6079,36 @@ def test_build_rpath(self): install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx')) self.assertEqual(install_rpath, 'baz') + def test_global_rpath(self): + if is_cygwin(): + raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH') + testdir = os.path.join(self.unit_test_dir, '77 global-rpath') + oldinstalldir = self.installdir + + # Build and install an external library without DESTDIR. + # The external library generates a .pc file without an rpath. + yonder_dir = os.path.join(testdir, 'yonder') + yonder_prefix = os.path.join(oldinstalldir, 'yonder') + yonder_libdir = os.path.join(yonder_prefix, self.libdir) + self.prefix = yonder_prefix + self.installdir = yonder_prefix + self.init(yonder_dir) + self.build() + self.install(use_destdir=False) + self.new_builddir() + + # Build an app that uses that installed library. + # Supply the rpath to the installed library via LDFLAGS + # (as systems like buildroot and guix are wont to do) + # and verify install preserves that rpath. + env = {'LDFLAGS': '-Wl,-rpath=' + yonder_libdir, + 'PKG_CONFIG_PATH': os.path.join(yonder_libdir, 'pkgconfig')} + self.init(testdir, override_envvars=env) + self.build() + self.install(use_destdir=False) + got_rpath = get_rpath(os.path.join(yonder_prefix, 'bin/rpathified')) + self.assertEqual(got_rpath, yonder_libdir) + @skip_if_not_base_option('b_sanitize') def test_pch_with_address_sanitizer(self): if is_cygwin(): diff --git a/test cases/unit/77 global-rpath/meson.build b/test cases/unit/77 global-rpath/meson.build new file mode 100644 index 000000000000..c67d9e08b984 --- /dev/null +++ b/test cases/unit/77 global-rpath/meson.build @@ -0,0 +1,3 @@ +project('global-rpath', 'cpp') +yonder_dep = dependency('yonder') +executable('rpathified', 'rpathified.cpp', dependencies: [yonder_dep], install: true) diff --git a/test cases/unit/77 global-rpath/rpathified.cpp b/test cases/unit/77 global-rpath/rpathified.cpp new file mode 100644 index 000000000000..3788906dc0b6 --- /dev/null +++ b/test cases/unit/77 global-rpath/rpathified.cpp @@ -0,0 +1,6 @@ +#include +#include +int main(int argc, char **argv) +{ + return strcmp(yonder(), "AB54 6BR"); +} diff --git a/test cases/unit/77 global-rpath/yonder/meson.build b/test cases/unit/77 global-rpath/yonder/meson.build new file mode 100644 index 000000000000..e32f3833019c --- /dev/null +++ b/test cases/unit/77 global-rpath/yonder/meson.build @@ -0,0 +1,5 @@ +project('yonder', 'cpp') +yonder = shared_library('yonder', 'yonder.cpp', install: true) +install_headers('yonder.h') +pkgconfig = import('pkgconfig') +pkgconfig.generate(yonder) diff --git a/test cases/unit/77 global-rpath/yonder/yonder.cpp b/test cases/unit/77 global-rpath/yonder/yonder.cpp new file mode 100644 index 000000000000..b182d34e323f --- /dev/null +++ b/test cases/unit/77 global-rpath/yonder/yonder.cpp @@ -0,0 +1,3 @@ +#include "yonder.h" + +char *yonder(void) { return "AB54 6BR"; } diff --git a/test cases/unit/77 global-rpath/yonder/yonder.h b/test cases/unit/77 global-rpath/yonder/yonder.h new file mode 100644 index 000000000000..9d9ad1617e5a --- /dev/null +++ b/test cases/unit/77 global-rpath/yonder/yonder.h @@ -0,0 +1 @@ +char *yonder(void);