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 base representation for import('modname') 16 17import os 18import typing as T 19 20from .. import build, mesonlib 21from ..mesonlib import relpath, HoldableObject, MachineChoice, OptionKey 22from ..interpreterbase.decorators import noKwargs, noPosargs 23 24if T.TYPE_CHECKING: 25 from ..interpreter import Interpreter 26 from ..interpreterbase import TYPE_var, TYPE_kwargs 27 from ..programs import ExternalProgram 28 from ..wrap import WrapMode 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].version 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 = T.cast('MachineHolder', interpreter.builtin['build_machine']).held_object 60 self.host_machine = T.cast('MachineHolder', interpreter.builtin['host_machine']).held_object 61 self.target_machine = T.cast('MachineHolder', 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, silent: bool = False) -> 'ExternalProgram': 93 return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent) 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 def get_option(self, name: str, subproject: str = '', 107 machine: MachineChoice = MachineChoice.HOST, 108 lang: T.Optional[str] = None, 109 module: T.Optional[str] = None) -> T.Union[str, int, bool, 'WrapMode']: 110 return self.environment.coredata.get_option(mesonlib.OptionKey(name, subproject, machine, lang, module)) 111 112 113class ModuleObject(HoldableObject): 114 """Base class for all objects returned by modules 115 """ 116 def __init__(self) -> None: 117 self.methods: T.Dict[ 118 str, 119 T.Callable[[ModuleState, T.List['TYPE_var'], 'TYPE_kwargs'], T.Union[ModuleReturnValue, 'TYPE_var']] 120 ] = {} 121 122 123class MutableModuleObject(ModuleObject): 124 pass 125 126 127# FIXME: Port all modules to stop using self.interpreter and use API on 128# ModuleState instead. Modules should stop using this class and instead use 129# ModuleObject base class. 130class ExtensionModule(ModuleObject): 131 def __init__(self, interpreter: 'Interpreter') -> None: 132 super().__init__() 133 self.interpreter = interpreter 134 self.methods.update({ 135 'found': self.found_method, 136 }) 137 138 @noPosargs 139 @noKwargs 140 def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: 141 return self.found() 142 143 @staticmethod 144 def found() -> bool: 145 return True 146 147 148class NewExtensionModule(ModuleObject): 149 150 """Class for modern modules 151 152 provides the found method. 153 """ 154 155 def __init__(self) -> None: 156 super().__init__() 157 self.methods.update({ 158 'found': self.found_method, 159 }) 160 161 @noPosargs 162 @noKwargs 163 def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: 164 return self.found() 165 166 @staticmethod 167 def found() -> bool: 168 return True 169 170 171class NotFoundExtensionModule(NewExtensionModule): 172 173 """Class for modern modules 174 175 provides the found method. 176 """ 177 178 @staticmethod 179 def found() -> bool: 180 return False 181 182 183def is_module_library(fname): 184 ''' 185 Check if the file is a library-like file generated by a module-specific 186 target, such as GirTarget or TypelibTarget 187 ''' 188 if hasattr(fname, 'fname'): 189 fname = fname.fname 190 suffix = fname.split('.')[-1] 191 return suffix in ('gir', 'typelib') 192 193 194class ModuleReturnValue: 195 def __init__(self, return_value: T.Optional['TYPE_var'], new_objects: T.Sequence['TYPE_var']) -> None: 196 self.return_value = return_value 197 assert isinstance(new_objects, list) 198 self.new_objects = new_objects 199 200class GResourceTarget(build.CustomTarget): 201 def __init__(self, name, subdir, subproject, kwargs): 202 super().__init__(name, subdir, subproject, kwargs) 203 204class GResourceHeaderTarget(build.CustomTarget): 205 def __init__(self, name, subdir, subproject, kwargs): 206 super().__init__(name, subdir, subproject, kwargs) 207 208class GirTarget(build.CustomTarget): 209 def __init__(self, name, subdir, subproject, kwargs): 210 super().__init__(name, subdir, subproject, kwargs) 211 212class TypelibTarget(build.CustomTarget): 213 def __init__(self, name, subdir, subproject, kwargs): 214 super().__init__(name, subdir, subproject, kwargs) 215 216class VapiTarget(build.CustomTarget): 217 def __init__(self, name, subdir, subproject, kwargs): 218 super().__init__(name, subdir, subproject, kwargs) 219