Fix default install tag for shared lib symlinks

Versioned shared libraries should have .so file in devel, .so.1 and
.so.1.2.3 in runtime.

Fixes: #9811
pull/10081/head
Xavier Claessens 3 years ago committed by Xavier Claessens
parent 219f40c1e4
commit 01e92dc543
  1. 19
      mesonbuild/backend/backends.py
  2. 3
      mesonbuild/backend/ninjabackend.py
  3. 14
      mesonbuild/build.py
  4. 10
      mesonbuild/minstall.py
  5. 20
      mesonbuild/mintro.py
  6. 6
      test cases/unit/99 install all targets/meson.build
  7. 21
      unittests/allplatformstests.py

@ -135,7 +135,6 @@ class TargetInstallData:
fname: str
outdir: str
outdir_name: InitVar[str]
aliases: T.Dict[str, str]
strip: bool
install_name_mappings: T.Mapping[str, str]
rpath_dirs_to_remove: T.Set[bytes]
@ -173,6 +172,7 @@ class InstallSymlinkData:
install_path: str
subproject: str
tag: T.Optional[str] = None
allow_missing: bool = False
# cannot use dataclass here because "exclude" is out of order
class SubdirInstallData(InstallDataBase):
@ -1581,12 +1581,17 @@ class Backend:
tag = t.install_tag[0] or ('devel' if isinstance(t, build.StaticLibrary) else 'runtime')
mappings = t.get_link_deps_mapping(d.prefix, self.environment)
i = TargetInstallData(self.get_target_filename(t), outdirs[0],
install_dir_name, t.get_aliases(),
install_dir_name,
should_strip, mappings, t.rpath_dirs_to_remove,
t.install_rpath, install_mode, t.subproject,
tag=tag)
d.targets.append(i)
for alias, to, tag in t.get_aliases():
alias = os.path.join(outdirs[0], alias)
s = InstallSymlinkData(to, alias, outdirs[0], t.subproject, tag, allow_missing=True)
d.symlinks.append(s)
if isinstance(t, (build.SharedLibrary, build.SharedModule, build.Executable)):
# On toolchains/platforms that use an import library for
# linking (separate from the shared library with all the
@ -1602,7 +1607,7 @@ class Backend:
# Install the import library; may not exist for shared modules
i = TargetInstallData(self.get_target_filename_for_linking(t),
implib_install_dir, install_dir_name,
{}, False, {}, set(), '', install_mode,
False, {}, set(), '', install_mode,
t.subproject, optional=isinstance(t, build.SharedModule),
tag='devel')
d.targets.append(i)
@ -1611,7 +1616,7 @@ class Backend:
debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename())
i = TargetInstallData(debug_file, outdirs[0],
install_dir_name,
{}, False, {}, set(), '',
False, {}, set(), '',
install_mode, t.subproject,
optional=True, tag='devel')
d.targets.append(i)
@ -1622,7 +1627,7 @@ class Backend:
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, install_dir_name, {}, False, {}, set(), None,
i = TargetInstallData(f, outdir, install_dir_name, False, {}, set(), None,
install_mode, t.subproject,
tag=tag)
d.targets.append(i)
@ -1639,7 +1644,7 @@ class Backend:
f = os.path.join(self.get_target_dir(t), output)
if not install_dir_name:
dir_name = os.path.join('{prefix}', outdirs[0])
i = TargetInstallData(f, outdirs[0], dir_name, {},
i = TargetInstallData(f, outdirs[0], dir_name,
False, {}, set(), None, install_mode,
t.subproject, optional=not t.build_by_default,
tag=tag)
@ -1653,7 +1658,7 @@ class Backend:
if not install_dir_name:
dir_name = os.path.join('{prefix}', outdir)
i = TargetInstallData(f, outdir, dir_name,
{}, False, {}, set(), None, install_mode,
False, {}, set(), None, install_mode,
t.subproject, optional=not t.build_by_default,
tag=tag)
d.targets.append(i)

@ -3092,8 +3092,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return self.get_target_filename(t)
def generate_shlib_aliases(self, target, outdir):
aliases = target.get_aliases()
for alias, to in aliases.items():
for alias, to, tag in target.get_aliases():
aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias)
try:
os.remove(aliasfile)

