PkgConfigDependency: Move CLI handling into its own abstraction

This makes the code cleaner and will allow to have other implementations
in the future.
pull/12074/head
Xavier Claessens 2 years ago committed by Xavier Claessens
parent f077cb2ee3
commit 183e4b8e90
  1. 25
      mesonbuild/dependencies/hdf5.py
  2. 347
      mesonbuild/dependencies/pkgconfig.py
  3. 16
      mesonbuild/dependencies/scalapack.py
  4. 6
      mesonbuild/modules/external_project.py
  5. 4
      mesonbuild/modules/gnome.py
  6. 4
      mesonbuild/modules/pkgconfig.py
  7. 6
      run_tests.py
  8. 38
      unittests/internaltests.py
  9. 7
      unittests/linuxliketests.py

@ -18,15 +18,13 @@ from __future__ import annotations
import functools
import os
import re
import subprocess
from pathlib import Path
from ..mesonlib import Popen_safe, OrderedSet, join_args
from ..programs import ExternalProgram
from ..mesonlib import OrderedSet, join_args
from .base import DependencyException, DependencyMethods
from .configtool import ConfigToolDependency
from .detect import packages
from .pkgconfig import PkgConfigDependency
from .pkgconfig import PkgConfigDependency, PkgConfigInterface
from .factory import factory_methods
import typing as T
@ -157,19 +155,14 @@ def hdf5_factory(env: 'Environment', for_machine: 'MachineChoice',
if DependencyMethods.PKGCONFIG in methods:
# Use an ordered set so that these remain the first tried pkg-config files
pkgconfig_files = OrderedSet(['hdf5', 'hdf5-serial'])
PCEXE = PkgConfigDependency._detect_pkgbin(False, env, for_machine)
pcenv = PkgConfigDependency.setup_env(os.environ, env, for_machine)
if PCEXE:
assert isinstance(PCEXE, ExternalProgram)
pkg = PkgConfigInterface.instance(env, for_machine, silent=False)
if pkg:
# some distros put hdf5-1.2.3.pc with version number in .pc filename.
ret, stdout, _ = Popen_safe(PCEXE.get_command() + ['--list-all'], stderr=subprocess.DEVNULL, env=pcenv)
if ret.returncode == 0:
for pkg in stdout.split('\n'):
if pkg.startswith('hdf5'):
pkgconfig_files.add(pkg.split(' ', 1)[0])
for pkg in pkgconfig_files:
candidates.append(functools.partial(HDF5PkgConfigDependency, pkg, env, kwargs, language))
for mod in pkg.list_all():
if mod.startswith('hdf5'):
pkgconfig_files.add(mod)
for mod in pkgconfig_files:
candidates.append(functools.partial(HDF5PkgConfigDependency, mod, env, kwargs, language))
if DependencyMethods.CONFIG_TOOL in methods:
candidates.append(functools.partial(HDF5ConfigToolDependency, 'hdf5', env, kwargs, language))

@ -31,64 +31,142 @@ if T.TYPE_CHECKING:
from ..utils.core import EnvironOrDict
from .._typing import ImmutableListProtocol
class PkgConfigDependency(ExternalDependency):
class PkgConfigInterface:
'''Base class wrapping a pkg-config implementation'''
@staticmethod
def instance(env: Environment, for_machine: MachineChoice, silent: bool) -> T.Optional[PkgConfigInterface]:
impl = PkgConfigCLI(env, for_machine, silent)
if not impl.found():
return None
return impl
def found(self) -> bool:
'''Return whether pkg-config is supported'''
raise NotImplementedError
def version(self, name: str) -> T.Optional[str]:
'''Return module version or None if not found'''
raise NotImplementedError
def cflags(self, name: str, allow_system: bool = False,
define_variable: T.Optional[ImmutableListProtocol[str]] = None) -> T.List[str]:
'''Return module cflags
@allow_system: If False, remove default system include paths
'''
raise NotImplementedError
def libs(self, name: str, static: bool = False, allow_system: bool = False,
define_variable: T.Optional[ImmutableListProtocol[str]] = None) -> T.List[str]:
'''Return module libs
@static: If True, also include private libraries
@allow_system: If False, remove default system libraries search paths
'''
raise NotImplementedError
def variable(self, name: str, variable_name: str,
define_variable: ImmutableListProtocol[str]) -> T.Optional[str]:
'''Return module variable or None if variable is not defined'''
raise NotImplementedError
def list_all(self) -> T.List[str]:
'''Return all available pkg-config modules'''
raise NotImplementedError
class PkgConfigCLI(PkgConfigInterface):
'''pkg-config CLI implementation'''
# The class's copy of the pkg-config path. Avoids having to search for it
# multiple times in the same Meson invocation.
class_pkgbin: PerMachine[T.Union[None, bool, ExternalProgram]] = PerMachine(None, None)
class_pkgbin: PerMachine[T.Union[None, T.Literal[False], ExternalProgram]] = PerMachine(None, None)
# We cache all pkg-config subprocess invocations to avoid redundant calls
pkgbin_cache: T.Dict[
T.Tuple[ExternalProgram, T.Tuple[str, ...], T.FrozenSet[T.Tuple[str, str]]],
T.Tuple[int, str, str]
] = {}
def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None:
super().__init__(DependencyTypeName('pkgconfig'), environment, kwargs, language=language)
self.name = name
self.is_libtool = False
def __init__(self, env: Environment, for_machine: MachineChoice, silent: bool) -> None:
self.env = env
self.for_machine = for_machine
# Store a copy of the pkg-config path on the object itself so it is
# stored in the pickled coredata and recovered.
self.pkgbin = self._detect_pkgbin(self.silent, self.env, self.for_machine)
if self.pkgbin is False:
self.pkgbin = None
msg = f'Pkg-config binary for machine {self.for_machine} not found. Giving up.'
if self.required:
raise DependencyException(msg)
else:
mlog.debug(msg)
return
self.pkgbin = self._detect_pkgbin(env, for_machine, silent)
assert isinstance(self.pkgbin, ExternalProgram)
mlog.debug('Determining dependency {!r} with pkg-config executable '
'{!r}'.format(name, self.pkgbin.get_path()))
ret, self.version, _ = self._call_pkgbin(['--modversion', name])
def found(self) -> bool:
return bool(self.pkgbin)
def version(self, name: str) -> T.Optional[str]:
mlog.debug(f'Determining dependency {name!r} with pkg-config executable {self.pkgbin.get_path()!r}')
ret, version, _ = self._call_pkgbin(['--modversion', name])
return version if ret == 0 else None
@staticmethod
def _define_variable_args(define_variable: T.Optional[ImmutableListProtocol[str]]) -> T.List[str]:
if define_variable:
return ['--define-variable=' + '='.join(define_variable)]
return []
def cflags(self, name: str, allow_system: bool = False,
define_variable: T.Optional[ImmutableListProtocol[str]] = None) -> T.List[str]:
env = None
if allow_system:
env = os.environ.copy()
env['PKG_CONFIG_ALLOW_SYSTEM_CFLAGS'] = '1'
args: T.List[str] = []
args += self._define_variable_args(define_variable)
args += ['--cflags', name]
ret, out, err = self._call_pkgbin(args, env=env)
if ret != 0:
return
raise DependencyException(f'Could not generate cflags for {name}:\n{err}\n')
return self._split_args(out)
self.is_found = True
def libs(self, name: str, static: bool = False, allow_system: bool = False,
define_variable: T.Optional[ImmutableListProtocol[str]] = None) -> T.List[str]:
env = None
if allow_system:
env = os.environ.copy()
env['PKG_CONFIG_ALLOW_SYSTEM_LIBS'] = '1'
args: T.List[str] = []
args += self._define_variable_args(define_variable)
if static:
args.append('--static')
args += ['--libs', name]
ret, out, err = self._call_pkgbin(args, env=env)
if ret != 0:
raise DependencyException(f'Could not generate libs for {name}:\n{err}\n')
return self._split_args(out)
def variable(self, name: str, variable_name: str,
define_variable: ImmutableListProtocol[str]) -> T.Optional[str]:
args: T.List[str] = []
args += self._define_variable_args(define_variable)
args += ['--variable=' + variable_name, name]
ret, out, err = self._call_pkgbin(args)
if ret != 0:
raise DependencyException(f'Could not get variable for {name}:\n{err}\n')
variable = out.strip()
# pkg-config doesn't distinguish between empty and nonexistent variables
# use the variable list to check for variable existence
if not variable:
ret, out, _ = self._call_pkgbin(['--print-variables', name])
if not re.search(rf'^{variable_name}$', out, re.MULTILINE):
return None
mlog.debug(f'Got pkg-config variable {variable_name} : {variable}')
return variable
try:
# Fetch cargs to be used while using this dependency
self._set_cargs()
# Fetch the libraries and library paths needed for using this
self._set_libs()
except DependencyException as e:
mlog.debug(f"pkg-config error with '{name}': {e}")
if self.required:
raise
else:
self.compile_args = []
self.link_args = []
self.is_found = False
self.reason = e
def list_all(self) -> T.List[str]:
ret, out, err = self._call_pkgbin(['--list-all'])
if ret != 0:
raise DependencyException(f'could not list modules:\n{err}\n')
return [i.split(' ', 1)[0] for i in out.splitlines()]
def __repr__(self) -> str:
s = '<{0} {1}: {2} {3}>'
return s.format(self.__class__.__name__, self.name, self.is_found,
self.version_reqs)
def _split_args(self, cmd: str) -> T.List[str]:
# pkg-config paths follow Unix conventions, even on Windows; split the
# output using shlex.split rather than mesonlib.split_args
return shlex.split(cmd)
@classmethod
def _detect_pkgbin(cls, silent: bool, env: Environment,
for_machine: MachineChoice) -> T.Union[None, bool, ExternalProgram]:
def _detect_pkgbin(cls, env: Environment, for_machine: MachineChoice, silent: bool) -> T.Optional[ExternalProgram]:
# Only search for pkg-config for each machine the first time and store
# the result in the class definition
if cls.class_pkgbin[for_machine] is False:
@ -111,12 +189,12 @@ class PkgConfigDependency(ExternalDependency):
break
else:
if not silent:
mlog.log('Found Pkg-config:', mlog.red('NO'))
mlog.log('Found pkg-config:', mlog.red('NO'))
# Set to False instead of None to signify that we've already
# searched for it and not found it
cls.class_pkgbin[for_machine] = False
return cls.class_pkgbin[for_machine]
return cls.class_pkgbin[for_machine] or None
def _call_pkgbin_real(self, args: T.List[str], env: T.Dict[str, str]) -> T.Tuple[int, str, str]:
assert isinstance(self.pkgbin, ExternalProgram)
@ -125,8 +203,34 @@ class PkgConfigDependency(ExternalDependency):
return p.returncode, out.strip(), err.strip()
@staticmethod
def get_env(environment: 'Environment', for_machine: MachineChoice,
uninstalled: bool = False) -> 'EnvironmentVariables':
def check_pkgconfig(env: Environment, pkgbin: ExternalProgram) -> T.Optional[str]:
if not pkgbin.found():
mlog.log(f'Did not find pkg-config by name {pkgbin.name!r}')
return None
command_as_string = ' '.join(pkgbin.get_command())
try:
helptext = Popen_safe(pkgbin.get_command() + ['--help'])[1]
if 'Pure-Perl' in helptext:
mlog.log(f'Found pkg-config {command_as_string!r} but it is Strawberry Perl and thus broken. Ignoring...')
return None
p, out = Popen_safe(pkgbin.get_command() + ['--version'])[0:2]
if p.returncode != 0:
mlog.warning(f'Found pkg-config {command_as_string!r} but it failed when ran')
return None
except FileNotFoundError:
mlog.warning(f'We thought we found pkg-config {command_as_string!r} but now it\'s not there. How odd!')
return None
except PermissionError:
msg = f'Found pkg-config {command_as_string!r} but didn\'t have permissions to run it.'
if not env.machines.build.is_windows():
msg += '\n\nOn Unix-like systems this is often caused by scripts that are not executable.'
mlog.warning(msg)
return None
return out.strip()
@staticmethod
def get_env(environment: Environment, for_machine: MachineChoice,
uninstalled: bool = False) -> EnvironmentVariables:
env = EnvironmentVariables()
key = OptionKey('pkg_config_path', machine=for_machine)
extra_paths: T.List[str] = environment.coredata.options[key].value[:]
@ -144,9 +248,9 @@ class PkgConfigDependency(ExternalDependency):
return env
@staticmethod
def setup_env(env: EnvironOrDict, environment: 'Environment', for_machine: MachineChoice,
def setup_env(env: EnvironOrDict, environment: Environment, for_machine: MachineChoice,
uninstalled: bool = False) -> T.Dict[str, str]:
envvars = PkgConfigDependency.get_env(environment, for_machine, uninstalled)
envvars = PkgConfigCLI.get_env(environment, for_machine, uninstalled)
env = envvars.get_env(env)
# Dump all PKG_CONFIG environment variables
for key, value in env.items():
@ -157,15 +261,57 @@ class PkgConfigDependency(ExternalDependency):
def _call_pkgbin(self, args: T.List[str], env: T.Optional[EnvironOrDict] = None) -> T.Tuple[int, str, str]:
assert isinstance(self.pkgbin, ExternalProgram)
env = env or os.environ
env = PkgConfigDependency.setup_env(env, self.env, self.for_machine)
env = self.setup_env(env, self.env, self.for_machine)
fenv = frozenset(env.items())
targs = tuple(args)
cache = PkgConfigDependency.pkgbin_cache
cache = self.pkgbin_cache
if (self.pkgbin, targs, fenv) not in cache:
cache[(self.pkgbin, targs, fenv)] = self._call_pkgbin_real(args, env)
return cache[(self.pkgbin, targs, fenv)]
class PkgConfigDependency(ExternalDependency):
def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None) -> None:
super().__init__(DependencyTypeName('pkgconfig'), environment, kwargs, language=language)
self.name = name
self.is_libtool = False
self.pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent)
if not self.pkgconfig:
msg = f'Pkg-config for machine {self.for_machine} not found. Giving up.'
if self.required:
raise DependencyException(msg)
mlog.debug(msg)
return
version = self.pkgconfig.version(name)
if version is None:
return
self.version = version
self.is_found = True
try:
# Fetch cargs to be used while using this dependency
self._set_cargs()
# Fetch the libraries and library paths needed for using this
self._set_libs()
except DependencyException as e:
mlog.debug(f"Pkg-config error with '{name}': {e}")
if self.required:
raise
else:
self.compile_args = []
self.link_args = []
self.is_found = False
self.reason = e
def __repr__(self) -> str:
s = '<{0} {1}: {2} {3}>'
return s.format(self.__class__.__name__, self.name, self.is_found,
self.version_reqs)
def _convert_mingw_paths(self, args: T.List[str]) -> T.List[str]:
'''
Both MSVC and native Python on Windows cannot handle MinGW-esque /c/foo
@ -197,27 +343,19 @@ class PkgConfigDependency(ExternalDependency):
converted.append(arg)
return converted
def _split_args(self, cmd: str) -> T.List[str]:
# pkg-config paths follow Unix conventions, even on Windows; split the
# output using shlex.split rather than mesonlib.split_args
return shlex.split(cmd)
def _set_cargs(self) -> None:
env = None
allow_system = False
if self.language == 'fortran':
# gfortran doesn't appear to look in system paths for INCLUDE files,
# so don't allow pkg-config to suppress -I flags for system paths
env = os.environ.copy()
env['PKG_CONFIG_ALLOW_SYSTEM_CFLAGS'] = '1'
ret, out, err = self._call_pkgbin(['--cflags', self.name], env=env)
if ret != 0:
raise DependencyException(f'Could not generate cargs for {self.name}:\n{err}\n')
self.compile_args = self._convert_mingw_paths(self._split_args(out))
allow_system = True
cflags = self.pkgconfig.cflags(self.name, allow_system)
self.compile_args = self._convert_mingw_paths(cflags)
def _search_libs(self, out: str, out_raw: str) -> T.Tuple[T.List[str], T.List[str]]:
def _search_libs(self, libs_in: T.List[str], raw_libs_in: T.List[str]) -> T.Tuple[T.List[str], T.List[str]]:
'''
@out: PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs
@out_raw: pkg-config --libs
@libs_in: PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs
@raw_libs_in: pkg-config --libs
We always look for the file ourselves instead of depending on the
compiler to find it with -lfoo or foo.lib (if possible) because:
@ -241,7 +379,7 @@ class PkgConfigDependency(ExternalDependency):
# always searched first.
prefix_libpaths: OrderedSet[str] = OrderedSet()
# We also store this raw_link_args on the object later
raw_link_args = self._convert_mingw_paths(self._split_args(out_raw))
raw_link_args = self._convert_mingw_paths(raw_libs_in)
for arg in raw_link_args:
if arg.startswith('-L') and not arg.startswith(('-L-l', '-L-L')):
path = arg[2:]
@ -262,7 +400,7 @@ class PkgConfigDependency(ExternalDependency):
pkg_config_path = self._convert_mingw_paths(pkg_config_path)
prefix_libpaths = OrderedSet(sort_libpaths(list(prefix_libpaths), pkg_config_path))
system_libpaths: OrderedSet[str] = OrderedSet()
full_args = self._convert_mingw_paths(self._split_args(out))
full_args = self._convert_mingw_paths(libs_in)
for arg in full_args:
if arg.startswith(('-L-l', '-L-L')):
# These are D language arguments, not library paths
@ -367,84 +505,25 @@ class PkgConfigDependency(ExternalDependency):
return link_args, raw_link_args
def _set_libs(self) -> None:
env = None
libcmd = ['--libs']
if self.static:
libcmd.append('--static')
libcmd.append(self.name)
# 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, err = self._call_pkgbin(libcmd, env=env)
if ret != 0:
raise DependencyException(f'Could not generate libs for {self.name}:\n{err}\n')
libs = self.pkgconfig.libs(self.name, self.static, allow_system=True)
# 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, err_raw = self._call_pkgbin(libcmd)
if ret != 0:
raise DependencyException(f'Could not generate libs for {self.name}:\n\n{out_raw}')
self.link_args, self.raw_link_args = self._search_libs(out, out_raw)
raw_libs = self.pkgconfig.libs(self.name, self.static, allow_system=False)
self.link_args, self.raw_link_args = self._search_libs(libs, raw_libs)
def get_pkgconfig_variable(self, variable_name: str,
define_variable: 'ImmutableListProtocol[str]',
define_variable: ImmutableListProtocol[str],
default: T.Optional[str]) -> str:
options = ['--variable=' + variable_name, self.name]
if define_variable:
options = ['--define-variable=' + '='.join(define_variable)] + options
ret, out, err = self._call_pkgbin(options)
variable = ''
if ret != 0:
if self.required:
raise DependencyException(f'dependency {self.name} not found:\n{err}\n')
else:
variable = out.strip()
# pkg-config doesn't distinguish between empty and nonexistent variables
# use the variable list to check for variable existence
if not variable:
ret, out, _ = self._call_pkgbin(['--print-variables', self.name])
if not re.search(r'^' + variable_name + r'$', out, re.MULTILINE):
if default is not None:
variable = default
else:
mlog.warning(f"pkgconfig variable '{variable_name}' not defined for dependency {self.name}.")
mlog.debug(f'Got pkgconfig variable {variable_name} : {variable}')
variable = self.pkgconfig.variable(self.name, variable_name, define_variable)
if variable is None:
if default is None:
mlog.warning(f'Pkg-config variable {variable_name!r} not defined for dependency {self.name}.')
variable = default or ''
return variable
@staticmethod
def check_pkgconfig(env: Environment, pkgbin: ExternalProgram) -> T.Optional[str]:
if not pkgbin.found():
mlog.log(f'Did not find pkg-config by name {pkgbin.name!r}')
return None
command_as_string = ' '.join(pkgbin.get_command())
try:
helptext = Popen_safe(pkgbin.get_command() + ['--help'])[1]
if 'Pure-Perl' in helptext:
mlog.log(f'found pkg-config {command_as_string!r} but it is Strawberry Perl and thus broken. Ignoring...')
return None
p, out = Popen_safe(pkgbin.get_command() + ['--version'])[0:2]
if p.returncode != 0:
mlog.warning(f'Found pkg-config {command_as_string!r} but it failed when run')
return None
except FileNotFoundError:
mlog.warning(f'We thought we found pkg-config {command_as_string!r} but now it\'s not there. How odd!')
return None
except PermissionError:
msg = f'Found pkg-config {command_as_string!r} but didn\'t have permissions to run it.'
if not env.machines.build.is_windows():
msg += '\n\nOn Unix-like systems this is often caused by scripts that are not executable.'
mlog.warning(msg)
return None
return out.strip()
def extract_field(self, la_file: str, fieldname: str) -> T.Optional[str]:
with open(la_file, encoding='utf-8') as f:
for line in f:

@ -20,7 +20,6 @@ import typing as T
from ..mesonlib import OptionKey
from .base import DependencyMethods
from .base import DependencyException
from .cmake import CMakeDependency
from .detect import packages
from .pkgconfig import PkgConfigDependency
@ -144,17 +143,10 @@ class MKLPkgConfigDependency(PkgConfigDependency):
self.link_args.insert(i + 1, '-lmkl_blacs_intelmpi_lp64')
def _set_cargs(self) -> None:
env = None
allow_system = False
if self.language == 'fortran':
# gfortran doesn't appear to look in system paths for INCLUDE files,
# so don't allow pkg-config to suppress -I flags for system paths
env = os.environ.copy()
env['PKG_CONFIG_ALLOW_SYSTEM_CFLAGS'] = '1'
ret, out, err = self._call_pkgbin([
'--cflags', self.name,
'--define-variable=prefix=' + self.__mklroot.as_posix()],
env=env)
if ret != 0:
raise DependencyException('Could not generate cargs for %s:\n%s\n' %
(self.name, err))
self.compile_args = self._convert_mingw_paths(self._split_args(out))
allow_system = True
cflags = self.pkgconfig.cflags(self.name, allow_system, define_variable=['prefix', self.__mklroot.as_posix()])
self.compile_args = self._convert_mingw_paths(cflags)

