PkgConfigDependency: Fix library path search order

We were searching the library paths in the reverse order, which meant
that we'd pick libraries from the wrong prefix.

Closes https://github.com/mesonbuild/meson/issues/3951
pull/3996/head
Nirbheek Chauhan 7 years ago committed by Nirbheek Chauhan
parent 8402a22233
commit 6c8f81333a
  1. 7
      mesonbuild/compilers/c.py
  2. 51
      mesonbuild/dependencies/base.py
  3. 3
      mesonbuild/mesonlib.py
  4. 44
      run_unittests.py

@ -16,6 +16,7 @@ import re
import glob
import os.path
import subprocess
from pathlib import Path
from .. import mlog
from .. import coredata
@ -885,13 +886,13 @@ class CCompiler(Compiler):
@classmethod
def _get_trials_from_pattern(cls, pattern, directory, libname):
f = os.path.join(directory, pattern.format(libname))
f = Path(directory) / pattern.format(libname)
# Globbing for OpenBSD
if '*' in pattern:
# NOTE: globbing matches directories and broken symlinks
# so we have to do an isfile test on it later
return cls._sort_shlibs_openbsd(glob.glob(f))
return [f]
return cls._sort_shlibs_openbsd(glob.glob(str(f)))
return [f.as_posix()]
@staticmethod
def _get_file_from_list(files):

@ -586,26 +586,7 @@ class PkgConfigDependency(ExternalDependency):
(self.name, out))
self.compile_args = self._convert_mingw_paths(shlex.split(out))
def _set_libs(self):
env = None
libcmd = [self.name, '--libs']
if self.static:
libcmd.append('--static')
# Force pkg-config to output -L fields even if they are system
# paths so we can do manual searching with cc.find_library() later.
env = os.environ.copy()
env['PKG_CONFIG_ALLOW_SYSTEM_LIBS'] = '1'
ret, out = self._call_pkgbin(libcmd, env=env)
if ret != 0:
raise DependencyException('Could not generate libs for %s:\n\n%s' %
(self.name, out))
# Also get the 'raw' output without -Lfoo system paths for usage when
# a library can't be found, and also in gnome.generate_gir
# + gnome.gtkdoc which need -L -l arguments.
ret, out_raw = self._call_pkgbin(libcmd)
if ret != 0:
raise DependencyException('Could not generate libs for %s:\n\n%s' %
(self.name, out_raw))
def _search_libs(self, out, out_raw):
link_args = []
raw_link_args = []
# Library paths should be safe to de-dup
@ -636,7 +617,7 @@ class PkgConfigDependency(ExternalDependency):
continue
if self.clib_compiler:
args = self.clib_compiler.find_library(lib[2:], self.env,
list(libpaths), libtype)
list(reversed(libpaths)), libtype)
# If the project only uses a non-clib language such as D, Rust,
# C#, Python, etc, all we can do is limp along by adding the
# arguments as-is and then adding the libpaths at the end.
@ -681,14 +662,34 @@ class PkgConfigDependency(ExternalDependency):
if lib.startswith('-L') and not lib.startswith(('-L-l', '-L-L')):
raw_libpaths.add(lib[2:])
raw_link_args.append(lib)
# Set everything
self.link_args = link_args
self.raw_link_args = raw_link_args
# Add all -Lbar args if we have -lfoo args in link_args
if libs_notfound:
# Order of -L flags doesn't matter with ld, but it might with other
# linkers such as MSVC, so prepend them.
self.link_args = ['-L' + lp for lp in raw_libpaths] + self.link_args
link_args = ['-L' + lp for lp in raw_libpaths] + link_args
return link_args, raw_link_args
def _set_libs(self):
env = None
libcmd = [self.name, '--libs']
if self.static:
libcmd.append('--static')
# Force pkg-config to output -L fields even if they are system
# paths so we can do manual searching with cc.find_library() later.
env = os.environ.copy()
env['PKG_CONFIG_ALLOW_SYSTEM_LIBS'] = '1'
ret, out = self._call_pkgbin(libcmd, env=env)
if ret != 0:
raise DependencyException('Could not generate libs for %s:\n\n%s' %
(self.name, out))
# Also get the 'raw' output without -Lfoo system paths for adding -L
# args with -lfoo when a library can't be found, and also in
# gnome.generate_gir + gnome.gtkdoc which need -L -l arguments.
ret, out_raw = self._call_pkgbin(libcmd)
if ret != 0:
raise DependencyException('Could not generate libs for %s:\n\n%s' %
(self.name, out_raw))
self.link_args, self.raw_link_args = self._search_libs(out, out_raw)
def get_pkgconfig_variable(self, variable_name, kwargs):
options = ['--variable=' + variable_name, self.name]

@ -1088,6 +1088,9 @@ class OrderedSet(collections.MutableSet):
'", "'.join(repr(e) for e in self.__container.keys()))
return 'OrderedSet()'
def __reversed__(self):
return reversed(self.__container)
def add(self, value):
self.__container[value] = None

@ -594,6 +594,50 @@ class InternalTests(unittest.TestCase):
'mesonbuild.compilers.c.for_windows', true):
self._test_all_naming(cc, env, patterns, 'windows-msvc')
def test_pkgconfig_parse_libs(self):
'''
Unit test for parsing of pkg-config output to search for libraries
https://github.com/mesonbuild/meson/issues/3951
'''
with tempfile.TemporaryDirectory() as tmpdir:
pkgbin = ExternalProgram('pkg-config', command=['pkg-config'], silent=True)
env = Environment('', '', get_fake_options(''))
compiler = env.detect_c_compiler(False)
env.coredata.compilers = {'c': compiler}
p1 = Path(tmpdir) / '1'
p2 = Path(tmpdir) / '2'
p1.mkdir()
p2.mkdir()
# libfoo.a is in one prefix
(p1 / 'libfoo.a').open('w').close()
# libbar.a is in both prefixes
(p1 / 'libbar.a').open('w').close()
(p2 / 'libbar.a').open('w').close()
def fake_call_pkgbin(self, args, env=None):
if '--libs' not in args:
return 0, ''
if args[0] == 'foo':
return 0, '-L{} -lfoo -L{} -lbar'.format(p1.as_posix(), p2.as_posix())
if args[0] == 'bar':
return 0, '-L{} -lbar'.format(p2.as_posix())
old_call = PkgConfigDependency._call_pkgbin
old_check = PkgConfigDependency.check_pkgconfig
PkgConfigDependency._call_pkgbin = fake_call_pkgbin
PkgConfigDependency.check_pkgconfig = lambda x: pkgbin
# Test begins
kwargs = {'required': True, 'silent': True}
foo_dep = PkgConfigDependency('foo', env, kwargs)
self.assertEqual(foo_dep.get_link_args(),
[(p1 / 'libfoo.a').as_posix(), (p2 / 'libbar.a').as_posix()])
bar_dep = PkgConfigDependency('bar', env, kwargs)
self.assertEqual(bar_dep.get_link_args(), [(p2 / 'libbar.a').as_posix()])
# Test ends
PkgConfigDependency._call_pkgbin = old_call
PkgConfigDependency.check_pkgconfig = old_check
@unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release')
class DataTests(unittest.TestCase):

Loading…
Cancel
Save