Fix shared library symlink aliasing on install

Set the rules for the symlinking on the target itself, and then reuse
that information while generating aliases during the build, and then
pass it to the install script too.
pull/1247/head
Nirbheek Chauhan 9 years ago
parent 18f581f2c4
commit 0ac965cc10
  1. 21
      mesonbuild/backend/ninjabackend.py
  2. 27
      mesonbuild/build.py
  3. 2
      mesonbuild/modules/rpm.py
  4. 4
      mesonbuild/scripts/meson_install.py

@ -626,7 +626,7 @@ int dummy;
self.environment.get_import_lib_dir(), self.environment.get_import_lib_dir(),
# It has no aliases, should not be stripped, and # It has no aliases, should not be stripped, and
# doesn't have an install_rpath # doesn't have an install_rpath
[], False, ''] {}, False, '']
d.targets.append(i) d.targets.append(i)
outdir = self.environment.get_shared_lib_dir() outdir = self.environment.get_shared_lib_dir()
elif isinstance(t, build.StaticLibrary): elif isinstance(t, build.StaticLibrary):
@ -641,16 +641,16 @@ int dummy;
# Install the debug symbols file in the same place as # Install the debug symbols file in the same place as
# the target itself. It has no aliases, should not be # the target itself. It has no aliases, should not be
# stripped, and doesn't have an install_rpath # stripped, and doesn't have an install_rpath
i = [self.get_target_debug_filename(t), outdir, [], False, ''] i = [self.get_target_debug_filename(t), outdir, {}, False, '']
d.targets.append(i) d.targets.append(i)
if isinstance(t, build.BuildTarget): if isinstance(t, build.BuildTarget):
i = [self.get_target_filename(t), outdir, t.get_aliaslist(),\ i = [self.get_target_filename(t), outdir, t.get_aliases(),
should_strip, t.install_rpath] should_strip, t.install_rpath]
d.targets.append(i) d.targets.append(i)
elif isinstance(t, build.CustomTarget): elif isinstance(t, build.CustomTarget):
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)
d.targets.append([f, outdir, [], False, None]) d.targets.append([f, outdir, {}, False, None])
def generate_custom_install_script(self, d): def generate_custom_install_script(self, d):
d.install_scripts = self.build.install_scripts d.install_scripts = self.build.install_scripts
@ -2087,22 +2087,15 @@ rule FORTRAN_DEP_HACK
def generate_shlib_aliases(self, target, outdir): def generate_shlib_aliases(self, target, outdir):
basename = target.get_filename() basename = target.get_filename()
aliases = target.get_aliaslist() aliases = target.get_aliases()
for i, alias in enumerate(aliases): for alias, to in aliases.items():
aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias) aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias)
try: try:
os.remove(aliasfile) os.remove(aliasfile)
except Exception: except Exception:
pass pass
# If both soversion and version are set and to different values,
# the .so symlink must point to the soversion symlink rather than the
# original file.
if i == 0 and len(aliases) > 1:
pointed_to_filename = aliases[1]
else:
pointed_to_filename = basename
try: try:
os.symlink(pointed_to_filename, aliasfile) os.symlink(to, aliasfile)
except NotImplementedError: except NotImplementedError:
mlog.debug("Library versioning disabled because symlinks are not supported.") mlog.debug("Library versioning disabled because symlinks are not supported.")
except OSError: except OSError:

