Skip to content

Commit 20b1ab5

Browse files
FFY00mgorny
authored andcommitted
python: move pkg-config discovery introspection logic to PythonPkgConfigDependency
Signed-off-by: Filipe Laíns <[email protected]>
1 parent e6222d2 commit 20b1ab5

File tree

2 files changed

+46
-64
lines changed

2 files changed

+46
-64
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)
@@ -258,7 +264,7 @@ def _get_env(self, uninstalled: bool = False) -> EnvironmentVariables:
258264
key = OptionKey('pkg_config_path', machine=self.for_machine)
259265
pathlist = self.env.coredata.optstore.get_value_for(key)
260266
assert isinstance(pathlist, list)
261-
extra_paths: T.List[str] = pathlist[:]
267+
extra_paths: T.List[str] = pathlist + self.extra_paths
262268
if uninstalled:
263269
bpath = self.env.get_build_dir()
264270
if bpath is not None:
@@ -297,11 +303,13 @@ def _call_pkgbin(self, args: T.List[str], env: T.Optional[EnvironOrDict] = None)
297303
class PkgConfigDependency(ExternalDependency):
298304

299305
def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any],
300-
language: T.Optional[str] = None) -> None:
306+
language: T.Optional[str] = None,
307+
extra_paths: T.Optional[T.List[str]] = None) -> None:
301308
super().__init__(DependencyTypeName('pkgconfig'), environment, kwargs, language=language)
302309
self.name = name
303310
self.is_libtool = False
304-
pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent)
311+
self.extra_paths = extra_paths or []
312+
pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent, self.extra_paths)
305313
if not pkgconfig:
306314
msg = f'Pkg-config for machine {self.for_machine} not found. Giving up.'
307315
if self.required:

mesonbuild/dependencies/python.py

Lines changed: 28 additions & 54 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,26 @@ 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(f'{pkg_name!r} could not be found in {pkg_libdir_origin}, '
388+
'this is likely due to a relocated python installation')
373389

374390
# pkg-config files are usually accurate starting with python 3.8
375391
if not self.link_libpython and mesonlib.version_compare(self.version, '< 3.8'):
@@ -453,51 +469,9 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
453469
installation = BasicPythonExternalProgram('python3', mesonlib.python_command)
454470
installation.sanity()
455471

456-
if installation.build_config:
457-
pkg_version = installation.build_config['language']['version']
458-
else:
459-
pkg_version = installation.info['variables'].get('LDVERSION') or installation.info['version']
460-
461472
if DependencyMethods.PKGCONFIG in methods:
462473
if from_installation:
463-
if installation.build_config:
464-
pkg_libdir = installation.build_config['c_api']['pkgconfig_path']
465-
else:
466-
pkg_libdir = installation.info['variables'].get('LIBPC')
467-
pkg_embed = '-embed' if embed and mesonlib.version_compare(installation.info['version'], '>=3.8') else ''
468-
pkg_name = f'python-{pkg_version}{pkg_embed}'
469-
470-
# If python-X.Y.pc exists in LIBPC, we will try to use it
471-
def wrap_in_pythons_pc_dir(name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
472-
installation: 'BasicPythonExternalProgram') -> 'ExternalDependency':
473-
if not pkg_libdir:
474-
# there is no LIBPC, so we can't search in it
475-
empty = ExternalDependency(DependencyTypeName('pkgconfig'), env, {})
476-
empty.name = 'python'
477-
return empty
478-
479-
old_pkg_libdir = os.environ.pop('PKG_CONFIG_LIBDIR', None)
480-
old_pkg_path = os.environ.pop('PKG_CONFIG_PATH', None)
481-
os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir
482-
try:
483-
return PythonPkgConfigDependency(name, env, kwargs, installation, True)
484-
finally:
485-
def set_env(name: str, value: str) -> None:
486-
if value is not None:
487-
os.environ[name] = value
488-
elif name in os.environ:
489-
del os.environ[name]
490-
set_env('PKG_CONFIG_LIBDIR', old_pkg_libdir)
491-
set_env('PKG_CONFIG_PATH', old_pkg_path)
492-
493-
# Otherwise this doesn't fulfill the interface requirements
494-
wrap_in_pythons_pc_dir.log_tried = PythonPkgConfigDependency.log_tried # type: ignore[attr-defined]
495-
496-
candidates.append(functools.partial(wrap_in_pythons_pc_dir, pkg_name, env, kwargs, installation))
497-
# We only need to check both, if a python install has a LIBPC. It might point to the wrong location,
498-
# e.g. relocated / cross compilation, but the presence of LIBPC indicates we should definitely look for something.
499-
if pkg_libdir is not None:
500-
candidates.append(functools.partial(PythonPkgConfigDependency, pkg_name, env, kwargs, installation))
474+
candidates.append(functools.partial(PythonPkgConfigDependency, env, kwargs, installation, embed))
501475
else:
502476
candidates.append(functools.partial(PkgConfigDependency, 'python3', env, kwargs))
503477

@@ -506,7 +480,7 @@ def set_env(name: str, value: str) -> None:
506480

507481
if DependencyMethods.EXTRAFRAMEWORK in methods:
508482
nkwargs = kwargs.copy()
509-
if mesonlib.version_compare(pkg_version, '>= 3'):
483+
if mesonlib.version_compare(installation.version, '>= 3'):
510484
# There is a python in /System/Library/Frameworks, but that's python 2.x,
511485
# Python 3 will always be in /Library
512486
nkwargs['paths'] = ['/Library/Frameworks']

0 commit comments

Comments
 (0)