aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2023-06-06 12:10:35 -0400
committerXavier Claessens <xclaesse@gmail.com>2023-08-03 16:27:52 -0400
commit183e4b8e903c6c4c057d09549e669d6292478a4e (patch)
tree02e0e2306ef78a797d992914c4badd2c34b180ed
parentf077cb2ee3fba55fc0e1be327ad791c19f3c3b5e (diff)
downloadmeson-183e4b8e903c6c4c057d09549e669d6292478a4e.zip
meson-183e4b8e903c6c4c057d09549e669d6292478a4e.tar.gz
meson-183e4b8e903c6c4c057d09549e669d6292478a4e.tar.bz2
PkgConfigDependency: Move CLI handling into its own abstraction
This makes the code cleaner and will allow to have other implementations in the future.
-rw-r--r--mesonbuild/dependencies/hdf5.py25
-rw-r--r--mesonbuild/dependencies/pkgconfig.py347
-rw-r--r--mesonbuild/dependencies/scalapack.py16
-rw-r--r--mesonbuild/modules/external_project.py6
-rw-r--r--mesonbuild/modules/gnome.py4
-rw-r--r--mesonbuild/modules/pkgconfig.py4
-rwxr-xr-xrun_tests.py6
-rw-r--r--unittests/internaltests.py38
-rw-r--r--unittests/linuxliketests.py7
9 files changed, 254 insertions, 199 deletions
diff --git a/mesonbuild/dependencies/hdf5.py b/mesonbuild/dependencies/hdf5.py
index 84f2812..501e89d 100644
--- a/mesonbuild/dependencies/hdf5.py
+++ b/mesonbuild/dependencies/hdf5.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))
diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py
index 37f2ecb..cfe9cbb 100644
--- a/mesonbuild/dependencies/pkgconfig.py
+++ b/mesonbuild/dependencies/pkgconfig.py
@@ -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:
diff --git a/mesonbuild/dependencies/scalapack.py b/mesonbuild/dependencies/scalapack.py
index 2442ede..fc2f720 100644
--- a/mesonbuild/dependencies/scalapack.py
+++ b/mesonbuild/dependencies/scalapack.py
@@ -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)
diff --git a/mesonbuild/modules/external_project.py b/mesonbuild/modules/external_project.py
index fd7e7c8..f7a72bc 100644
--- a/mesonbuild/modules/external_project.py
+++ b/mesonbuild/modules/external_project.py
@@ -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)
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 3249212..18862e7 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -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], ' ')
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index dd2efad..44182d7 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -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])
diff --git a/run_tests.py b/run_tests.py
index 66341e4..cf06337 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -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]:
diff --git a/unittests/internaltests.py b/unittests/internaltests.py
index 672a5a0..86a30f8 100644
--- a/unittests/internaltests.py
+++ b/unittests/internaltests.py
@@ -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
diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py
index 7ebb575..bd73857 100644
--- a/unittests/linuxliketests.py
+++ b/unittests/linuxliketests.py
@@ -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])