@ -798,8 +798,8 @@ class BuildTarget():
else: else:
self.extra_args[language] = args self.extra_args[language] = args
def get_aliaslist(self): def get_aliases(self):
return [] return {}
def get_clike_dynamic_linker(self): def get_clike_dynamic_linker(self):
''' '''
@ -1029,7 +1029,7 @@ class SharedLibrary(BuildTarget):
First we determine the filename template (self.filename_tpl), then we First we determine the filename template (self.filename_tpl), then we
set the output filename (self.filename). set the output filename (self.filename).
The template is needed while creating aliases (self.get_aliaslist), The template is needed while creating aliases (self.get_aliases),
which are needed while generating .so shared libraries for Linux. which are needed while generating .so shared libraries for Linux.
Besides this, there's also the import library name, which is only used Besides this, there's also the import library name, which is only used
@ -1175,25 +1175,30 @@ class SharedLibrary(BuildTarget):
def get_all_link_deps(self): def get_all_link_deps(self):
return [self] + self.get_transitive_link_deps() return [self] + self.get_transitive_link_deps()
def get_aliaslist(self): def get_aliases(self):
""" """
If the versioned library name is libfoo.so.0.100.0, aliases are: If the versioned library name is libfoo.so.0.100.0, aliases are:
* libfoo.so.0 (soversion) * libfoo.so.0 (soversion) -> libfoo.so.0.100.0
* libfoo.so (unversioned; for linking) * libfoo.so (unversioned; for linking) -> libfoo.so.0
""" """
aliases = {}
# Aliases are only useful with .so libraries. Also if the .so library # Aliases are only useful with .so libraries. Also if the .so library
# ends with .so (no versioning), we don't need aliases. # ends with .so (no versioning), we don't need aliases.
if self.suffix != 'so' or self.filename.endswith('.so'): if self.suffix != 'so' or self.filename.endswith('.so'):
return [] return {}
# Unversioned alias: libfoo.so # If ltversion != soversion we create an soversion alias:
aliases = [self.basic_filename_tpl.format(self)] # libfoo.so.0 -> libfoo.so.0.100.0
# If ltversion != soversion we create an soversion alias: libfoo.so.X
if self.ltversion and self.ltversion != self.soversion: if self.ltversion and self.ltversion != self.soversion:
if not self.soversion: if not self.soversion:
# This is done in self.process_kwargs() # This is done in self.process_kwargs()
raise AssertionError('BUG: If library version is defined, soversion must have been defined') raise AssertionError('BUG: If library version is defined, soversion must have been defined')
alias_tpl = self.filename_tpl.replace('ltversion', 'soversion') alias_tpl = self.filename_tpl.replace('ltversion', 'soversion')
aliases.append(alias_tpl.format(self)) ltversion_filename = alias_tpl.format(self)
aliases[ltversion_filename] = self.filename
else:
ltversion_filename = self.filename
# Unversioned alias: libfoo.so -> libfoo.so.0
aliases[self.basic_filename_tpl.format(self)] = ltversion_filename
return aliases return aliases
def type_suffix(self): def type_suffix(self):

@ -55,7 +55,7 @@ class RPMModule:
files.add('%%{_bindir}/%s' % target.get_filename()) files.add('%%{_bindir}/%s' % target.get_filename())
elif isinstance(target, build.SharedLibrary) and target.need_install: elif isinstance(target, build.SharedLibrary) and target.need_install:
files.add('%%{_libdir}/%s' % target.get_filename()) files.add('%%{_libdir}/%s' % target.get_filename())
for alias in target.get_aliaslist(): for alias in target.get_aliases():
if alias.endswith('.so'): if alias.endswith('.so'):
files_devel.add('%%{_libdir}/%s' % alias) files_devel.add('%%{_libdir}/%s' % alias)
else: else:

@ -222,14 +222,14 @@ def install_targets(d):
else: else:
raise RuntimeError('Unknown file type for {!r}'.format(fname)) raise RuntimeError('Unknown file type for {!r}'.format(fname))
printed_symlink_error = False printed_symlink_error = False
for alias in aliases: for alias, to in aliases.items():
try: try:
symlinkfilename = os.path.join(outdir, alias) symlinkfilename = os.path.join(outdir, alias)
try: try:
os.unlink(symlinkfilename) os.unlink(symlinkfilename)
except FileNotFoundError: except FileNotFoundError:
pass pass
os.symlink(os.path.split(fname)[-1], symlinkfilename) os.symlink(to, symlinkfilename)
append_to_log(symlinkfilename) append_to_log(symlinkfilename)
except (NotImplementedError, OSError): except (NotImplementedError, OSError):
if not printed_symlink_error: if not printed_symlink_error:

Loading…
Cancel
Save