diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2018-01-02 22:25:24 +0200 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2018-02-08 00:33:44 +0200 |
commit | 54d78170876e7b762aa3452faa44ec1b7cd44372 (patch) | |
tree | 7c65bb478818a90fa3d3991da4c6ca8b2c641eb2 | |
parent | 0204895143d4aacf143be952f962dc47e415c187 (diff) | |
download | meson-54d78170876e7b762aa3452faa44ec1b7cd44372.zip meson-54d78170876e7b762aa3452faa44ec1b7cd44372.tar.gz meson-54d78170876e7b762aa3452faa44ec1b7cd44372.tar.bz2 |
User options can "yield to" a user option of the same name in superproject. Closes ##2853.
-rw-r--r-- | docs/markdown/Build-options.md | 18 | ||||
-rw-r--r-- | docs/markdown/snippets/yield.md | 8 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 30 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 8 | ||||
-rw-r--r-- | mesonbuild/optinterpreter.py | 37 | ||||
-rw-r--r-- | test cases/common/176 yield/meson.build | 6 | ||||
-rw-r--r-- | test cases/common/176 yield/meson_options.txt | 2 | ||||
-rw-r--r-- | test cases/common/176 yield/subprojects/sub/meson.build | 4 | ||||
-rw-r--r-- | test cases/common/176 yield/subprojects/sub/meson_options.txt | 2 |
9 files changed, 90 insertions, 25 deletions
diff --git a/docs/markdown/Build-options.md b/docs/markdown/Build-options.md index 85e8274..74d2355 100644 --- a/docs/markdown/Build-options.md +++ b/docs/markdown/Build-options.md @@ -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. diff --git a/docs/markdown/snippets/yield.md b/docs/markdown/snippets/yield.md new file mode 100644 index 0000000..3880e67 --- /dev/null +++ b/docs/markdown/snippets/yield.md @@ -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). diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index c96a09e..f87e62c 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -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): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index f68e25f..31d7616 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -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'): diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 7eeaa48..d4ea06a 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -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, diff --git a/test cases/common/176 yield/meson.build b/test cases/common/176 yield/meson.build new file mode 100644 index 0000000..ba3e426 --- /dev/null +++ b/test cases/common/176 yield/meson.build @@ -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..') diff --git a/test cases/common/176 yield/meson_options.txt b/test cases/common/176 yield/meson_options.txt new file mode 100644 index 0000000..36bad4b --- /dev/null +++ b/test cases/common/176 yield/meson_options.txt @@ -0,0 +1,2 @@ +option('unshared_option', type : 'string', value : 'one') +option('shared_option', type : 'string', value : 'two') diff --git a/test cases/common/176 yield/subprojects/sub/meson.build b/test cases/common/176 yield/subprojects/sub/meson.build new file mode 100644 index 0000000..3a506e0 --- /dev/null +++ b/test cases/common/176 yield/subprojects/sub/meson.build @@ -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.') diff --git a/test cases/common/176 yield/subprojects/sub/meson_options.txt b/test cases/common/176 yield/subprojects/sub/meson_options.txt new file mode 100644 index 0000000..a96c307 --- /dev/null +++ b/test cases/common/176 yield/subprojects/sub/meson_options.txt @@ -0,0 +1,2 @@ +option('unshared_option', type : 'string', value : 'three', yield : false) +option('shared_option', type : 'string', value : 'four', yield : true) |