Skip to content

Commit 6f172df

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

File tree

2 files changed

+44
-60
lines changed

2 files changed

+44
-60
lines changed

mesonbuild/dependencies/pkgconfig.py

Lines changed: 17 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,8 @@ 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, silent: bool = False,
60+
extra_paths: T.Optional[T.List[str]] = None) -> T.Optional[PkgConfigCLI]:
5961
'''Return the CLI pkg-config implementation singleton
6062
Even when we use another implementation internally, external tools might
6163
still need the CLI implementation.
@@ -66,15 +68,16 @@ def _cli(env: Environment, for_machine: MachineChoice, silent: bool = False) ->
6668
if impl and not isinstance(impl, PkgConfigCLI):
6769
impl = PkgConfigInterface.class_cli_impl[for_machine]
6870
if impl is False:
69-
impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine])
71+
impl = PkgConfigCLI(env, for_machine, silent, PkgConfigInterface.pkg_bin_per_machine[for_machine], extra_paths)
7072
if not impl.found():
7173
impl = None
7274
PkgConfigInterface.class_cli_impl[for_machine] = impl
7375
return T.cast('T.Optional[PkgConfigCLI]', impl) # Trust me, mypy
7476

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

8083
@staticmethod
@@ -123,11 +126,13 @@ class PkgConfigCLI(PkgConfigInterface):
123126
'''pkg-config CLI implementation'''
124127

125128
def __init__(self, env: Environment, for_machine: MachineChoice, silent: bool,
126-
pkgbin: T.Optional[ExternalProgram] = None) -> None:
129+
pkgbin: T.Optional[ExternalProgram] = None,
130+
extra_paths: T.Optional[T.List[str]] = None) -> None:
127131
super().__init__(env, for_machine)
128132
self._detect_pkgbin(pkgbin)
129133
if self.pkgbin and not silent:
130134
mlog.log('Found pkg-config:', mlog.green('YES'), mlog.bold(f'({self.pkgbin.get_path()})'), mlog.blue(self.pkgbin_version))
135+
self.extra_paths = extra_paths or []
131136

132137
def found(self) -> bool:
133138
return bool(self.pkgbin)
@@ -256,7 +261,7 @@ def _check_pkgconfig(self, pkgbin: ExternalProgram) -> T.Optional[str]:
256261
def _get_env(self, uninstalled: bool = False) -> EnvironmentVariables:
257262
env = EnvironmentVariables()
258263
key = OptionKey('pkg_config_path', machine=self.for_machine)
259-
extra_paths: T.List[str] = self.env.coredata.optstore.get_value(key)[:]
264+
extra_paths: T.List[str] = self.env.coredata.optstore.get_value(key)[:] + self.extra_paths
260265
if uninstalled:
261266
bpath = self.env.get_build_dir()
262267
if bpath is not None:
@@ -295,11 +300,13 @@ def _call_pkgbin(self, args: T.List[str], env: T.Optional[EnvironOrDict] = None)
295300
class PkgConfigDependency(ExternalDependency):
296301

297302
def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.Any],
298-
language: T.Optional[str] = None) -> None:
303+
language: T.Optional[str] = None,
304+
extra_paths: T.Optional[T.List[str]] = None) -> None:
299305
super().__init__(DependencyTypeName('pkgconfig'), environment, kwargs, language=language)
300306
self.name = name
301307
self.is_libtool = False
302-
pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent)
308+
self.extra_paths = extra_paths
309+
pkgconfig = PkgConfigInterface.instance(self.env, self.for_machine, self.silent, self.extra_paths)
303310
if not pkgconfig:
304311
msg = f'Pkg-config for machine {self.for_machine} not found. Giving up.'
305312
if self.required:

mesonbuild/dependencies/python.py

Lines changed: 27 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ 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+
return self.build_config['language']['version']
123+
else:
124+
return self.info['variables'].get('LDVERSION') or self.info['version']
125+
119126
def _check_version(self, version: str) -> bool:
120127
if self.name == 'python2':
121128
return mesonlib.version_compare(version, '< 3.0')
@@ -357,19 +364,28 @@ def find_libpy_windows(self, env: 'Environment', limited_api: bool = False) -> N
357364

358365
class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
359366

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')
367+
def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any],
368+
installation: 'BasicPythonExternalProgram', embed: bool):
369+
pkg_embed = '-embed' if embed and mesonlib.version_compare(installation.info['version'], '>=3.8') else ''
370+
pkg_name = f'python-{installation.version}{pkg_embed}'
371+
372+
if installation.build_config:
373+
pkg_libdir = installation.build_config['c_api']['pkgconfig_path']
374+
pkg_libdir_origin = 'c_api.pkgconfig_path from the Python build config'
365375
else:
366-
mlog.debug(f'Searching for {name!r} via fallback pkgconfig lookup in default paths')
376+
pkg_libdir = installation.info['variables'].get('LIBPC')
377+
pkg_libdir_origin = 'LIBPC' if pkg_libdir else 'the default paths'
378+
mlog.debug(f'Searching for {pkg_libdir!r} via pkgconfig lookup in {pkg_libdir_origin}')
379+
pkgconfig_paths = [pkg_libdir] if pkg_libdir else []
367380

368-
PkgConfigDependency.__init__(self, name, environment, kwargs)
381+
PkgConfigDependency.__init__(self, pkg_name, environment, kwargs, extra_paths=pkgconfig_paths)
369382
_PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
370383

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')
384+
if pkg_libdir and not self.is_found:
385+
mlog.debug(
386+
f'{pkg_name!r} could not be found in {pkg_libdir_origin}, '
387+
'this is likely due to a relocated python installation'
388+
)
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'):
@@ -438,48 +454,9 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
438454
installation = BasicPythonExternalProgram('python3', mesonlib.python_command)
439455
installation.sanity()
440456

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-
446457
if DependencyMethods.PKGCONFIG in methods:
447458
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))
459+
candidates.append(functools.partial(PythonPkgConfigDependency, env, kwargs, installation, embed))
483460
else:
484461
candidates.append(functools.partial(PkgConfigDependency, 'python3', env, kwargs))
485462

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

489466
if DependencyMethods.EXTRAFRAMEWORK in methods:
490467
nkwargs = kwargs.copy()
491-
if mesonlib.version_compare(pkg_version, '>= 3'):
468+
if mesonlib.version_compare(installation.version, '>= 3'):
492469
# There is a python in /System/Library/Frameworks, but that's python 2.x,
493470
# Python 3 will always be in /Library
494471
nkwargs['paths'] = ['/Library/Frameworks']

0 commit comments

Comments
 (0)