diff options
-rw-r--r-- | mesonbuild/dependencies/base.py | 40 | ||||
-rwxr-xr-x | run_unittests.py | 20 |
2 files changed, 60 insertions, 0 deletions
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 9170400..60239de 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -788,6 +788,22 @@ class PkgConfigDependency(ExternalDependency): # Resolve the path as a compiler in the build directory would path = os.path.join(self.env.get_build_dir(), path) prefix_libpaths.add(path) + # Library paths are not always ordered in a meaningful way + # + # Instead of relying on pkg-config or pkgconf to provide -L flags in a + # specific order, we reorder library paths ourselves, according to th + # order specified in PKG_CONFIG_PATH. See: + # https://github.com/mesonbuild/meson/issues/4271 + # + # Only prefix_libpaths are reordered here because there should not be + # too many system_libpaths to cause library version issues. + pkg_config_path = os.environ.get('PKG_CONFIG_PATH') + if pkg_config_path: + pkg_config_path = pkg_config_path.split(os.pathsep) + else: + pkg_config_path = [] + pkg_config_path = self._convert_mingw_paths(pkg_config_path) + prefix_libpaths = sort_libpaths(prefix_libpaths, pkg_config_path) system_libpaths = OrderedSet() full_args = self._convert_mingw_paths(self._split_args(out)) for arg in full_args: @@ -2308,6 +2324,30 @@ def _build_external_dependency_list(name, env: Environment, kwargs: Dict[str, An return candidates +def sort_libpaths(libpaths: List[str], refpaths: List[str]) -> List[str]: + """Sort <libpaths> according to <refpaths> + + It is intended to be used to sort -L flags returned by pkg-config. + Pkg-config returns flags in random order which cannot be relied on. + """ + if len(refpaths) == 0: + return list(libpaths) + + def key_func(libpath): + common_lengths = [] + for refpath in refpaths: + try: + common_path = os.path.commonpath([libpath, refpath]) + except ValueError: + common_path = '' + common_lengths.append(len(common_path)) + max_length = max(common_lengths) + max_index = common_lengths.index(max_length) + reversed_max_length = len(refpaths[max_index]) - max_length + return (max_index, reversed_max_length) + return sorted(libpaths, key=key_func) + + def strip_system_libdirs(environment, for_machine: MachineChoice, link_args): """Remove -L<system path> arguments. diff --git a/run_unittests.py b/run_unittests.py index 78de65f..669c8ff 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -58,6 +58,7 @@ from mesonbuild.mesonlib import ( from mesonbuild.environment import detect_ninja from mesonbuild.mesonlib import MesonException, EnvironmentException from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram +import mesonbuild.dependencies.base from mesonbuild.build import Target import mesonbuild.modules.pkgconfig @@ -1170,6 +1171,25 @@ class InternalTests(unittest.TestCase): actual = f.getvalue().strip() self.assertEqual(actual.count('bar'), 1, actual) + def test_sort_libpaths(self): + sort_libpaths = mesonbuild.dependencies.base.sort_libpaths + self.assertEqual(sort_libpaths( + ['/home/mesonuser/.local/lib', '/usr/local/lib', '/usr/lib'], + ['/home/mesonuser/.local/lib/pkgconfig', '/usr/local/lib/pkgconfig']), + ['/home/mesonuser/.local/lib', '/usr/local/lib', '/usr/lib']) + self.assertEqual(sort_libpaths( + ['/usr/local/lib', '/home/mesonuser/.local/lib', '/usr/lib'], + ['/home/mesonuser/.local/lib/pkgconfig', '/usr/local/lib/pkgconfig']), + ['/home/mesonuser/.local/lib', '/usr/local/lib', '/usr/lib']) + self.assertEqual(sort_libpaths( + ['/usr/lib', '/usr/local/lib', '/home/mesonuser/.local/lib'], + ['/home/mesonuser/.local/lib/pkgconfig', '/usr/local/lib/pkgconfig']), + ['/home/mesonuser/.local/lib', '/usr/local/lib', '/usr/lib']) + self.assertEqual(sort_libpaths( + ['/usr/lib', '/usr/local/lib', '/home/mesonuser/.local/lib'], + ['/home/mesonuser/.local/lib/pkgconfig', '/usr/local/libdata/pkgconfig']), + ['/home/mesonuser/.local/lib', '/usr/local/lib', '/usr/lib']) + @unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release') class DataTests(unittest.TestCase): |