interpreter: add feature.require()
Add a method to perform a logical AND on a feature object. The method also takes care of raising an error if 'enabled' is ANDed with false. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
5298d8eaf1
commit
2f2d99e1d8
|
@ -2819,6 +2819,21 @@ The following methods are defined for all [`feature` options](Build-options.md#f
|
||||||
- `disabled()`: returns whether the feature was set to `'disabled'`
|
- `disabled()`: returns whether the feature was set to `'disabled'`
|
||||||
- `auto()`: returns whether the feature was set to `'auto'`
|
- `auto()`: returns whether the feature was set to `'auto'`
|
||||||
- `allowed()` *(since 0.59.0)*: returns whether the feature was set to `'enabled'` or `'auto'`
|
- `allowed()` *(since 0.59.0)*: returns whether the feature was set to `'enabled'` or `'auto'`
|
||||||
|
- `require(value, error_message: '')` *(since 0.59.0)*: returns
|
||||||
|
the object itself if the value is true; an error if the object is
|
||||||
|
`'enabled'` and the value is false; a disabled feature if the object
|
||||||
|
is `'auto'` or `'disabled'` and the value is false.
|
||||||
|
|
||||||
|
`require` is useful to restrict the applicability of `'auto'` features,
|
||||||
|
for example based on other features or on properties of the host machine:
|
||||||
|
|
||||||
|
```
|
||||||
|
if get_option('directx').require(host_machine.system() == 'windows',
|
||||||
|
error_message: 'DirectX only available on Windows').allowed() then
|
||||||
|
src += ['directx.c']
|
||||||
|
config.set10('HAVE_DIRECTX', 1)
|
||||||
|
endif
|
||||||
|
```
|
||||||
|
|
||||||
### `generator` object
|
### `generator` object
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder[coredata.UserFeatureOp
|
||||||
def __init__(self, env: 'Environment', name: str, option: coredata.UserFeatureOption):
|
def __init__(self, env: 'Environment', name: str, option: coredata.UserFeatureOption):
|
||||||
InterpreterObject.__init__(self)
|
InterpreterObject.__init__(self)
|
||||||
ObjectHolder.__init__(self, option)
|
ObjectHolder.__init__(self, option)
|
||||||
if option.is_auto():
|
if option and option.is_auto():
|
||||||
# TODO: we need to case here because options is not a TypedDict
|
# TODO: we need to case here because options is not a TypedDict
|
||||||
self.held_object = T.cast(coredata.UserFeatureOption, env.coredata.options[OptionKey('auto_features')])
|
self.held_object = T.cast(coredata.UserFeatureOption, env.coredata.options[OptionKey('auto_features')])
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -74,11 +74,15 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder[coredata.UserFeatureOp
|
||||||
'disabled': self.disabled_method,
|
'disabled': self.disabled_method,
|
||||||
'allowed': self.allowed_method,
|
'allowed': self.allowed_method,
|
||||||
'auto': self.auto_method,
|
'auto': self.auto_method,
|
||||||
|
'require': self.require_method,
|
||||||
})
|
})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
return self.held_object.value
|
return 'disabled' if not self.held_object else self.held_object.value
|
||||||
|
|
||||||
|
def as_disabled(self):
|
||||||
|
return FeatureOptionHolder(None, self.name, None)
|
||||||
|
|
||||||
@noPosargs
|
@noPosargs
|
||||||
@permittedKwargs({})
|
@permittedKwargs({})
|
||||||
|
@ -100,6 +104,25 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder[coredata.UserFeatureOp
|
||||||
def auto_method(self, args, kwargs):
|
def auto_method(self, args, kwargs):
|
||||||
return self.value == 'auto'
|
return self.value == 'auto'
|
||||||
|
|
||||||
|
@permittedKwargs({'error_message'})
|
||||||
|
def require_method(self, args, kwargs):
|
||||||
|
if len(args) != 1:
|
||||||
|
raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), ))
|
||||||
|
if not isinstance(args[0], bool):
|
||||||
|
raise InvalidArguments('boolean argument expected.')
|
||||||
|
error_message = kwargs.pop('error_message', '')
|
||||||
|
if error_message and not isinstance(error_message, str):
|
||||||
|
raise InterpreterException("Error message must be a string.")
|
||||||
|
if args[0]:
|
||||||
|
return self
|
||||||
|
|
||||||
|
if self.value == 'enabled':
|
||||||
|
prefix = 'Feature {} cannot be enabled'.format(self.name)
|
||||||
|
prefix = prefix + ': ' if error_message else ''
|
||||||
|
raise InterpreterException(prefix + error_message)
|
||||||
|
return self.as_disabled()
|
||||||
|
|
||||||
|
|
||||||
class RunProcess(InterpreterObject):
|
class RunProcess(InterpreterObject):
|
||||||
|
|
||||||
def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
|
def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
|
||||||
|
|
|
@ -14,16 +14,21 @@ assert(required_opt.enabled(), 'Should be enabled option')
|
||||||
assert(not required_opt.disabled(), 'Should be enabled option')
|
assert(not required_opt.disabled(), 'Should be enabled option')
|
||||||
assert(not required_opt.auto(), 'Should be enabled option')
|
assert(not required_opt.auto(), 'Should be enabled option')
|
||||||
assert(required_opt.allowed(), 'Should be enabled option')
|
assert(required_opt.allowed(), 'Should be enabled option')
|
||||||
|
assert(required_opt.require(true, error_message: 'xyz').enabled(), 'Should be enabled option')
|
||||||
|
|
||||||
assert(not optional_opt.enabled(), 'Should be auto option')
|
assert(not optional_opt.enabled(), 'Should be auto option')
|
||||||
assert(not optional_opt.disabled(), 'Should be auto option')
|
assert(not optional_opt.disabled(), 'Should be auto option')
|
||||||
assert(optional_opt.auto(), 'Should be auto option')
|
assert(optional_opt.auto(), 'Should be auto option')
|
||||||
assert(optional_opt.allowed(), 'Should be auto option')
|
assert(optional_opt.allowed(), 'Should be auto option')
|
||||||
|
assert(optional_opt.require(true).auto(), 'Should be auto option')
|
||||||
|
assert(optional_opt.require(false, error_message: 'xyz').disabled(), 'Should be disabled auto option')
|
||||||
|
|
||||||
assert(not disabled_opt.enabled(), 'Should be disabled option')
|
assert(not disabled_opt.enabled(), 'Should be disabled option')
|
||||||
assert(disabled_opt.disabled(), 'Should be disabled option')
|
assert(disabled_opt.disabled(), 'Should be disabled option')
|
||||||
assert(not disabled_opt.auto(), 'Should be disabled option')
|
assert(not disabled_opt.auto(), 'Should be disabled option')
|
||||||
assert(not disabled_opt.allowed(), 'Should be disabled option')
|
assert(not disabled_opt.allowed(), 'Should be disabled option')
|
||||||
|
assert(disabled_opt.require(true).disabled(), 'Should be disabled option')
|
||||||
|
assert(disabled_opt.require(false, error_message: 'xyz').disabled(), 'Should be disabled option')
|
||||||
|
|
||||||
dep = dependency('threads', required : required_opt)
|
dep = dependency('threads', required : required_opt)
|
||||||
assert(dep.found(), 'Should find required "threads" dep')
|
assert(dep.found(), 'Should find required "threads" dep')
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
project('no fallback', 'c')
|
||||||
|
foo = get_option('reqfeature').require(false, error_message: 'frobnicator not available')
|
|
@ -0,0 +1,2 @@
|
||||||
|
option('reqfeature', type : 'feature', value : 'enabled', description : 'A required feature')
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"stdout": [
|
||||||
|
{
|
||||||
|
"match": "re",
|
||||||
|
"line": ".*/meson\\.build:2:0: ERROR: Feature reqfeature cannot be enabled: frobnicator not available"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue