diff options
Diffstat (limited to 'mesonbuild/interpreter/mesonmain.py')
-rw-r--r-- | mesonbuild/interpreter/mesonmain.py | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py new file mode 100644 index 0000000..01d0029 --- /dev/null +++ b/mesonbuild/interpreter/mesonmain.py @@ -0,0 +1,456 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2012-2021 The Meson development team +# Copyright © 2021 Intel Corporation + +import os +import typing as T + +from .. import mesonlib +from .. import dependencies +from .. import build +from .. import mlog + +from ..mesonlib import MachineChoice, OptionKey +from ..programs import OverrideProgram, ExternalProgram +from ..interpreter.type_checking import ENV_KW, ENV_METHOD_KW, ENV_SEPARATOR_KW, env_convertor_with_method +from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated, + typed_pos_args, noArgsFlattening, noPosargs, noKwargs, + typed_kwargs, KwargInfo, InterpreterException) +from .primitives import MesonVersionString +from .type_checking import NATIVE_KW, NoneType + +if T.TYPE_CHECKING: + from typing_extensions import Literal + from ..backend.backends import ExecutableSerialisation + from ..compilers import Compiler + from ..interpreterbase import TYPE_kwargs, TYPE_var + from .interpreter import Interpreter + + from typing_extensions import TypedDict + + class FuncOverrideDependency(TypedDict): + + native: mesonlib.MachineChoice + static: T.Optional[bool] + + class AddInstallScriptKW(TypedDict): + + skip_if_destdir: bool + install_tag: str + + class NativeKW(TypedDict): + + native: mesonlib.MachineChoice + + class AddDevenvKW(TypedDict): + method: Literal['set', 'prepend', 'append'] + separator: str + + +class MesonMain(MesonInterpreterObject): + def __init__(self, build: 'build.Build', interpreter: 'Interpreter'): + super().__init__(subproject=interpreter.subproject) + self.build = build + self.interpreter = interpreter + self.methods.update({'get_compiler': self.get_compiler_method, + 'is_cross_build': self.is_cross_build_method, + 'has_exe_wrapper': self.has_exe_wrapper_method, + 'can_run_host_binaries': self.can_run_host_binaries_method, + 'is_unity': self.is_unity_method, + 'is_subproject': self.is_subproject_method, + 'current_source_dir': self.current_source_dir_method, + 'current_build_dir': self.current_build_dir_method, + 'source_root': self.source_root_method, + 'build_root': self.build_root_method, + 'project_source_root': self.project_source_root_method, + 'project_build_root': self.project_build_root_method, + 'global_source_root': self.global_source_root_method, + 'global_build_root': self.global_build_root_method, + 'add_install_script': self.add_install_script_method, + 'add_postconf_script': self.add_postconf_script_method, + 'add_dist_script': self.add_dist_script_method, + 'install_dependency_manifest': self.install_dependency_manifest_method, + 'override_dependency': self.override_dependency_method, + 'override_find_program': self.override_find_program_method, + 'project_version': self.project_version_method, + 'project_license': self.project_license_method, + 'version': self.version_method, + 'project_name': self.project_name_method, + 'get_cross_property': self.get_cross_property_method, + 'get_external_property': self.get_external_property_method, + 'has_external_property': self.has_external_property_method, + 'backend': self.backend_method, + 'add_devenv': self.add_devenv_method, + }) + + def _find_source_script( + self, name: str, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram], + args: T.List[str]) -> 'ExecutableSerialisation': + largs: T.List[T.Union[str, build.Executable, ExternalProgram]] = [] + + if isinstance(prog, (build.Executable, ExternalProgram)): + FeatureNew.single_use(f'Passing executable/found program object to script parameter of {name}', + '0.55.0', self.subproject, location=self.current_node) + largs.append(prog) + else: + if isinstance(prog, mesonlib.File): + FeatureNew.single_use(f'Passing file object to script parameter of {name}', + '0.57.0', self.subproject, location=self.current_node) + found = self.interpreter.find_program_impl([prog]) + largs.append(found) + + largs.extend(args) + es = self.interpreter.backend.get_executable_serialisation(largs) + es.subproject = self.interpreter.subproject + return es + + def _process_script_args( + self, name: str, args: T.Sequence[T.Union[ + str, mesonlib.File, build.BuildTarget, build.CustomTarget, + build.CustomTargetIndex, + ExternalProgram, + ]]) -> T.List[str]: + script_args = [] # T.List[str] + new = False + for a in args: + if isinstance(a, str): + script_args.append(a) + elif isinstance(a, mesonlib.File): + new = True + script_args.append(a.rel_to_builddir(self.interpreter.environment.source_dir)) + elif isinstance(a, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): + new = True + script_args.extend([os.path.join(a.get_subdir(), o) for o in a.get_outputs()]) + + # This feels really hacky, but I'm not sure how else to fix + # this without completely rewriting install script handling. + # This is complicated by the fact that the install target + # depends on all. + if isinstance(a, build.CustomTargetIndex): + a.target.build_by_default = True + else: + a.build_by_default = True + else: + script_args.extend(a.command) + new = True + + if new: + FeatureNew.single_use( + f'Calling "{name}" with File, CustomTarget, Index of CustomTarget, ' + 'Executable, or ExternalProgram', + '0.55.0', self.interpreter.subproject, location=self.current_node) + return script_args + + @typed_pos_args( + 'meson.add_install_script', + (str, mesonlib.File, build.Executable, ExternalProgram), + varargs=(str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram) + ) + @typed_kwargs( + 'meson.add_install_script', + KwargInfo('skip_if_destdir', bool, default=False, since='0.57.0'), + KwargInfo('install_tag', (str, NoneType), since='0.60.0'), + ) + def add_install_script_method( + self, + args: T.Tuple[T.Union[str, mesonlib.File, build.Executable, ExternalProgram], + T.List[T.Union[str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram]]], + kwargs: 'AddInstallScriptKW') -> None: + script_args = self._process_script_args('add_install_script', args[1]) + script = self._find_source_script('add_install_script', args[0], script_args) + script.skip_if_destdir = kwargs['skip_if_destdir'] + script.tag = kwargs['install_tag'] + self.build.install_scripts.append(script) + + @typed_pos_args( + 'meson.add_postconf_script', + (str, mesonlib.File, ExternalProgram), + varargs=(str, mesonlib.File, ExternalProgram) + ) + @noKwargs + def add_postconf_script_method( + self, + args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram], + T.List[T.Union[str, mesonlib.File, ExternalProgram]]], + kwargs: 'TYPE_kwargs') -> None: + script_args = self._process_script_args('add_postconf_script', args[1]) + script = self._find_source_script('add_postconf_script', args[0], script_args) + self.build.postconf_scripts.append(script) + + @typed_pos_args( + 'meson.add_dist_script', + (str, mesonlib.File, ExternalProgram), + varargs=(str, mesonlib.File, ExternalProgram) + ) + @noKwargs + def add_dist_script_method( + self, + args: T.Tuple[T.Union[str, mesonlib.File, ExternalProgram], + T.List[T.Union[str, mesonlib.File, ExternalProgram]]], + kwargs: 'TYPE_kwargs') -> None: + if args[1]: + FeatureNew.single_use('Calling "add_dist_script" with multiple arguments', + '0.49.0', self.interpreter.subproject, location=self.current_node) + if self.interpreter.subproject != '': + FeatureNew.single_use('Calling "add_dist_script" in a subproject', + '0.58.0', self.interpreter.subproject, location=self.current_node) + script_args = self._process_script_args('add_dist_script', args[1]) + script = self._find_source_script('add_dist_script', args[0], script_args) + self.build.dist_scripts.append(script) + + @noPosargs + @noKwargs + def current_source_dir_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + src = self.interpreter.environment.source_dir + sub = self.interpreter.subdir + if sub == '': + return src + return os.path.join(src, sub) + + @noPosargs + @noKwargs + def current_build_dir_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + src = self.interpreter.environment.build_dir + sub = self.interpreter.subdir + if sub == '': + return src + return os.path.join(src, sub) + + @noPosargs + @noKwargs + def backend_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.interpreter.backend.name + + @noPosargs + @noKwargs + @FeatureDeprecated('meson.source_root', '0.56.0', 'use meson.project_source_root() or meson.global_source_root() instead.') + def source_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.interpreter.environment.source_dir + + @noPosargs + @noKwargs + @FeatureDeprecated('meson.build_root', '0.56.0', 'use meson.project_build_root() or meson.global_build_root() instead.') + def build_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.interpreter.environment.build_dir + + @noPosargs + @noKwargs + @FeatureNew('meson.project_source_root', '0.56.0') + def project_source_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + src = self.interpreter.environment.source_dir + sub = self.interpreter.root_subdir + if sub == '': + return src + return os.path.join(src, sub) + + @noPosargs + @noKwargs + @FeatureNew('meson.project_build_root', '0.56.0') + def project_build_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + src = self.interpreter.environment.build_dir + sub = self.interpreter.root_subdir + if sub == '': + return src + return os.path.join(src, sub) + + @noPosargs + @noKwargs + @FeatureNew('meson.global_source_root', '0.58.0') + def global_source_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.interpreter.environment.source_dir + + @noPosargs + @noKwargs + @FeatureNew('meson.global_build_root', '0.58.0') + def global_build_root_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.interpreter.environment.build_dir + + @noPosargs + @noKwargs + @FeatureDeprecated('meson.has_exe_wrapper', '0.55.0', 'use meson.can_run_host_binaries instead.') + def has_exe_wrapper_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: + return self._can_run_host_binaries_impl() + + @noPosargs + @noKwargs + @FeatureNew('meson.can_run_host_binaries', '0.55.0') + def can_run_host_binaries_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: + return self._can_run_host_binaries_impl() + + def _can_run_host_binaries_impl(self) -> bool: + return not ( + self.build.environment.is_cross_build() and + self.build.environment.need_exe_wrapper() and + self.build.environment.exe_wrapper is None + ) + + @noPosargs + @noKwargs + def is_cross_build_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: + return self.build.environment.is_cross_build() + + @typed_pos_args('meson.get_compiler', str) + @typed_kwargs('meson.get_compiler', NATIVE_KW) + def get_compiler_method(self, args: T.Tuple[str], kwargs: 'NativeKW') -> 'Compiler': + cname = args[0] + for_machine = kwargs['native'] + clist = self.interpreter.coredata.compilers[for_machine] + try: + return clist[cname] + except KeyError: + raise InterpreterException(f'Tried to access compiler for language "{cname}", not specified for {for_machine.get_lower_case_name()} machine.') + + @noPosargs + @noKwargs + def is_unity_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: + optval = self.interpreter.environment.coredata.get_option(OptionKey('unity')) + return optval == 'on' or (optval == 'subprojects' and self.interpreter.is_subproject()) + + @noPosargs + @noKwargs + def is_subproject_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: + return self.interpreter.is_subproject() + + @typed_pos_args('meson.install_dependency_manifest', str) + @noKwargs + def install_dependency_manifest_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> None: + self.build.dep_manifest_name = args[0] + + @FeatureNew('meson.override_find_program', '0.46.0') + @typed_pos_args('meson.override_find_program', str, (mesonlib.File, ExternalProgram, build.Executable)) + @noKwargs + def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, ExternalProgram, build.Executable]], kwargs: 'TYPE_kwargs') -> None: + name, exe = args + if isinstance(exe, mesonlib.File): + abspath = exe.absolute_path(self.interpreter.environment.source_dir, + self.interpreter.environment.build_dir) + if not os.path.exists(abspath): + raise InterpreterException(f'Tried to override {name} with a file that does not exist.') + exe = OverrideProgram(name, [abspath]) + self.interpreter.add_find_program_override(name, exe) + + @typed_kwargs( + 'meson.override_dependency', + NATIVE_KW, + KwargInfo('static', (bool, NoneType), since='0.60.0'), + ) + @typed_pos_args('meson.override_dependency', str, dependencies.Dependency) + @FeatureNew('meson.override_dependency', '0.54.0') + def override_dependency_method(self, args: T.Tuple[str, dependencies.Dependency], kwargs: 'FuncOverrideDependency') -> None: + name, dep = args + if not name: + raise InterpreterException('First argument must be a string and cannot be empty') + + optkey = OptionKey('default_library', subproject=self.interpreter.subproject) + default_library = self.interpreter.coredata.get_option(optkey) + assert isinstance(default_library, str), 'for mypy' + static = kwargs['static'] + if static is None: + # We don't know if dep represents a static or shared library, could + # be a mix of both. We assume it is following default_library + # value. + self._override_dependency_impl(name, dep, kwargs, static=None) + if default_library == 'static': + self._override_dependency_impl(name, dep, kwargs, static=True) + elif default_library == 'shared': + self._override_dependency_impl(name, dep, kwargs, static=False) + else: + self._override_dependency_impl(name, dep, kwargs, static=True) + self._override_dependency_impl(name, dep, kwargs, static=False) + else: + # dependency('foo') without specifying static kwarg should find this + # override regardless of the static value here. But do not raise error + # if it has already been overridden, which would happen when overriding + # static and shared separately: + # meson.override_dependency('foo', shared_dep, static: false) + # meson.override_dependency('foo', static_dep, static: true) + # In that case dependency('foo') would return the first override. + self._override_dependency_impl(name, dep, kwargs, static=None, permissive=True) + self._override_dependency_impl(name, dep, kwargs, static=static) + + def _override_dependency_impl(self, name: str, dep: dependencies.Dependency, kwargs: 'FuncOverrideDependency', + static: T.Optional[bool], permissive: bool = False) -> None: + # We need the cast here as get_dep_identifier works on such a dict, + # which FuncOverrideDependency is, but mypy can't fgure that out + nkwargs = T.cast('T.Dict[str, T.Any]', kwargs.copy()) + if static is None: + del nkwargs['static'] + else: + nkwargs['static'] = static + identifier = dependencies.get_dep_identifier(name, nkwargs) + for_machine = kwargs['native'] + override = self.build.dependency_overrides[for_machine].get(identifier) + if override: + if permissive: + return + m = 'Tried to override dependency {!r} which has already been resolved or overridden at {}' + location = mlog.get_error_location_string(override.node.filename, override.node.lineno) + raise InterpreterException(m.format(name, location)) + self.build.dependency_overrides[for_machine][identifier] = \ + build.DependencyOverride(dep, self.interpreter.current_node) + + @noPosargs + @noKwargs + def project_version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.build.dep_manifest[self.interpreter.active_projectname].version + + @FeatureNew('meson.project_license()', '0.45.0') + @noPosargs + @noKwargs + def project_license_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> T.List[str]: + return self.build.dep_manifest[self.interpreter.active_projectname].license + + @noPosargs + @noKwargs + def version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> MesonVersionString: + return MesonVersionString(self.interpreter.coredata.version) + + @noPosargs + @noKwargs + def project_name_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.interpreter.active_projectname + + def __get_external_property_impl(self, propname: str, fallback: T.Optional[object], machine: MachineChoice) -> object: + """Shared implementation for get_cross_property and get_external_property.""" + try: + return self.interpreter.environment.properties[machine][propname] + except KeyError: + if fallback is not None: + return fallback + raise InterpreterException(f'Unknown property for {machine.get_lower_case_name()} machine: {propname}') + + @noArgsFlattening + @FeatureDeprecated('meson.get_cross_property', '0.58.0', 'Use meson.get_external_property() instead') + @typed_pos_args('meson.get_cross_property', str, optargs=[object]) + @noKwargs + def get_cross_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: 'TYPE_kwargs') -> object: + propname, fallback = args + return self.__get_external_property_impl(propname, fallback, MachineChoice.HOST) + + @noArgsFlattening + @FeatureNew('meson.get_external_property', '0.54.0') + @typed_pos_args('meson.get_external_property', str, optargs=[object]) + @typed_kwargs('meson.get_external_property', NATIVE_KW) + def get_external_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: 'NativeKW') -> object: + propname, fallback = args + return self.__get_external_property_impl(propname, fallback, kwargs['native']) + + @FeatureNew('meson.has_external_property', '0.58.0') + @typed_pos_args('meson.has_external_property', str) + @typed_kwargs('meson.has_external_property', NATIVE_KW) + def has_external_property_method(self, args: T.Tuple[str], kwargs: 'NativeKW') -> bool: + prop_name = args[0] + return prop_name in self.interpreter.environment.properties[kwargs['native']] + + @FeatureNew('add_devenv', '0.58.0') + @typed_kwargs('environment', ENV_METHOD_KW, ENV_SEPARATOR_KW.evolve(since='0.62.0')) + @typed_pos_args('add_devenv', (str, list, dict, build.EnvironmentVariables)) + def add_devenv_method(self, args: T.Tuple[T.Union[str, list, dict, build.EnvironmentVariables]], + kwargs: 'AddDevenvKW') -> None: + env = args[0] + msg = ENV_KW.validator(env) + if msg: + raise build.InvalidArguments(f'"add_devenv": {msg}') + converted = env_convertor_with_method(env, kwargs['method'], kwargs['separator']) + assert isinstance(converted, build.EnvironmentVariables) + self.build.devenv.append(converted) |