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