mirror of
https://github.com/mesonbuild/meson.git
synced 2026-11-06 05:16:42 +00:00
interpreter: Add a default() function
This gives a way to conditionally override a keyword argument, such as when setting a value specifically for one platform. For example, ```meson library( ..., name_prefix : host_machine.system() == 'windows' ? '' : default(), ) ```
This commit is contained in:
1 parent
15231ceccd
commit
e4b644b9c7
12 files changed
+126
-4
No files matched your search
@@ -86,6 +86,7 @@ syn keyword mesonBuiltin
|
||||
\ custom_target
|
||||
\ debug
|
||||
\ declare_dependency
|
||||
\ default
|
||||
\ dependency
|
||||
\ disabler
|
||||
\ environment
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
## Add a new `default()` function and object
|
||||
|
||||
This function and object allow setting a keyword argument to it's default value.
|
||||
This is especially useful for cases when a non-default value is wanted in some
|
||||
cases but not all.
|
||||
|
||||
For example:
|
||||
```meson
|
||||
library(
|
||||
...
|
||||
name_prefix : host_machine.system() == 'windows' ? '' : default(),
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
name: default
|
||||
returns: default
|
||||
description: Returns a [[@default]] object.
|
||||
since: 1.12.0
|
||||
@@ -0,0 +1,5 @@
|
||||
name: default
|
||||
long_name: Default
|
||||
description: |
|
||||
A default object can be passed to keyword arguments to get the default value.
|
||||
|
||||
@@ -242,6 +242,7 @@ class AstInterpreter(InterpreterBase):
|
||||
'is_disabler': self.func_do_nothing,
|
||||
'is_variable': self.func_do_nothing,
|
||||
'disabler': self.func_do_nothing,
|
||||
'default': self.func_do_nothing,
|
||||
'jar': self.func_do_nothing,
|
||||
'warning': self.func_do_nothing,
|
||||
'shared_module': self.func_do_nothing,
|
||||
|
||||
@@ -32,7 +32,7 @@ from .decorators import apply_machine_map
|
||||
from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest
|
||||
from ..interpreterbase import Disabler, disablerIfNotFound
|
||||
from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs
|
||||
from ..interpreterbase import ObjectHolder, ContextManagerObject
|
||||
from ..interpreterbase import ObjectHolder, ContextManagerObject, DefaultObject
|
||||
from ..interpreterbase import stringifyUserArguments, Feature, FeatureValue
|
||||
from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule, __path__ as modules_path
|
||||
from ..optinterpreter import optname_regex
|
||||
@@ -390,6 +390,7 @@ class Interpreter(InterpreterBase, HoldableObject):
|
||||
'declare_dependency': self.func_declare_dependency,
|
||||
'dependency': self.func_dependency,
|
||||
'disabler': self.func_disabler,
|
||||
'default': self.func_default,
|
||||
'environment': self.func_environment,
|
||||
'error': self.func_error,
|
||||
'executable': self.func_executable,
|
||||
@@ -1972,6 +1973,12 @@ class Interpreter(InterpreterBase, HoldableObject):
|
||||
f.use(self.subproject, node)
|
||||
return d
|
||||
|
||||
@FeatureNew('default', '1.12.0')
|
||||
@noKwargs
|
||||
@noPosargs
|
||||
def func_default(self, node: mparser.BaseNode, args: list[TYPE_var], kwargs: TYPE_kwargs) -> DefaultObject:
|
||||
return DefaultObject()
|
||||
|
||||
@FeatureNew('disabler', '0.44.0')
|
||||
@noKwargs
|
||||
@noPosargs
|
||||
@@ -4056,7 +4063,7 @@ class Interpreter(InterpreterBase, HoldableObject):
|
||||
def is_subproject(self) -> bool:
|
||||
return self.subproject != ''
|
||||
|
||||
@typed_pos_args('set_variable', str, object)
|
||||
@typed_pos_args('set_variable', str, (object, DefaultObject))
|
||||
@noKwargs
|
||||
@noArgsFlattening
|
||||
@noSecondLevelHolderResolving
|
||||
@@ -4066,7 +4073,7 @@ class Interpreter(InterpreterBase, HoldableObject):
|
||||
raise InvalidCode('Invalid variable name: ' + varname)
|
||||
self.set_variable(varname, value, holderify=True)
|
||||
|
||||
@typed_pos_args('get_variable', (str, Disabler), optargs=[object])
|
||||
@typed_pos_args('get_variable', (str, Disabler, DefaultObject), optargs=[object])
|
||||
@noKwargs
|
||||
@noArgsFlattening
|
||||
@unholder_return
|
||||
|
||||
@@ -8,6 +8,7 @@ __all__ = [
|
||||
'IterableObject',
|
||||
'MutableInterpreterObject',
|
||||
'ContextManagerObject',
|
||||
'DefaultObject',
|
||||
|
||||
'MesonOperator',
|
||||
|
||||
@@ -70,6 +71,7 @@ from .baseobjects import (
|
||||
IterableObject,
|
||||
MutableInterpreterObject,
|
||||
ContextManagerObject,
|
||||
DefaultObject,
|
||||
|
||||
TV_func,
|
||||
TYPE_elementary,
|
||||
|
||||
@@ -233,3 +233,7 @@ class IterableObject(metaclass=SimpleABC):
|
||||
class ContextManagerObject(MesonInterpreterObject, AbstractContextManager):
|
||||
def __init__(self, subproject: 'SubProject') -> None:
|
||||
super().__init__(subproject=subproject)
|
||||
|
||||
|
||||
class DefaultObject(MesonInterpreterObject):
|
||||
"""A default value to a keyword argument."""
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
|
||||
from .. import coredata, mesonlib, mlog
|
||||
from .disabler import Disabler
|
||||
from .baseobjects import DefaultObject
|
||||
from .exceptions import InterpreterException, InvalidArguments
|
||||
from ._unholder import _unholder
|
||||
|
||||
@@ -216,6 +217,7 @@ def typed_pos_args(name: str, *types: T.Union[T.Type, T.Tuple[T.Type, ...]],
|
||||
num_args = len(args)
|
||||
num_types = len(types)
|
||||
a_types = types
|
||||
last_pos = num_args
|
||||
|
||||
if varargs:
|
||||
min_args = num_types + min_varargs
|
||||
@@ -238,6 +240,18 @@ def typed_pos_args(name: str, *types: T.Union[T.Type, T.Tuple[T.Type, ...]],
|
||||
|
||||
for i, (arg, type_) in enumerate(itertools.zip_longest(args, a_types, fillvalue=varargs), start=1):
|
||||
if not isinstance(arg, type_):
|
||||
# if DefaultObject is an explicit allowed allowed type allow
|
||||
# it through.
|
||||
if isinstance(arg, DefaultObject):
|
||||
if i >= last_pos:
|
||||
if varargs:
|
||||
msg = 'not allowed for variadic arguments'
|
||||
else:
|
||||
msg = 'not allowed for optional positional arguments'
|
||||
else:
|
||||
msg = 'not allowed for required positional arguments'
|
||||
raise InvalidArguments(f'default() objects are {msg}')
|
||||
|
||||
if isinstance(type_, tuple):
|
||||
shouldbe = 'one of: {}'.format(", ".join(f'"{t.__name__}"' for t in type_))
|
||||
else:
|
||||
@@ -565,6 +579,15 @@ def typed_kwargs(name: str, *types: KwargInfo, allow_unknown: bool = False) -> T
|
||||
for info in types:
|
||||
types_tuple = info.types if isinstance(info.types, tuple) else (info.types,)
|
||||
value = kwargs.get(info.name)
|
||||
if isinstance(value, DefaultObject):
|
||||
# Ensure that default() is not used for required options
|
||||
# Otherwise, set the value to None, which will send us down
|
||||
# the "unset" path
|
||||
if info.required:
|
||||
raise InvalidArguments(f'"{name}" got a default() value for the required keyword argument "{info.name}". '
|
||||
'default() may not be used for required keyword arguments.')
|
||||
value = None
|
||||
|
||||
if value is not None:
|
||||
if info.since:
|
||||
feature_name = info.name + ' arg in ' + name
|
||||
|
||||
@@ -14,6 +14,7 @@ from .baseobjects import (
|
||||
ObjectHolder,
|
||||
IterableObject,
|
||||
ContextManagerObject,
|
||||
DefaultObject,
|
||||
|
||||
HoldableTypes,
|
||||
)
|
||||
@@ -612,7 +613,12 @@ class InterpreterBase:
|
||||
def expand_default_kwargs(self, kwargs: T.Dict[str, T.Optional[InterpreterObject]]) -> T.Dict[str, T.Optional[InterpreterObject]]:
|
||||
if 'kwargs' not in kwargs:
|
||||
return kwargs
|
||||
to_expand = _unholder(kwargs.pop('kwargs'))
|
||||
|
||||
kwargs_var = kwargs.pop('kwargs')
|
||||
if isinstance(kwargs_var, DefaultObject):
|
||||
return kwargs
|
||||
|
||||
to_expand = _unholder(kwargs_var)
|
||||
if not isinstance(to_expand, dict):
|
||||
raise InterpreterException('Value of "kwargs" must be dictionary.')
|
||||
if 'kwargs' in to_expand:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright © 2026 Intel Corporation
|
||||
|
||||
project(
|
||||
'default',
|
||||
'cpp',
|
||||
meson_version : '>= 1.12.0',
|
||||
default_options : {'cpp_std' : 'c++14'},
|
||||
)
|
||||
|
||||
exe = executable(
|
||||
'prog',
|
||||
'prog.cpp',
|
||||
override_options : default(),
|
||||
)
|
||||
|
||||
exe2 = executable(
|
||||
'prog2',
|
||||
'prog.cpp',
|
||||
kwargs : default(),
|
||||
)
|
||||
|
||||
test_kwargs = {
|
||||
'protocol' : default(),
|
||||
}
|
||||
|
||||
test('exe_test', exe, kwargs : test_kwargs)
|
||||
|
||||
testcase expect_error('default() objects are not allowed for required positional arguments')
|
||||
executable(default(), 'prog.cpp')
|
||||
endtestcase
|
||||
|
||||
testcase expect_error('default() objects are not allowed for variadic arguments')
|
||||
add_languages(default())
|
||||
endtestcase
|
||||
|
||||
testcase expect_error('default() objects are not allowed for optional positional arguments')
|
||||
assert(true, default())
|
||||
endtestcase
|
||||
|
||||
testcase expect_error('"install_symlink" got a default() value for the required keyword argument "install_dir". default() may not be used for required keyword arguments.')
|
||||
install_symlink('foo', pointing_to : '/foo', install_dir : default())
|
||||
endtestcase
|
||||
|
||||
foo = default()
|
||||
get_variable('foo')
|
||||
unset_variable('foo')
|
||||
set_variable('var', default())
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Copyright © 2026 Intel Corporation
|
||||
*/
|
||||
|
||||
int main() {
|
||||
return __cplusplus == 201402L ? 0 : 1;
|
||||
}
|
||||
Reference in new issue
Block a user