# SPDX-License-Identifier: Apache-2.0 # Copyright 2013-2021 The Meson development team # Copyright © 2021-2025 Intel Corporation from __future__ import annotations import functools import typing as T from ..mesonlib import MachineChoice from .base import DependencyCandidate, 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 typing_extensions import TypeAlias from .base import DependencyObjectKWs, ExternalDependency, DepType from .configtool import ConfigToolDependency from ..environment import Environment # TODO: remove this? DependencyGenerator: TypeAlias = DependencyCandidate[ExternalDependency] FactoryFunc = T.Callable[ [ 'Environment', DependencyObjectKWs, T.List[DependencyMethods] ], T.List[DependencyGenerator] ] WrappedFactoryFunc = T.Callable[ [ 'Environment', DependencyObjectKWs, ], T.List[DependencyGenerator] ] 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. :param 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. :param methods: An ordered list of DependencyMethods. This is the order dependencies will be returned in unless they are removed by the _process_method function :param extra_kwargs: Additional keyword arguments to add when creating the DependencyCandidate :param pkgconfig: A custom PackageConfig lookup to use :param cmake: A custom CMake lookup to use :param framework: A custom AppleFramework lookup to use :param configtool: A custom ConfigTool lookup to use. If DependencyMethods.CONFIG_TOOL is in the `:param:methods` argument, this must be set. :param builtin: A custom Builtin lookup to use. If DependencyMethods.BUILTIN is in the `:param:methods` argument, this must be set. :param system: A custom System lookup to use. If DependencyMethods.SYSTEM is in the `:param:methods` argument, this must be set. """ def __init__(self, name: str, methods: T.List[DependencyMethods], *, extra_kwargs: T.Optional[DependencyObjectKWs] = None, pkgconfig: T.Union[DependencyCandidate[PkgConfigDependency], T.Type[PkgConfigDependency], None] = PkgConfigDependency, cmake: T.Union[DependencyCandidate[CMakeDependency], T.Type[CMakeDependency], None] = CMakeDependency, framework: T.Union[DependencyCandidate[ExtraFrameworkDependency], T.Type[ExtraFrameworkDependency], None] = ExtraFrameworkDependency, configtool: T.Union[DependencyCandidate[ConfigToolDependency], T.Type[ConfigToolDependency], None] = None, builtin: T.Union[DependencyCandidate[BuiltinDependency], T.Type[BuiltinDependency], None] = None, system: T.Union[DependencyCandidate[SystemDependency], T.Type[SystemDependency], None] = None): if DependencyMethods.CONFIG_TOOL in methods and not configtool: raise DependencyException('A configtool dependency must have a custom class') if DependencyMethods.BUILTIN in methods and not builtin: raise DependencyException('A builtin dependency must have a custom class') if DependencyMethods.SYSTEM in methods and not system: raise DependencyException('A system dependency must have a custom class') def make(arg: T.Union[DependencyCandidate[DepType], T.Type[DepType], None]) -> T.Optional[DependencyCandidate[DepType]]: if arg is None or isinstance(arg, DependencyCandidate): return arg return DependencyCandidate.from_dependency(name, arg) self.extra_kwargs = extra_kwargs self.methods = methods self.classes: T.Mapping[DependencyMethods, T.Optional[DependencyCandidate[ExternalDependency]]] = { # Just attach the correct name right now, either the generic name # or the method specific name. DependencyMethods.EXTRAFRAMEWORK: make(framework), DependencyMethods.PKGCONFIG: make(pkgconfig), DependencyMethods.CMAKE: make(cmake), DependencyMethods.SYSTEM: make(system), DependencyMethods.BUILTIN: make(builtin), DependencyMethods.CONFIG_TOOL: make(configtool), } @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', kwargs: DependencyObjectKWs) -> T.List['DependencyGenerator']: """Return a list of Dependencies with the arguments already attached.""" methods = process_method_kw(self.methods, kwargs) if self.extra_kwargs: nwargs = self.extra_kwargs.copy() nwargs.update(kwargs) else: nwargs = kwargs.copy() ret: T.List[DependencyGenerator] = [] for m in methods: if self._process_method(m, env, kwargs['native']): c = self.classes[m] if c is None: continue c.arguments = (env, nwargs) ret.append(c) return ret 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: DependencyObjectKWs, methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']: >>> pass """ def inner(func: 'FactoryFunc') -> 'WrappedFactoryFunc': @functools.wraps(func) def wrapped(env: 'Environment', kwargs: DependencyObjectKWs) -> T.List['DependencyGenerator']: return func(env, kwargs, process_method_kw(methods, kwargs)) return wrapped return inner