aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/interpreter
diff options
context:
space:
mode:
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,
+)