aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/dependencies/base.py40
-rwxr-xr-xrun_unittests.py20
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):