aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2021-07-12 16:03:06 -0700
committerDylan Baker <dylan@pnwbakers.com>2021-07-13 16:43:14 -0700
commit4d67dd19e5b7dcec6716840d30984fa41eef55c6 (patch)
treec6ada224f5e6ced67fbd233ec6d999c3ded6e447
parenta881e849b5789c7d47d36c09fcd4cc7bdd2ee3c9 (diff)
downloadmeson-4d67dd19e5b7dcec6716840d30984fa41eef55c6.zip
meson-4d67dd19e5b7dcec6716840d30984fa41eef55c6.tar.gz
meson-4d67dd19e5b7dcec6716840d30984fa41eef55c6.tar.bz2
modules/python: use factory for dependency
This removes the odd 'pkgdep' attribute thing, and makes it behave more like a proper dependency
-rw-r--r--mesonbuild/modules/python.py173
1 files changed, 83 insertions, 90 deletions
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index a8e55bd..69ee89f 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -13,8 +13,9 @@
# limitations under the License.
from pathlib import Path
-import os
+import functools
import json
+import os
import shutil
import typing as T
@@ -23,6 +24,7 @@ from .. import mesonlib
from .. import mlog
from ..build import known_shmod_kwargs
from ..dependencies import DependencyMethods, PkgConfigDependency, NotFoundDependency, SystemDependency
+from ..dependencies.base import process_method_kw
from ..environment import detect_cpu_family
from ..interpreter import ExternalProgramHolder, extract_required_kwarg, permitted_dependency_kwargs
from ..interpreterbase import (
@@ -30,13 +32,14 @@ from ..interpreterbase import (
InvalidArguments, typed_pos_args, typed_kwargs, KwargInfo,
FeatureNew, FeatureNewKwargs, disablerIfNotFound
)
-from ..mesonlib import MachineChoice, MesonException
+from ..mesonlib import MachineChoice
from ..programs import ExternalProgram, NonExistingExternalProgram
if T.TYPE_CHECKING:
from . import ModuleState
from ..build import SharedModule, Data
- from ..dependencies import ExternalDependency
+ from ..dependencies import ExternalDependency, Dependency
+ from ..dependencies.factory import DependencyGenerator
from ..environment import Environment
from ..interpreter import Interpreter
from ..interpreterbase.interpreterbase import TYPE_var, TYPE_kwargs
@@ -49,17 +52,18 @@ mod_kwargs.update(known_shmod_kwargs)
mod_kwargs -= {'name_prefix', 'name_suffix'}
-class PythonDependency(SystemDependency):
+if T.TYPE_CHECKING:
+ _Base = ExternalDependency
+else:
+ _Base = object
+
+class _PythonDependencyBase(_Base):
- def __init__(self, python_holder: 'PythonInstallation', environment: 'Environment',
- kwargs: T.Dict[str, T.Any]):
- super().__init__('python', environment, kwargs)
- self.name = 'python'
- self.static = kwargs.get('static', False)
- self.embed = kwargs.get('embed', False)
- self.version = python_holder.version
+ def __init__(self, python_holder: 'PythonInstallation', embed: bool):
+ self.name = 'python' # override the name from the "real" dependency lookup
+ self.embed = embed
+ self.version: str = python_holder.version
self.platform = python_holder.platform
- self.pkgdep = None
self.variables = python_holder.variables
self.paths = python_holder.paths
self.link_libpython = python_holder.link_libpython
@@ -69,73 +73,26 @@ class PythonDependency(SystemDependency):
else:
self.major_version = 2
- # We first try to find the necessary python variables using pkgconfig
- if DependencyMethods.PKGCONFIG in self.methods and not python_holder.is_pypy:
- pkg_version = self.variables.get('LDVERSION') or self.version
- pkg_libdir = self.variables.get('LIBPC')
- pkg_embed = '-embed' if self.embed and mesonlib.version_compare(self.version, '>=3.8') else ''
- pkg_name = f'python-{pkg_version}{pkg_embed}'
- # If python-X.Y.pc exists in LIBPC, we will try to use it
- if pkg_libdir is not None and Path(os.path.join(pkg_libdir, f'{pkg_name}.pc')).is_file():
- old_pkg_libdir = os.environ.get('PKG_CONFIG_LIBDIR')
- old_pkg_path = os.environ.get('PKG_CONFIG_PATH')
+class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
- os.environ.pop('PKG_CONFIG_PATH', None)
+ def __init__(self, name: str, environment: 'Environment',
+ kwargs: T.Dict[str, T.Any], installation: 'PythonInstallation'):
+ PkgConfigDependency.__init__(self, name, environment, kwargs)
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
- if pkg_libdir:
- os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir
- try:
- self.pkgdep = PkgConfigDependency(pkg_name, environment, kwargs)
- mlog.debug(f'Found "{pkg_name}" via pkgconfig lookup in LIBPC ({pkg_libdir})')
- py_lookup_method = 'pkgconfig'
- except MesonException as e:
- mlog.debug(f'"{pkg_name}" could not be found in LIBPC ({pkg_libdir})')
- mlog.debug(e)
+class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
- if old_pkg_path is not None:
- os.environ['PKG_CONFIG_PATH'] = old_pkg_path
+ def __init__(self, name: str, environment: 'Environment',
+ kwargs: T.Dict[str, T.Any], installation: 'PythonInstallation'):
+ SystemDependency.__init__(self, name, environment, kwargs)
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
- if old_pkg_libdir is not None:
- os.environ['PKG_CONFIG_LIBDIR'] = old_pkg_libdir
- else:
- os.environ.pop('PKG_CONFIG_LIBDIR', None)
- else:
- mlog.debug(f'"{pkg_name}" could not be found in LIBPC ({pkg_libdir}), this is likely due to a relocated python installation')
-
- # If lookup via LIBPC failed, try to use fallback PKG_CONFIG_LIBDIR/PKG_CONFIG_PATH mechanisms
- if self.pkgdep is None or not self.pkgdep.found():
- try:
- self.pkgdep = PkgConfigDependency(pkg_name, environment, kwargs)
- mlog.debug(f'Found "{pkg_name}" via fallback pkgconfig lookup in PKG_CONFIG_LIBDIR/PKG_CONFIG_PATH')
- py_lookup_method = 'pkgconfig-fallback'
- except MesonException as e:
- mlog.debug(f'"{pkg_name}" could not be found via fallback pkgconfig lookup in PKG_CONFIG_LIBDIR/PKG_CONFIG_PATH')
- mlog.debug(e)
-
- if self.pkgdep and self.pkgdep.found():
- self.compile_args = self.pkgdep.get_compile_args()
- self.link_args = self.pkgdep.get_link_args()
- self.is_found = True
- self.pcdep = self.pkgdep
- else:
- self.pkgdep = None
-
- # Finally, try to find python via SYSCONFIG as a final measure
- if DependencyMethods.SYSCONFIG in self.methods:
- if mesonlib.is_windows():
- self._find_libpy_windows(environment)
- else:
- self._find_libpy(python_holder, environment)
- if self.is_found:
- mlog.debug(f'Found "python-{self.version}" via SYSCONFIG module')
- py_lookup_method = 'sysconfig'
-
- if self.is_found:
- mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green(f'YES ({py_lookup_method})'))
+ if mesonlib.is_windows():
+ self._find_libpy_windows(environment)
else:
- mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO'))
+ self._find_libpy(installation, environment)
def _find_libpy(self, python_holder: 'PythonInstallation', environment: 'Environment') -> None:
if python_holder.is_pypy:
@@ -253,20 +210,45 @@ class PythonDependency(SystemDependency):
self.is_found = True
- @staticmethod
- def get_methods() -> T.List[DependencyMethods]:
- if mesonlib.is_windows():
- return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSCONFIG]
- elif mesonlib.is_osx():
- return [DependencyMethods.PKGCONFIG, DependencyMethods.EXTRAFRAMEWORK]
- else:
- return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSCONFIG]
- def get_pkgconfig_variable(self, variable_name: str, kwargs: T.Dict[str, T.Any]) -> str:
- if self.pkgdep:
- return self.pkgdep.get_pkgconfig_variable(variable_name, kwargs)
- else:
- return super().get_pkgconfig_variable(variable_name, kwargs)
+def python_factory(env: 'Environment', for_machine: 'MachineChoice',
+ kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods],
+ installation: 'PythonInstallation') -> T.List['DependencyGenerator']:
+ # We can't use the factory_methods decorator here, as we need to pass the
+ # extra installation argument
+ embed = kwargs.get('embed', False)
+ candidates: T.List['DependencyGenerator'] = []
+
+ if DependencyMethods.PKGCONFIG in methods:
+ pkg_version = installation.variables.get('LDVERSION') or installation.version
+ pkg_libdir = installation.variables.get('LIBPC')
+ pkg_embed = '-embed' if embed and mesonlib.version_compare(installation.version, '>=3.8') else ''
+ pkg_name = f'python-{pkg_version}{pkg_embed}'
+
+ # If python-X.Y.pc exists in LIBPC, we will try to use it
+ def wrap_in_pythons_pc_dir(name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
+ installation: 'PythonInstallation') -> 'ExternalDependency':
+ old_pkg_libdir = os.environ.pop('PKG_CONFIG_LIBDIR', None)
+ old_pkg_path = os.environ.pop('PKG_CONFIG_PATH', None)
+ if pkg_libdir:
+ os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir
+ try:
+ return PythonPkgConfigDependency(name, env, kwargs, installation)
+ finally:
+ if old_pkg_libdir is not None:
+ os.environ['PKG_CONFIG_LIBDIR'] = old_pkg_libdir
+ if old_pkg_path is not None:
+ os.environ['PKG_CONFIG_PATH'] = old_pkg_path
+
+ candidates.extend([
+ functools.partial(wrap_in_pythons_pc_dir, pkg_name, env, kwargs, installation),
+ functools.partial(PythonPkgConfigDependency, pkg_name, env, kwargs, installation)
+ ])
+
+ if DependencyMethods.SYSTEM in methods:
+ candidates.append(functools.partial(PythonSystemDependency, 'python', env, kwargs, installation))
+
+ return candidates
INTROSPECT_COMMAND = '''\
@@ -388,7 +370,7 @@ class PythonInstallation(ExternalProgramHolder):
if not self.link_libpython:
new_deps = []
for dep in mesonlib.extract_as_list(kwargs, 'dependencies'):
- if isinstance(dep, PythonDependency):
+ if isinstance(dep, _PythonDependencyBase):
dep = dep.get_partial_dependency(compile_args=True)
new_deps.append(dep)
kwargs['dependencies'] = new_deps
@@ -408,15 +390,26 @@ class PythonInstallation(ExternalProgramHolder):
@permittedKwargs(permitted_dependency_kwargs | {'embed'})
@FeatureNewKwargs('python_installation.dependency', '0.53.0', ['embed'])
@noPosargs
- def dependency_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> 'ExternalDependency':
+ def dependency_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> 'Dependency':
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
+
+ # it's theoretically (though not practically) possible for the else clse
+ # to not bind dep, let's ensure it is.
+ dep: 'Dependency' = NotFoundDependency(self.interpreter.environment)
if disabled:
mlog.log('Dependency', mlog.bold('python'), 'skipped: feature', mlog.bold(feature), 'disabled')
- dep = NotFoundDependency(self.interpreter.environment)
else:
- dep = PythonDependency(self, self.interpreter.environment, kwargs)
+ for d in python_factory(self.interpreter.environment,
+ MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST,
+ kwargs,
+ process_method_kw({DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM}, kwargs),
+ self):
+ dep = d()
+ if dep.found():
+ break
if required and not dep.found():
raise mesonlib.MesonException('Python dependency not found')
+
return dep
@typed_pos_args('install_data', varargs=(str, mesonlib.File))
@@ -509,7 +502,7 @@ class PythonModule(ExtensionModule):
return None
@staticmethod
- def _check_version(name_or_path: str, version) -> bool:
+ def _check_version(name_or_path: str, version: str) -> bool:
if name_or_path == 'python2':
return mesonlib.version_compare(version, '< 3.0')
elif name_or_path == 'python3':