Merge pull request #7103 from dankegel/bug4027-rpath-remember

Let .pc files and LDFLAGS provide rpaths.
pull/7084/head
Jussi Pakkanen 5 years ago committed by GitHub
commit 751ea3df72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      docs/markdown/snippets/rpath_behavior.md
  2. 33
      mesonbuild/backend/backends.py
  3. 7
      mesonbuild/backend/ninjabackend.py
  4. 2
      mesonbuild/build.py
  5. 2
      mesonbuild/compilers/compilers.py
  6. 7
      mesonbuild/compilers/cuda.py
  7. 9
      mesonbuild/compilers/d.py
  8. 4
      mesonbuild/compilers/mixins/islinker.py
  9. 49
      mesonbuild/linkers.py
  10. 2
      mesonbuild/minstall.py
  11. 32
      mesonbuild/scripts/depfixer.py
  12. 92
      run_unittests.py
  13. 5
      test cases/unit/40 external, internal library rpath/built library/meson.build
  14. 6
      test cases/unit/40 external, internal library rpath/external library/meson.build
  15. 8
      test cases/unit/76 pkgconfig prefixes/client/client.c
  16. 3
      test cases/unit/76 pkgconfig prefixes/client/meson.build
  17. 5
      test cases/unit/76 pkgconfig prefixes/val1/meson.build
  18. 3
      test cases/unit/76 pkgconfig prefixes/val1/val1.c
  19. 1
      test cases/unit/76 pkgconfig prefixes/val1/val1.h
  20. 8
      test cases/unit/76 pkgconfig prefixes/val2/meson.build
  21. 4
      test cases/unit/76 pkgconfig prefixes/val2/val2.c
  22. 1
      test cases/unit/76 pkgconfig prefixes/val2/val2.h
  23. 3
      test cases/unit/77 global-rpath/meson.build
  24. 6
      test cases/unit/77 global-rpath/rpathified.cpp
  25. 5
      test cases/unit/77 global-rpath/yonder/meson.build
  26. 3
      test cases/unit/77 global-rpath/yonder/yonder.cpp
  27. 1
      test cases/unit/77 global-rpath/yonder/yonder.h

@ -0,0 +1,7 @@
## rpath removal now more careful
On Linux-like systems, meson adds rpath entries to allow running apps
in the build tree, and then removes those build-time-only
rpath entries when installing. Rpath entries may also come
in via LDFLAGS and via .pc files. Meson used to remove those
latter rpath entries by accident, but is now more careful.

