Skip to content

Commit 64e51ab

Browse files
committed
python: move pkg-config discovery introspection logic to PythonPkgConfigDependency
Signed-off-by: Filipe Laíns <[email protected]>
1 parent 624b7b1 commit 64e51ab

File tree

2 files changed

+48
-61
lines changed

2 files changed

+48
-61
lines changed

mesonbuild/dependencies/pkgconfig.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ def set_program_override(pkg_bin: ExternalProgram, for_machine: MachineChoice) -
4141
PkgConfigInterface.pkg_bin_per_machine[for_machine] = pkg_bin
4242

4343
@staticmethod
44-
def instance(env: Environment, for_machine: MachineChoice, silent: bool) -> T.Optional[PkgConfigInterface]:
44+
def instance(env: Environment, for_machine: MachineChoice, silent: bool,
45+
extra_paths: T.Optional[T.List[str]] = None) -> T.Optional[PkgConfigInterface]:
4546
'''Return a pkg-config implementation singleton'''
4647
for_machine = for_machine if env.is_cross_build() else MachineChoice.HOST
4748
impl = PkgConfigInterface.class_impl[for_machine]
4849
if impl is False:
49-
impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine])
50+
impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine], extra_paths)
5051
if not impl.found():
5152
impl = None
5253
if not impl and not silent:
@@ -55,7 +56,9 @@ def instance(env: Environment, for_machine: MachineChoice, silent: bool) -> T.Op
5556
return impl
5657

