summaryrefslogtreecommitdiffstats
path: root/mesonbuild/interpreter/interpreterobjects.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-29 04:41:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-29 04:41:38 +0000
commit7b6e527f440cd7e6f8be2b07cee320ee6ca18786 (patch)
tree4a2738d69fa2814659fdadddf5826282e73d81f4 /mesonbuild/interpreter/interpreterobjects.py
parentInitial commit. (diff)
downloadmeson-7b6e527f440cd7e6f8be2b07cee320ee6ca18786.tar.xz
meson-7b6e527f440cd7e6f8be2b07cee320ee6ca18786.zip
Adding upstream version 1.0.1.upstream/1.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mesonbuild/interpreter/interpreterobjects.py')
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py987
1 files changed, 987 insertions, 0 deletions
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
new file mode 100644
index 0000000..538d134
--- /dev/null
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -0,0 +1,987 @@
+from __future__ import annotations
+import os
+import shlex
+import subprocess
+import copy
+import textwrap
+
+from pathlib import Path, PurePath
+
+from .. import mesonlib
+from .. import coredata
+from .. import build
+from .. import mlog
+
+from ..modules import ModuleReturnValue, ModuleObject, ModuleState, ExtensionModule
+from ..backend.backends import TestProtocol
+from ..interpreterbase import (
+ ContainerTypeInfo, KwargInfo, MesonOperator,
+ MesonInterpreterObject, ObjectHolder, MutableInterpreterObject,
+ FeatureNew, FeatureDeprecated,
+ typed_pos_args, typed_kwargs, typed_operator,
+ noArgsFlattening, noPosargs, noKwargs, unholder_return,
+ flatten, resolve_second_level_holders, InterpreterException, InvalidArguments, InvalidCode)
+from ..interpreter.type_checking import NoneType, ENV_SEPARATOR_KW
+from ..dependencies import Dependency, ExternalLibrary, InternalDependency
+from ..programs import ExternalProgram
+from ..mesonlib import HoldableObject, OptionKey, listify, Popen_safe
+
+import typing as T
+
+if T.TYPE_CHECKING:
+ from . import kwargs
+ from ..cmake.interpreter import CMakeInterpreter
+ from ..envconfig import MachineInfo
+ from ..interpreterbase import FeatureCheckBase, InterpreterObject, SubProject, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs
+ from .interpreter import Interpreter
+
+ from typing_extensions import TypedDict
+
+ class EnvironmentSeparatorKW(TypedDict):
+
+ separator: str
+
+
+def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
+ subproject: 'SubProject',
+ feature_check: T.Optional[FeatureCheckBase] = None,
+ default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]:
+ val = kwargs.get('required', default)
+ disabled = False
+ required = False
+ feature: T.Optional[str] = None
+ if isinstance(val, coredata.UserFeatureOption):
+ if not feature_check:
+ feature_check = FeatureNew('User option "feature"', '0.47.0')
+ feature_check.use(subproject)
+ feature = val.name
+ if val.is_disabled():
+ disabled = True
+ elif val.is_enabled():
+ required = True
+ elif isinstance(val, bool):
+ required = val
+ else:
+ raise InterpreterException('required keyword argument must be boolean or a feature option')
+
+ # Keep boolean value in kwargs to simplify other places where this kwarg is
+ # checked.
+ # TODO: this should be removed, and those callers should learn about FeatureOptions
+ kwargs['required'] = required
+
+ return disabled, required, feature
+
+def extract_search_dirs(kwargs: 'kwargs.ExtractSearchDirs') -> T.List[str]:
+ search_dirs_str = mesonlib.stringlistify(kwargs.get('dirs', []))
+ search_dirs = [Path(d).expanduser() for d in search_dirs_str]
+ for d in search_dirs:
+ if mesonlib.is_windows() and d.root.startswith('\\'):
+ # a Unix-path starting with `/` that is not absolute on Windows.
+ # discard without failing for end-user ease of cross-platform directory arrays
+ continue
+ if not d.is_absolute():
+ raise InvalidCode(f'Search directory {d} is not an absolute path.')
+ return [str(s) for s in search_dirs]
+
+class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
+ def __init__(self, option: coredata.UserFeatureOption, interpreter: 'Interpreter'):
+ super().__init__(option, interpreter)
+ if option and option.is_auto():
+ # TODO: we need to cast here because options is not a TypedDict
+ auto = T.cast('coredata.UserFeatureOption', self.env.coredata.options[OptionKey('auto_features')])
+ self.held_object = copy.copy(auto)
+ self.held_object.name = option.name
+ self.methods.update({'enabled': self.enabled_method,
+ 'disabled': self.disabled_method,
+ 'allowed': self.allowed_method,
+ 'auto': self.auto_method,
+ 'require': self.require_method,
+ 'disable_auto_if': self.disable_auto_if_method,
+ })
+
+ @property
+ def value(self) -> str:
+ return 'disabled' if not self.held_object else self.held_object.value
+
+ def as_disabled(self) -> coredata.UserFeatureOption:
+ disabled = copy.deepcopy(self.held_object)
+ disabled.value = 'disabled'
+ return disabled
+
+ @noPosargs
+ @noKwargs
+ def enabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.value == 'enabled'
+
+ @noPosargs
+ @noKwargs
+ def disabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.value == 'disabled'
+
+ @noPosargs
+ @noKwargs
+ @FeatureNew('feature_option.allowed()', '0.59.0')
+ def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.value != 'disabled'
+
+ @noPosargs
+ @noKwargs
+ def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.value == 'auto'
+
+ @FeatureNew('feature_option.require()', '0.59.0')
+ @typed_pos_args('feature_option.require', bool)
+ @typed_kwargs(
+ 'feature_option.require',
+ KwargInfo('error_message', (str, NoneType))
+ )
+ def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> coredata.UserFeatureOption:
+ if args[0]:
+ return copy.deepcopy(self.held_object)
+
+ if self.value == 'enabled':
+ err_msg = f'Feature {self.held_object.name} cannot be enabled'
+ if kwargs['error_message']:
+ err_msg += f': {kwargs["error_message"]}'
+ raise InterpreterException(err_msg)
+ return self.as_disabled()
+
+ @FeatureNew('feature_option.disable_auto_if()', '0.59.0')
+ @noKwargs
+ @typed_pos_args('feature_option.disable_auto_if', bool)
+ def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
+ return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled()
+
+
+class RunProcess(MesonInterpreterObject):
+
+ def __init__(self,
+ cmd: ExternalProgram,
+ args: T.List[str],
+ env: build.EnvironmentVariables,
+ source_dir: str,
+ build_dir: str,
+ subdir: str,
+ mesonintrospect: T.List[str],
+ in_builddir: bool = False,
+ check: bool = False,
+ capture: bool = True) -> None:
+ super().__init__()
+ if not isinstance(cmd, ExternalProgram):
+ raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
+ self.capture = capture
+ self.returncode, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
+ self.methods.update({'returncode': self.returncode_method,
+ 'stdout': self.stdout_method,
+ 'stderr': self.stderr_method,
+ })
+
+ def run_command(self,
+ cmd: ExternalProgram,
+ args: T.List[str],
+ env: build.EnvironmentVariables,
+ source_dir: str,
+ build_dir: str,
+ subdir: str,
+ mesonintrospect: T.List[str],
+ in_builddir: bool,
+ check: bool = False) -> T.Tuple[int, str, str]:
+ command_array = cmd.get_command() + args
+ menv = {'MESON_SOURCE_ROOT': source_dir,
+ 'MESON_BUILD_ROOT': build_dir,
+ 'MESON_SUBDIR': subdir,
+ 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
+ }
+ if in_builddir:
+ cwd = os.path.join(build_dir, subdir)
+ else:
+ cwd = os.path.join(source_dir, subdir)
+ child_env = os.environ.copy()
+ child_env.update(menv)
+ child_env = env.get_env(child_env)
+ stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL
+ mlog.debug('Running command:', mesonlib.join_args(command_array))
+ try:
+ p, o, e = Popen_safe(command_array, stdout=stdout, env=child_env, cwd=cwd)
+ if self.capture:
+ mlog.debug('--- stdout ---')
+ mlog.debug(o)
+ else:
+ o = ''
+ mlog.debug('--- stdout disabled ---')
+ mlog.debug('--- stderr ---')
+ mlog.debug(e)
+ mlog.debug('')
+
+ if check and p.returncode != 0:
+ raise InterpreterException('Command `{}` failed with status {}.'.format(mesonlib.join_args(command_array), p.returncode))
+
+ return p.returncode, o, e
+ except FileNotFoundError:
+ raise InterpreterException('Could not execute command `%s`.' % mesonlib.join_args(command_array))
+
+ @noPosargs
+ @noKwargs
+ def returncode_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
+ return self.returncode
+
+ @noPosargs
+ @noKwargs
+ def stdout_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.stdout
+
+ @noPosargs
+ @noKwargs
+ def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.stderr
+
+class EnvironmentVariablesHolder(ObjectHolder[build.EnvironmentVariables], MutableInterpreterObject):
+
+ def __init__(self, obj: build.EnvironmentVariables, interpreter: 'Interpreter'):
+ super().__init__(obj, interpreter)
+ self.methods.update({'set': self.set_method,
+ 'append': self.append_method,
+ 'prepend': self.prepend_method,
+ })
+
+ def __repr__(self) -> str:
+ repr_str = "<{0}: {1}>"
+ return repr_str.format(self.__class__.__name__, self.held_object.envvars)
+
+ def __deepcopy__(self, memo: T.Dict[str, object]) -> 'EnvironmentVariablesHolder':
+ # Avoid trying to copy the interpreter
+ return EnvironmentVariablesHolder(copy.deepcopy(self.held_object), self.interpreter)
+
+ def warn_if_has_name(self, name: str) -> None:
+ # Multiple append/prepend operations was not supported until 0.58.0.
+ if self.held_object.has_name(name):
+ m = f'Overriding previous value of environment variable {name!r} with a new one'
+ FeatureNew(m, '0.58.0').use(self.subproject, self.current_node)
+
+ @typed_pos_args('environment.set', str, varargs=str, min_varargs=1)
+ @typed_kwargs('environment.set', ENV_SEPARATOR_KW)
+ def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
+ name, values = args
+ self.held_object.set(name, values, kwargs['separator'])
+
+ @typed_pos_args('environment.append', str, varargs=str, min_varargs=1)
+ @typed_kwargs('environment.append', ENV_SEPARATOR_KW)
+ def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
+ name, values = args
+ self.warn_if_has_name(name)
+ self.held_object.append(name, values, kwargs['separator'])
+
+ @typed_pos_args('environment.prepend', str, varargs=str, min_varargs=1)
+ @typed_kwargs('environment.prepend', ENV_SEPARATOR_KW)
+ def prepend_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
+ name, values = args
+ self.warn_if_has_name(name)
+ self.held_object.prepend(name, values, kwargs['separator'])
+
+
+_CONF_DATA_SET_KWS: KwargInfo[T.Optional[str]] = KwargInfo('description', (str, NoneType))
+
+
+class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInterpreterObject):
+
+ def __init__(self, obj: build.ConfigurationData, interpreter: 'Interpreter'):
+ super().__init__(obj, interpreter)
+ self.methods.update({'set': self.set_method,
+ 'set10': self.set10_method,
+ 'set_quoted': self.set_quoted_method,
+ 'has': self.has_method,
+ 'get': self.get_method,
+ 'keys': self.keys_method,
+ 'get_unquoted': self.get_unquoted_method,
+ 'merge_from': self.merge_from_method,
+ })
+
+ def __deepcopy__(self, memo: T.Dict) -> 'ConfigurationDataHolder':
+ return ConfigurationDataHolder(copy.deepcopy(self.held_object), self.interpreter)
+
+ def is_used(self) -> bool:
+ return self.held_object.used
+
+ def __check_used(self) -> None:
+ if self.is_used():
+ raise InterpreterException("Can not set values on configuration object that has been used.")
+
+ @typed_pos_args('configuration_data.set', str, (str, int, bool))
+ @typed_kwargs('configuration_data.set', _CONF_DATA_SET_KWS)
+ def set_method(self, args: T.Tuple[str, T.Union[str, int, bool]], kwargs: 'kwargs.ConfigurationDataSet') -> None:
+ self.__check_used()
+ self.held_object.values[args[0]] = (args[1], kwargs['description'])
+
+ @typed_pos_args('configuration_data.set_quoted', str, str)
+ @typed_kwargs('configuration_data.set_quoted', _CONF_DATA_SET_KWS)
+ def set_quoted_method(self, args: T.Tuple[str, str], kwargs: 'kwargs.ConfigurationDataSet') -> None:
+ self.__check_used()
+ escaped_val = '\\"'.join(args[1].split('"'))
+ self.held_object.values[args[0]] = (f'"{escaped_val}"', kwargs['description'])
+
+ @typed_pos_args('configuration_data.set10', str, (int, bool))
+ @typed_kwargs('configuration_data.set10', _CONF_DATA_SET_KWS)
+ def set10_method(self, args: T.Tuple[str, T.Union[int, bool]], kwargs: 'kwargs.ConfigurationDataSet') -> None:
+ self.__check_used()
+ # bool is a subclass of int, so we need to check for bool explicitly.
+ # We already have typed_pos_args checking that this is either a bool or
+ # an int.
+ if not isinstance(args[1], bool):
+ mlog.deprecation('configuration_data.set10 with number. the `set10` '
+ 'method should only be used with booleans',
+ location=self.interpreter.current_node)
+ if args[1] < 0:
+ mlog.warning('Passing a number that is less than 0 may not have the intended result, '
+ 'as meson will treat all non-zero values as true.',
+ location=self.interpreter.current_node)
+ self.held_object.values[args[0]] = (int(args[1]), kwargs['description'])
+
+ @typed_pos_args('configuration_data.has', (str, int, bool))
+ @noKwargs
+ def has_method(self, args: T.Tuple[T.Union[str, int, bool]], kwargs: TYPE_kwargs) -> bool:
+ return args[0] in self.held_object.values
+
+ @FeatureNew('configuration_data.get()', '0.38.0')
+ @typed_pos_args('configuration_data.get', str, optargs=[(str, int, bool)])
+ @noKwargs
+ def get_method(self, args: T.Tuple[str, T.Optional[T.Union[str, int, bool]]],
+ kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
+ name = args[0]
+ if name in self.held_object:
+ return self.held_object.get(name)[0]
+ elif args[1] is not None:
+ return args[1]
+ raise InterpreterException(f'Entry {name} not in configuration data.')
+
+ @FeatureNew('configuration_data.get_unquoted()', '0.44.0')
+ @typed_pos_args('configuration_data.get_unquoted', str, optargs=[(str, int, bool)])
+ @noKwargs
+ def get_unquoted_method(self, args: T.Tuple[str, T.Optional[T.Union[str, int, bool]]],
+ kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
+ name = args[0]
+ if name in self.held_object:
+ val = self.held_object.get(name)[0]
+ elif args[1] is not None:
+ val = args[1]
+ else:
+ raise InterpreterException(f'Entry {name} not in configuration data.')
+ if isinstance(val, str) and val[0] == '"' and val[-1] == '"':
+ return val[1:-1]
+ return val
+
+ def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]:
+ return self.held_object.values[name]
+
+ @FeatureNew('configuration_data.keys()', '0.57.0')
+ @noPosargs
+ @noKwargs
+ def keys_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
+ return sorted(self.keys())
+
+ def keys(self) -> T.List[str]:
+ return list(self.held_object.values.keys())
+
+ @typed_pos_args('configuration_data.merge_from', build.ConfigurationData)
+ @noKwargs
+ def merge_from_method(self, args: T.Tuple[build.ConfigurationData], kwargs: TYPE_kwargs) -> None:
+ from_object = args[0]
+ self.held_object.values.update(from_object.values)
+
+
+_PARTIAL_DEP_KWARGS = [
+ KwargInfo('compile_args', bool, default=False),
+ KwargInfo('link_args', bool, default=False),
+ KwargInfo('links', bool, default=False),
+ KwargInfo('includes', bool, default=False),
+ KwargInfo('sources', bool, default=False),
+]
+
+class DependencyHolder(ObjectHolder[Dependency]):
+ def __init__(self, dep: Dependency, interpreter: 'Interpreter'):
+ super().__init__(dep, interpreter)
+ self.methods.update({'found': self.found_method,
+ 'type_name': self.type_name_method,
+ 'version': self.version_method,
+ 'name': self.name_method,
+ 'get_pkgconfig_variable': self.pkgconfig_method,
+ 'get_configtool_variable': self.configtool_method,
+ 'get_variable': self.variable_method,
+ 'partial_dependency': self.partial_dependency_method,
+ 'include_type': self.include_type_method,
+ 'as_system': self.as_system_method,
+ 'as_link_whole': self.as_link_whole_method,
+ })
+
+ def found(self) -> bool:
+ return self.found_method([], {})
+
+ @noPosargs
+ @noKwargs
+ def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.type_name
+
+ @noPosargs
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ if self.held_object.type_name == 'internal':
+ return True
+ return self.held_object.found()
+
+ @noPosargs
+ @noKwargs
+ def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.get_version()
+
+ @noPosargs
+ @noKwargs
+ def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.get_name()
+
+ @FeatureDeprecated('dependency.get_pkgconfig_variable', '0.56.0',
+ 'use dependency.get_variable(pkgconfig : ...) instead')
+ @typed_pos_args('dependency.get_pkgconfig_variable', str)
+ @typed_kwargs(
+ 'dependency.get_pkgconfig_variable',
+ KwargInfo('default', (str, NoneType)),
+ KwargInfo(
+ 'define_variable',
+ ContainerTypeInfo(list, str, pairs=True),
+ default=[],
+ listify=True,
+ validator=lambda x: 'must be of length 2 or empty' if len(x) not in {0, 2} else None,
+ ),
+ )
+ def pkgconfig_method(self, args: T.Tuple[str], kwargs: 'kwargs.DependencyPkgConfigVar') -> str:
+ return self.held_object.get_pkgconfig_variable(args[0], **kwargs)
+
+ @FeatureNew('dependency.get_configtool_variable', '0.44.0')
+ @FeatureDeprecated('dependency.get_configtool_variable', '0.56.0',
+ 'use dependency.get_variable(configtool : ...) instead')
+ @noKwargs
+ @typed_pos_args('dependency.get_config_tool_variable', str)
+ def configtool_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.get_configtool_variable(args[0])
+
+ @FeatureNew('dependency.partial_dependency', '0.46.0')
+ @noPosargs
+ @typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
+ pdep = self.held_object.get_partial_dependency(**kwargs)
+ return pdep
+
+ @FeatureNew('dependency.get_variable', '0.51.0')
+ @typed_pos_args('dependency.get_variable', optargs=[str])
+ @typed_kwargs(
+ 'dependency.get_variable',
+ KwargInfo('cmake', (str, NoneType)),
+ KwargInfo('pkgconfig', (str, NoneType)),
+ KwargInfo('configtool', (str, NoneType)),
+ KwargInfo('internal', (str, NoneType), since='0.54.0'),
+ KwargInfo('default_value', (str, NoneType)),
+ KwargInfo('pkgconfig_define', ContainerTypeInfo(list, str, pairs=True), default=[], listify=True),
+ )
+ def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: 'kwargs.DependencyGetVariable') -> str:
+ default_varname = args[0]
+ if default_varname is not None:
+ FeatureNew('Positional argument to dependency.get_variable()', '0.58.0').use(self.subproject, self.current_node)
+ return self.held_object.get_variable(
+ cmake=kwargs['cmake'] or default_varname,
+ pkgconfig=kwargs['pkgconfig'] or default_varname,
+ configtool=kwargs['configtool'] or default_varname,
+ internal=kwargs['internal'] or default_varname,
+ default_value=kwargs['default_value'],
+ pkgconfig_define=kwargs['pkgconfig_define'],
+ )
+
+ @FeatureNew('dependency.include_type', '0.52.0')
+ @noPosargs
+ @noKwargs
+ def include_type_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.get_include_type()
+
+ @FeatureNew('dependency.as_system', '0.52.0')
+ @noKwargs
+ @typed_pos_args('dependency.as_system', optargs=[str])
+ def as_system_method(self, args: T.Tuple[T.Optional[str]], kwargs: TYPE_kwargs) -> Dependency:
+ return self.held_object.generate_system_dependency(args[0] or 'system')
+
+ @FeatureNew('dependency.as_link_whole', '0.56.0')
+ @noKwargs
+ @noPosargs
+ def as_link_whole_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency:
+ if not isinstance(self.held_object, InternalDependency):
+ raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects')
+ new_dep = self.held_object.generate_link_whole_dependency()
+ return new_dep
+
+class ExternalProgramHolder(ObjectHolder[ExternalProgram]):
+ def __init__(self, ep: ExternalProgram, interpreter: 'Interpreter') -> None:
+ super().__init__(ep, interpreter)
+ self.methods.update({'found': self.found_method,
+ 'path': self.path_method,
+ 'version': self.version_method,
+ 'full_path': self.full_path_method})
+
+ @noPosargs
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.found()
+
+ @noPosargs
+ @noKwargs
+ @FeatureDeprecated('ExternalProgram.path', '0.55.0',
+ 'use ExternalProgram.full_path() instead')
+ def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self._full_path()
+
+ @noPosargs
+ @noKwargs
+ @FeatureNew('ExternalProgram.full_path', '0.55.0')
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self._full_path()
+
+ def _full_path(self) -> str:
+ if not self.found():
+ raise InterpreterException('Unable to get the path of a not-found external program')
+ path = self.held_object.get_path()
+ assert path is not None
+ return path
+
+ @noPosargs
+ @noKwargs
+ @FeatureNew('ExternalProgram.version', '0.62.0')
+ def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ if not self.found():
+ raise InterpreterException('Unable to get the version of a not-found external program')
+ try:
+ return self.held_object.get_version(self.interpreter)
+ except mesonlib.MesonException:
+ return 'unknown'
+
+ def found(self) -> bool:
+ return self.held_object.found()
+
+class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
+ def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'):
+ super().__init__(el, interpreter)
+ self.methods.update({'found': self.found_method,
+ 'type_name': self.type_name_method,
+ 'partial_dependency': self.partial_dependency_method,
+ })
+
+ @noPosargs
+ @noKwargs
+ def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.type_name
+
+ @noPosargs
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.held_object.found()
+
+ @FeatureNew('dependency.partial_dependency', '0.46.0')
+ @noPosargs
+ @typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
+ pdep = self.held_object.get_partial_dependency(**kwargs)
+ return pdep
+
+# A machine that's statically known from the cross file
+class MachineHolder(ObjectHolder['MachineInfo']):
+ def __init__(self, machine_info: 'MachineInfo', interpreter: 'Interpreter'):
+ super().__init__(machine_info, interpreter)
+ self.methods.update({'system': self.system_method,
+ 'cpu': self.cpu_method,
+ 'cpu_family': self.cpu_family_method,
+ 'endian': self.endian_method,
+ })
+
+ @noPosargs
+ @noKwargs
+ def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.cpu_family
+
+ @noPosargs
+ @noKwargs
+ def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.cpu
+
+ @noPosargs
+ @noKwargs
+ def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.system
+
+ @noPosargs
+ @noKwargs
+ def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.endian
+
+class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]):
+ pass
+
+class FileHolder(ObjectHolder[mesonlib.File]):
+ pass
+
+class HeadersHolder(ObjectHolder[build.Headers]):
+ pass
+
+class DataHolder(ObjectHolder[build.Data]):
+ pass
+
+class SymlinkDataHolder(ObjectHolder[build.SymlinkData]):
+ pass
+
+class InstallDirHolder(ObjectHolder[build.InstallDir]):
+ pass
+
+class ManHolder(ObjectHolder[build.Man]):
+ pass
+
+class EmptyDirHolder(ObjectHolder[build.EmptyDir]):
+ pass
+
+class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]):
+ pass
+
+class Test(MesonInterpreterObject):
+ def __init__(self, name: str, project: str, suite: T.List[str],
+ exe: T.Union[ExternalProgram, build.Executable, build.CustomTarget],
+ depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]],
+ is_parallel: bool,
+ cmd_args: T.List[T.Union[str, mesonlib.File, build.Target]],
+ env: build.EnvironmentVariables,
+ should_fail: bool, timeout: int, workdir: T.Optional[str], protocol: str,
+ priority: int, verbose: bool):
+ super().__init__()
+ self.name = name
+ self.suite = listify(suite)
+ self.project_name = project
+ self.exe = exe
+ self.depends = depends
+ self.is_parallel = is_parallel
+ self.cmd_args = cmd_args
+ self.env = env
+ self.should_fail = should_fail
+ self.timeout = timeout
+ self.workdir = workdir
+ self.protocol = TestProtocol.from_str(protocol)
+ self.priority = priority
+ self.verbose = verbose
+
+ def get_exe(self) -> T.Union[ExternalProgram, build.Executable, build.CustomTarget]:
+ return self.exe
+
+ def get_name(self) -> str:
+ return self.name
+
+class NullSubprojectInterpreter(HoldableObject):
+ pass
+
+# TODO: This should really be an `ObjectHolder`, but the additional stuff in this
+# class prevents this. Thus, this class should be split into a pure
+# `ObjectHolder` and a class specifically for storing in `Interpreter`.
+class SubprojectHolder(MesonInterpreterObject):
+
+ def __init__(self, subinterpreter: T.Union['Interpreter', NullSubprojectInterpreter],
+ subdir: str,
+ warnings: int = 0,
+ disabled_feature: T.Optional[str] = None,
+ exception: T.Optional[Exception] = None) -> None:
+ super().__init__()
+ self.held_object = subinterpreter
+ self.warnings = warnings
+ self.disabled_feature = disabled_feature
+ self.exception = exception
+ self.subdir = PurePath(subdir).as_posix()
+ self.cm_interpreter: T.Optional[CMakeInterpreter] = None
+ self.methods.update({'get_variable': self.get_variable_method,
+ 'found': self.found_method,
+ })
+
+ @noPosargs
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.found()
+
+ def found(self) -> bool:
+ return not isinstance(self.held_object, NullSubprojectInterpreter)
+
+ @noKwargs
+ @noArgsFlattening
+ @unholder_return
+ def get_variable_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]:
+ if len(args) < 1 or len(args) > 2:
+ raise InterpreterException('Get_variable takes one or two arguments.')
+ if isinstance(self.held_object, NullSubprojectInterpreter): # == not self.found()
+ raise InterpreterException(f'Subproject "{self.subdir}" disabled can\'t get_variable on it.')
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('Get_variable first argument must be a string.')
+ try:
+ return self.held_object.variables[varname]
+ except KeyError:
+ pass
+
+ if len(args) == 2:
+ return self.held_object._holderify(args[1])
+
+ raise InvalidArguments(f'Requested variable "{varname}" not found.')
+
+class ModuleObjectHolder(ObjectHolder[ModuleObject]):
+ def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var:
+ modobj = self.held_object
+ method = modobj.methods.get(method_name)
+ if not method:
+ raise InvalidCode(f'Unknown method {method_name!r} in object.')
+ if not getattr(method, 'no-args-flattening', False):
+ args = flatten(args)
+ if not getattr(method, 'no-second-level-holder-flattening', False):
+ args, kwargs = resolve_second_level_holders(args, kwargs)
+ state = ModuleState(self.interpreter)
+ # Many modules do for example self.interpreter.find_program_impl(),
+ # so we have to ensure they use the current interpreter and not the one
+ # that first imported that module, otherwise it will use outdated
+ # overrides.
+ if isinstance(modobj, ExtensionModule):
+ modobj.interpreter = self.interpreter
+ ret = method(state, args, kwargs)
+ if isinstance(ret, ModuleReturnValue):
+ self.interpreter.process_new_values(ret.new_objects)
+ ret = ret.return_value
+ return ret
+
+class MutableModuleObjectHolder(ModuleObjectHolder, MutableInterpreterObject):
+ def __deepcopy__(self, memo: T.Dict[int, T.Any]) -> 'MutableModuleObjectHolder':
+ # Deepcopy only held object, not interpreter
+ modobj = copy.deepcopy(self.held_object, memo)
+ return MutableModuleObjectHolder(modobj, self.interpreter)
+
+
+_BuildTarget = T.TypeVar('_BuildTarget', bound=T.Union[build.BuildTarget, build.BothLibraries])
+
+class BuildTargetHolder(ObjectHolder[_BuildTarget]):
+ def __init__(self, target: _BuildTarget, interp: 'Interpreter'):
+ super().__init__(target, interp)
+ self.methods.update({'extract_objects': self.extract_objects_method,
+ 'extract_all_objects': self.extract_all_objects_method,
+ 'name': self.name_method,
+ 'get_id': self.get_id_method,
+ 'outdir': self.outdir_method,
+ 'full_path': self.full_path_method,
+ 'path': self.path_method,
+ 'found': self.found_method,
+ 'private_dir_include': self.private_dir_include_method,
+ })
+
+ def __repr__(self) -> str:
+ r = '<{} {}: {}>'
+ h = self.held_object
+ assert isinstance(h, build.BuildTarget)
+ return r.format(self.__class__.__name__, h.get_id(), h.filename)
+
+ @property
+ def _target_object(self) -> build.BuildTarget:
+ if isinstance(self.held_object, build.BothLibraries):
+ return self.held_object.get_default_object()
+ assert isinstance(self.held_object, build.BuildTarget)
+ return self.held_object
+
+ def is_cross(self) -> bool:
+ return not self._target_object.environment.machines.matches_build_machine(self._target_object.for_machine)
+
+ @noPosargs
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ if not (isinstance(self.held_object, build.Executable) and self.held_object.was_returned_by_find_program):
+ FeatureNew.single_use('BuildTarget.found', '0.59.0', subproject=self.held_object.subproject)
+ return True
+
+ @noPosargs
+ @noKwargs
+ def private_dir_include_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.IncludeDirs:
+ return build.IncludeDirs('', [], False, [self.interpreter.backend.get_target_private_dir(self._target_object)])
+
+ @noPosargs
+ @noKwargs
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.interpreter.backend.get_target_filename_abs(self._target_object)
+
+ @noPosargs
+ @noKwargs
+ @FeatureDeprecated('BuildTarget.path', '0.55.0', 'Use BuildTarget.full_path instead')
+ def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.interpreter.backend.get_target_filename_abs(self._target_object)
+
+ @noPosargs
+ @noKwargs
+ def outdir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.interpreter.backend.get_target_dir(self._target_object)
+
+ @noKwargs
+ @typed_pos_args('extract_objects', varargs=(mesonlib.File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList))
+ def extract_objects_method(self, args: T.Tuple[T.List[T.Union[mesonlib.FileOrString, 'build.GeneratedTypes']]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects:
+ return self._target_object.extract_objects(args[0])
+
+ @noPosargs
+ @typed_kwargs(
+ 'extract_all_objects',
+ KwargInfo(
+ 'recursive', bool, default=False, since='0.46.0',
+ not_set_warning=textwrap.dedent('''\
+ extract_all_objects called without setting recursive
+ keyword argument. Meson currently defaults to
+ non-recursive to maintain backward compatibility but
+ the default will be changed in the future.
+ ''')
+ )
+ )
+ def extract_all_objects_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.BuildTargeMethodExtractAllObjects') -> build.ExtractedObjects:
+ return self._target_object.extract_all_objects(kwargs['recursive'])
+
+ @noPosargs
+ @noKwargs
+ def get_id_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self._target_object.get_id()
+
+ @FeatureNew('name', '0.54.0')
+ @noPosargs
+ @noKwargs
+ def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self._target_object.name
+
+class ExecutableHolder(BuildTargetHolder[build.Executable]):
+ pass
+
+class StaticLibraryHolder(BuildTargetHolder[build.StaticLibrary]):
+ pass
+
+class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]):
+ pass
+
+class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
+ def __init__(self, libs: build.BothLibraries, interp: 'Interpreter'):
+ # FIXME: This build target always represents the shared library, but
+ # that should be configurable.
+ super().__init__(libs, interp)
+ self.methods.update({'get_shared_lib': self.get_shared_lib_method,
+ 'get_static_lib': self.get_static_lib_method,
+ })
+
+ def __repr__(self) -> str:
+ r = '<{} {}: {}, {}: {}>'
+ h1 = self.held_object.shared
+ h2 = self.held_object.static
+ return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename)
+
+ @noPosargs
+ @noKwargs
+ def get_shared_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.SharedLibrary:
+ return self.held_object.shared
+
+ @noPosargs
+ @noKwargs
+ def get_static_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.StaticLibrary:
+ return self.held_object.static
+
+class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
+ pass
+
+class JarHolder(BuildTargetHolder[build.Jar]):
+ pass
+
+class CustomTargetIndexHolder(ObjectHolder[build.CustomTargetIndex]):
+ def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'):
+ super().__init__(target, interp)
+ self.methods.update({'full_path': self.full_path_method,
+ })
+
+ @FeatureNew('custom_target[i].full_path', '0.54.0')
+ @noPosargs
+ @noKwargs
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ assert self.interpreter.backend is not None
+ return self.interpreter.backend.get_target_filename_abs(self.held_object)
+
+class CustomTargetHolder(ObjectHolder[build.CustomTarget]):
+ def __init__(self, target: 'build.CustomTarget', interp: 'Interpreter'):
+ super().__init__(target, interp)
+ self.methods.update({'full_path': self.full_path_method,
+ 'to_list': self.to_list_method,
+ })
+
+ self.operators.update({
+ MesonOperator.INDEX: self.op_index,
+ })
+
+ def __repr__(self) -> str:
+ r = '<{} {}: {}>'
+ h = self.held_object
+ return r.format(self.__class__.__name__, h.get_id(), h.command)
+
+ @noPosargs
+ @noKwargs
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.interpreter.backend.get_target_filename_abs(self.held_object)
+
+ @FeatureNew('custom_target.to_list', '0.54.0')
+ @noPosargs
+ @noKwargs
+ def to_list_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[build.CustomTargetIndex]:
+ result = []
+ for i in self.held_object:
+ result.append(i)
+ return result
+
+ @noKwargs
+ @typed_operator(MesonOperator.INDEX, int)
+ def op_index(self, other: int) -> build.CustomTargetIndex:
+ try:
+ return self.held_object[other]
+ except IndexError:
+ raise InvalidArguments(f'Index {other} out of bounds of custom target {self.held_object.name} output of size {len(self.held_object)}.')
+
+class RunTargetHolder(ObjectHolder[build.RunTarget]):
+ pass
+
+class AliasTargetHolder(ObjectHolder[build.AliasTarget]):
+ pass
+
+class GeneratedListHolder(ObjectHolder[build.GeneratedList]):
+ pass
+
+class GeneratorHolder(ObjectHolder[build.Generator]):
+ def __init__(self, gen: build.Generator, interpreter: 'Interpreter'):
+ super().__init__(gen, interpreter)
+ self.methods.update({'process': self.process_method})
+
+ @typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList))
+ @typed_kwargs(
+ 'generator.process',
+ KwargInfo('preserve_path_from', (str, NoneType), since='0.45.0'),
+ KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
+ )
+ def process_method(self,
+ args: T.Tuple[T.List[T.Union[str, mesonlib.File, 'build.GeneratedTypes']]],
+ kwargs: 'kwargs.GeneratorProcess') -> build.GeneratedList:
+ preserve_path_from = kwargs['preserve_path_from']
+ if preserve_path_from is not None:
+ preserve_path_from = os.path.normpath(preserve_path_from)
+ if not os.path.isabs(preserve_path_from):
+ # This is a bit of a hack. Fix properly before merging.
+ raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
+
+ if any(isinstance(a, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for a in args[0]):
+ FeatureNew.single_use(
+ 'Calling generator.process with CustomTarget or Index of CustomTarget.',
+ '0.57.0', self.interpreter.subproject)
+
+ gl = self.held_object.process_files(args[0], self.interpreter,
+ preserve_path_from, extra_args=kwargs['extra_args'])
+
+ return gl
+
+
+class StructuredSourcesHolder(ObjectHolder[build.StructuredSources]):
+
+ def __init__(self, sources: build.StructuredSources, interp: 'Interpreter'):
+ super().__init__(sources, interp)