1# Copyright 2019 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# This file contains the detection logic for external dependencies that 16# are UI-related. 17 18import os 19import typing as T 20 21from .. import build, mesonlib 22from ..mesonlib import relpath, HoldableObject 23from ..interpreterbase.decorators import noKwargs, noPosargs 24 25if T.TYPE_CHECKING: 26 from ..interpreter import Interpreter 27 from ..interpreterbase import TYPE_var, TYPE_kwargs 28 from ..programs import ExternalProgram 29 30class ModuleState: 31 """Object passed to all module methods. 32 33 This is a WIP API provided to modules, it should be extended to have everything 34 needed so modules does not touch any other part of Meson internal APIs. 35 """ 36 37 def __init__(self, interpreter: 'Interpreter') -> None: 38 # Keep it private, it should be accessed only through methods. 39 self._interpreter = interpreter 40 41 self.source_root = interpreter.environment.get_source_dir() 42 self.build_to_src = relpath(interpreter.environment.get_source_dir(), 43 interpreter.environment.get_build_dir()) 44 self.subproject = interpreter.subproject 45 self.subdir = interpreter.subdir 46 self.current_lineno = interpreter.current_lineno 47 self.environment = interpreter.environment 48 self.project_name = interpreter.build.project_name 49 self.project_version = interpreter.build.dep_manifest[interpreter.active_projectname] 50 # The backend object is under-used right now, but we will need it: 51 # https://github.com/mesonbuild/meson/issues/1419 52 self.backend = interpreter.backend 53 self.targets = interpreter.build.targets 54 self.data = interpreter.build.data 55 self.headers = interpreter.build.get_headers() 56 self.man = interpreter.build.get_man() 57 self.global_args = interpreter.build.global_args.host 58 self.project_args = interpreter.build.projects_args.host.get(interpreter.subproject, {}) 59 self.build_machine = interpreter.builtin['build_machine'].held_object 60 self.host_machine = interpreter.builtin['host_machine'].held_object 61 self.target_machine = interpreter.builtin['target_machine'].held_object 62 self.current_node = interpreter.current_node 63 64 def get_include_args(self, include_dirs: T.Iterable[T.Union[str, build.IncludeDirs]], prefix: str = '-I') -> T.List[str]: 65 if not include_dirs: 66 return [] 67 68 srcdir = self.environment.get_source_dir() 69 builddir = self.environment.get_build_dir() 70 71 dirs_str: T.List[str] = [] 72 for dirs in include_dirs: 73 if isinstance(dirs, str): 74 dirs_str += [f'{prefix}{dirs}'] 75 continue 76 77 # Should be build.IncludeDirs object. 78 basedir = dirs.get_curdir() 79 for d in dirs.get_incdirs(): 80 expdir = os.path.join(basedir, d) 81 srctreedir = os.path.join(srcdir, expdir) 82 buildtreedir = os.path.join(builddir, expdir) 83 dirs_str += [f'{prefix}{buildtreedir}', 84 f'{prefix}{srctreedir}'] 85 for d in dirs.get_extra_build_dirs(): 86 dirs_str += [f'{prefix}{d}'] 87 88 return dirs_str 89 90 def find_program(self, prog: T.Union[str, T.List[str]], required: bool = True, 91 version_func: T.Optional[T.Callable[['ExternalProgram'], str]] = None, 92 wanted: T.Optional[str] = None) -> 'ExternalProgram': 93 return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted) 94 95 def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'ExternalProgram', mesonlib.File]], 96 workdir: T.Optional[str] = None, 97 env: T.Union[T.List[str], T.Dict[str, str], str] = None, 98 depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] = None) -> None: 99 kwargs = {'workdir': workdir, 100 'env': env, 101 'depends': depends, 102 } 103 # TODO: Use interpreter internal API, but we need to go through @typed_kwargs 104 self._interpreter.func_test(self.current_node, args, kwargs) 105 106 107class ModuleObject(HoldableObject): 108 """Base class for all objects returned by modules 109 """ 110 def __init__(self) -> None: 111 self.methods: T.Dict[ 112 str, 113 T.Callable[[ModuleState, T.List['TYPE_var'], 'TYPE_kwargs'], T.Union[ModuleReturnValue, 'TYPE_var']] 114 ] = {} 115 116 117class MutableModuleObject(ModuleObject): 118 pass 119 120 121# FIXME: Port all modules to stop using self.interpreter and use API on 122# ModuleState instead. Modules should stop using this class and instead use 123# ModuleObject base class. 124class ExtensionModule(ModuleObject): 125 def __init__(self, interpreter: 'Interpreter') -> None: 126 super().__init__() 127 self.interpreter = interpreter 128 self.methods.update({ 129 'found': self.found_method, 130 }) 131 132 @noPosargs 133 @noKwargs 134 def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: 135 return self.found() 136 137 @staticmethod 138 def found() -> bool: 139 return True 140 141 142class NewExtensionModule(ModuleObject): 143 144 """Class for modern modules 145 146 provides the found method. 147 """ 148 149 def __init__(self) -> None: 150 super().__init__() 151 self.methods.update({ 152 'found': self.found_method, 153 }) 154 155 @noPosargs 156 @noKwargs 157 def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: 158 return self.found() 159 160 @staticmethod 161 def found() -> bool: 162 return True 163 164 165class NotFoundExtensionModule(NewExtensionModule): 166 167 """Class for modern modules 168 169 provides the found method. 170 """ 171 172 @staticmethod 173 def found() -> bool: 174 return False 175 176 177def is_module_library(fname): 178 ''' 179 Check if the file is a library-like file generated by a module-specific 180 target, such as GirTarget or TypelibTarget 181 ''' 182 if hasattr(fname, 'fname'): 183 fname = fname.fname 184 suffix = fname.split('.')[-1] 185 return suffix in ('gir', 'typelib') 186 187 188class ModuleReturnValue: 189 def __init__(self, return_value: T.Optional['TYPE_var'], new_objects: T.List['TYPE_var']) -> None: 190 self.return_value = return_value 191 assert(isinstance(new_objects, list)) 192 self.new_objects = new_objects 193 194class GResourceTarget(build.CustomTarget): 195 def __init__(self, name, subdir, subproject, kwargs): 196 super().__init__(name, subdir, subproject, kwargs) 197 198class GResourceHeaderTarget(build.CustomTarget): 199 def __init__(self, name, subdir, subproject, kwargs): 200 super().__init__(name, subdir, subproject, kwargs) 201 202class GirTarget(build.CustomTarget): 203 def __init__(self, name, subdir, subproject, kwargs): 204 super().__init__(name, subdir, subproject, kwargs) 205 206class TypelibTarget(build.CustomTarget): 207 def __init__(self, name, subdir, subproject, kwargs): 208 super().__init__(name, subdir, subproject, kwargs) 209 210class VapiTarget(build.CustomTarget): 211 def __init__(self, name, subdir, subproject, kwargs): 212 super().__init__(name, subdir, subproject, kwargs) 213