@ -90,12 +90,13 @@ class InstallData:
self.mesonintrospect = mesonintrospect self.mesonintrospect = mesonintrospect
class TargetInstallData: class TargetInstallData:
def __init__(self, fname, outdir, aliases, strip, install_name_mappings, install_rpath, install_mode, optional=False): def __init__(self, fname, outdir, aliases, strip, install_name_mappings, rpath_dirs_to_remove, install_rpath, install_mode, optional=False):
self.fname = fname self.fname = fname
self.outdir = outdir self.outdir = outdir
self.aliases = aliases self.aliases = aliases
self.strip = strip self.strip = strip
self.install_name_mappings = install_name_mappings self.install_name_mappings = install_name_mappings
self.rpath_dirs_to_remove = rpath_dirs_to_remove
self.install_rpath = install_rpath self.install_rpath = install_rpath
self.install_mode = install_mode self.install_mode = install_mode
self.optional = optional self.optional = optional
@ -443,6 +444,21 @@ class Backend:
return True return True
return False 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 Exception:
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): def rpaths_for_bundled_shared_libraries(self, target, exclude_system=True):
paths = [] paths = []
for dep in target.external_deps: for dep in target.external_deps:
@ -457,6 +473,9 @@ class Backend:
if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment): if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment):
# No point in adding system paths. # No point in adding system paths.
continue 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 # Windows doesn't support rpaths, but we use this function to
# emulate rpaths by setting PATH, so also accept DLLs here # emulate rpaths by setting PATH, so also accept DLLs here
if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']: if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']:
@ -476,6 +495,7 @@ class Backend:
result = OrderedSet() result = OrderedSet()
result.add('meson-out') result.add('meson-out')
result.update(self.rpaths_for_bundled_shared_libraries(target)) result.update(self.rpaths_for_bundled_shared_libraries(target))
target.rpath_dirs_to_remove.update([d.encode('utf8') for d in result])
return tuple(result) return tuple(result)
def object_filename_from_source(self, target, source): def object_filename_from_source(self, target, source):
@ -1140,6 +1160,7 @@ class Backend:
mappings = t.get_link_deps_mapping(d.prefix, self.environment) mappings = t.get_link_deps_mapping(d.prefix, self.environment)
i = TargetInstallData(self.get_target_filename(t), outdirs[0], i = TargetInstallData(self.get_target_filename(t), outdirs[0],
t.get_aliases(), should_strip, mappings, t.get_aliases(), should_strip, mappings,
t.rpath_dirs_to_remove,
t.install_rpath, install_mode) t.install_rpath, install_mode)
d.targets.append(i) d.targets.append(i)
@ -1157,14 +1178,14 @@ class Backend:
implib_install_dir = self.environment.get_import_lib_dir() implib_install_dir = self.environment.get_import_lib_dir()
# Install the import library; may not exist for shared modules # Install the import library; may not exist for shared modules
i = TargetInstallData(self.get_target_filename_for_linking(t), i = TargetInstallData(self.get_target_filename_for_linking(t),
implib_install_dir, {}, False, {}, '', install_mode, implib_install_dir, {}, False, {}, set(), '', install_mode,
optional=isinstance(t, build.SharedModule)) optional=isinstance(t, build.SharedModule))
d.targets.append(i) d.targets.append(i)
if not should_strip and t.get_debug_filename(): if not should_strip and t.get_debug_filename():
debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename()) debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename())
i = TargetInstallData(debug_file, outdirs[0], i = TargetInstallData(debug_file, outdirs[0],
{}, False, {}, '', {}, False, {}, set(), '',
install_mode, optional=True) install_mode, optional=True)
d.targets.append(i) d.targets.append(i)
# Install secondary outputs. Only used for Vala right now. # Install secondary outputs. Only used for Vala right now.
@ -1174,7 +1195,7 @@ class Backend:
if outdir is False: if outdir is False:
continue continue
f = os.path.join(self.get_target_dir(t), output) f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode) i = TargetInstallData(f, outdir, {}, False, {}, set(), None, install_mode)
d.targets.append(i) d.targets.append(i)
elif isinstance(t, build.CustomTarget): elif isinstance(t, build.CustomTarget):
# If only one install_dir is specified, assume that all # If only one install_dir is specified, assume that all
@ -1187,7 +1208,7 @@ class Backend:
if num_outdirs == 1 and num_out > 1: if num_outdirs == 1 and num_out > 1:
for output in t.get_outputs(): for output in t.get_outputs():
f = os.path.join(self.get_target_dir(t), output) f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdirs[0], {}, False, {}, None, install_mode, i = TargetInstallData(f, outdirs[0], {}, False, {}, set(), None, install_mode,
optional=not t.build_by_default) optional=not t.build_by_default)
d.targets.append(i) d.targets.append(i)
else: else:
@ -1196,7 +1217,7 @@ class Backend:
if outdir is False: if outdir is False:
continue continue
f = os.path.join(self.get_target_dir(t), output) f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode, i = TargetInstallData(f, outdir, {}, False, {}, set(), None, install_mode,
optional=not t.build_by_default) optional=not t.build_by_default)
d.targets.append(i) d.targets.append(i)

@ -1348,7 +1348,8 @@ int dummy;
self.get_target_dir(target)) self.get_target_dir(target))
else: else:
target_slashname_workaround_dir = self.get_target_dir(target) target_slashname_workaround_dir = self.get_target_dir(target)
rpath_args = rustc.build_rpath_args(self.environment, (rpath_args, target.rpath_dirs_to_remove) = \
rustc.build_rpath_args(self.environment,
self.environment.get_build_dir(), self.environment.get_build_dir(),
target_slashname_workaround_dir, target_slashname_workaround_dir,
self.determine_rpath_dirs(target), self.determine_rpath_dirs(target),
@ -2580,12 +2581,14 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.get_target_dir(target)) self.get_target_dir(target))
else: else:
target_slashname_workaround_dir = self.get_target_dir(target) target_slashname_workaround_dir = self.get_target_dir(target)
commands += linker.build_rpath_args(self.environment, (rpath_args, target.rpath_dirs_to_remove) = \
linker.build_rpath_args(self.environment,
self.environment.get_build_dir(), self.environment.get_build_dir(),
target_slashname_workaround_dir, target_slashname_workaround_dir,
self.determine_rpath_dirs(target), self.determine_rpath_dirs(target),
target.build_rpath, target.build_rpath,
target.install_rpath) target.install_rpath)
commands += rpath_args
# Add libraries generated by custom targets # Add libraries generated by custom targets
custom_target_libraries = self.get_custom_target_provided_libraries(target) custom_target_libraries = self.get_custom_target_provided_libraries(target)
commands += extra_args commands += extra_args

