blob: aac09cafa61650b68dbd67840954efe33869f502 [file] [log] [blame]
# SPDX-License-Identifier: Apache-2.0
# Copyright 2013-2021 The Meson development team
# Copyright © 2021-2023 Intel Corporation
from __future__ import annotations
import functools
import typing as T
from .base import DependencyException, DependencyMethods
from .base import process_method_kw
from .base import BuiltinDependency, SystemDependency
from .cmake import CMakeDependency
from .framework import ExtraFrameworkDependency
from .pkgconfig import PkgConfigDependency
if T.TYPE_CHECKING:
from .base import ExternalDependency
from .configtool import ConfigToolDependency
from ..environment import Environment
from ..mesonlib import MachineChoice
DependencyGenerator = T.Callable[[], ExternalDependency]
FactoryFunc = T.Callable[
[
'Environment',
MachineChoice,
T.Dict[str, T.Any],
T.List[DependencyMethods]
],
T.List[DependencyGenerator]
]
WrappedFactoryFunc = T.Callable[
[
'Environment',
MachineChoice,
T.Dict[str, T.Any]
],
T.List[DependencyGenerator]
]
# This should be str, Environment, T.Dict[str, T.Any], T.Optional[str]
# But if you try that, you get error: Cannot infer type of lambda
CmakeDependencyFunc = T.Callable[..., CMakeDependency]
class DependencyFactory:
"""Factory to get dependencies from multiple sources.
This class provides an initializer that takes a set of names and classes
for various kinds of dependencies. When the initialized object is called
it returns a list of callables return Dependency objects to try in order.
:name: The name of the dependency. This will be passed as the name
parameter of the each dependency unless it is overridden on a per
type basis.
:methods: An ordered list of DependencyMethods. This is the order
dependencies will be returned in unless they are removed by the
_process_method function
:*_name: This will overwrite the name passed to the corresponding class.
For example, if the name is 'zlib', but cmake calls the dependency
'Z', then using `cmake_name='Z'` will pass the name as 'Z' to cmake.
:*_class: A *type* or callable that creates a class, and has the
signature of an ExternalDependency
:system_class: If you pass DependencyMethods.SYSTEM in methods, you must
set this argument.
"""
def __init__(self, name: str, methods: T.List[DependencyMethods], *,
extra_kwargs: T.Optional[T.Dict[str, T.Any]] = None,
pkgconfig_name: T.Optional[str] = None,
pkgconfig_class: 'T.Type[PkgConfigDependency]' = PkgConfigDependency,
cmake_name: T.Optional[str] = None,
cmake_class: 'T.Union[T.Type[CMakeDependency], CmakeDependencyFunc]' = CMakeDependency,
configtool_class: 'T.Optional[T.Type[ConfigToolDependency]]' = None,
framework_name: T.Optional[str] = None,
framework_class: 'T.Type[ExtraFrameworkDependency]' = ExtraFrameworkDependency,
builtin_class: 'T.Type[BuiltinDependency]' = BuiltinDependency,
system_class: 'T.Type[SystemDependency]' = SystemDependency):
if DependencyMethods.CONFIG_TOOL in methods and not configtool_class:
raise DependencyException('A configtool must have a custom class')
self.extra_kwargs = extra_kwargs or {}
self.methods = methods
self.classes: T.Dict[
DependencyMethods,
T.Callable[['Environment', T.Dict[str, T.Any]], ExternalDependency]
] = {
# Just attach the correct name right now, either the generic name
# or the method specific name.
DependencyMethods.EXTRAFRAMEWORK: functools.partial(framework_class, framework_name or name),
DependencyMethods.PKGCONFIG: functools.partial(pkgconfig_class, pkgconfig_name or name),
DependencyMethods.CMAKE: functools.partial(cmake_class, cmake_name or name),
DependencyMethods.SYSTEM: functools.partial(system_class, name),
DependencyMethods.BUILTIN: functools.partial(builtin_class, name),
DependencyMethods.CONFIG_TOOL: None,
}
if configtool_class is not None:
self.classes[DependencyMethods.CONFIG_TOOL] = functools.partial(configtool_class, name)
@staticmethod
def _process_method(method: DependencyMethods, env: 'Environment', for_machine: MachineChoice) -> bool:
"""Report whether a method is valid or not.
If the method is valid, return true, otherwise return false. This is
used in a list comprehension to filter methods that are not possible.
By default this only remove EXTRAFRAMEWORK dependencies for non-mac platforms.
"""
# Extra frameworks are only valid for macOS and other apple products
if (method is DependencyMethods.EXTRAFRAMEWORK and
not env.machines[for_machine].is_darwin()):
return False
return True
def __call__(self, env: 'Environment', for_machine: MachineChoice,
kwargs: T.Dict[str, T.Any]) -> T.List['DependencyGenerator']:
"""Return a list of Dependencies with the arguments already attached."""
methods = process_method_kw(self.methods, kwargs)
nwargs = self.extra_kwargs.copy()
nwargs.update(kwargs)
return [functools.partial(self.classes[m], env, nwargs) for m in methods
if self._process_method(m, env, for_machine)]
def factory_methods(methods: T.Set[DependencyMethods]) -> T.Callable[['FactoryFunc'], 'WrappedFactoryFunc']:
"""Decorator for handling methods for dependency factory functions.
This helps to make factory functions self documenting
>>> @factory_methods([DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE])
>>> def factory(env: Environment, for_machine: MachineChoice, kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
>>> pass
"""
def inner(func: 'FactoryFunc') -> 'WrappedFactoryFunc':
@functools.wraps(func)
def wrapped(env: 'Environment', for_machine: MachineChoice, kwargs: T.Dict[str, T.Any]) -> T.List['DependencyGenerator']:
return func(env, for_machine, kwargs, process_method_kw(methods, kwargs))
return wrapped
return inner