aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2018-01-02 22:25:24 +0200
committerJussi Pakkanen <jpakkane@gmail.com>2018-02-08 00:33:44 +0200
commit54d78170876e7b762aa3452faa44ec1b7cd44372 (patch)
tree7c65bb478818a90fa3d3991da4c6ca8b2c641eb2
parent0204895143d4aacf143be952f962dc47e415c187 (diff)
downloadmeson-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.md18
-rw-r--r--docs/markdown/snippets/yield.md8
-rw-r--r--mesonbuild/coredata.py30
-rw-r--r--mesonbuild/interpreter.py8
-rw-r--r--mesonbuild/optinterpreter.py37
-rw-r--r--test cases/common/176 yield/meson.build6
-rw-r--r--test cases/common/176 yield/meson_options.txt2
-rw-r--r--test cases/common/176 yield/subprojects/sub/meson.build4
-rw-r--r--test cases/common/176 yield/subprojects/sub/meson_options.txt2
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)