@ -512,6 +512,8 @@ class BuildTarget(Target):
self.d_features = {} self.d_features = {}
self.pic = False self.pic = False
self.pie = False self.pie = False
# Track build_rpath entries so we can remove them at install time
self.rpath_dirs_to_remove = set()
# Sources can be: # Sources can be:
# 1. Pre-existing source files in the source tree # 1. Pre-existing source files in the source tree
# 2. Pre-existing sources generated by configure_file in the build tree # 2. Pre-existing sources generated by configure_file in the build tree

@ -1077,7 +1077,7 @@ class Compiler:
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
return self.linker.build_rpath_args( return self.linker.build_rpath_args(
env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)

@ -271,9 +271,10 @@ class CudaCompiler(Compiler):
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
return self._cook_link_args(self.host_compiler.build_rpath_args( (rpath_args, rpath_dirs_to_remove) = self.host_compiler.build_rpath_args(
env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)) env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
return (self._cook_link_args(rpath_args), rpath_dirs_to_remove)
def linker_to_compiler_args(self, args): def linker_to_compiler_args(self, args):
return args return args

@ -220,7 +220,7 @@ class DmdLikeCompilerMixin:
def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
if self.info.is_windows(): if self.info.is_windows():
return [] return ([], set())
# GNU ld, solaris ld, and lld acting like GNU ld # GNU ld, solaris ld, and lld acting like GNU ld
if self.linker.id.startswith('ld'): if self.linker.id.startswith('ld'):
@ -228,15 +228,16 @@ class DmdLikeCompilerMixin:
# do directly, each argument -rpath and the value to rpath, need to be # do directly, each argument -rpath and the value to rpath, need to be
# split into two separate arguments both prefaced with the -L=. # split into two separate arguments both prefaced with the -L=.
args = [] args = []
for r in super().build_rpath_args( (rpath_args, rpath_dirs_to_remove) = super().build_rpath_args(
env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
for r in rpath_args:
if ',' in r: if ',' in r:
a, b = r.split(',', maxsplit=1) a, b = r.split(',', maxsplit=1)
args.append(a) args.append(a)
args.append(self.LINKER_PREFIX + b) args.append(self.LINKER_PREFIX + b)
else: else:
args.append(r) args.append(r)
return args return (args, rpath_dirs_to_remove)
return super().build_rpath_args( return super().build_rpath_args(
env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)

@ -107,8 +107,8 @@ class BasicLinkerIsCompilerMixin:
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
return [] return ([], set())
def get_linker_debug_crt_args(self) -> T.List[str]: def get_linker_debug_crt_args(self) -> T.List[str]:
return [] return []

@ -56,8 +56,8 @@ class StaticLinker:
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
return [] return ([], set())
def thread_link_flags(self, env: 'Environment') -> T.List[str]: def thread_link_flags(self, env: 'Environment') -> T.List[str]:
return [] return []
@ -444,8 +444,8 @@ class DynamicLinker(LinkerEnvVarsMixin, metaclass=abc.ABCMeta):
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
return [] return ([], set())
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], suffix: str, soversion: str, darwin_versions: T.Tuple[str, str],
@ -551,12 +551,12 @@ class GnuLikeDynamicLinkerMixin:
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
m = env.machines[self.for_machine] m = env.machines[self.for_machine]
if m.is_windows() or m.is_cygwin(): if m.is_windows() or m.is_cygwin():
return [] return ([], set())
if not rpath_paths and not install_rpath and not build_rpath: if not rpath_paths and not install_rpath and not build_rpath:
return [] return ([], set())
args = [] args = []
origin_placeholder = '$ORIGIN' origin_placeholder = '$ORIGIN'
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir)
@ -564,9 +564,14 @@ class GnuLikeDynamicLinkerMixin:
# is *very* allergic to duplicate -delete_rpath arguments # is *very* allergic to duplicate -delete_rpath arguments
# when calling depfixer on installation. # when calling depfixer on installation.
all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths]) all_paths = mesonlib.OrderedSet([os.path.join(origin_placeholder, p) for p in processed_rpaths])
rpath_dirs_to_remove = set()
for p in all_paths:
rpath_dirs_to_remove.add(p.encode('utf8'))
# Build_rpath is used as-is (it is usually absolute). # Build_rpath is used as-is (it is usually absolute).
if build_rpath != '': if build_rpath != '':
all_paths.add(build_rpath) all_paths.add(build_rpath)
for p in build_rpath.split(':'):
rpath_dirs_to_remove.add(p.encode('utf8'))
# TODO: should this actually be "for (dragonfly|open)bsd"? # TODO: should this actually be "for (dragonfly|open)bsd"?
if mesonlib.is_dragonflybsd() or mesonlib.is_openbsd(): if mesonlib.is_dragonflybsd() or mesonlib.is_openbsd():
@ -590,7 +595,7 @@ class GnuLikeDynamicLinkerMixin:
# TODO: should this actually be "for solaris/sunos"? # TODO: should this actually be "for solaris/sunos"?
if mesonlib.is_sunos(): if mesonlib.is_sunos():
return args return (args, rpath_dirs_to_remove)
# Rpaths to use while linking must be absolute. These are not # Rpaths to use while linking must be absolute. These are not
# written to the binary. Needed only with GNU ld: # written to the binary. Needed only with GNU ld:
@ -610,7 +615,7 @@ class GnuLikeDynamicLinkerMixin:
for p in rpath_paths: for p in rpath_paths:
args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p))) args.extend(self._apply_prefix('-rpath-link,' + os.path.join(build_dir, p)))
return args return (args, rpath_dirs_to_remove)
class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker): class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
@ -676,9 +681,9 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
if not rpath_paths and not install_rpath and not build_rpath: if not rpath_paths and not install_rpath and not build_rpath:
return [] return ([], set())
# Ensure that there is enough space for install_name_tool in-place # Ensure that there is enough space for install_name_tool in-place
# editing of large RPATHs # editing of large RPATHs
args = self._apply_prefix('-headerpad_max_install_names') args = self._apply_prefix('-headerpad_max_install_names')
@ -692,7 +697,7 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
for rp in all_paths: for rp in all_paths:
args.extend(self._apply_prefix('-rpath,' + rp)) args.extend(self._apply_prefix('-rpath,' + rp))
return args return (args, set())
class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker): class GnuDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, DynamicLinker):
@ -763,8 +768,8 @@ class WASMDynamicLinker(GnuLikeDynamicLinkerMixin, PosixDynamicLinkerMixin, Dyna
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
return [] return ([], set())
class CcrxDynamicLinker(DynamicLinker): class CcrxDynamicLinker(DynamicLinker):
@ -839,8 +844,8 @@ class Xc16DynamicLinker(DynamicLinker):
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
return [] return ([], set())
class C2000DynamicLinker(DynamicLinker): class C2000DynamicLinker(DynamicLinker):
@ -938,10 +943,10 @@ class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
if not env.machines[self.for_machine].is_windows(): if not env.machines[self.for_machine].is_windows():
return ['-R' + os.path.join(build_dir, p) for p in rpath_paths] return (['-R' + os.path.join(build_dir, p) for p in rpath_paths], set())
return [] return ([], set())
class PGIStaticLinker(StaticLinker): class PGIStaticLinker(StaticLinker):
@ -1091,9 +1096,9 @@ class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str, rpath_paths: str, build_rpath: str,
install_rpath: str) -> T.List[str]: install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
if not rpath_paths and not install_rpath and not build_rpath: if not rpath_paths and not install_rpath and not build_rpath:
return [] return ([], set())
processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir) processed_rpaths = prepare_rpaths(rpath_paths, build_dir, from_dir)
all_paths = mesonlib.OrderedSet([os.path.join('$ORIGIN', p) for p in processed_rpaths]) all_paths = mesonlib.OrderedSet([os.path.join('$ORIGIN', p) for p in processed_rpaths])
if build_rpath != '': if build_rpath != '':
@ -1108,7 +1113,7 @@ class SolarisDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
paths = padding paths = padding
else: else:
paths = paths + ':' + padding paths = paths + ':' + padding
return self._apply_prefix('-rpath,{}'.format(paths)) return (self._apply_prefix('-rpath,{}'.format(paths)), set())
def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
suffix: str, soversion: str, darwin_versions: T.Tuple[str, str], suffix: str, soversion: str, darwin_versions: T.Tuple[str, str],

