diff --git a/mesonbuild/modules/sourceset.py b/mesonbuild/modules/sourceset.py index 63dd45fce..de555e2c5 100644 --- a/mesonbuild/modules/sourceset.py +++ b/mesonbuild/modules/sourceset.py @@ -13,7 +13,6 @@ # limitations under the License. from __future__ import annotations -from collections import namedtuple import typing as T from . import ExtensionModule, ModuleObject, MutableModuleObject @@ -21,19 +20,55 @@ from .. import build from .. import dependencies from .. import mesonlib from ..interpreterbase import ( - noPosargs, noKwargs, permittedKwargs, + noPosargs, noKwargs, InterpreterException, InvalidArguments, InvalidCode, FeatureNew, ) -from ..interpreterbase.decorators import typed_pos_args -from ..mesonlib import listify, OrderedSet +from ..interpreterbase.decorators import ContainerTypeInfo, KwargInfo, typed_kwargs, typed_pos_args +from ..mesonlib import OrderedSet if T.TYPE_CHECKING: + from typing_extensions import TypedDict + from . import ModuleState from ..interpreter import Interpreter from ..interpreterbase import TYPE_var, TYPE_kwargs -SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps') -SourceFiles = namedtuple('SourceFiles', 'sources dependencies') + class AddKwargs(TypedDict): + + when: T.List[T.Union[str, dependencies.Dependency]] + if_true: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]] + if_false: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] + + class AddAllKw(TypedDict): + + when: T.List[T.Union[str, dependencies.Dependency]] + if_true: T.List[SourceSet] + + class ApplyKw(TypedDict): + + strict: bool + + +_WHEN_KW: KwargInfo[T.List[T.Union[str, dependencies.Dependency]]] = KwargInfo( + 'when', + ContainerTypeInfo(list, (str, dependencies.Dependency)), + listify=True, + default=[], +) + + +class SourceSetRule(T.NamedTuple): + keys: T.List[str] + sources: T.Any + if_false: T.Any + sourcesets: T.List[SourceSet] + dependencies: T.List[dependencies.Dependency] + extra_deps: T.Any + + +class SourceFiles(T.NamedTuple): + sources: OrderedSet + dependencies: OrderedSet class SourceSet: @@ -47,7 +82,7 @@ class SourceSet: class SourceSetImpl(SourceSet, MutableModuleObject): def __init__(self, interpreter: Interpreter): super().__init__() - self.rules = [] + self.rules: T.List['SourceSetRule'] = [] self.subproject = interpreter.subproject self.environment = interpreter.environment self.subdir = interpreter.subdir @@ -60,63 +95,80 @@ class SourceSetImpl(SourceSet, MutableModuleObject): 'apply': self.apply_method, }) - def check_source_files(self, arg, allow_deps): - sources = [] - deps = [] - for x in arg: - if isinstance(x, (str, mesonlib.File, - build.GeneratedList, build.CustomTarget, - build.CustomTargetIndex)): - sources.append(x) - elif hasattr(x, 'found'): - if not allow_deps: - msg = 'Dependencies are not allowed in the if_false argument.' - raise InvalidArguments(msg) + def check_source_files(self, args: T.Sequence[T.Union[mesonlib.FileOrString, 'build.GeneratedTypes', dependencies.Dependency]], + ) -> T.Tuple[T.List[T.Union[mesonlib.FileOrString, 'build.GeneratedTypes']], T.List[dependencies.Dependency]]: + sources: T.List[T.Union[mesonlib.FileOrString, 'build.GeneratedTypes']] = [] + deps: T.List['dependencies.Dependency'] = [] + for x in args: + if isinstance(x, dependencies.Dependency): deps.append(x) else: - msg = 'Sources must be strings or file-like objects.' - raise InvalidArguments(msg) + sources.append(x) mesonlib.check_direntry_issues(sources) return sources, deps - def check_conditions(self, arg): - keys = [] - deps = [] - for x in listify(arg): + def check_conditions(self, args: T.Sequence[T.Union[str, 'dependencies.Dependency']] + ) -> T.Tuple[T.List[str], T.List['dependencies.Dependency']]: + keys: T.List[str] = [] + deps: T.List['dependencies.Dependency'] = [] + for x in args: if isinstance(x, str): keys.append(x) - elif hasattr(x, 'found'): - deps.append(x) else: - raise InvalidArguments('Conditions must be strings or dependency object') + deps.append(x) return keys, deps - @permittedKwargs(['when', 'if_false', 'if_true']) @typed_pos_args('sourceset.add', varargs=(str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex, dependencies.Dependency)) + @typed_kwargs( + 'sourceset.add', + _WHEN_KW, + KwargInfo( + 'if_true', + ContainerTypeInfo(list, (str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex, dependencies.Dependency)), + listify=True, + default=[], + ), + KwargInfo( + 'if_false', + ContainerTypeInfo(list, (str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex)), + listify=True, + default=[], + ), + ) def add_method(self, state: ModuleState, args: T.Tuple[T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes, dependencies.Dependency]]], - kwargs): + kwargs: AddKwargs) -> None: if self.frozen: raise InvalidCode('Tried to use \'add\' after querying the source set') - when = listify(kwargs.get('when', [])) - if_true = listify(kwargs.get('if_true', [])) - if_false = listify(kwargs.get('if_false', [])) - if not when and not if_true and not if_false: + when = kwargs['when'] + if_true = kwargs['if_true'] + if_false = kwargs['if_false'] + if not any([when, if_true, if_false]): if_true = args[0] elif args[0]: raise InterpreterException('add called with both positional and keyword arguments') keys, dependencies = self.check_conditions(when) - sources, extra_deps = self.check_source_files(if_true, True) - if_false, _ = self.check_source_files(if_false, False) + sources, extra_deps = self.check_source_files(if_true) + if_false, _ = self.check_source_files(if_false) self.rules.append(SourceSetRule(keys, sources, if_false, [], dependencies, extra_deps)) - @permittedKwargs(['when', 'if_true']) @typed_pos_args('sourceset.add_all', varargs=SourceSet) - def add_all_method(self, state: ModuleState, args: T.Tuple[T.List[SourceSetImpl]], kwargs): + @typed_kwargs( + 'sourceset.add_all', + _WHEN_KW, + KwargInfo( + 'if_true', + ContainerTypeInfo(list, SourceSet), + listify=True, + default=[], + ) + ) + def add_all_method(self, state: ModuleState, args: T.Tuple[T.List[SourceSet]], + kwargs: AddAllKw) -> None: if self.frozen: raise InvalidCode('Tried to use \'add_all\' after querying the source set') - when = listify(kwargs.get('when', [])) - if_true = listify(kwargs.get('if_true', [])) + when = kwargs['when'] + if_true = kwargs['if_true'] if not when and not if_true: if_true = args[0] elif args[0]: @@ -125,7 +177,7 @@ class SourceSetImpl(SourceSet, MutableModuleObject): for s in if_true: if not isinstance(s, SourceSetImpl): raise InvalidCode('Arguments to \'add_all\' after the first must be source sets') - s.frozen = True + s.frozen = True self.rules.append(SourceSetRule(keys, [], [], if_true, dependencies, [])) def collect(self, enabled_fn, all_sources, into=None): @@ -159,12 +211,12 @@ class SourceSetImpl(SourceSet, MutableModuleObject): files = self.collect(lambda x: True, True) return list(files.dependencies) - @permittedKwargs(['strict']) @typed_pos_args('sourceset.apply', (build.ConfigurationData, dict)) - def apply_method(self, state: ModuleState, args: T.Tuple[T.Union[build.ConfigurationData, T.Dict[str, TYPE_var]]], kwargs): + @typed_kwargs('sourceset.apply', KwargInfo('strict', bool, default=True)) + def apply_method(self, state: ModuleState, args: T.Tuple[T.Union[build.ConfigurationData, T.Dict[str, TYPE_var]]], kwargs: ApplyKw): config_data = args[0] self.frozen = True - strict = kwargs.get('strict', True) + strict = kwargs['strict'] if isinstance(config_data, dict): def _get_from_config_data(key): if strict and key not in config_data: @@ -189,7 +241,7 @@ class SourceSetImpl(SourceSet, MutableModuleObject): return res class SourceFilesObject(ModuleObject): - def __init__(self, files): + def __init__(self, files: 'SourceFiles'): super().__init__() self.files = files self.methods.update({