Created a new disabler type.

pull/2731/head
Jussi Pakkanen 7 years ago
parent 754e33e574
commit 678daad6cc
  1. 1
      mesonbuild/coredata.py
  2. 8
      mesonbuild/interpreter.py
  3. 89
      mesonbuild/interpreterbase.py
  4. 34
      test cases/common/168 disabler/meson.build

@ -125,6 +125,7 @@ class UserComboOption(UserOption):
raise MesonException('Value %s not one of accepted values.' % value) raise MesonException('Value %s not one of accepted values.' % value)
return value return value
class UserStringArrayOption(UserOption): class UserStringArrayOption(UserOption):
def __init__(self, name, description, value, **kwargs): def __init__(self, name, description, value, **kwargs):
super().__init__(name, description, kwargs.get('choices', [])) super().__init__(name, description, kwargs.get('choices', []))

@ -27,7 +27,7 @@ from .dependencies import InternalDependency, Dependency, DependencyException
from .interpreterbase import InterpreterBase from .interpreterbase import InterpreterBase
from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs
from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode
from .interpreterbase import InterpreterObject, MutableInterpreterObject from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler
from .modules import ModuleReturnValue from .modules import ModuleReturnValue
import os, sys, shutil, uuid import os, sys, shutil, uuid
@ -1451,6 +1451,7 @@ class Interpreter(InterpreterBase):
'custom_target': self.func_custom_target, 'custom_target': self.func_custom_target,
'declare_dependency': self.func_declare_dependency, 'declare_dependency': self.func_declare_dependency,
'dependency': self.func_dependency, 'dependency': self.func_dependency,
'disabler': self.func_disabler,
'environment': self.func_environment, 'environment': self.func_environment,
'error': self.func_error, 'error': self.func_error,
'executable': self.func_executable, 'executable': self.func_executable,
@ -2197,6 +2198,11 @@ to directly access options of other subprojects.''')
self.coredata.deps[identifier] = dep self.coredata.deps[identifier] = dep
return DependencyHolder(dep) return DependencyHolder(dep)
@noKwargs
@noPosargs
def func_disabler(self, node, args, kwargs):
return Disabler()
def get_subproject_infos(self, kwargs): def get_subproject_infos(self, kwargs):
fbinfo = kwargs['fallback'] fbinfo = kwargs['fallback']
check_stringlist(fbinfo) check_stringlist(fbinfo)

@ -100,6 +100,29 @@ class MutableInterpreterObject(InterpreterObject):
def __init__(self): def __init__(self):
super().__init__() 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: class InterpreterBase:
def __init__(self, source_root, subdir): def __init__(self, source_root, subdir):
@ -230,6 +253,8 @@ class InterpreterBase:
assert(isinstance(node, mparser.IfClauseNode)) assert(isinstance(node, mparser.IfClauseNode))
for i in node.ifs: for i in node.ifs:
result = self.evaluate_statement(i.condition) result = self.evaluate_statement(i.condition)
if is_disabler(result):
return result
if not(isinstance(result, bool)): if not(isinstance(result, bool)):
raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result)) raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result))
if result: if result:
@ -240,7 +265,11 @@ class InterpreterBase:
def evaluate_comparison(self, node): def evaluate_comparison(self, node):
val1 = self.evaluate_statement(node.left) val1 = self.evaluate_statement(node.left)
if is_disabler(val1):
return val1
val2 = self.evaluate_statement(node.right) val2 = self.evaluate_statement(node.right)
if is_disabler(val2):
return val2
if node.ctype == '==': if node.ctype == '==':
return val1 == val2 return val1 == val2
elif node.ctype == '!=': elif node.ctype == '!=':
@ -267,35 +296,49 @@ class InterpreterBase:
def evaluate_andstatement(self, cur): def evaluate_andstatement(self, cur):
l = self.evaluate_statement(cur.left) l = self.evaluate_statement(cur.left)
if is_disabler(l):
return l
if not isinstance(l, bool): if not isinstance(l, bool):
raise InterpreterException('First argument to "and" is not a boolean.') raise InterpreterException('First argument to "and" is not a boolean.')
if not l: if not l:
return False return False
r = self.evaluate_statement(cur.right) r = self.evaluate_statement(cur.right)
if is_disabler(r):
return r
if not isinstance(r, bool): if not isinstance(r, bool):
raise InterpreterException('Second argument to "and" is not a boolean.') raise InterpreterException('Second argument to "and" is not a boolean.')
return r return r
def evaluate_orstatement(self, cur): def evaluate_orstatement(self, cur):
l = self.evaluate_statement(cur.left) l = self.evaluate_statement(cur.left)
if is_disabler(l):
return l
if not isinstance(l, bool): if not isinstance(l, bool):
raise InterpreterException('First argument to "or" is not a boolean.') raise InterpreterException('First argument to "or" is not a boolean.')
if l: if l:
return True return True
r = self.evaluate_statement(cur.right) r = self.evaluate_statement(cur.right)
if is_disabler(r):
return r
if not isinstance(r, bool): if not isinstance(r, bool):
raise InterpreterException('Second argument to "or" is not a boolean.') raise InterpreterException('Second argument to "or" is not a boolean.')
return r return r
def evaluate_uminusstatement(self, cur): def evaluate_uminusstatement(self, cur):
v = self.evaluate_statement(cur.value) v = self.evaluate_statement(cur.value)
if is_disabler(v):
return v
if not isinstance(v, int): if not isinstance(v, int):
raise InterpreterException('Argument to negation is not an integer.') raise InterpreterException('Argument to negation is not an integer.')
return -v return -v
def evaluate_arithmeticstatement(self, cur): def evaluate_arithmeticstatement(self, cur):
l = self.evaluate_statement(cur.left) l = self.evaluate_statement(cur.left)
if is_disabler(l):
return l
r = self.evaluate_statement(cur.right) r = self.evaluate_statement(cur.right)
if is_disabler(r):
return r
if cur.operation == 'add': if cur.operation == 'add':
try: try:
@ -324,6 +367,8 @@ class InterpreterBase:
def evaluate_ternary(self, node): def evaluate_ternary(self, node):
assert(isinstance(node, mparser.TernaryNode)) assert(isinstance(node, mparser.TernaryNode))
result = self.evaluate_statement(node.condition) result = self.evaluate_statement(node.condition)
if is_disabler(result):
return result
if not isinstance(result, bool): if not isinstance(result, bool):
raise InterpreterException('Ternary condition is not boolean.') raise InterpreterException('Ternary condition is not boolean.')
if result: if result:
@ -335,6 +380,8 @@ class InterpreterBase:
assert(isinstance(node, mparser.ForeachClauseNode)) assert(isinstance(node, mparser.ForeachClauseNode))
varname = node.varname.value varname = node.varname.value
items = self.evaluate_statement(node.items) items = self.evaluate_statement(node.items)
if is_disabler(items):
return items
if not isinstance(items, list): if not isinstance(items, list):
raise InvalidArguments('Items of foreach loop is not an array') raise InvalidArguments('Items of foreach loop is not an array')
for item in items: for item in items:
@ -345,6 +392,9 @@ class InterpreterBase:
assert(isinstance(node, mparser.PlusAssignmentNode)) assert(isinstance(node, mparser.PlusAssignmentNode))
varname = node.var_name varname = node.var_name
addition = self.evaluate_statement(node.value) 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 # Remember that all variables are immutable. We must always create a
# full new variable and then assign it. # full new variable and then assign it.
old_variable = self.get_variable(varname) old_variable = self.get_variable(varname)
@ -369,6 +419,8 @@ class InterpreterBase:
def evaluate_indexing(self, node): def evaluate_indexing(self, node):
assert(isinstance(node, mparser.IndexNode)) assert(isinstance(node, mparser.IndexNode))
iobject = self.evaluate_statement(node.iobject) iobject = self.evaluate_statement(node.iobject)
if is_disabler(iobject):
return iobject
if not hasattr(iobject, '__getitem__'): if not hasattr(iobject, '__getitem__'):
raise InterpreterException( raise InterpreterException(
'Tried to index an object that doesn\'t support indexing.') 'Tried to index an object that doesn\'t support indexing.')
@ -383,6 +435,8 @@ class InterpreterBase:
def function_call(self, node): def function_call(self, node):
func_name = node.func_name func_name = node.func_name
(posargs, kwargs) = self.reduce_arguments(node.args) (posargs, kwargs) = self.reduce_arguments(node.args)
if is_disabled(posargs, kwargs):
return Disabler()
if func_name in self.funcs: if func_name in self.funcs:
return self.funcs[func_name](node, self.flatten(posargs), kwargs) return self.funcs[func_name](node, self.flatten(posargs), kwargs)
else: else:
@ -404,18 +458,26 @@ class InterpreterBase:
if isinstance(obj, int): if isinstance(obj, int):
return self.int_method_call(obj, method_name, args) return self.int_method_call(obj, method_name, args)
if isinstance(obj, list): 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): if isinstance(obj, mesonlib.File):
raise InvalidArguments('File object "%s" is not callable.' % obj) raise InvalidArguments('File object "%s" is not callable.' % obj)
if not isinstance(obj, InterpreterObject): if not isinstance(obj, InterpreterObject):
raise InvalidArguments('Variable "%s" is not callable.' % object_name) raise InvalidArguments('Variable "%s" is not callable.' % object_name)
(args, kwargs) = self.reduce_arguments(args) (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': if method_name == 'extract_objects':
self.validate_extraction(obj.held_object) self.validate_extraction(obj.held_object)
return obj.method_call(method_name, self.flatten(args), kwargs) return obj.method_call(method_name, self.flatten(args), kwargs)
def bool_method_call(self, obj, method_name, args): 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 method_name == 'to_string':
if not posargs: if not posargs:
if obj: if obj:
@ -438,7 +500,9 @@ class InterpreterBase:
raise InterpreterException('Unknown method "%s" for a boolean.' % method_name) raise InterpreterException('Unknown method "%s" for a boolean.' % method_name)
def int_method_call(self, obj, method_name, args): 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 method_name == 'is_even':
if not posargs: if not posargs:
return obj % 2 == 0 return obj % 2 == 0
@ -471,7 +535,9 @@ class InterpreterBase:
return None return None
def string_method_call(self, obj, method_name, args): 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': if method_name == 'strip':
s = self._get_one_string_posarg(posargs, 'strip') s = self._get_one_string_posarg(posargs, 'strip')
if s is not None: if s is not None:
@ -520,19 +586,22 @@ class InterpreterBase:
raise InterpreterException('Unknown method "%s" for a string.' % method_name) raise InterpreterException('Unknown method "%s" for a string.' % method_name)
def unknown_function_called(self, func_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): 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': if method_name == 'contains':
return self.check_contains(obj, args) return self.check_contains(obj, posargs)
elif method_name == 'length': elif method_name == 'length':
return len(obj) return len(obj)
elif method_name == 'get': elif method_name == 'get':
index = args[0] index = posargs[0]
fallback = None fallback = None
if len(args) == 2: if len(posargs) == 2:
fallback = args[1] fallback = posargs[1]
elif len(args) > 2: elif len(posargs) > 2:
m = 'Array method \'get()\' only takes two arguments: the ' \ m = 'Array method \'get()\' only takes two arguments: the ' \
'index and an optional fallback value if the index is ' \ 'index and an optional fallback value if the index is ' \
'out of range.' 'out of range.'

@ -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))
Loading…
Cancel
Save