aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/ast/interpreter.py111
-rw-r--r--mesonbuild/ast/introspection.py7
-rw-r--r--mesonbuild/interpreter.py18
-rw-r--r--mesonbuild/interpreterbase.py18
4 files changed, 110 insertions, 44 deletions
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index a75c0b7..eb9cb9f 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -20,9 +20,22 @@ from .. import interpreterbase, mparser, mesonlib
from .. import environment
from ..interpreterbase import InvalidArguments, BreakRequest, ContinueRequest
+from ..mparser import (
+ ArgumentNode,
+ ArithmeticNode,
+ ArrayNode,
+ AssignmentNode,
+ BaseNode,
+ ElementaryNode,
+ EmptyNode,
+ IdNode,
+ MethodNode,
+ PlusAssignmentNode,
+ TernaryNode,
+)
import os, sys
-from typing import List, Optional
+from typing import List, Any, Optional
class DontCareObject(interpreterbase.InterpreterObject):
pass
@@ -162,13 +175,13 @@ class AstInterpreter(interpreterbase.InterpreterBase):
return 0
def evaluate_ternary(self, node):
- assert(isinstance(node, mparser.TernaryNode))
+ assert(isinstance(node, TernaryNode))
self.evaluate_statement(node.condition)
self.evaluate_statement(node.trueblock)
self.evaluate_statement(node.falseblock)
def evaluate_plusassign(self, node):
- assert(isinstance(node, mparser.PlusAssignmentNode))
+ assert(isinstance(node, PlusAssignmentNode))
if node.var_name not in self.assignments:
self.assignments[node.var_name] = []
self.assign_vals[node.var_name] = []
@@ -184,10 +197,12 @@ class AstInterpreter(interpreterbase.InterpreterBase):
pass
def reduce_arguments(self, args):
- assert(isinstance(args, mparser.ArgumentNode))
- if args.incorrect_order():
- raise InvalidArguments('All keyword arguments must be after positional arguments.')
- return args.arguments, args.kwargs
+ if isinstance(args, ArgumentNode):
+ if args.incorrect_order():
+ raise InvalidArguments('All keyword arguments must be after positional arguments.')
+ return self.flatten_args(args.arguments), args.kwargs
+ else:
+ return self.flatten_args(args), {}
def evaluate_comparison(self, node):
self.evaluate_statement(node.left)
@@ -215,45 +230,95 @@ class AstInterpreter(interpreterbase.InterpreterBase):
def evaluate_if(self, node):
for i in node.ifs:
self.evaluate_codeblock(i.block)
- if not isinstance(node.elseblock, mparser.EmptyNode):
+ if not isinstance(node.elseblock, EmptyNode):
self.evaluate_codeblock(node.elseblock)
def get_variable(self, varname):
return 0
def assignment(self, node):
- assert(isinstance(node, mparser.AssignmentNode))
+ assert(isinstance(node, AssignmentNode))
self.assignments[node.var_name] = [node.value] # Save a reference to the value node
if hasattr(node.value, 'ast_id'):
self.reverse_assignment[node.value.ast_id] = node
self.assign_vals[node.var_name] = [self.evaluate_statement(node.value)] # Evaluate the value just in case
- def flatten_args(self, args, include_unknown_args: bool = False):
- # Resolve mparser.ArrayNode if needed
+ def flatten_args(self, args: Any, include_unknown_args: bool = False, id_loop_detect: Optional[List[str]] = None) -> List[str]:
+ def quick_resolve(n: BaseNode, loop_detect: Optional[List[str]] = None) -> Any:
+ if loop_detect is None:
+ loop_detect = []
+ if isinstance(n, IdNode):
+ if n.value in loop_detect or n.value not in self.assignments:
+ return []
+ return quick_resolve(self.assignments[n.value][0], loop_detect = loop_detect + [n.value])
+ elif isinstance(n, ElementaryNode):
+ return n.value
+ else:
+ return n
+
+ if id_loop_detect is None:
+ id_loop_detect = []
flattend_args = []
- temp_args = []
- if isinstance(args, mparser.ArrayNode):
+
+ if isinstance(args, ArrayNode):
args = [x for x in args.args.arguments]
- elif isinstance(args, mparser.ArgumentNode):
+
+ elif isinstance(args, ArgumentNode):
args = [x for x in args.arguments]
- for i in args:
- if isinstance(i, mparser.ArrayNode):
- temp_args += [x for x in i.args.arguments]
+
+ elif isinstance(args, ArithmeticNode):
+ if args.operation != 'add':
+ return [] # Only handle string and array concats
+ l = quick_resolve(args.left)
+ r = quick_resolve(args.right)
+ if isinstance(l, str) and isinstance(r, str):
+ args = [l + r] # String concatination detected
else:
- temp_args += [i]
- for i in temp_args:
- if isinstance(i, mparser.ElementaryNode) and not isinstance(i, mparser.IdNode):
+ args = self.flatten_args(l, include_unknown_args, id_loop_detect) + self.flatten_args(r, include_unknown_args, id_loop_detect)
+
+ elif isinstance(args, MethodNode):
+ src = quick_resolve(args.source_object)
+ margs = self.flatten_args(args.args, include_unknown_args, id_loop_detect)
+ try:
+ if isinstance(src, str):
+ args = [self.string_method_call(src, args.name, margs)]
+ elif isinstance(src, bool):
+ args = [self.bool_method_call(src, args.name, margs)]
+ elif isinstance(src, int):
+ args = [self.int_method_call(src, args.name, margs)]
+ elif isinstance(src, list):
+ args = [self.array_method_call(src, args.name, margs)]
+ elif isinstance(src, dict):
+ args = [self.dict_method_call(src, args.name, margs)]
+ else:
+ return []
+ except mesonlib.MesonException:
+ return []
+
+ # Make sure we are always dealing with lists
+ if not isinstance(args, list):
+ args = [args]
+
+ # Resolve the contents of args
+ for i in args:
+ if isinstance(i, IdNode) and i.value not in id_loop_detect:
+ flattend_args += self.flatten_args(quick_resolve(i), include_unknown_args, id_loop_detect + [i.value])
+ elif isinstance(i, (ArrayNode, ArgumentNode, ArithmeticNode, MethodNode)):
+ flattend_args += self.flatten_args(i, include_unknown_args, id_loop_detect)
+ elif isinstance(i, mparser.ElementaryNode):
flattend_args += [i.value]
- elif isinstance(i, (str, bool, int, float)) or include_unknown_args:
+ elif isinstance(i, (str, bool, int, float)):
+ flattend_args += [i]
+ elif include_unknown_args:
flattend_args += [i]
return flattend_args
def flatten_kwargs(self, kwargs: object, include_unknown_args: bool = False):
flattend_kwargs = {}
for key, val in kwargs.items():
- if isinstance(val, mparser.ElementaryNode):
+ if isinstance(val, ElementaryNode):
flattend_kwargs[key] = val.value
- elif isinstance(val, (mparser.ArrayNode, mparser.ArgumentNode)):
+ elif isinstance(val, (ArrayNode, ArgumentNode)):
flattend_kwargs[key] = self.flatten_args(val, include_unknown_args)
elif isinstance(val, (str, bool, int, float)) or include_unknown_args:
flattend_kwargs[key] = val
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index f8fb2e8..49d531f 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -182,11 +182,12 @@ class IntrospectionInterpreter(AstInterpreter):
srcqueue += [curr.left, curr.right]
if arg_node is None:
continue
- elemetary_nodes = list(filter(lambda x: isinstance(x, (str, StringNode)), arg_node.arguments))
- srcqueue += list(filter(lambda x: isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode)), arg_node.arguments))
+ arg_nodes = arg_node.arguments.copy()
# Pop the first element if the function is a build target function
if isinstance(curr, FunctionNode) and curr.func_name in build_target_functions:
- elemetary_nodes.pop(0)
+ arg_nodes.pop(0)
+ elemetary_nodes = [x for x in arg_nodes if isinstance(x, (str, StringNode))]
+ srcqueue += [x for x in arg_nodes if isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode))]
if elemetary_nodes:
source_nodes += [curr]
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index b107a25..8ca7758 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -4076,24 +4076,6 @@ This will become a hard error in the future.''', location=self.current_node)
if not os.path.isfile(fname):
raise InterpreterException('Tried to add non-existing source file %s.' % s)
- def format_string(self, templ, args):
- if isinstance(args, mparser.ArgumentNode):
- args = args.arguments
- arg_strings = []
- for arg in args:
- arg = self.evaluate_statement(arg)
- if isinstance(arg, bool): # Python boolean is upper case.
- arg = str(arg).lower()
- arg_strings.append(str(arg))
-
- def arg_replace(match):
- idx = int(match.group(1))
- if idx >= len(arg_strings):
- raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx))
- return arg_strings[idx]
-
- return re.sub(r'@(\d+)@', arg_replace, templ)
-
# Only permit object extraction from the same subproject
def validate_extraction(self, buildtarget):
if not self.subdir.startswith(self.subproject_dir):
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 650d1e0..c148cbd 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -920,6 +920,24 @@ The result of this is undefined and will become a hard error in a future Meson r
return mesonlib.version_compare(obj, cmpr)
raise InterpreterException('Unknown method "%s" for a string.' % method_name)
+ def format_string(self, templ, args):
+ if isinstance(args, mparser.ArgumentNode):
+ args = args.arguments
+ arg_strings = []
+ for arg in args:
+ arg = self.evaluate_statement(arg)
+ if isinstance(arg, bool): # Python boolean is upper case.
+ arg = str(arg).lower()
+ arg_strings.append(str(arg))
+
+ def arg_replace(match):
+ idx = int(match.group(1))
+ if idx >= len(arg_strings):
+ raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx))
+ return arg_strings[idx]
+
+ return re.sub(r'@(\d+)@', arg_replace, templ)
+
def unknown_function_called(self, func_name):
raise InvalidCode('Unknown function "%s".' % func_name)