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
pull/8973/merge
Nirbheek Chauhan 3 years ago committed by Nirbheek Chauhan
parent 95a4c6a62a
commit 06b1132f82
  1. 55
      mesonbuild/backend/backends.py
  2. 3
      test cases/common/44 pkgconfig-gen/answer.c
  3. 7
      test cases/common/44 pkgconfig-gen/foo.c
  4. 14
      test cases/common/44 pkgconfig-gen/meson.build
  5. 1
      test cases/common/44 pkgconfig-gen/test.json
  6. 2
      unittests/allplatformstests.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)

@ -0,0 +1,3 @@
int answer_to_life_the_universe_and_everything(void) {
return 42;
}

@ -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();
}

@ -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.',

@ -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"},

@ -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')

Loading…
Cancel
Save