aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNirbheek Chauhan <nirbheek@centricular.com>2021-11-15 17:44:34 +0530
committerNirbheek Chauhan <nirbheek.chauhan@gmail.com>2021-12-22 12:12:11 +0530
commit06b1132f82eaaf805021b4b088701d9754e2af38 (patch)
treef0d27ceae0864a09cba5ac5a8295d3c0345b6459
parent95a4c6a62a925fc888e7c07ae3d9937c87e1c759 (diff)
downloadmeson-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.py55
-rw-r--r--test cases/common/44 pkgconfig-gen/answer.c3
-rw-r--r--test cases/common/44 pkgconfig-gen/foo.c7
-rw-r--r--test cases/common/44 pkgconfig-gen/meson.build14
-rw-r--r--test cases/common/44 pkgconfig-gen/test.json1
-rw-r--r--unittests/allplatformstests.py2
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')