1# Copyright 2013-2021 The Meson development team 2# Copyright © 2021 Intel Corporation 3 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7 8# http://www.apache.org/licenses/LICENSE-2.0 9 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import functools 17import typing as T 18 19from ..mesonlib import MachineChoice 20from .base import DependencyException, DependencyMethods 21from .base import ExternalDependency 22from .base import process_method_kw 23from .base import BuiltinDependency, SystemDependency 24from .cmake import CMakeDependency 25from .framework import ExtraFrameworkDependency 26from .pkgconfig import PkgConfigDependency 27 28if T.TYPE_CHECKING: 29 from ..environment import Environment 30 from .configtool import ConfigToolDependency 31 32 DependencyGenerator = T.Callable[[], ExternalDependency] 33 FactoryFunc = T.Callable[ 34 [ 35 'Environment', 36 MachineChoice, 37 T.Dict[str, T.Any], 38 T.List[DependencyMethods] 39 ], 40 T.List[DependencyGenerator] 41 ] 42 43 WrappedFactoryFunc = T.Callable[ 44 [ 45 'Environment', 46 MachineChoice, 47 T.Dict[str, T.Any] 48 ], 49 T.List[DependencyGenerator] 50 ] 51 52class DependencyFactory: 53 54 """Factory to get dependencies from multiple sources. 55 56 This class provides an initializer that takes a set of names and classes 57 for various kinds of dependencies. When the initialized object is called 58 it returns a list of callables return Dependency objects to try in order. 59 60 :name: The name of the dependency. This will be passed as the name 61 parameter of the each dependency unless it is overridden on a per 62 type basis. 63 :methods: An ordered list of DependencyMethods. This is the order 64 dependencies will be returned in unless they are removed by the 65 _process_method function 66 :*_name: This will overwrite the name passed to the corresponding class. 67 For example, if the name is 'zlib', but cmake calls the dependency 68 'Z', then using `cmake_name='Z'` will pass the name as 'Z' to cmake. 69 :*_class: A *type* or callable that creates a class, and has the 70 signature of an ExternalDependency 71 :system_class: If you pass DependencyMethods.SYSTEM in methods, you must 72 set this argument. 73 """ 74 75 def __init__(self, name: str, methods: T.List[DependencyMethods], *, 76 extra_kwargs: T.Optional[T.Dict[str, T.Any]] = None, 77 pkgconfig_name: T.Optional[str] = None, 78 pkgconfig_class: 'T.Type[PkgConfigDependency]' = PkgConfigDependency, 79 cmake_name: T.Optional[str] = None, 80 cmake_class: 'T.Type[CMakeDependency]' = CMakeDependency, 81 configtool_class: 'T.Optional[T.Type[ConfigToolDependency]]' = None, 82 framework_name: T.Optional[str] = None, 83 framework_class: 'T.Type[ExtraFrameworkDependency]' = ExtraFrameworkDependency, 84 builtin_class: 'T.Type[BuiltinDependency]' = BuiltinDependency, 85 system_class: 'T.Type[SystemDependency]' = SystemDependency): 86 87 if DependencyMethods.CONFIG_TOOL in methods and not configtool_class: 88 raise DependencyException('A configtool must have a custom class') 89 90 self.extra_kwargs = extra_kwargs or {} 91 self.methods = methods 92 self.classes: T.Dict[ 93 DependencyMethods, 94 T.Callable[['Environment', T.Dict[str, T.Any]], ExternalDependency] 95 ] = { 96 # Just attach the correct name right now, either the generic name 97 # or the method specific name. 98 DependencyMethods.EXTRAFRAMEWORK: lambda env, kwargs: framework_class(framework_name or name, env, kwargs), 99 DependencyMethods.PKGCONFIG: lambda env, kwargs: pkgconfig_class(pkgconfig_name or name, env, kwargs), 100 DependencyMethods.CMAKE: lambda env, kwargs: cmake_class(cmake_name or name, env, kwargs), 101 DependencyMethods.SYSTEM: lambda env, kwargs: system_class(name, env, kwargs), 102 DependencyMethods.BUILTIN: lambda env, kwargs: builtin_class(name, env, kwargs), 103 DependencyMethods.CONFIG_TOOL: None, 104 } 105 if configtool_class is not None: 106 self.classes[DependencyMethods.CONFIG_TOOL] = lambda env, kwargs: configtool_class(name, env, kwargs) 107 108 @staticmethod 109 def _process_method(method: DependencyMethods, env: 'Environment', for_machine: MachineChoice) -> bool: 110 """Report whether a method is valid or not. 111 112 If the method is valid, return true, otherwise return false. This is 113 used in a list comprehension to filter methods that are not possible. 114 115 By default this only remove EXTRAFRAMEWORK dependencies for non-mac platforms. 116 """ 117 # Extra frameworks are only valid for macOS and other apple products 118 if (method is DependencyMethods.EXTRAFRAMEWORK and 119 not env.machines[for_machine].is_darwin()): 120 return False 121 return True 122 123 def __call__(self, env: 'Environment', for_machine: MachineChoice, 124 kwargs: T.Dict[str, T.Any]) -> T.List['DependencyGenerator']: 125 """Return a list of Dependencies with the arguments already attached.""" 126 methods = process_method_kw(self.methods, kwargs) 127 nwargs = self.extra_kwargs.copy() 128 nwargs.update(kwargs) 129 130 return [functools.partial(self.classes[m], env, nwargs) for m in methods 131 if self._process_method(m, env, for_machine)] 132 133 134def factory_methods(methods: T.Set[DependencyMethods]) -> T.Callable[['FactoryFunc'], 'WrappedFactoryFunc']: 135 """Decorator for handling methods for dependency factory functions. 136 137 This helps to make factory functions self documenting 138 >>> @factory_methods([DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE]) 139 >>> def factory(env: Environment, for_machine: MachineChoice, kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']: 140 >>> pass 141 """ 142 143 def inner(func: 'FactoryFunc') -> 'WrappedFactoryFunc': 144 145 @functools.wraps(func) 146 def wrapped(env: 'Environment', for_machine: MachineChoice, kwargs: T.Dict[str, T.Any]) -> T.List['DependencyGenerator']: 147 return func(env, for_machine, kwargs, process_method_kw(methods, kwargs)) 148 149 return wrapped 150 151 return inner 152