@ -24,7 +24,7 @@ from .. import mlog, build
from ..compilers.compilers import CFLAGS_MAPPING
from ..envconfig import ENV_VAR_PROG_MAP
from ..dependencies import InternalDependency
from ..dependencies.pkgconfig import PkgConfigDependency
from ..dependencies.pkgconfig import PkgConfigCLI
from ..interpreterbase import FeatureNew
from ..interpreter.type_checking import ENV_KW, DEPENDS_KW
from ..interpreterbase.decorators import ContainerTypeInfo, KwargInfo, typed_kwargs, typed_pos_args
@ -165,8 +165,8 @@ class ExternalProject(NewExtensionModule):
self.run_env['LDFLAGS'] = self._quote_and_join(link_args)
self.run_env = self.user_env.get_env(self.run_env)
self.run_env = PkgConfigDependency.setup_env(self.run_env, self.env, MachineChoice.HOST,
uninstalled=True)
self.run_env = PkgConfigCLI.setup_env(self.run_env, self.env, MachineChoice.HOST,
uninstalled=True)
self.build_dir.mkdir(parents=True, exist_ok=True)
self._run('configure', configure_cmd, workdir)

@ -34,7 +34,7 @@ from .. import mesonlib
from .. import mlog
from ..build import CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments
from ..dependencies import Dependency, InternalDependency
from ..dependencies.pkgconfig import PkgConfigDependency
from ..dependencies.pkgconfig import PkgConfigDependency, PkgConfigCLI
from ..interpreter.type_checking import DEPENDS_KW, DEPEND_FILES_KW, ENV_KW, INSTALL_DIR_KW, INSTALL_KW, NoneType, SOURCES_KW, in_set_validator
from ..interpreterbase import noPosargs, noKwargs, FeatureNew, FeatureDeprecated
from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo
@ -967,7 +967,7 @@ class GnomeModule(ExtensionModule):
# -uninstalled.pc files Meson generated. It also must respect pkgconfig
# settings user could have set in machine file, like PKG_CONFIG_LIBDIR,
# SYSROOT, etc.
run_env = PkgConfigDependency.get_env(state.environment, MachineChoice.HOST, uninstalled=True)
run_env = PkgConfigCLI.get_env(state.environment, MachineChoice.HOST, uninstalled=True)
# g-ir-scanner uses Python's distutils to find the compiler, which uses 'CC'
cc_exelist = state.environment.coredata.compilers.host['c'].get_exelist()
run_env.set('CC', [quote_arg(x) for x in cc_exelist], ' ')