@ -512,7 +512,7 @@ class Installer:
if file_copied: if file_copied:
self.did_install_something = True self.did_install_something = True
try: try:
depfixer.fix_rpath(outname, install_rpath, final_path, depfixer.fix_rpath(outname, t.rpath_dirs_to_remove, install_rpath, final_path,
install_name_mappings, verbose=False) install_name_mappings, verbose=False)
except SystemExit as e: except SystemExit as e:
if isinstance(e.code, int) and e.code == 0: if isinstance(e.code, int) and e.code == 0:

@ -290,13 +290,13 @@ class Elf(DataSizes):
self.bf.seek(offset) self.bf.seek(offset)
self.bf.write(newname) self.bf.write(newname)
def fix_rpath(self, new_rpath): def fix_rpath(self, rpath_dirs_to_remove, new_rpath):
# The path to search for can be either rpath or runpath. # The path to search for can be either rpath or runpath.
# Fix both of them to be sure. # Fix both of them to be sure.
self.fix_rpathtype_entry(new_rpath, DT_RPATH) self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RPATH)
self.fix_rpathtype_entry(new_rpath, DT_RUNPATH) self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RUNPATH)
def fix_rpathtype_entry(self, new_rpath, entrynum): def fix_rpathtype_entry(self, rpath_dirs_to_remove, new_rpath, entrynum):
if isinstance(new_rpath, str): if isinstance(new_rpath, str):
new_rpath = new_rpath.encode('utf8') new_rpath = new_rpath.encode('utf8')
rp_off = self.get_entry_offset(entrynum) rp_off = self.get_entry_offset(entrynum)
@ -305,7 +305,23 @@ class Elf(DataSizes):
print('File does not have rpath. It should be a fully static executable.') print('File does not have rpath. It should be a fully static executable.')
return return
self.bf.seek(rp_off) self.bf.seek(rp_off)
old_rpath = self.read_str() old_rpath = self.read_str()
new_rpaths = []
if new_rpath:
new_rpaths.append(new_rpath)
if old_rpath:
# Filter out build-only rpath entries
# added by get_link_dep_subdirs() or
# specified by user with build_rpath.
for dir in old_rpath.split(b':'):
if not (dir in rpath_dirs_to_remove or
dir == (b'X' * len(dir))):
new_rpaths.append(dir)
# Prepend user-specified new entries while preserving the ones that came from pkgconfig etc.
new_rpath = b':'.join(new_rpaths)
if len(old_rpath) < len(new_rpath): if len(old_rpath) < len(new_rpath):
sys.exit("New rpath must not be longer than the old one.") sys.exit("New rpath must not be longer than the old one.")
# The linker does read-only string deduplication. If there is a # The linker does read-only string deduplication. If there is a
@ -343,13 +359,13 @@ class Elf(DataSizes):
entry.write(self.bf) entry.write(self.bf)
return None return None
def fix_elf(fname, new_rpath, verbose=True): def fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose=True):
with Elf(fname, verbose) as e: with Elf(fname, verbose) as e:
if new_rpath is None: if new_rpath is None:
e.print_rpath() e.print_rpath()
e.print_runpath() e.print_runpath()
else: else:
e.fix_rpath(new_rpath) e.fix_rpath(rpath_dirs_to_remove, new_rpath)
def get_darwin_rpaths_to_remove(fname): def get_darwin_rpaths_to_remove(fname):
out = subprocess.check_output(['otool', '-l', fname], out = subprocess.check_output(['otool', '-l', fname],
@ -430,7 +446,7 @@ def fix_jar(fname):
f.truncate() f.truncate()
subprocess.check_call(['jar', 'ufm', fname, 'META-INF/MANIFEST.MF']) subprocess.check_call(['jar', 'ufm', fname, 'META-INF/MANIFEST.MF'])
def fix_rpath(fname, new_rpath, final_path, install_name_mappings, verbose=True): def fix_rpath(fname, rpath_dirs_to_remove, new_rpath, final_path, install_name_mappings, verbose=True):
global INSTALL_NAME_TOOL global INSTALL_NAME_TOOL
# Static libraries, import libraries, debug information, headers, etc # Static libraries, import libraries, debug information, headers, etc
# never have rpaths # never have rpaths
@ -441,7 +457,7 @@ def fix_rpath(fname, new_rpath, final_path, install_name_mappings, verbose=True)
if fname.endswith('.jar'): if fname.endswith('.jar'):
fix_jar(fname) fix_jar(fname)
return return
fix_elf(fname, new_rpath, verbose) fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose)
return return
except SystemExit as e: except SystemExit as e:
if isinstance(e.code, int) and e.code == 0: if isinstance(e.code, int) and e.code == 0:

@ -56,7 +56,7 @@ from mesonbuild.mesonlib import (
BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows, BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows,
is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos, is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos,
windows_proof_rmtree, python_command, version_compare, split_args, windows_proof_rmtree, python_command, version_compare, split_args,
quote_arg, relpath quote_arg, relpath, is_linux
) )
from mesonbuild.environment import detect_ninja from mesonbuild.environment import detect_ninja
from mesonbuild.mesonlib import MesonException, EnvironmentException from mesonbuild.mesonlib import MesonException, EnvironmentException
@ -6080,6 +6080,39 @@ class LinuxlikeTests(BasePlatformTests):
install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx')) install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx'))
self.assertEqual(install_rpath, 'baz') self.assertEqual(install_rpath, 'baz')
def test_global_rpath(self):
if is_cygwin():
raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH')
if is_osx():
raise unittest.SkipTest('Global RPATHs via LDFLAGS not yet supported on MacOS (does anybody need it?)')
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') @skip_if_not_base_option('b_sanitize')
def test_pch_with_address_sanitizer(self): def test_pch_with_address_sanitizer(self):
if is_cygwin(): if is_cygwin():
@ -6391,13 +6424,15 @@ class LinuxlikeTests(BasePlatformTests):
self.build(override_envvars=env) self.build(override_envvars=env)
# test uninstalled # test uninstalled
self.run_tests(override_envvars=env) self.run_tests(override_envvars=env)
if not is_osx(): if not (is_osx() or is_linux()):
# Rest of the workflow only works on macOS
return return
# test running after installation # test running after installation
self.install(use_destdir=False) self.install(use_destdir=False)
prog = os.path.join(self.installdir, 'bin', 'prog') prog = os.path.join(self.installdir, 'bin', 'prog')
self._run([prog]) self._run([prog])
if not is_osx():
# Rest of the workflow only works on macOS
return
out = self._run(['otool', '-L', prog]) out = self._run(['otool', '-L', prog])
self.assertNotIn('@rpath', out) self.assertNotIn('@rpath', out)
## New builddir for testing that DESTDIR is not added to install_name ## New builddir for testing that DESTDIR is not added to install_name
@ -6414,6 +6449,57 @@ class LinuxlikeTests(BasePlatformTests):
# Ensure that the otool output does not contain self.installdir # Ensure that the otool output does not contain self.installdir
self.assertNotRegex(out, self.installdir + '.*dylib ') self.assertNotRegex(out, self.installdir + '.*dylib ')
@skipIfNoPkgconfig
def test_usage_pkgconfig_prefixes(self):
'''
Build and install two external libraries, to different prefixes,
then build and install a client program that finds them via pkgconfig,
and verify the installed client program runs.
'''
oldinstalldir = self.installdir
# Build and install both external libraries without DESTDIR
val1dir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'val1')
val1prefix = os.path.join(oldinstalldir, 'val1')
self.prefix = val1prefix
self.installdir = val1prefix
self.init(val1dir)
self.build()
self.install(use_destdir=False)
self.new_builddir()
env1 = {}
env1['PKG_CONFIG_PATH'] = os.path.join(val1prefix, self.libdir, 'pkgconfig')
val2dir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'val2')
val2prefix = os.path.join(oldinstalldir, 'val2')
self.prefix = val2prefix
self.installdir = val2prefix
self.init(val2dir, override_envvars=env1)
self.build()
self.install(use_destdir=False)
self.new_builddir()
# Build, install, and run the client program
env2 = {}
env2['PKG_CONFIG_PATH'] = os.path.join(val2prefix, self.libdir, 'pkgconfig')
testdir = os.path.join(self.unit_test_dir, '76 pkgconfig prefixes', 'client')
testprefix = os.path.join(oldinstalldir, 'client')
self.prefix = testprefix
self.installdir = testprefix
self.init(testdir, override_envvars=env2)
self.build()
self.install(use_destdir=False)
prog = os.path.join(self.installdir, 'bin', 'client')
env3 = {}
if is_cygwin():
env3['PATH'] = os.path.join(val1prefix, 'bin') + \
os.pathsep + \
os.path.join(val2prefix, 'bin') + \
os.pathsep + os.environ['PATH']
out = self._run([prog], override_envvars=env3).strip()
# Expected output is val1 + val2 = 3
self.assertEqual(out, '3')
def install_subdir_invalid_symlinks(self, testdir, subdir_path): def install_subdir_invalid_symlinks(self, testdir, subdir_path):
''' '''
Test that installation of broken symlinks works fine. Test that installation of broken symlinks works fine.

