aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/interpreter
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2021-08-10 20:07:39 -0700
committerDaniel Mensinger <daniel@mensinger-ka.de>2021-09-30 21:01:38 +0200
commitc3c30d4b060239654c9b848092692ab346ebed9d (patch)
tree52b2ebd5e5c788fb529dac1717051093311ca255 /mesonbuild/interpreter
parentbb706231bd3bfd8983f1a5df24111efe1ad0734d (diff)
downloadmeson-c3c30d4b060239654c9b848092692ab346ebed9d.zip
meson-c3c30d4b060239654c9b848092692ab346ebed9d.tar.gz
meson-c3c30d4b060239654c9b848092692ab346ebed9d.tar.bz2
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
Diffstat (limited to 'mesonbuild/interpreter')
-rw-r--r--mesonbuild/interpreter/interpreter.py90
-rw-r--r--mesonbuild/interpreter/kwargs.py26
-rw-r--r--mesonbuild/interpreter/type_checking.py46
3 files changed, 138 insertions, 24 deletions
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):
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 4281ee4..2229984 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -10,7 +10,7 @@ from typing_extensions import TypedDict, Literal
from .. import build
from .. import coredata
-from ..mesonlib import MachineChoice, File, FileMode, FileOrString
+from ..mesonlib import MachineChoice, File, FileMode, FileOrString, OptionKey
from ..programs import ExternalProgram
@@ -162,3 +162,27 @@ class RunTarget(TypedDict):
command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, ExternalProgram, File]]
depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
env: build.EnvironmentVariables
+
+
+class CustomTarget(TypedDict):
+
+ build_always: bool
+ build_always_stale: bool
+ build_by_default: bool
+ capture: bool
+ command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget,
+ build.CustomTargetIndex, ExternalProgram, File]]
+ consonle: bool
+ depend_files: T.List[FileOrString]
+ depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
+ depfile: T.Optional[str]
+ env: build.EnvironmentVariables
+ feed: bool
+ input: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex,
+ build.ExtractedObjects, build.GeneratedList, ExternalProgram, File]]
+ install: bool
+ install_dir: T.List[T.Union[str, bool]]
+ install_mode: FileMode
+ install_tag: T.List[T.Union[str, bool]]
+ output: T.List[str]
+ override_options: T.Dict[OptionKey, str]
diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py
index fb63fca..d8f1030 100644
--- a/mesonbuild/interpreter/type_checking.py
+++ b/mesonbuild/interpreter/type_checking.py
@@ -6,11 +6,11 @@
import typing as T
from .. import compilers
-from ..build import EnvironmentVariables, CustomTarget, BuildTarget
+from ..build import EnvironmentVariables, CustomTarget, BuildTarget, CustomTargetIndex
from ..coredata import UserFeatureOption
from ..interpreterbase import TYPE_var
from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo
-from ..mesonlib import File, FileMode, MachineChoice, listify, has_path_sep
+from ..mesonlib import File, FileMode, MachineChoice, listify, has_path_sep, OptionKey
from ..programs import ExternalProgram
# Helper definition for type checks that are `Optional[T]`
@@ -158,13 +158,21 @@ def _env_validator(value: T.Union[EnvironmentVariables, T.List['TYPE_var'], T.Di
return None
-def _env_convertor(value: T.Union[EnvironmentVariables, T.List[str], T.Dict[str, str], str, None]) -> EnvironmentVariables:
- def splitter(input: str) -> T.Tuple[str, str]:
- a, b = input.split('=', 1)
- return (a.strip(), b.strip())
- if isinstance(value, (str, list)):
- return EnvironmentVariables(dict(splitter(v) for v in listify(value)))
+def split_equal_string(input: str) -> T.Tuple[str, str]:
+ """Split a string in the form `x=y`
+
+ This assumes that the string has already been validated to split properly.
+ """
+ a, b = input.split('=', 1)
+ return (a, b)
+
+
+def _env_convertor(value: T.Union[EnvironmentVariables, T.List[str], T.List[T.List[str]], T.Dict[str, str], str, None]) -> EnvironmentVariables:
+ if isinstance(value, str):
+ return EnvironmentVariables(dict([split_equal_string(value)]))
+ elif isinstance(value, list):
+ return EnvironmentVariables(dict(split_equal_string(v) for v in listify(value)))
elif isinstance(value, dict):
return EnvironmentVariables(value)
elif value is None:
@@ -199,11 +207,29 @@ DEPEND_FILES_KW: KwargInfo[T.List[T.Union[str, File]]] = KwargInfo(
default=[],
)
-COMMAND_KW: KwargInfo[T.List[T.Union[BuildTarget, CustomTarget, ExternalProgram, File]]] = KwargInfo(
+COMMAND_KW: KwargInfo[T.List[T.Union[str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File]]] = KwargInfo(
'command',
# TODO: should accept CustomTargetIndex as well?
- ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, ExternalProgram, File), allow_empty=False),
+ ContainerTypeInfo(list, (str, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram, File), allow_empty=False),
required=True,
listify=True,
default=[],
)
+
+def _override_options_convertor(raw: T.List[str]) -> T.Dict[OptionKey, str]:
+ output: T.Dict[OptionKey, str] = {}
+ for each in raw:
+ k, v = split_equal_string(each)
+ output[OptionKey.from_string(k)] = v
+ return output
+
+
+OVERRIDE_OPTIONS_KW: KwargInfo[T.List[str]] = KwargInfo(
+ 'override_options',
+ ContainerTypeInfo(list, str),
+ listify=True,
+ default=[],
+ # Reusing the env validator is a littl overkill, but nicer than duplicating the code
+ validator=_env_validator,
+ convertor=_override_options_convertor,
+)