diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 40776a9a9..b43977ea1 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -626,7 +626,7 @@ int dummy; self.environment.get_import_lib_dir(), # It has no aliases, should not be stripped, and # doesn't have an install_rpath - [], False, ''] + {}, False, ''] d.targets.append(i) outdir = self.environment.get_shared_lib_dir() elif isinstance(t, build.StaticLibrary): @@ -641,16 +641,16 @@ int dummy; # Install the debug symbols file in the same place as # the target itself. It has no aliases, should not be # 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) if isinstance(t, build.BuildTarget): - i = [self.get_target_filename(t), outdir, t.get_aliaslist(),\ - should_strip, t.install_rpath] + i = [self.get_target_filename(t), outdir, t.get_aliases(), + should_strip, t.install_rpath] d.targets.append(i) elif isinstance(t, build.CustomTarget): for output in t.get_outputs(): 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): d.install_scripts = self.build.install_scripts @@ -2087,22 +2087,15 @@ rule FORTRAN_DEP_HACK def generate_shlib_aliases(self, target, outdir): basename = target.get_filename() - aliases = target.get_aliaslist() - for i, alias in enumerate(aliases): + aliases = target.get_aliases() + for alias, to in aliases.items(): aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias) try: os.remove(aliasfile) except Exception: 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: - os.symlink(pointed_to_filename, aliasfile) + os.symlink(to, aliasfile) except NotImplementedError: mlog.debug("Library versioning disabled because symlinks are not supported.") except OSError: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6e3325d75..c7d41259d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -798,8 +798,8 @@ class BuildTarget(): else: self.extra_args[language] = args - def get_aliaslist(self): - return [] + def get_aliases(self): + return {} def get_clike_dynamic_linker(self): ''' @@ -1038,7 +1038,7 @@ class SharedLibrary(BuildTarget): First we determine the filename template (self.filename_tpl), then we 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. Besides this, there's also the import library name, which is only used @@ -1184,25 +1184,30 @@ class SharedLibrary(BuildTarget): def get_all_link_deps(self): 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: - * libfoo.so.0 (soversion) - * libfoo.so (unversioned; for linking) + * libfoo.so.0 (soversion) -> libfoo.so.0.100.0 + * libfoo.so (unversioned; for linking) -> libfoo.so.0 """ + aliases = {} # Aliases are only useful with .so libraries. Also if the .so library # ends with .so (no versioning), we don't need aliases. if self.suffix != 'so' or self.filename.endswith('.so'): - return [] - # Unversioned alias: libfoo.so - aliases = [self.basic_filename_tpl.format(self)] - # If ltversion != soversion we create an soversion alias: libfoo.so.X + return {} + # If ltversion != soversion we create an soversion alias: + # libfoo.so.0 -> libfoo.so.0.100.0 if self.ltversion and self.ltversion != self.soversion: if not self.soversion: # This is done in self.process_kwargs() raise AssertionError('BUG: If library version is defined, soversion must have been defined') 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 def type_suffix(self): diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index e3c45c0a0..ece16102d 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -55,7 +55,7 @@ class RPMModule: files.add('%%{_bindir}/%s' % target.get_filename()) elif isinstance(target, build.SharedLibrary) and target.need_install: files.add('%%{_libdir}/%s' % target.get_filename()) - for alias in target.get_aliaslist(): + for alias in target.get_aliases(): if alias.endswith('.so'): files_devel.add('%%{_libdir}/%s' % alias) else: diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index 11dd32094..c749b4fae 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -222,14 +222,14 @@ def install_targets(d): else: raise RuntimeError('Unknown file type for {!r}'.format(fname)) printed_symlink_error = False - for alias in aliases: + for alias, to in aliases.items(): try: symlinkfilename = os.path.join(outdir, alias) try: os.unlink(symlinkfilename) except FileNotFoundError: pass - os.symlink(os.path.split(fname)[-1], symlinkfilename) + os.symlink(to, symlinkfilename) append_to_log(symlinkfilename) except (NotImplementedError, OSError): if not printed_symlink_error: diff --git a/run_unittests.py b/run_unittests.py index d11c3f3bc..3d5a237ea 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -57,6 +57,9 @@ class LinuxlikeTests(unittest.TestCase): src_root = os.path.dirname(__file__) src_root = os.path.join(os.getcwd(), src_root) self.builddir = tempfile.mkdtemp() + self.prefix = '/usr' + self.libdir = os.path.join(self.prefix, 'lib') + self.installdir = os.path.join(self.builddir, 'install') self.meson_command = [sys.executable, os.path.join(src_root, 'meson.py')] self.mconf_command = [sys.executable, os.path.join(src_root, 'mesonconf.py')] self.mintro_command = [sys.executable, os.path.join(src_root, 'mesonintrospect.py')] @@ -77,11 +80,18 @@ class LinuxlikeTests(unittest.TestCase): self.output += subprocess.check_output(command, env=os.environ.copy()) def init(self, srcdir): - self._run(self.meson_command + [srcdir, self.builddir]) + args = [srcdir, self.builddir, + '--prefix', self.prefix, + '--libdir', self.libdir] + self._run(self.meson_command + args) def build(self): self._run(self.ninja_command) + def install(self): + os.environ['DESTDIR'] = self.installdir + self._run(self.ninja_command + ['install']) + def run_target(self, target): self.output += subprocess.check_output(self.ninja_command + [target]) @@ -236,8 +246,8 @@ class LinuxlikeTests(unittest.TestCase): intro = self.introspect('--targets') if intro[0]['type'] == 'executable': intro = intro[::-1] - self.assertEqual(intro[0]['install_filename'], '/usr/local/libtest/libstat.a') - self.assertEqual(intro[1]['install_filename'], '/usr/local/bin/prog') + self.assertEqual(intro[0]['install_filename'], '/usr/lib/libstat.a') + self.assertEqual(intro[1]['install_filename'], '/usr/bin/prog') def test_run_target_files_path(self): ''' @@ -282,47 +292,55 @@ class LinuxlikeTests(unittest.TestCase): return line.split('[')[1].split(']')[0] raise RuntimeError('Readelf gave no SONAME.') - def test_soname(self): + def _test_soname_impl(self, libpath, install): testdir = os.path.join(self.unit_test_dir, '1 soname') self.init(testdir) self.build() + if install: + self.install() # File without aliases set. - nover = os.path.join(self.builddir, 'libnover.so') + nover = os.path.join(libpath, 'libnover.so') self.assertTrue(os.path.exists(nover)) self.assertFalse(os.path.islink(nover)) self.assertEqual(self.get_soname(nover), 'libnover.so') self.assertEqual(len(glob(nover[:-3] + '*')), 1) # File with version set - verset = os.path.join(self.builddir, 'libverset.so') + verset = os.path.join(libpath, 'libverset.so') self.assertTrue(os.path.exists(verset + '.4.5.6')) self.assertEqual(os.readlink(verset), 'libverset.so.4') self.assertEqual(self.get_soname(verset), 'libverset.so.4') self.assertEqual(len(glob(verset[:-3] + '*')), 3) # File with soversion set - soverset = os.path.join(self.builddir, 'libsoverset.so') + soverset = os.path.join(libpath, 'libsoverset.so') self.assertTrue(os.path.exists(soverset + '.1.2.3')) self.assertEqual(os.readlink(soverset), 'libsoverset.so.1.2.3') self.assertEqual(self.get_soname(soverset), 'libsoverset.so.1.2.3') self.assertEqual(len(glob(soverset[:-3] + '*')), 2) # File with version and soversion set to same values - settosame = os.path.join(self.builddir, 'libsettosame.so') + settosame = os.path.join(libpath, 'libsettosame.so') self.assertTrue(os.path.exists(settosame + '.7.8.9')) self.assertEqual(os.readlink(settosame), 'libsettosame.so.7.8.9') self.assertEqual(self.get_soname(settosame), 'libsettosame.so.7.8.9') self.assertEqual(len(glob(settosame[:-3] + '*')), 2) # File with version and soversion set to different values - bothset = os.path.join(self.builddir, 'libbothset.so') + bothset = os.path.join(libpath, 'libbothset.so') self.assertTrue(os.path.exists(bothset + '.1.2.3')) self.assertEqual(os.readlink(bothset), 'libbothset.so.1.2.3') self.assertEqual(os.readlink(bothset + '.1.2.3'), 'libbothset.so.4.5.6') self.assertEqual(self.get_soname(bothset), 'libbothset.so.1.2.3') self.assertEqual(len(glob(bothset[:-3] + '*')), 3) + def test_soname(self): + self._test_soname_impl(self.builddir, False) + + def test_installed_soname(self): + self._test_soname_impl(self.installdir + self.libdir, True) + def test_compiler_check_flags_order(self): ''' Test that compiler check flags override all other flags. This can't be diff --git a/test cases/unit/1 soname/meson.build b/test cases/unit/1 soname/meson.build index d956afed9..950dadc5d 100644 --- a/test cases/unit/1 soname/meson.build +++ b/test cases/unit/1 soname/meson.build @@ -1,18 +1,22 @@ project('vertest', 'c') -shared_library('nover', 'versioned.c') +shared_library('nover', 'versioned.c', + install : true) shared_library('verset', 'versioned.c', + install : true, version : '4.5.6') shared_library('soverset', 'versioned.c', + install : true, soversion : '1.2.3') shared_library('bothset', 'versioned.c', + install : true, soversion : '1.2.3', version : '4.5.6') shared_library('settosame', 'versioned.c', + install : true, soversion : '7.8.9', version : '7.8.9') -