@ -1471,8 +1471,8 @@ You probably should put it in link_with instead.''')
else:
self.extra_args[language] = args
def get_aliases(self) -> T.Dict[str, str]:
return {}
def get_aliases(self) -> T.List[T.Tuple[str, str, str]]:
return []
def get_langs_used_by_deps(self) -> T.List[str]:
'''
@ -2240,7 +2240,7 @@ class SharedLibrary(BuildTarget):
def get_all_link_deps(self):
return [self] + self.get_transitive_link_deps()
def get_aliases(self) -> T.Dict[str, str]:
def get_aliases(self) -> T.List[T.Tuple[str, str, str]]:
"""
If the versioned library name is libfoo.so.0.100.0, aliases are:
* libfoo.so.0 (soversion) -> libfoo.so.0.100.0
@ -2248,7 +2248,7 @@ class SharedLibrary(BuildTarget):
Same for dylib:
* libfoo.dylib (unversioned; for linking) -> libfoo.0.dylib
"""
aliases: T.Dict[str, str] = {}
aliases: T.List[T.Tuple[str, str, str]] = []
# Aliases are only useful with .so and .dylib libraries. Also if
# there's no self.soversion (no versioning), we don't need aliases.
if self.suffix not in ('so', 'dylib') or not self.soversion:
@ -2260,14 +2260,16 @@ class SharedLibrary(BuildTarget):
if self.suffix == 'so' and self.ltversion and self.ltversion != self.soversion:
alias_tpl = self.filename_tpl.replace('ltversion', 'soversion')
ltversion_filename = alias_tpl.format(self)
aliases[ltversion_filename] = self.filename
tag = self.install_tag[0] or 'runtime'
aliases.append((ltversion_filename, self.filename, tag))
# libfoo.so.0/libfoo.0.dylib is the actual library
else:
ltversion_filename = self.filename
# Unversioned alias:
# libfoo.so -> libfoo.so.0
# libfoo.dylib -> libfoo.0.dylib
aliases[self.basic_filename_tpl.format(self)] = ltversion_filename
tag = self.install_tag[0] or 'devel'
aliases.append((self.basic_filename_tpl.format(self), ltversion_filename, tag))
return aliases
def type_suffix(self):

@ -428,11 +428,11 @@ class Installer:
append_to_log(self.lf, to_file)
return True
def do_symlink(self, target: str, link: str, full_dst_dir: str) -> bool:
def do_symlink(self, target: str, link: str, full_dst_dir: str, allow_missing: bool) -> bool:
abs_target = target
if not os.path.isabs(target):
abs_target = os.path.join(full_dst_dir, target)
if not os.path.exists(abs_target):
if not os.path.exists(abs_target) and not allow_missing:
raise MesonException(f'Tried to install symlink to missing file {abs_target}')
if os.path.exists(link):
if not os.path.islink(link):
@ -592,7 +592,7 @@ class Installer:
full_dst_dir = get_destdir_path(destdir, fullprefix, s.install_path)
full_link_name = get_destdir_path(destdir, fullprefix, s.name)
dm.makedirs(full_dst_dir, exist_ok=True)
if self.do_symlink(s.target, full_link_name, full_dst_dir):
if self.do_symlink(s.target, full_link_name, full_dst_dir, s.allow_missing):
self.did_install_something = True
def install_man(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
@ -676,7 +676,6 @@ class Installer:
outdir = get_destdir_path(destdir, fullprefix, t.outdir)
outname = os.path.join(outdir, os.path.basename(fname))
final_path = os.path.join(d.prefix, t.outdir, os.path.basename(fname))
aliases = t.aliases
should_strip = t.strip
install_rpath = t.install_rpath
install_name_mappings = t.install_name_mappings
@ -711,9 +710,6 @@ class Installer:
self.do_copydir(d, fname, outname, None, install_mode, dm)
else:
raise RuntimeError(f'Unknown file type for {fname!r}')
for alias, target in aliases.items():
symlinkfilename = os.path.join(outdir, alias)
self.do_symlink(target, symlinkfilename, outdir)
if file_copied:
self.did_install_something = True
try:

@ -107,9 +107,6 @@ def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]:
for t in installdata.targets:
res[os.path.join(installdata.build_dir, t.fname)] = \
os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname))
for alias in t.aliases.keys():
res[os.path.join(installdata.build_dir, alias)] = \
os.path.join(installdata.prefix, t.outdir, os.path.basename(alias))
for i in installdata.data:
res[i.path] = os.path.join(installdata.prefix, i.install_path)
for i in installdata.headers:
@ -118,6 +115,9 @@ def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]:
res[i.path] = os.path.join(installdata.prefix, i.install_path)
for i in installdata.install_subdirs:
res[i.path] = os.path.join(installdata.prefix, i.install_path)
for s in installdata.symlinks:
basename = os.path.basename(s.name)
res[basename] = os.path.join(installdata.prefix, s.install_path, basename)
return res
def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Optional[str]]]]:
@ -216,9 +216,17 @@ def list_targets(builddata: build.Build, installdata: backends.InstallData, back
# Fast lookup table for installation files
install_lookuptable = {}
for i in installdata.targets:
out = [os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname))]
out += [os.path.join(installdata.prefix, i.outdir, os.path.basename(x)) for x in i.aliases]
install_lookuptable[os.path.basename(i.fname)] = [str(PurePath(x)) for x in out]
basename = os.path.basename(i.fname)
install_lookuptable[basename] = [str(PurePath(installdata.prefix, i.outdir, basename))]
for s in installdata.symlinks:
# Symlink's target must already be in the table. They share the same list
# to support symlinks to symlinks recursively, such as .so -> .so.0 -> .so.1.2.3
basename = os.path.basename(s.name)
try:
install_lookuptable[basename] = install_lookuptable[os.path.basename(s.target)]
install_lookuptable[basename].append(str(PurePath(installdata.prefix, s.install_path, basename)))
except KeyError:
pass
for (idname, target) in builddata.get_targets().items():
if not isinstance(target, build.Target):

@ -43,6 +43,12 @@ both_libraries('both', 'lib.c',
install: true,
)
# Unversioned .so file should have 'devel' tag, others should have 'runtime' tag
shared_library('versioned_shared', 'lib.c',
install: true,
version: '1.2.3',
)
# Those files should have custom tag
install_data('bar-custom.txt',
install_dir: get_option('datadir'),

@ -3886,10 +3886,12 @@ class AllPlatformTests(BasePlatformTests):
Path(installpath, 'usr/bin/both2.pdb'),
Path(installpath, 'usr/bin/bothcustom.pdb'),
Path(installpath, 'usr/bin/shared.pdb'),
Path(installpath, 'usr/bin/versioned_shared-1.pdb'),
Path(installpath, 'usr/lib/both.lib'),
Path(installpath, 'usr/lib/both2.lib'),
Path(installpath, 'usr/lib/bothcustom.lib'),
Path(installpath, 'usr/lib/shared.lib'),
Path(installpath, 'usr/lib/versioned_shared.lib'),
}
elif is_windows() or is_cygwin():
expected_devel |= {
@ -3897,6 +3899,11 @@ class AllPlatformTests(BasePlatformTests):
Path(installpath, 'usr/lib/libboth2.dll.a'),
Path(installpath, 'usr/lib/libshared.dll.a'),
Path(installpath, 'usr/lib/libbothcustom.dll.a'),
Path(installpath, 'usr/lib/libversioned_shared.dll.a'),
}
else:
expected_devel |= {
Path(installpath, 'usr/' + shared_lib_name('versioned_shared')),
}
expected_runtime = expected_common | {
@ -3908,6 +3915,20 @@ class AllPlatformTests(BasePlatformTests):
Path(installpath, 'usr/' + shared_lib_name('both2')),
}
if is_windows() or is_cygwin():
expected_runtime |= {
Path(installpath, 'usr/' + shared_lib_name('versioned_shared-1')),
}
elif is_osx():
expected_runtime |= {
Path(installpath, 'usr/' + shared_lib_name('versioned_shared.1')),
}
else:
expected_runtime |= {
Path(installpath, 'usr/' + shared_lib_name('versioned_shared') + '.1'),
Path(installpath, 'usr/' + shared_lib_name('versioned_shared') + '.1.2.3'),
}
expected_custom = expected_common | {
Path(installpath, 'usr/share'),
Path(installpath, 'usr/share/bar-custom.txt'),

Loading…
Cancel
Save