The Meson Build System http://mesonbuild.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
9.8 KiB

# Copyright 2016 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.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This class contains the basic functionality needed to run any interpreter
# or an interpreter-based tool.
from .visitor import AstVisitor
from .. import interpreterbase, mparser, mesonlib
from .. import environment
from ..interpreterbase import InvalidArguments, BreakRequest, ContinueRequest
import os, sys
from typing import List
class DontCareObject(interpreterbase.InterpreterObject):
pass
class MockExecutable(interpreterbase.InterpreterObject):
pass
class MockStaticLibrary(interpreterbase.InterpreterObject):
pass
class MockSharedLibrary(interpreterbase.InterpreterObject):
pass
class MockCustomTarget(interpreterbase.InterpreterObject):
pass
class MockRunTarget(interpreterbase.InterpreterObject):
pass
ADD_SOURCE = 0
REMOVE_SOURCE = 1
class AstInterpreter(interpreterbase.InterpreterBase):
def __init__(self, source_root: str, subdir: str, visitors: List[AstVisitor] = []):
super().__init__(source_root, subdir)
self.visitors = visitors
self.visited_subdirs = {}
self.assignments = {}
self.reverse_assignment = {}
self.funcs.update({'project': self.func_do_nothing,
'test': self.func_do_nothing,
'benchmark': self.func_do_nothing,
'install_headers': self.func_do_nothing,
'install_man': self.func_do_nothing,
'install_data': self.func_do_nothing,
'install_subdir': self.func_do_nothing,
'configuration_data': self.func_do_nothing,
'configure_file': self.func_do_nothing,
'find_program': self.func_do_nothing,
'include_directories': self.func_do_nothing,
'add_global_arguments': self.func_do_nothing,
'add_global_link_arguments': self.func_do_nothing,
'add_project_arguments': self.func_do_nothing,
'add_project_link_arguments': self.func_do_nothing,
'message': self.func_do_nothing,
'generator': self.func_do_nothing,
'error': self.func_do_nothing,
'run_command': self.func_do_nothing,
'assert': self.func_do_nothing,
'subproject': self.func_do_nothing,
'dependency': self.func_do_nothing,
'get_option': self.func_do_nothing,
'join_paths': self.func_do_nothing,
'environment': self.func_do_nothing,
'import': self.func_do_nothing,
'vcs_tag': self.func_do_nothing,
'add_languages': self.func_do_nothing,
'declare_dependency': self.func_do_nothing,
'files': self.func_do_nothing,
'executable': self.func_do_nothing,
'static_library': self.func_do_nothing,
'shared_library': self.func_do_nothing,
'library': self.func_do_nothing,
'build_target': self.func_do_nothing,
'custom_target': self.func_do_nothing,
'run_target': self.func_do_nothing,
'subdir': self.func_subdir,
'set_variable': self.func_do_nothing,
'get_variable': self.func_do_nothing,
'is_variable': self.func_do_nothing,
'disabler': self.func_do_nothing,
'gettext': self.func_do_nothing,
'jar': self.func_do_nothing,
'warning': self.func_do_nothing,
'shared_module': self.func_do_nothing,
'option': self.func_do_nothing,
'both_libraries': self.func_do_nothing,
'add_test_setup': self.func_do_nothing,
'find_library': self.func_do_nothing,
'subdir_done': self.func_do_nothing,
})
def func_do_nothing(self, node, args, kwargs):
return True
def load_root_meson_file(self):
super().load_root_meson_file()
for i in self.visitors:
self.ast.accept(i)
def func_subdir(self, node, args, kwargs):
args = self.flatten_args(args)
if len(args) != 1 or not isinstance(args[0], str):
sys.stderr.write('Unable to evaluate subdir({}) in AstInterpreter --> Skipping\n'.format(args))
return
prev_subdir = self.subdir
subdir = os.path.join(prev_subdir, args[0])
absdir = os.path.join(self.source_root, subdir)
buildfilename = os.path.join(subdir, environment.build_filename)
absname = os.path.join(self.source_root, buildfilename)
symlinkless_dir = os.path.realpath(absdir)
if symlinkless_dir in self.visited_subdirs:
sys.stderr.write('Trying to enter {} which has already been visited --> Skipping\n'.format(args[0]))
return
self.visited_subdirs[symlinkless_dir] = True
if not os.path.isfile(absname):
sys.stderr.write('Unable to find build file {} --> Skipping\n'.format(buildfilename))
return
with open(absname, encoding='utf8') as f:
code = f.read()
assert(isinstance(code, str))
try:
codeblock = mparser.Parser(code, subdir).parse()
except mesonlib.MesonException as me:
me.file = buildfilename
raise me
self.subdir = subdir
for i in self.visitors:
codeblock.accept(i)
self.evaluate_codeblock(codeblock)
self.subdir = prev_subdir
def method_call(self, node):
return True
def evaluate_arithmeticstatement(self, cur):
return 0
def evaluate_plusassign(self, node):
assert(isinstance(node, mparser.PlusAssignmentNode))
if node.var_name not in self.assignments:
self.assignments[node.var_name] = []
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.evaluate_statement(node.value) # Evaluate the value just in case
def evaluate_indexing(self, node):
return 0
def unknown_function_called(self, func_name):
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
def evaluate_comparison(self, node):
return False
def evaluate_foreach(self, node):
try:
self.evaluate_codeblock(node.block)
except ContinueRequest:
pass
except BreakRequest:
pass
def evaluate_if(self, node):
for i in node.ifs:
self.evaluate_codeblock(i.block)
if not isinstance(node.elseblock, mparser.EmptyNode):
self.evaluate_codeblock(node.elseblock)
def get_variable(self, varname):
return 0
def assignment(self, node):
assert(isinstance(node, mparser.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.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
flattend_args = []
temp_args = []
if isinstance(args, mparser.ArrayNode):
args = [x for x in args.args.arguments]
elif isinstance(args, mparser.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]
else:
temp_args += [i]
for i in temp_args:
if isinstance(i, mparser.ElementaryNode) and not isinstance(i, mparser.IdNode):
flattend_args += [i.value]
elif isinstance(i, (str, bool, int, float)) or 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):
flattend_kwargs[key] = val.value
elif isinstance(val, (mparser.ArrayNode, mparser.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
return flattend_kwargs