User options can "yield to" a user option of the same name in superproject. Closes ##2853.
This commit is contained in:
parent
0204895143
commit
54d7817087
|
@ -108,3 +108,21 @@ double quotes.
|
|||
**NOTE:** If you cannot call `meson configure` you likely have a old
|
||||
version of Meson. In that case you can call `mesonconf` instead, but
|
||||
that is deprecated in newer versions
|
||||
|
||||
## Yielding to superproject option
|
||||
|
||||
Suppose you have a master project and a subproject. In some cases it
|
||||
might be useful to have an option that has the same value in both of
|
||||
them. This can be achieved with the `yield` keyword. Suppose you have
|
||||
an option definition like this:
|
||||
|
||||
```meson
|
||||
option('some_option', type : 'string', value : 'value', yield : true)
|
||||
```
|
||||
|
||||
If you build this project on its own, this option behaves like
|
||||
usual. However if you build this project as a subproject of another
|
||||
project which also has an option called `some_option`, then calling
|
||||
`get_option` returns the value of the superproject. If the value of
|
||||
`yield` is `false`, `get_option` returns the value of the subproject's
|
||||
option.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
## Yielding subproject option to superproject
|
||||
|
||||
Normally project options are specific to the current project. However
|
||||
sometimes you want to have an option whose value is the same over all
|
||||
projects. This can be achieved with the new `yield` keyword for
|
||||
options. When set to `true`, getting the value of this option in
|
||||
`meson.build` files gets the value from the option with the same name
|
||||
in the master project (if such an option exists).
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
# Copyright 2012-2017 The Meson development team
|
||||
# Copyright 2012-2018 The Meson development team
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -25,12 +24,19 @@ import ast
|
|||
version = '0.45.0.dev1'
|
||||
backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
|
||||
|
||||
default_yielding = False
|
||||
|
||||
class UserOption:
|
||||
def __init__(self, name, description, choices):
|
||||
def __init__(self, name, description, choices, yielding):
|
||||
super().__init__()
|
||||
self.name = name
|
||||
self.choices = choices
|
||||
self.description = description
|
||||
if yielding is None:
|
||||
yielding = default_yielding
|
||||
if not isinstance(yielding, bool):
|
||||
raise MesonException('Value of "yielding" must be a boolean.')
|
||||
self.yielding = yielding
|
||||
|
||||
# Check that the input is a valid value and return the
|
||||
# "cleaned" or "native" version. For example the Boolean
|
||||
|
@ -39,8 +45,8 @@ class UserOption:
|
|||
raise RuntimeError('Derived option class did not override validate_value.')
|
||||
|
||||
class UserStringOption(UserOption):
|
||||
def __init__(self, name, description, value, choices=None):
|
||||
super().__init__(name, description, choices)
|
||||
def __init__(self, name, description, value, choices=None, yielding=None):
|
||||
super().__init__(name, description, choices, yielding)
|
||||
self.set_value(value)
|
||||
|
||||
def validate(self, value):
|
||||
|
@ -56,8 +62,8 @@ class UserStringOption(UserOption):
|
|||
return value
|
||||
|
||||
class UserBooleanOption(UserOption):
|
||||
def __init__(self, name, description, value):
|
||||
super().__init__(name, description, [True, False])
|
||||
def __init__(self, name, description, value, yielding=None):
|
||||
super().__init__(name, description, [True, False], yielding)
|
||||
self.set_value(value)
|
||||
|
||||
def tobool(self, thing):
|
||||
|
@ -79,8 +85,8 @@ class UserBooleanOption(UserOption):
|
|||
return self.tobool(value)
|
||||
|
||||
class UserIntegerOption(UserOption):
|
||||
def __init__(self, name, description, min_value, max_value, value):
|
||||
super().__init__(name, description, None)
|
||||
def __init__(self, name, description, min_value, max_value, value, yielding=None):
|
||||
super().__init__(name, description, [True, False], yielding)
|
||||
self.min_value = min_value
|
||||
self.max_value = max_value
|
||||
self.set_value(value)
|
||||
|
@ -112,8 +118,8 @@ class UserIntegerOption(UserOption):
|
|||
return self.toint(value)
|
||||
|
||||
class UserComboOption(UserOption):
|
||||
def __init__(self, name, description, choices, value):
|
||||
super().__init__(name, description, choices)
|
||||
def __init__(self, name, description, choices, value, yielding=None):
|
||||
super().__init__(name, description, choices, yielding)
|
||||
if not isinstance(self.choices, list):
|
||||
raise MesonException('Combo choices must be an array.')
|
||||
for i in self.choices:
|
||||
|
@ -134,7 +140,7 @@ class UserComboOption(UserOption):
|
|||
|
||||
class UserArrayOption(UserOption):
|
||||
def __init__(self, name, description, value, **kwargs):
|
||||
super().__init__(name, description, kwargs.get('choices', []))
|
||||
super().__init__(name, description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None))
|
||||
self.set_value(value, user_input=False)
|
||||
|
||||
def validate(self, value, user_input):
|
||||
|
|
|
@ -1768,7 +1768,7 @@ external dependencies (including libraries) must go to "dependencies".''')
|
|||
def func_get_option(self, nodes, args, kwargs):
|
||||
if len(args) != 1:
|
||||
raise InterpreterException('Argument required for get_option.')
|
||||
optname = args[0]
|
||||
undecorated_optname = optname = args[0]
|
||||
if ':' in optname:
|
||||
raise InterpreterException('''Having a colon in option name is forbidden, projects are not allowed
|
||||
to directly access options of other subprojects.''')
|
||||
|
@ -1787,7 +1787,11 @@ to directly access options of other subprojects.''')
|
|||
if not coredata.is_builtin_option(optname) and self.is_subproject():
|
||||
optname = self.subproject + ':' + optname
|
||||
try:
|
||||
return self.environment.coredata.user_options[optname].value
|
||||
opt = self.environment.coredata.user_options[optname]
|
||||
if opt.yielding and ':' in optname:
|
||||
# If option not present in superproject, keep the original.
|
||||
opt = self.environment.coredata.user_options.get(undecorated_optname, opt)
|
||||
return opt.value
|
||||
except KeyError:
|
||||
pass
|
||||
if optname.endswith('_link_args'):
|
||||
|
|
|
@ -64,16 +64,21 @@ def permitted_kwargs(permitted):
|
|||
|
||||
optname_regex = re.compile('[^a-zA-Z0-9_-]')
|
||||
|
||||
@permitted_kwargs({'value'})
|
||||
@permitted_kwargs({'value', 'yield'})
|
||||
def StringParser(name, description, kwargs):
|
||||
return coredata.UserStringOption(name, description,
|
||||
kwargs.get('value', ''), kwargs.get('choices', []))
|
||||
return coredata.UserStringOption(name,
|
||||
description,
|
||||
kwargs.get('value', ''),
|
||||
kwargs.get('choices', []),
|
||||
kwargs.get('yield', coredata.default_yielding))
|
||||
|
||||
@permitted_kwargs({'value'})
|
||||
@permitted_kwargs({'value', 'yield'})
|
||||
def BooleanParser(name, description, kwargs):
|
||||
return coredata.UserBooleanOption(name, description, kwargs.get('value', True))
|
||||
return coredata.UserBooleanOption(name, description,
|
||||
kwargs.get('value', True),
|
||||
kwargs.get('yield', coredata.default_yielding))
|
||||
|
||||
@permitted_kwargs({'value', 'choices'})
|
||||
@permitted_kwargs({'value', 'yiel', 'choices'})
|
||||
def ComboParser(name, description, kwargs):
|
||||
if 'choices' not in kwargs:
|
||||
raise OptionException('Combo option missing "choices" keyword.')
|
||||
|
@ -83,9 +88,14 @@ def ComboParser(name, description, kwargs):
|
|||
for i in choices:
|
||||
if not isinstance(i, str):
|
||||
raise OptionException('Combo choice elements must be strings.')
|
||||
return coredata.UserComboOption(name, description, choices, kwargs.get('value', choices[0]))
|
||||
return coredata.UserComboOption(name,
|
||||
description,
|
||||
choices,
|
||||
kwargs.get('value', choices[0]),
|
||||
kwargs.get('yield', coredata.default_yielding),)
|
||||
|
||||
@permitted_kwargs({'value', 'min', 'max'})
|
||||
|
||||
@permitted_kwargs({'value', 'min', 'max', 'yield'})
|
||||
def IntegerParser(name, description, kwargs):
|
||||
if 'value' not in kwargs:
|
||||
raise OptionException('Integer option must contain value argument.')
|
||||
|
@ -93,9 +103,10 @@ def IntegerParser(name, description, kwargs):
|
|||
description,
|
||||
kwargs.get('min', None),
|
||||
kwargs.get('max', None),
|
||||
kwargs['value'])
|
||||
kwargs['value'],
|
||||
kwargs.get('yield', coredata.default_yielding))
|
||||
|
||||
@permitted_kwargs({'value', 'choices'})
|
||||
@permitted_kwargs({'value', 'yield', 'choices'})
|
||||
def string_array_parser(name, description, kwargs):
|
||||
if 'choices' in kwargs:
|
||||
choices = kwargs['choices']
|
||||
|
@ -110,7 +121,11 @@ def string_array_parser(name, description, kwargs):
|
|||
value = kwargs.get('value', [])
|
||||
if not isinstance(value, list):
|
||||
raise OptionException('Array choices must be passed as an array.')
|
||||
return coredata.UserArrayOption(name, description, value, choices=choices)
|
||||
return coredata.UserArrayOption(name,
|
||||
description,
|
||||
value,
|
||||
choices=choices,
|
||||
yielding=kwargs.get('yield', coredata.default_yielding))
|
||||
|
||||
option_types = {'string': StringParser,
|
||||
'boolean': BooleanParser,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
project('yield_options', 'c')
|
||||
|
||||
subproject('sub')
|
||||
|
||||
assert(get_option('unshared_option') == 'one', 'Unshared option has wrong value in superproject.')
|
||||
assert(get_option('shared_option') == 'two', 'Unshared option has wrong value in superproject..')
|
|
@ -0,0 +1,2 @@
|
|||
option('unshared_option', type : 'string', value : 'one')
|
||||
option('shared_option', type : 'string', value : 'two')
|
|
@ -0,0 +1,4 @@
|
|||
project('subbie', 'c')
|
||||
|
||||
assert(get_option('unshared_option') == 'three', 'Unshared option has wrong value in subproject.')
|
||||
assert(get_option('shared_option') == 'two', 'Shared option has wrong value in subproject.')
|
|
@ -0,0 +1,2 @@
|
|||
option('unshared_option', type : 'string', value : 'three', yield : false)
|
||||
option('shared_option', type : 'string', value : 'four', yield : true)
|
Loading…
Reference in New Issue