diff options
author | Nirbheek Chauhan <nirbheek@centricular.com> | 2021-11-15 17:44:34 +0530 |
---|---|---|
committer | Nirbheek Chauhan <nirbheek.chauhan@gmail.com> | 2021-12-22 12:12:11 +0530 |
commit | 06b1132f82eaaf805021b4b088701d9754e2af38 (patch) | |
tree | f0d27ceae0864a09cba5ac5a8295d3c0345b6459 | |
parent | 95a4c6a62a925fc888e7c07ae3d9937c87e1c759 (diff) | |
download | meson-06b1132f82eaaf805021b4b088701d9754e2af38.zip meson-06b1132f82eaaf805021b4b088701d9754e2af38.tar.gz meson-06b1132f82eaaf805021b4b088701d9754e2af38.tar.bz2 |
Set RPATH for all non-system libs with absolute paths
If a pkg-config dependency has multiple libraries in it, which is the
most common case when it has a Requires: directive, or when it has
multiple -l args in Libs: (rare), then we don't add -Wl,-rpath
directives to it when linking.
The existing test wasn't catching it because it was linking to
a pkgconfig file with a single library in it. Update the test to
demonstrate this.
This function was originally added for shared libraries in the source
directory, which explains the name:
https://github.com/mesonbuild/meson/pull/2397
However, since now it is also used for linking to *all* non-system
shared libraries that we link to with absolute paths:
https://github.com/mesonbuild/meson/pull/3092
But that PR is incomplete / wrong, because only adding RPATHs for
dependencies that specify a single library, which is simply
inconsistent. Things will work for some dependencies and not work for
others, with no logical reason for it.
We should add RPATHs for *all* libraries. There are no special length
limits for RPATHs that I can find.
For ELF, DT_RPATH or DT_RUNPATH are used, which are just stored in
a string table (DT_STRTAB). The maximum length is only a problem when
editing pre-existing tags.
For Mach-O, each RPATH is stored in a separate LC_RPATH entry so there
are no length issues there either.
Fixes https://github.com/mesonbuild/meson/issues/9543
Fixes https://github.com/mesonbuild/meson/issues/4372
-rw-r--r-- | mesonbuild/backend/backends.py | 55 | ||||
-rw-r--r-- | test cases/common/44 pkgconfig-gen/answer.c | 3 | ||||
-rw-r--r-- | test cases/common/44 pkgconfig-gen/foo.c | 7 | ||||
-rw-r--r-- | test cases/common/44 pkgconfig-gen/meson.build | 14 | ||||
-rw-r--r-- | test cases/common/44 pkgconfig-gen/test.json | 1 | ||||
-rw-r--r-- | unittests/allplatformstests.py | 2 |
6 files changed, 50 insertions, 32 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4b368e7..177f24a 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -751,37 +751,36 @@ class Backend: return dirs @lru_cache(maxsize=None) - def rpaths_for_bundled_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]': - paths: T.List[str] = [] + def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]': + paths: OrderedSet[str] = OrderedSet() for dep in target.external_deps: if not isinstance(dep, (dependencies.ExternalLibrary, dependencies.PkgConfigDependency)): continue - la = dep.link_args - if len(la) != 1 or not os.path.isabs(la[0]): - continue - # The only link argument is an absolute path to a library file. - libpath = la[0] - libdir = os.path.dirname(libpath) - 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']: - continue - if libdir.startswith(self.environment.get_source_dir()): - rel_to_src = libdir[len(self.environment.get_source_dir()) + 1:] - assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute' - paths.append(os.path.join(self.build_to_src, rel_to_src)) - else: - paths.append(libdir) + for libpath in dep.link_args: + # For all link args that are absolute paths to a library file, add RPATH args + if not os.path.isabs(libpath): + continue + libdir = os.path.dirname(libpath) + 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']: + continue + if libdir.startswith(self.environment.get_source_dir()): + rel_to_src = libdir[len(self.environment.get_source_dir()) + 1:] + assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute' + paths.add(os.path.join(self.build_to_src, rel_to_src)) + else: + paths.add(libdir) for i in chain(target.link_targets, target.link_whole_targets): if isinstance(i, build.BuildTarget): - paths.extend(self.rpaths_for_bundled_shared_libraries(i, exclude_system)) - return paths + paths.update(self.rpaths_for_non_system_absolute_shared_libraries(i, exclude_system)) + return list(paths) # This may take other types def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex] @@ -794,7 +793,7 @@ class Backend: result = OrderedSet() result.add('meson-out') if isinstance(target, build.BuildTarget): - result.update(self.rpaths_for_bundled_shared_libraries(target)) + result.update(self.rpaths_for_non_system_absolute_shared_libraries(target)) target.rpath_dirs_to_remove.update([d.encode('utf-8') for d in result]) return tuple(result) @@ -1072,7 +1071,7 @@ class Backend: if isinstance(target, build.BuildTarget): prospectives.update(target.get_transitive_link_deps()) # External deps - for deppath in self.rpaths_for_bundled_shared_libraries(target, exclude_system=False): + for deppath in self.rpaths_for_non_system_absolute_shared_libraries(target, exclude_system=False): result.add(os.path.normpath(os.path.join(self.environment.get_build_dir(), deppath))) for bdep in extra_bdeps: prospectives.add(bdep) diff --git a/test cases/common/44 pkgconfig-gen/answer.c b/test cases/common/44 pkgconfig-gen/answer.c new file mode 100644 index 0000000..df5f54f --- /dev/null +++ b/test cases/common/44 pkgconfig-gen/answer.c @@ -0,0 +1,3 @@ +int answer_to_life_the_universe_and_everything(void) { + return 42; +} diff --git a/test cases/common/44 pkgconfig-gen/foo.c b/test cases/common/44 pkgconfig-gen/foo.c new file mode 100644 index 0000000..83bb06a --- /dev/null +++ b/test cases/common/44 pkgconfig-gen/foo.c @@ -0,0 +1,7 @@ +#include"simple.h" + +int answer_to_life_the_universe_and_everything (void); + +int simple_function(void) { + return answer_to_life_the_universe_and_everything(); +} diff --git a/test cases/common/44 pkgconfig-gen/meson.build b/test cases/common/44 pkgconfig-gen/meson.build index 7021d5d..5e9141f 100644 --- a/test cases/common/44 pkgconfig-gen/meson.build +++ b/test cases/common/44 pkgconfig-gen/meson.build @@ -42,13 +42,21 @@ test('pkgconfig-validation', pkgconfig, args: ['--validate', 'simple'], env: [ 'PKG_CONFIG_PATH=' + meson.current_build_dir() + '/meson-private' ]) +answerlib = shared_library('answer', 'answer.c') + +pkgg.generate(answerlib, + name : 'libanswer', + description : 'An answer library.', +) + # Test that name_prefix='' and name='libfoo' results in '-lfoo' -lib2 = shared_library('libfoo', 'simple.c', +lib2 = shared_library('libfoo', 'foo.c', + link_with: answerlib, name_prefix : '', version : libver) -pkgg.generate( - libraries : lib2, +pkgg.generate(lib2, + libraries : [lib2, answerlib], name : 'libfoo', version : libver, description : 'A foo library.', diff --git a/test cases/common/44 pkgconfig-gen/test.json b/test cases/common/44 pkgconfig-gen/test.json index 8add563..c7bdd43 100644 --- a/test cases/common/44 pkgconfig-gen/test.json +++ b/test cases/common/44 pkgconfig-gen/test.json @@ -3,6 +3,7 @@ {"type": "file", "file": "usr/include/simple.h"}, {"type": "file", "file": "usr/lib/libstat2.a"}, {"type": "file", "file": "usr/lib/pkgconfig/simple.pc"}, + {"type": "file", "file": "usr/lib/pkgconfig/libanswer.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libfoo.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libhello.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libhello_nolib.pc"}, diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index daa1385..7437d78 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -1595,7 +1595,7 @@ class AllPlatformTests(BasePlatformTests): foo_dep = PkgConfigDependency('libfoo', env, kwargs) # Ensure link_args are properly quoted libdir = PurePath(prefix) / PurePath(libdir) - link_args = ['-L' + libdir.as_posix(), '-lfoo'] + link_args = ['-L' + libdir.as_posix(), '-lfoo', '-lanswer'] self.assertEqual(foo_dep.get_link_args(), link_args) # Ensure include args are properly quoted incdir = PurePath(prefix) / PurePath('include') |