5758
@staticmethod
58-
def _cli(env: Environment, for_machine: MachineChoice, silent: bool = False) -> T.Optional[PkgConfigCLI]:
59+
def _cli(env: Environment, for_machine: MachineChoice,
60+
extra_paths: T.Optional[T.List[str]] = None,
61+
silent: bool = False) -> T.Optional[PkgConfigCLI]:
5962
'''Return the CLI pkg-config implementation singleton
6063
Even when we use another implementation internally, external tools might
6164
still need the CLI implementation.
@@ -66,15 +69,16 @@ def _cli(env: Environment, for_machine: MachineChoice, silent: bool = False) ->
6669
if impl and not isinstance(impl, PkgConfigCLI):
6770
impl = PkgConfigInterface.class_cli_impl[for_machine]
6871
if impl is False:
69-
impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine])
72+
impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine], extra_paths)
7073
if not impl.found():
7174
impl = None
7275
PkgConfigInterface.class_cli_impl[for_machine] = impl
7376
return T.cast('T.Optional[PkgConfigCLI]', impl) # Trust me, mypy
7477

7578
@staticmethod
76-
def get_env(env: Environment, for_machine: MachineChoice, uninstalled: bool = False) -> EnvironmentVariables:
77-
cli = PkgConfigInterface._cli(env, for_machine)
79+
def get_env(env: Environment, for_machine: MachineChoice, uninstalled: bool = False,
80+
extra_paths: T.Optional[T.List[str]] = None) -> EnvironmentVariables:
81+
cli = PkgConfigInterface._cli(env, for_machine, extra_paths)
7882
return cli._get_env(uninstalled) if cli else EnvironmentVariables()
7983

8084
@staticmethod
@@ -123,11 +127,13 @@ class PkgConfigCLI(PkgConfigInterface):
123127
'''pkg-config CLI implementation'''
124128

125129
def __init__(self, env: Environment, for_machine: MachineChoice, silent: bool,
126-
pkgbin: T.Optional[ExternalProgram] = None) -> None:
130+
pkgbin: T.Optional[ExternalProgram] = None,
131+
extra_paths: T.Optional[T.List[str]] = None) -> None:
127132
super().__init__(env, for_machine)
128133
self._detect_pkgbin(pkgbin)
129134
if self.pkgbin and not silent:
130135
mlog.log('Found pkg-config:', mlog.green('YES'), mlog.bold(f'({self.pkgbin.get_path()})'), mlog.blue(self.pkgbin_version))
136+
self.extra_paths = extra_paths or []
131137

132138
def found(self) -> bool:
133139
return bool(self.pkgbin)
@@ -256,7 +262,7 @@ def _check_pkgconfig(self, pkgbin: ExternalProgram) -> T.Optional[str]:
256262
def _get_env(self, uninstalled: bool = False) -> EnvironmentVariables:
257263
env = EnvironmentVariables()
258264
key = OptionKey('pkg_config_path', machine=self.for_machine)
259-
extra_paths: T.List[str] = self.env.coredata.optstore.get_value(key)[:]
265+
extra_paths: T.List[str] = self.env.coredata.optstore.get_value(key)[:] + self.extra_paths
260266
if uninstalled:
261267
bpath = self.env.get_build_dir()
262268
if bpath is not None:
@@ -295,11 +301,13 @@ def _call_pkgbin(self, args: T.List[str], env: T.Optional[EnvironOrDict] = None)
295301
class PkgConfigDependency(ExternalDependency):
296302

297303
def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any],
298-
language: T.Optional[str] = None) -> None:
304+
language: T.Optional[str] = None,
305+
extra_paths: T.Optional[T.List[str]] = None) -> None:
299306
super().__init__(DependencyTypeName('pkgconfig'), environment, kwargs, language=language)
300307
self.name = name
301308
self.is_libtool = False
302-
pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent)
309+
self.extra_paths = extra_paths
310+
pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent, self.extra_paths)
303311
if not pkgconfig:
304312
msg = f'Pkg-config for machine {self.for_machine} not found. Giving up.'
305313
if self.required:

mesonbuild/dependencies/python.py

Lines changed: 30 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import typing as T
99

1010
from .. import mesonlib, mlog
11-
from .base import process_method_kw, DependencyException, DependencyMethods, DependencyTypeName, ExternalDependency, SystemDependency
11+
from .base import process_method_kw, DependencyException, DependencyMethods, ExternalDependency, SystemDependency
1212
from .configtool import ConfigToolDependency
1313
from .detect import packages
1414
from .factory import DependencyFactory
@@ -116,6 +116,15 @@ def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
116116
}
117117
self.pure: bool = True
118118

119+
@property
120+
def version(self) -> str:
121+
if self.build_config:
122+
value = self.build_config['language']['version']
123+
else:
124+
value = self.info['variables'].get('LDVERSION') or self.info['version']
125+
assert isinstance(value, str)
126+
return value
127+
119128
def _check_version(self, version: str) -> bool:
120129
if self.name == 'python2':
121130
return mesonlib.version_compare(version, '< 3.0')
@@ -357,19 +366,28 @@ def find_libpy_windows(self, env: 'Environment', limited_api: bool = False) -> N
357366

358367
class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
359368

360-
def __init__(self, name: str, environment: 'Environment',
361-
kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram',
362-
libpc: bool = False):
363-
if libpc:
364-
mlog.debug(f'Searching for {name!r} via pkgconfig lookup in LIBPC')
369+
def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any],
370+
installation: 'BasicPythonExternalProgram', embed: bool):
371+
pkg_embed = '-embed' if embed and mesonlib.version_compare(installation.info['version'], '>=3.8') else ''
372+
pkg_name = f'python-{installation.version}{pkg_embed}'
373+
374+
if installation.build_config:
375+
pkg_libdir = installation.build_config['c_api']['pkgconfig_path']
376+
pkg_libdir_origin = 'c_api.pkgconfig_path from the Python build config'
365377
else:
366-
mlog.debug(f'Searching for {name!r} via fallback pkgconfig lookup in default paths')
378+
pkg_libdir = installation.info['variables'].get('LIBPC')
379+
pkg_libdir_origin = 'LIBPC' if pkg_libdir else 'the default paths'
380+
mlog.debug(f'Searching for {pkg_libdir!r} via pkgconfig lookup in {pkg_libdir_origin}')
381+
pkgconfig_paths = [pkg_libdir] if pkg_libdir else []
367382

368-
PkgConfigDependency.__init__(self, name, environment, kwargs)
383+
PkgConfigDependency.__init__(self, pkg_name, environment, kwargs, extra_paths=pkgconfig_paths)
369384
_PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
370385

371-
if libpc and not self.is_found:
372-
mlog.debug(f'"python-{self.version}" could not be found in LIBPC, this is likely due to a relocated python installation')
386+
if pkg_libdir and not self.is_found:
387+
mlog.debug(
388+
f'{pkg_name!r} could not be found in {pkg_libdir_origin}, '
389+
'this is likely due to a relocated python installation'
390+
)
373391

374392
# pkg-config files are usually accurate starting with python 3.8
375393
if not self.link_libpython and mesonlib.version_compare(self.version, '< 3.8'):
@@ -438,48 +456,9 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
438456
installation = BasicPythonExternalProgram('python3', mesonlib.python_command)
439457
installation.sanity()
440458

441-
if installation.build_config:
442-
pkg_version = installation.build_config['language']['version']
443-
else:
444-
pkg_version = installation.info['variables'].get('LDVERSION') or installation.info['version']
445-
446459
if DependencyMethods.PKGCONFIG in methods:
447460
if from_installation:
448-
if installation.build_config:
449-
pkg_libdir = installation.build_config['c_api']['pkgconfig_path']
450-
else:
451-
pkg_libdir = installation.info['variables'].get('LIBPC')
452-
pkg_embed = '-embed' if embed and mesonlib.version_compare(installation.info['version'], '>=3.8') else ''
453-
pkg_name = f'python-{pkg_version}{pkg_embed}'
454-
455-
# If python-X.Y.pc exists in LIBPC, we will try to use it
456-
def wrap_in_pythons_pc_dir(name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
457-
installation: 'BasicPythonExternalProgram') -> 'ExternalDependency':
458-
if not pkg_libdir:
459-
# there is no LIBPC, so we can't search in it
460-
empty = ExternalDependency(DependencyTypeName('pkgconfig'), env, {})
461-
empty.name = 'python'
462-
return empty
463-
464-
old_pkg_libdir = os.environ.pop('PKG_CONFIG_LIBDIR', None)
465-
old_pkg_path = os.environ.pop('PKG_CONFIG_PATH', None)
466-
os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir
467-
try:
468-
return PythonPkgConfigDependency(name, env, kwargs, installation, True)
469-
finally:
470-
def set_env(name: str, value: str) -> None:
471-
if value is not None:
472-
os.environ[name] = value
473-
elif name in os.environ:
474-
del os.environ[name]
475-
set_env('PKG_CONFIG_LIBDIR', old_pkg_libdir)
476-
set_env('PKG_CONFIG_PATH', old_pkg_path)
477-
478-
candidates.append(functools.partial(wrap_in_pythons_pc_dir, pkg_name, env, kwargs, installation))
479-
# We only need to check both, if a python install has a LIBPC. It might point to the wrong location,
480-
# e.g. relocated / cross compilation, but the presence of LIBPC indicates we should definitely look for something.
481-
if pkg_libdir is not None:
482-
candidates.append(functools.partial(PythonPkgConfigDependency, pkg_name, env, kwargs, installation))
461+
candidates.append(functools.partial(PythonPkgConfigDependency, env, kwargs, installation, embed))
483462
else:
484463
candidates.append(functools.partial(PkgConfigDependency, 'python3', env, kwargs))
485464

@@ -488,7 +467,7 @@ def set_env(name: str, value: str) -> None:
488467

489468
if DependencyMethods.EXTRAFRAMEWORK in methods:
490469
nkwargs = kwargs.copy()
491-
if mesonlib.version_compare(pkg_version, '>= 3'):
470+
if mesonlib.version_compare(installation.version, '>= 3'):
492471
# There is a python in /System/Library/Frameworks, but that's python 2.x,
493472
# Python 3 will always be in /Library
494473
nkwargs['paths'] = ['/Library/Frameworks']

0 commit comments

Comments
 (0)