aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/interpreter/interpreterobjects.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/interpreter/interpreterobjects.py')
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py303
1 files changed, 136 insertions, 167 deletions
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
index f078698..9560221 100644
--- a/mesonbuild/interpreter/interpreterobjects.py
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -16,8 +16,8 @@ from ..backend.backends import TestProtocol
from ..interpreterbase import (
ContainerTypeInfo, KwargInfo, MesonOperator,
InterpreterObject, MesonInterpreterObject, ObjectHolder, MutableInterpreterObject,
- FeatureCheckBase, FeatureNewKwargs, FeatureNew, FeatureDeprecated,
- typed_pos_args, typed_kwargs, typed_operator, permittedKwargs,
+ FeatureCheckBase, FeatureNew, FeatureDeprecated,
+ typed_pos_args, typed_kwargs, typed_operator,
noArgsFlattening, noPosargs, noKwargs, unholder_return, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs,
flatten, resolve_second_level_holders, InterpreterException, InvalidArguments, InvalidCode)
from ..interpreter.type_checking import NoneType
@@ -124,32 +124,25 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'auto'
- @permittedKwargs({'error_message'})
- def require_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
- if len(args) != 1:
- raise InvalidArguments(f'Expected 1 argument, got {len(args)}.')
- if not isinstance(args[0], bool):
- raise InvalidArguments('boolean argument expected.')
- error_message = kwargs.pop('error_message', '')
- if error_message and not isinstance(error_message, str):
- raise InterpreterException("Error message must be a string.")
+ @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)
- assert isinstance(error_message, str)
if self.value == 'enabled':
- prefix = f'Feature {self.held_object.name} cannot be enabled'
- if error_message:
- prefix += ': '
- raise InterpreterException(prefix + error_message)
+ 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()
@noKwargs
- def disable_auto_if_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
- if len(args) != 1:
- raise InvalidArguments(f'Expected 1 argument, got {len(args)}.')
- if not isinstance(args[0], bool):
- raise InvalidArguments('boolean argument expected.')
+ @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()
@@ -283,11 +276,13 @@ class EnvironmentVariablesHolder(ObjectHolder[build.EnvironmentVariables], Mutab
self.held_object.prepend(name, values, kwargs['separator'])
-class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject):
- def __init__(self, subproject: str, initial_values: T.Optional[T.Dict[str, T.Any]] = None) -> None:
- self.used = False # These objects become immutable after use in configure_file.
- super().__init__(subproject=subproject)
- self.conf_data = build.ConfigurationData()
+_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,
@@ -297,97 +292,70 @@ class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject):
'get_unquoted': self.get_unquoted_method,
'merge_from': self.merge_from_method,
})
- if isinstance(initial_values, dict):
- for k, v in initial_values.items():
- self.set_method([k, v], {})
- elif initial_values:
- raise AssertionError('Unsupported ConfigurationDataObject initial_values')
+
+ def __deepcopy__(self, memo: T.Dict) -> 'ConfigurationDataHolder':
+ return ConfigurationDataHolder(copy.deepcopy(self.held_object), self.interpreter)
def is_used(self) -> bool:
- return self.used
-
- def mark_used(self) -> None:
- self.used = True
-
- def validate_args(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Tuple[str, T.Union[str, int, bool], T.Optional[str]]:
- if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
- mlog.deprecation('Passing a list as the single argument to '
- 'configuration_data.set is deprecated. This will '
- 'become a hard error in the future.',
- location=self.current_node)
- args = args[0]
-
- if len(args) != 2:
- raise InterpreterException("Configuration set requires 2 arguments.")
- if self.used:
- raise InterpreterException("Can not set values on configuration object that has been used.")
- name, val = args
- if not isinstance(val, (int, str)):
- msg = f'Setting a configuration data value to {val!r} is invalid, ' \
- 'and will fail at configure_file(). If you are using it ' \
- 'just to store some values, please use a dict instead.'
- mlog.deprecation(msg, location=self.current_node)
- desc = kwargs.get('description', None)
- if not isinstance(name, str):
- raise InterpreterException("First argument to set must be a string.")
- if desc is not None and not isinstance(desc, str):
- raise InterpreterException('Description must be a string.')
-
- # TODO: Remove the cast once we get rid of the deprecation
- return name, T.cast(T.Union[str, bool, int], val), desc
+ return self.held_object.used
- @noArgsFlattening
- def set_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
- (name, val, desc) = self.validate_args(args, kwargs)
- self.conf_data.values[name] = (val, desc)
-
- def set_quoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
- (name, val, desc) = self.validate_args(args, kwargs)
- if not isinstance(val, str):
- raise InterpreterException("Second argument to set_quoted must be a string.")
- escaped_val = '\\"'.join(val.split('"'))
- self.conf_data.values[name] = ('"' + escaped_val + '"', desc)
-
- def set10_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
- (name, val, desc) = self.validate_args(args, kwargs)
- if val:
- self.conf_data.values[name] = (1, desc)
- else:
- self.conf_data.values[name] = (0, desc)
+ def __check_used(self) -> None:
+ if self.is_used():
+ raise InterpreterException("Can not set values on configuration object that has been used.")
- def has_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
- return args[0] in self.conf_data.values
+ @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()
+ if isinstance(args[1], int):
+ 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')
- @noArgsFlattening
- def get_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get method takes one or two arguments.')
- if not isinstance(args[0], str):
- raise InterpreterException('The variable name must be a string.')
+ @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.conf_data:
- return self.conf_data.get(name)[0]
- if len(args) > 1:
- # Assertion does not work because setting other values is still
- # supported, but deprecated. Use T.cast in the meantime (even though
- # this is a lie).
- # TODO: Fix this once the deprecation is removed
- # assert isinstance(args[1], (int, str, bool))
- return T.cast(T.Union[str, int, bool], args[1])
+ 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')
- def get_unquoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get method takes one or two arguments.')
- if not isinstance(args[0], str):
- raise InterpreterException('The variable name must be a string.')
+ @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.conf_data:
- val = self.conf_data.get(name)[0]
- elif len(args) > 1:
- assert isinstance(args[1], (str, int, bool))
+ 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.')
@@ -396,25 +364,22 @@ class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject):
return val
def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]:
- return self.conf_data.values[name]
+ 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.conf_data.values.keys())
+ return list(self.held_object.values.keys())
- def merge_from_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
- if len(args) != 1:
- raise InterpreterException('Merge_from takes one positional argument.')
- from_object_holder = args[0]
- if not isinstance(from_object_holder, ConfigurationDataObject):
- raise InterpreterException('Merge_from argument must be a configuration data object.')
- from_object = from_object_holder.conf_data
- for k, v in from_object.values.items():
- self.conf_data.values[k] = v
+ @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 = [
@@ -466,71 +431,75 @@ class DependencyHolder(ObjectHolder[Dependency]):
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')
- @permittedKwargs({'define_variable', 'default'})
- def pkgconfig_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
- args = listify(args)
- if len(args) != 1:
- raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Variable name must be a string.')
- return self.held_object.get_pkgconfig_variable(varname, kwargs)
+ @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('dep.get_configtool_variable', '0.44.0')
- @FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0',
- 'use Dependency.get_variable(configtool : ...) instead')
+ @FeatureNew('dependency.get_configtool_variable', '0.44.0')
+ @FeatureDeprecated('dependency.get_configtool_variable', '0.56.0',
+ 'use dependency.get_variable(configtool : ...) instead')
@noKwargs
- def configtool_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
- args = listify(args)
- if len(args) != 1:
- raise InterpreterException('get_configtool_variable takes exactly one argument.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Variable name must be a string.')
- return self.held_object.get_configtool_variable(varname)
+ @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('dep.partial_dependency', '0.46.0')
+ @FeatureNew('dependency.partial_dependency', '0.46.0')
@noPosargs
- @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ @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('dep.get_variable', '0.51.0')
- @typed_pos_args('dep.get_variable', optargs=[str])
- @permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'})
- @FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal'])
- def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> T.Union[str, T.List[str]]:
+ @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') -> T.Union[str, T.List[str]]:
default_varname = args[0]
if default_varname is not None:
- FeatureNew('Positional argument to dep.get_variable()', '0.58.0', location=self.current_node).use(self.subproject)
- for k in ['cmake', 'pkgconfig', 'configtool', 'internal']:
- kwargs.setdefault(k, default_varname)
- return self.held_object.get_variable(**kwargs)
+ FeatureNew('Positional argument to dependency.get_variable()', '0.58.0', location=self.current_node).use(self.subproject)
+ 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('dep.include_type', '0.52.0')
+ @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('dep.as_system', '0.52.0')
- @noKwargs
- def as_system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency:
- args = listify(args)
- new_is_system = 'system'
- if len(args) > 1:
- raise InterpreterException('as_system takes only one optional value')
- if len(args) == 1:
- if not isinstance(args[0], str):
- raise InterpreterException('as_system takes exactly one string parameter')
- new_is_system = args[0]
- new_dep = self.held_object.generate_system_dependency(new_is_system)
- return new_dep
+ @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('dep.as_link_whole', '0.56.0')
+ @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:
@@ -592,9 +561,9 @@ class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.held_object.found()
- @FeatureNew('dep.partial_dependency', '0.46.0')
+ @FeatureNew('dependency.partial_dependency', '0.46.0')
@noPosargs
- @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ @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