summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-09-22 20:51:30 +0300
committerJussi Pakkanen <jpakkane@gmail.com>2017-12-04 23:53:23 +0200
commit678daad6cc11e850f5d7aa7f1744725d3acaf621 (patch)
tree34de305580a587258970fb26fd3509636690e63a
parent754e33e574dd37ab0efb0d336bb805861bc3e6cf (diff)
downloadmeson-678daad6cc11e850f5d7aa7f1744725d3acaf621.tar.gz
Created a new disabler type.
-rw-r--r--mesonbuild/coredata.py1
-rw-r--r--mesonbuild/interpreter.py8
-rw-r--r--mesonbuild/interpreterbase.py89
-rw-r--r--test cases/common/168 disabler/meson.build34
4 files changed, 121 insertions, 11 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 7fbf18ab0..ecb24927a 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -125,6 +125,7 @@ class UserComboOption(UserOption):
raise MesonException('Value %s not one of accepted values.' % value)
return value
+
class UserStringArrayOption(UserOption):
def __init__(self, name, description, value, **kwargs):
super().__init__(name, description, kwargs.get('choices', []))
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 3e8930554..a9ced7d00 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -27,7 +27,7 @@ from .dependencies import InternalDependency, Dependency, DependencyException
from .interpreterbase import InterpreterBase
from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs
from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode
-from .interpreterbase import InterpreterObject, MutableInterpreterObject
+from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler
from .modules import ModuleReturnValue
import os, sys, shutil, uuid
@@ -1451,6 +1451,7 @@ class Interpreter(InterpreterBase):
'custom_target': self.func_custom_target,
'declare_dependency': self.func_declare_dependency,
'dependency': self.func_dependency,
+ 'disabler': self.func_disabler,
'environment': self.func_environment,
'error': self.func_error,
'executable': self.func_executable,
@@ -2197,6 +2198,11 @@ to directly access options of other subprojects.''')
self.coredata.deps[identifier] = dep
return DependencyHolder(dep)
+ @noKwargs
+ @noPosargs
+ def func_disabler(self, node, args, kwargs):
+ return Disabler()
+
def get_subproject_infos(self, kwargs):
fbinfo = kwargs['fallback']
check_stringlist(fbinfo)
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 7ccc8b2ba..91f4bd3e9 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -100,6 +100,29 @@ class MutableInterpreterObject(InterpreterObject):
def __init__(self):
super().__init__()
+class Disabler(InterpreterObject):
+ def __init__(self):
+ super().__init__()
+ self.methods.update({'found': self.found_method})
+
+ def found_method(self, args, kwargs):
+ return False
+
+def is_disabler(i):
+ return isinstance(i, Disabler)
+
+def is_disabled(args, kwargs):
+ for i in args:
+ if isinstance(i, Disabler):
+ return True
+ for i in kwargs.values():
+ if isinstance(i, Disabler):
+ return True
+ if isinstance(i, list):
+ for j in i:
+ if isinstance(j, Disabler):
+ return True
+ return False
class InterpreterBase:
def __init__(self, source_root, subdir):
@@ -230,6 +253,8 @@ class InterpreterBase:
assert(isinstance(node, mparser.IfClauseNode))
for i in node.ifs:
result = self.evaluate_statement(i.condition)
+ if is_disabler(result):
+ return result
if not(isinstance(result, bool)):
raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result))
if result:
@@ -240,7 +265,11 @@ class InterpreterBase:
def evaluate_comparison(self, node):
val1 = self.evaluate_statement(node.left)
+ if is_disabler(val1):
+ return val1
val2 = self.evaluate_statement(node.right)
+ if is_disabler(val2):
+ return val2
if node.ctype == '==':
return val1 == val2
elif node.ctype == '!=':
@@ -267,35 +296,49 @@ class InterpreterBase:
def evaluate_andstatement(self, cur):
l = self.evaluate_statement(cur.left)
+ if is_disabler(l):
+ return l
if not isinstance(l, bool):
raise InterpreterException('First argument to "and" is not a boolean.')
if not l:
return False
r = self.evaluate_statement(cur.right)
+ if is_disabler(r):
+ return r
if not isinstance(r, bool):
raise InterpreterException('Second argument to "and" is not a boolean.')
return r
def evaluate_orstatement(self, cur):
l = self.evaluate_statement(cur.left)
+ if is_disabler(l):
+ return l
if not isinstance(l, bool):
raise InterpreterException('First argument to "or" is not a boolean.')
if l:
return True
r = self.evaluate_statement(cur.right)
+ if is_disabler(r):
+ return r
if not isinstance(r, bool):
raise InterpreterException('Second argument to "or" is not a boolean.')
return r
def evaluate_uminusstatement(self, cur):
v = self.evaluate_statement(cur.value)
+ if is_disabler(v):
+ return v
if not isinstance(v, int):
raise InterpreterException('Argument to negation is not an integer.')
return -v
def evaluate_arithmeticstatement(self, cur):
l = self.evaluate_statement(cur.left)
+ if is_disabler(l):
+ return l
r = self.evaluate_statement(cur.right)
+ if is_disabler(r):
+ return r
if cur.operation == 'add':
try:
@@ -324,6 +367,8 @@ class InterpreterBase:
def evaluate_ternary(self, node):
assert(isinstance(node, mparser.TernaryNode))
result = self.evaluate_statement(node.condition)
+ if is_disabler(result):
+ return result
if not isinstance(result, bool):
raise InterpreterException('Ternary condition is not boolean.')
if result:
@@ -335,6 +380,8 @@ class InterpreterBase:
assert(isinstance(node, mparser.ForeachClauseNode))
varname = node.varname.value
items = self.evaluate_statement(node.items)
+ if is_disabler(items):
+ return items
if not isinstance(items, list):
raise InvalidArguments('Items of foreach loop is not an array')
for item in items:
@@ -345,6 +392,9 @@ class InterpreterBase:
assert(isinstance(node, mparser.PlusAssignmentNode))
varname = node.var_name
addition = self.evaluate_statement(node.value)
+ if is_disabler(addition):
+ set_variable(varname, addition)
+ return
# Remember that all variables are immutable. We must always create a
# full new variable and then assign it.
old_variable = self.get_variable(varname)
@@ -369,6 +419,8 @@ class InterpreterBase:
def evaluate_indexing(self, node):
assert(isinstance(node, mparser.IndexNode))
iobject = self.evaluate_statement(node.iobject)
+ if is_disabler(iobject):
+ return iobject
if not hasattr(iobject, '__getitem__'):
raise InterpreterException(
'Tried to index an object that doesn\'t support indexing.')
@@ -383,6 +435,8 @@ class InterpreterBase:
def function_call(self, node):
func_name = node.func_name
(posargs, kwargs) = self.reduce_arguments(node.args)
+ if is_disabled(posargs, kwargs):
+ return Disabler()
if func_name in self.funcs:
return self.funcs[func_name](node, self.flatten(posargs), kwargs)
else:
@@ -404,18 +458,26 @@ class InterpreterBase:
if isinstance(obj, int):
return self.int_method_call(obj, method_name, args)
if isinstance(obj, list):
- return self.array_method_call(obj, method_name, self.reduce_arguments(args)[0])
+ return self.array_method_call(obj, method_name, args)
if isinstance(obj, mesonlib.File):
raise InvalidArguments('File object "%s" is not callable.' % obj)
if not isinstance(obj, InterpreterObject):
raise InvalidArguments('Variable "%s" is not callable.' % object_name)
(args, kwargs) = self.reduce_arguments(args)
+ # Special case. This is the only thing you can do with a disabler
+ # object. Every other use immediately returns the disabler object.
+ if isinstance(obj, Disabler) and method_name == 'found':
+ return False
+ if is_disabled(args, kwargs):
+ return Disabler()
if method_name == 'extract_objects':
self.validate_extraction(obj.held_object)
return obj.method_call(method_name, self.flatten(args), kwargs)
def bool_method_call(self, obj, method_name, args):
- (posargs, _) = self.reduce_arguments(args)
+ (posargs, kwargs) = self.reduce_arguments(args)
+ if is_disabled(posargs, kwargs):
+ return Disabler()
if method_name == 'to_string':
if not posargs:
if obj:
@@ -438,7 +500,9 @@ class InterpreterBase:
raise InterpreterException('Unknown method "%s" for a boolean.' % method_name)
def int_method_call(self, obj, method_name, args):
- (posargs, _) = self.reduce_arguments(args)
+ (posargs, kwargs) = self.reduce_arguments(args)
+ if is_disabled(posargs, kwargs):
+ return Disabler()
if method_name == 'is_even':
if not posargs:
return obj % 2 == 0
@@ -471,7 +535,9 @@ class InterpreterBase:
return None
def string_method_call(self, obj, method_name, args):
- (posargs, _) = self.reduce_arguments(args)
+ (posargs, kwargs) = self.reduce_arguments(args)
+ if is_disabled(posargs, kwargs):
+ return Disabler()
if method_name == 'strip':
s = self._get_one_string_posarg(posargs, 'strip')
if s is not None:
@@ -520,19 +586,22 @@ class InterpreterBase:
raise InterpreterException('Unknown method "%s" for a string.' % method_name)
def unknown_function_called(self, func_name):
- raise InvalidCode('Unknown function "%s".' % func_name)
+ raise InvalidCode('Unknown function "%s".' % func_name)
def array_method_call(self, obj, method_name, args):
+ (posargs, kwargs) = self.reduce_arguments(args)
+ if is_disabled(posargs, kwargs):
+ return Disabler()
if method_name == 'contains':
- return self.check_contains(obj, args)
+ return self.check_contains(obj, posargs)
elif method_name == 'length':
return len(obj)
elif method_name == 'get':
- index = args[0]
+ index = posargs[0]
fallback = None
- if len(args) == 2:
- fallback = args[1]
- elif len(args) > 2:
+ if len(posargs) == 2:
+ fallback = posargs[1]
+ elif len(posargs) > 2:
m = 'Array method \'get()\' only takes two arguments: the ' \
'index and an optional fallback value if the index is ' \
'out of range.'
diff --git a/test cases/common/168 disabler/meson.build b/test cases/common/168 disabler/meson.build
new file mode 100644
index 000000000..7ca82b786
--- /dev/null
+++ b/test cases/common/168 disabler/meson.build
@@ -0,0 +1,34 @@
+project('dolphin option', 'c')
+
+d = disabler()
+
+d2 = dependency(d)
+d3 = (d == d2)
+d4 = d + 0
+d5 = d2 or true
+
+assert(d, 'Disabler did not cause this to be skipped.')
+assert(d2, 'Function laundered disabler did not cause this to be skipped.')
+assert(d3, 'Disabler comparison should yield disabler and thus this would not be called.')
+assert(d4, 'Disabler addition should yield disabler and thus this would not be called.')
+assert(d5, 'Disabler logic op should yield disabler and thus this would not be called.')
+
+number = 0
+
+if d
+ number = 1
+else
+ number = 2
+endif
+
+assert(d == 0, 'Plain if handled incorrectly, value should be 0 but is @0@'.format(number))
+
+if d.found()
+ number = 1
+else
+ number = 2
+endif
+
+assert(d == 1, 'If found handled incorrectly, value should be 1 but is @0@'.format(number))
+
+