@ -26,7 +26,7 @@ from .. import dependencies
from .. import mesonlib
from .. import mlog
from ..coredata import BUILTIN_DIR_OPTIONS
from ..dependencies.pkgconfig import PkgConfigDependency
from ..dependencies.pkgconfig import PkgConfigDependency, PkgConfigCLI
from ..interpreter.type_checking import D_MODULE_VERSIONS_KW, INSTALL_DIR_KW, VARIABLES_KW, NoneType
from ..interpreterbase import FeatureNew, FeatureDeprecated
from ..interpreterbase.decorators import ContainerTypeInfo, KwargInfo, typed_kwargs, typed_pos_args
@ -741,7 +741,7 @@ class PkgConfigModule(NewExtensionModule):
self._metadata[lib.get_id()] = MetaData(
filebase, name, state.current_node)
if self.devenv is None:
self.devenv = PkgConfigDependency.get_env(state.environment, mesonlib.MachineChoice.HOST, uninstalled=True)
self.devenv = PkgConfigCLI.get_env(state.environment, mesonlib.MachineChoice.HOST, uninstalled=True)
return ModuleReturnValue(res, [res])

@ -36,7 +36,7 @@ import typing as T
from mesonbuild.compilers.c import CCompiler
from mesonbuild.compilers.detect import detect_c_compiler
from mesonbuild.dependencies.pkgconfig import PkgConfigDependency
from mesonbuild.dependencies.pkgconfig import PkgConfigCLI
from mesonbuild import mesonlib
from mesonbuild import mesonmain
from mesonbuild import mtest
@ -302,8 +302,8 @@ def run_mtest_inprocess(commandlist: T.List[str]) -> T.Tuple[int, str, str]:
def clear_meson_configure_class_caches() -> None:
CCompiler.find_library_cache = {}
CCompiler.find_framework_cache = {}
PkgConfigDependency.pkgbin_cache = {}
PkgConfigDependency.class_pkgbin = mesonlib.PerMachine(None, None)
PkgConfigCLI.pkgbin_cache = {}
PkgConfigCLI.class_pkgbin = mesonlib.PerMachine(None, None)
mesonlib.project_meson_versions = collections.defaultdict(str)
def run_configure_inprocess(commandlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None, catch_exception: bool = False) -> T.Tuple[int, str, str]:

@ -47,7 +47,7 @@ from mesonbuild.mesonlib import (
OptionType
)
from mesonbuild.interpreter.type_checking import in_set_validator, NoneType
from mesonbuild.dependencies.pkgconfig import PkgConfigDependency
from mesonbuild.dependencies.pkgconfig import PkgConfigDependency, PkgConfigInterface, PkgConfigCLI
from mesonbuild.programs import ExternalProgram
import mesonbuild.modules.pkgconfig
@ -640,22 +640,19 @@ class InternalTests(unittest.TestCase):
create_static_lib(p1 / 'libdl.a')
create_static_lib(p1 / 'librt.a')
def fake_call_pkgbin(self, args, env=None):
if '--libs' not in args:
return 0, '', ''
if args[-1] == 'foo':
return 0, f'-L{p2.as_posix()} -lfoo -L{p1.as_posix()} -lbar', ''
if args[-1] == 'bar':
return 0, f'-L{p2.as_posix()} -lbar', ''
if args[-1] == 'internal':
return 0, f'-L{p1.as_posix()} -lpthread -lm -lc -lrt -ldl', ''
old_call = PkgConfigDependency._call_pkgbin
old_check = PkgConfigDependency.check_pkgconfig
PkgConfigDependency._call_pkgbin = fake_call_pkgbin
PkgConfigDependency.check_pkgconfig = lambda x, _: pkgbin
# Test begins
try:
class FakeInstance(PkgConfigCLI):
def _call_pkgbin(self, args, env=None):
if '--libs' not in args:
return 0, '', ''
if args[-1] == 'foo':
return 0, f'-L{p2.as_posix()} -lfoo -L{p1.as_posix()} -lbar', ''
if args[-1] == 'bar':
return 0, f'-L{p2.as_posix()} -lbar', ''
if args[-1] == 'internal':
return 0, f'-L{p1.as_posix()} -lpthread -lm -lc -lrt -ldl', ''
with mock.patch.object(PkgConfigInterface, 'instance') as instance_method:
instance_method.return_value = FakeInstance(env, MachineChoice.HOST, silent=True)
kwargs = {'required': True, 'silent': True}
foo_dep = PkgConfigDependency('foo', env, kwargs)
self.assertEqual(foo_dep.get_link_args(),
@ -670,13 +667,6 @@ class InternalTests(unittest.TestCase):
for link_arg in link_args:
for lib in ('pthread', 'm', 'c', 'dl', 'rt'):
self.assertNotIn(f'lib{lib}.a', link_arg, msg=link_args)
finally:
# Test ends
PkgConfigDependency._call_pkgbin = old_call
PkgConfigDependency.check_pkgconfig = old_check
# Reset dependency class to ensure that in-process configure doesn't mess up
PkgConfigDependency.pkgbin_cache = {}
PkgConfigDependency.class_pkgbin = PerMachine(None, None)
def test_version_compare(self):
comparefunc = mesonbuild.mesonlib.version_compare_many

@ -45,7 +45,7 @@ from mesonbuild.compilers.c import AppleClangCCompiler
from mesonbuild.compilers.cpp import AppleClangCPPCompiler
from mesonbuild.compilers.objc import AppleClangObjCCompiler
from mesonbuild.compilers.objcpp import AppleClangObjCPPCompiler
from mesonbuild.dependencies.pkgconfig import PkgConfigDependency
from mesonbuild.dependencies.pkgconfig import PkgConfigDependency, PkgConfigCLI
import mesonbuild.modules.pkgconfig
PKG_CONFIG = os.environ.get('PKG_CONFIG', 'pkg-config')
@ -173,7 +173,8 @@ class LinuxlikeTests(BasePlatformTests):
self.assertEqual(libhello_nolib.get_compile_args(), [])
self.assertEqual(libhello_nolib.get_pkgconfig_variable('foo', [], None), 'bar')
self.assertEqual(libhello_nolib.get_pkgconfig_variable('prefix', [], None), self.prefix)
if version_compare(PkgConfigDependency.check_pkgconfig(env, libhello_nolib.pkgbin),">=0.29.1"):
impl = libhello_nolib.pkgconfig
if not isinstance(impl, PkgConfigCLI) or version_compare(PkgConfigCLI.check_pkgconfig(env, impl.pkgbin),">=0.29.1"):
self.assertEqual(libhello_nolib.get_pkgconfig_variable('escaped_var', [], None), r'hello\ world')
self.assertEqual(libhello_nolib.get_pkgconfig_variable('unescaped_var', [], None), 'hello world')
@ -1168,7 +1169,7 @@ class LinuxlikeTests(BasePlatformTests):
# Regression test: This used to modify the value of `pkg_config_path`
# option, adding the meson-uninstalled directory to it.
PkgConfigDependency.setup_env({}, env, MachineChoice.HOST, uninstalled=True)
PkgConfigCLI.setup_env({}, env, MachineChoice.HOST, uninstalled=True)
pkg_config_path = env.coredata.options[OptionKey('pkg_config_path')].value
self.assertEqual(pkg_config_path, [pkg_dir])

Loading…
Cancel
Save