@ -18,4 +18,9 @@ l = shared_library('bar_built', 'bar.c',
if host_machine.system() == 'darwin' if host_machine.system() == 'darwin'
e = executable('prog', 'prog.c', link_with: l, install: true) e = executable('prog', 'prog.c', link_with: l, install: true)
test('testprog', e) test('testprog', e)
elif host_machine.system() == 'linux'
e = executable('prog', 'prog.c', link_with: l, install: true,
install_rpath: '$ORIGIN/..' / get_option('libdir'),
)
test('testprog', e)
endif endif

@ -4,16 +4,16 @@ shared_library('foo_in_system', 'foo.c', install : true)
l = shared_library('faa_pkg', 'faa.c', install: true) l = shared_library('faa_pkg', 'faa.c', install: true)
if host_machine.system() == 'darwin' if host_machine.system() == 'darwin'
frameworks = ['-framework', 'CoreFoundation', '-framework', 'CoreMedia'] ldflags = ['-framework', 'CoreFoundation', '-framework', 'CoreMedia']
allow_undef_args = ['-Wl,-undefined,dynamic_lookup'] allow_undef_args = ['-Wl,-undefined,dynamic_lookup']
else else
frameworks = [] ldflags = ['-Wl,-rpath,${libdir}']
allow_undef_args = [] allow_undef_args = []
endif endif
pkg = import('pkgconfig') pkg = import('pkgconfig')
pkg.generate(name: 'faa_pkg', pkg.generate(name: 'faa_pkg',
libraries: [l] + frameworks, libraries: [l] + ldflags,
description: 'FAA, a pkg-config test library') description: 'FAA, a pkg-config test library')
# cygwin DLLs can't have undefined symbols # cygwin DLLs can't have undefined symbols

@ -0,0 +1,8 @@
#include <val2.h>
#include <stdio.h>
int main(int argc, char **argv)
{
printf("%d\n", val2());
return 0;
}

@ -0,0 +1,3 @@
project('client', 'c')
val2_dep = dependency('val2')
executable('client', 'client.c', dependencies : [val2_dep], install: true)

@ -0,0 +1,5 @@
project('val1', 'c')
val1 = shared_library('val1', 'val1.c', install: true)
install_headers('val1.h')
pkgconfig = import('pkgconfig')
pkgconfig.generate(val1, libraries : ['-Wl,-rpath,${libdir}'])

@ -0,0 +1,3 @@
#include "val1.h"
int val1(void) { return 1; }

@ -0,0 +1,8 @@
project('val2', 'c')
val1_dep = dependency('val1')
val2 = shared_library('val2', 'val2.c',
dependencies : [val1_dep],
install: true)
install_headers('val2.h')
pkgconfig = import('pkgconfig')
pkgconfig.generate(val2, libraries : ['-Wl,-rpath,${libdir}'])

@ -0,0 +1,4 @@
#include "val1.h"
#include "val2.h"
int val2(void) { return val1() + 2; }

@ -0,0 +1,3 @@
project('global-rpath', 'cpp')
yonder_dep = dependency('yonder')
executable('rpathified', 'rpathified.cpp', dependencies: [yonder_dep], install: true)

@ -0,0 +1,6 @@
#include <yonder.h>
#include <string.h>
int main(int argc, char **argv)
{
return strcmp(yonder(), "AB54 6BR");
}

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

@ -0,0 +1,3 @@
#include "yonder.h"
char *yonder(void) { return "AB54 6BR"; }
Loading…
Cancel
Save