From c3c30d4b060239654c9b848092692ab346ebed9d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 10 Aug 2021 20:07:39 -0700 Subject: interpreter: Use typed_kwargs for func_custom_target This does not convert the build side, or remove any of the checking it does. We still need that for other callers of custom target. What we'll do for those is add an internal interface that defaults things, then we'll be able to have those callers do their own validation, and the CustomTarget validation machinary can be removed. Fixes #9096 --- mesonbuild/interpreter/interpreter.py | 90 ++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 13 deletions(-) (limited to 'mesonbuild/interpreter/interpreter.py') diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 78c8f95..c5997f2 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -52,11 +52,12 @@ from .interpreterobjects import ( from .type_checking import ( COMMAND_KW, DEPENDS_KW, + DEPEND_FILES_KW, DEPFILE_KW, ENV_KW, INSTALL_MODE_KW, LANGUAGE_KW, - NATIVE_KW, + NATIVE_KW, OVERRIDE_OPTIONS_KW, REQUIRED_KW, NoneType, in_set_validator, @@ -89,6 +90,18 @@ if T.TYPE_CHECKING: build.GeneratedList] +def _output_validator(outputs: T.List[str]) -> T.Optional[str]: + for i in outputs: + if i == '': + return 'Output must not be empty.' + elif i.strip() == '': + return 'Output must not consist only of whitespace.' + elif has_path_sep(i): + return f'Output {i!r} must not contain a path segment.' + + return None + + def stringifyUserArguments(args, quote=False): if isinstance(args, list): return '[%s]' % ', '.join([stringifyUserArguments(x, True) for x in args]) @@ -1626,20 +1639,71 @@ external dependencies (including libraries) must go to "dependencies".''') def func_subdir_done(self, node, args, kwargs): raise SubdirDoneRequest() - @FeatureNewKwargs('custom_target', '0.60.0', ['install_tag']) - @FeatureNewKwargs('custom_target', '0.57.0', ['env']) - @FeatureNewKwargs('custom_target', '0.48.0', ['console']) - @FeatureNewKwargs('custom_target', '0.47.0', ['install_mode', 'build_always_stale']) - @FeatureNewKwargs('custom_target', '0.40.0', ['build_by_default']) - @FeatureNewKwargs('custom_target', '0.59.0', ['feed']) - @permittedKwargs({'input', 'output', 'command', 'install', 'install_dir', 'install_mode', - 'build_always', 'capture', 'depends', 'depend_files', 'depfile', - 'build_by_default', 'build_always_stale', 'console', 'env', - 'feed', 'install_tag'}) @typed_pos_args('custom_target', optargs=[str]) - def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[T.Optional[str]], kwargs: 'TYPE_kwargs') -> build.CustomTarget: - if 'depfile' in kwargs and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']): + @typed_kwargs( + 'custom_target', + COMMAND_KW, + DEPEND_FILES_KW, + DEPENDS_KW, + DEPFILE_KW, + ENV_KW.evolve(since='0.57.0'), + INSTALL_MODE_KW.evolve(since='0.47.0'), + OVERRIDE_OPTIONS_KW, + KwargInfo('build_by_default', (bool, type(None)), since='0.40.0'), + KwargInfo('build_always', (bool, type(None)), deprecated='0.47.0'), + KwargInfo('build_always_stale', (bool, type(None)), since='0.47.0'), + KwargInfo('feed', bool, default=False, since='0.59.0'), + KwargInfo('capture', bool, default=False), + KwargInfo('console', bool, default=False, since='0.48.0'), + KwargInfo('install', bool, default=False), + KwargInfo('install_dir', ContainerTypeInfo(list, (str, bool)), listify=True, default=[]), + KwargInfo( + 'output', + ContainerTypeInfo(list, str, allow_empty=False), + listify=True, + required=True, + default=[], + validator=_output_validator, + ), + KwargInfo( + 'input', + ContainerTypeInfo(list, (str, mesonlib.File, ExternalProgram, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, build.ExtractedObjects, build.GeneratedList)), + listify=True, + default=[], + ), + KwargInfo('install_tag', ContainerTypeInfo(list, (str, bool)), listify=True, default=[], since='0.60.0'), + ) + def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], + kwargs: 'kwargs.CustomTarget') -> build.CustomTarget: + if kwargs['depfile'] and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']): FeatureNew.single_use('substitutions in custom_target depfile', '0.47.0', self.subproject) + + # Don't mutate the kwargs + kwargs = kwargs.copy() + + # Remap build_always to build_by_default and build_always_stale + if kwargs['build_always'] is not None and kwargs['build_always_stale'] is not None: + raise InterpreterException('CustomTarget: "build_always" and "build_always_stale" are mutually exclusive') + + if kwargs['build_by_default'] is None and kwargs['install']: + kwargs['build_by_default'] = True + + elif kwargs['build_always'] is not None: + if kwargs['build_by_default'] is None: + kwargs['build_by_default'] = kwargs['build_always'] + kwargs['build_always_stale'] = kwargs['build_by_default'] + + # Set this to None to satisfy process_kwargs + kwargs['build_always'] = None + + # These are are nullaable so that we can konw whether they're explicitly + # set or not. If they haven't been overwritten, set them to their true + # default + if kwargs['build_by_default'] is None: + kwargs['build_by_default'] = False + if kwargs['build_always_stale'] is None: + kwargs['build_always_stale'] = False + return self._func_custom_target_impl(node, args, kwargs) def _func_custom_target_impl(self, node, args, kwargs): -- cgit v1.1