|
|
|
@ -28,6 +28,7 @@ from mesonbuild.mesonlib import MesonException |
|
|
|
|
from . import mlog, mparser, environment |
|
|
|
|
from functools import wraps |
|
|
|
|
from pprint import pprint |
|
|
|
|
from .mparser import Token, ArrayNode, ArgumentNode, AssignmentNode, IdNode, FunctionNode, StringNode |
|
|
|
|
import json, os |
|
|
|
|
|
|
|
|
|
class RewriterException(MesonException): |
|
|
|
@ -251,8 +252,10 @@ rewriter_keys = { |
|
|
|
|
}, |
|
|
|
|
'target': { |
|
|
|
|
'target': (str, None, None), |
|
|
|
|
'operation': (str, None, ['src_add', 'src_rm', 'info']), |
|
|
|
|
'operation': (str, None, ['src_add', 'src_rm', 'tgt_rm', 'tgt_add', 'info']), |
|
|
|
|
'sources': (list, [], None), |
|
|
|
|
'subdir': (str, '', None), |
|
|
|
|
'target_type': (str, 'executable', ['both_libraries', 'executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library']), |
|
|
|
|
'debug': (bool, False, None) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -292,9 +295,10 @@ rewriter_func_kwargs = { |
|
|
|
|
class Rewriter: |
|
|
|
|
def __init__(self, sourcedir: str, generator: str = 'ninja'): |
|
|
|
|
self.sourcedir = sourcedir |
|
|
|
|
self.interpreter = IntrospectionInterpreter(sourcedir, '', generator) |
|
|
|
|
self.id_generator = AstIDGenerator() |
|
|
|
|
self.interpreter = IntrospectionInterpreter(sourcedir, '', generator, visitors = [AstIDGenerator(), AstIndentationGenerator()]) |
|
|
|
|
self.modefied_nodes = [] |
|
|
|
|
self.to_remove_nodes = [] |
|
|
|
|
self.to_add_nodes = [] |
|
|
|
|
self.functions = { |
|
|
|
|
'kwargs': self.process_kwargs, |
|
|
|
|
'target': self.process_target, |
|
|
|
@ -306,8 +310,6 @@ class Rewriter: |
|
|
|
|
self.interpreter.analyze() |
|
|
|
|
mlog.log(' -- Project:', mlog.bold(self.interpreter.project_data['descriptive_name'])) |
|
|
|
|
mlog.log(' -- Version:', mlog.cyan(self.interpreter.project_data['version'])) |
|
|
|
|
self.interpreter.ast.accept(AstIndentationGenerator()) |
|
|
|
|
self.interpreter.ast.accept(self.id_generator) |
|
|
|
|
|
|
|
|
|
def add_info(self, cmd_type: str, cmd_id: str, data: dict): |
|
|
|
|
if self.info_dump is None: |
|
|
|
@ -456,11 +458,16 @@ class Rewriter: |
|
|
|
|
if num_changed > 0 and node not in self.modefied_nodes: |
|
|
|
|
self.modefied_nodes += [node] |
|
|
|
|
|
|
|
|
|
def find_assignment_node(self, node: mparser) -> AssignmentNode: |
|
|
|
|
if hasattr(node, 'ast_id') and node.ast_id in self.interpreter.reverse_assignment: |
|
|
|
|
return self.interpreter.reverse_assignment[node.ast_id] |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
@RequiredKeys(rewriter_keys['target']) |
|
|
|
|
def process_target(self, cmd): |
|
|
|
|
mlog.log('Processing target', mlog.bold(cmd['target']), 'operation', mlog.cyan(cmd['operation'])) |
|
|
|
|
target = self.find_target(cmd['target']) |
|
|
|
|
if target is None: |
|
|
|
|
if target is None and cmd['operation'] != 'tgt_add': |
|
|
|
|
mlog.error('Unknown target "{}" --> skipping'.format(cmd['target'])) |
|
|
|
|
if cmd['debug']: |
|
|
|
|
pprint(self.interpreter.targets) |
|
|
|
@ -471,13 +478,13 @@ class Rewriter: |
|
|
|
|
# Utility function to get a list of the sources from a node |
|
|
|
|
def arg_list_from_node(n): |
|
|
|
|
args = [] |
|
|
|
|
if isinstance(n, mparser.FunctionNode): |
|
|
|
|
if isinstance(n, FunctionNode): |
|
|
|
|
args = list(n.args.arguments) |
|
|
|
|
if n.func_name in build_target_functions: |
|
|
|
|
args.pop(0) |
|
|
|
|
elif isinstance(n, mparser.ArrayNode): |
|
|
|
|
elif isinstance(n, ArrayNode): |
|
|
|
|
args = n.args.arguments |
|
|
|
|
elif isinstance(n, mparser.ArgumentNode): |
|
|
|
|
elif isinstance(n, ArgumentNode): |
|
|
|
|
args = n.arguments |
|
|
|
|
return args |
|
|
|
|
|
|
|
|
@ -494,15 +501,15 @@ class Rewriter: |
|
|
|
|
for i in cmd['sources']: |
|
|
|
|
mlog.log(' -- Adding source', mlog.green(i), 'at', |
|
|
|
|
mlog.yellow('{}:{}'.format(os.path.join(node.subdir, environment.build_filename), node.lineno))) |
|
|
|
|
token = mparser.Token('string', node.subdir, 0, 0, 0, None, i) |
|
|
|
|
to_append += [mparser.StringNode(token)] |
|
|
|
|
token = Token('string', node.subdir, 0, 0, 0, None, i) |
|
|
|
|
to_append += [StringNode(token)] |
|
|
|
|
|
|
|
|
|
# Append to the AST at the right place |
|
|
|
|
if isinstance(node, mparser.FunctionNode): |
|
|
|
|
if isinstance(node, FunctionNode): |
|
|
|
|
node.args.arguments += to_append |
|
|
|
|
elif isinstance(node, mparser.ArrayNode): |
|
|
|
|
elif isinstance(node, ArrayNode): |
|
|
|
|
node.args.arguments += to_append |
|
|
|
|
elif isinstance(node, mparser.ArgumentNode): |
|
|
|
|
elif isinstance(node, ArgumentNode): |
|
|
|
|
node.arguments += to_append |
|
|
|
|
|
|
|
|
|
# Mark the node as modified |
|
|
|
@ -514,7 +521,7 @@ class Rewriter: |
|
|
|
|
def find_node(src): |
|
|
|
|
for i in target['sources']: |
|
|
|
|
for j in arg_list_from_node(i): |
|
|
|
|
if isinstance(j, mparser.StringNode): |
|
|
|
|
if isinstance(j, StringNode): |
|
|
|
|
if j.value == src: |
|
|
|
|
return i, j |
|
|
|
|
return None, None |
|
|
|
@ -528,11 +535,11 @@ class Rewriter: |
|
|
|
|
|
|
|
|
|
# Remove the found string node from the argument list |
|
|
|
|
arg_node = None |
|
|
|
|
if isinstance(root, mparser.FunctionNode): |
|
|
|
|
if isinstance(root, FunctionNode): |
|
|
|
|
arg_node = root.args |
|
|
|
|
if isinstance(root, mparser.ArrayNode): |
|
|
|
|
if isinstance(root, ArrayNode): |
|
|
|
|
arg_node = root.args |
|
|
|
|
if isinstance(root, mparser.ArgumentNode): |
|
|
|
|
if isinstance(root, ArgumentNode): |
|
|
|
|
arg_node = root |
|
|
|
|
assert(arg_node is not None) |
|
|
|
|
mlog.log(' -- Removing source', mlog.green(i), 'from', |
|
|
|
@ -543,12 +550,47 @@ class Rewriter: |
|
|
|
|
if root not in self.modefied_nodes: |
|
|
|
|
self.modefied_nodes += [root] |
|
|
|
|
|
|
|
|
|
elif cmd['operation'] == 'tgt_add': |
|
|
|
|
if target is not None: |
|
|
|
|
mlog.error('Can not add target', mlog.bold(cmd['target']), 'because it already exists') |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
# Build src list |
|
|
|
|
src_arg_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, '')) |
|
|
|
|
src_arr_node = ArrayNode(src_arg_node, 0, 0) |
|
|
|
|
src_far_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, '')) |
|
|
|
|
src_fun_node = FunctionNode(cmd['subdir'], 0, 0, 'files', src_far_node) |
|
|
|
|
src_ass_node = AssignmentNode(cmd['subdir'], 0, 0, '{}_src'.format(cmd['target']), src_fun_node) |
|
|
|
|
src_arg_node.arguments = [StringNode(Token('string', cmd['subdir'], 0, 0, 0, None, x)) for x in cmd['sources']] |
|
|
|
|
src_far_node.arguments = [src_arr_node] |
|
|
|
|
|
|
|
|
|
# Build target |
|
|
|
|
tgt_arg_node = ArgumentNode(Token('string', cmd['subdir'], 0, 0, 0, None, '')) |
|
|
|
|
tgt_fun_node = FunctionNode(cmd['subdir'], 0, 0, cmd['target_type'], tgt_arg_node) |
|
|
|
|
tgt_ass_node = AssignmentNode(cmd['subdir'], 0, 0, '{}_tgt'.format(cmd['target']), tgt_fun_node) |
|
|
|
|
tgt_arg_node.arguments = [ |
|
|
|
|
StringNode(Token('string', cmd['subdir'], 0, 0, 0, None, cmd['target'])), |
|
|
|
|
IdNode(Token('string', cmd['subdir'], 0, 0, 0, None, '{}_src'.format(cmd['target']))) |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
src_ass_node.accept(AstIndentationGenerator()) |
|
|
|
|
tgt_ass_node.accept(AstIndentationGenerator()) |
|
|
|
|
self.to_add_nodes += [src_ass_node, tgt_ass_node] |
|
|
|
|
|
|
|
|
|
elif cmd['operation'] == 'tgt_rm': |
|
|
|
|
to_remove = self.find_assignment_node(target['node']) |
|
|
|
|
if to_remove is None: |
|
|
|
|
to_remove = target['node'] |
|
|
|
|
self.to_remove_nodes += [to_remove] |
|
|
|
|
mlog.log(' -- Removing target', mlog.green(cmd['target']), 'at', |
|
|
|
|
mlog.yellow('{}:{}'.format(os.path.join(to_remove.subdir, environment.build_filename), to_remove.lineno))) |
|
|
|
|
|
|
|
|
|
elif cmd['operation'] == 'info': |
|
|
|
|
# List all sources in the target |
|
|
|
|
src_list = [] |
|
|
|
|
for i in target['sources']: |
|
|
|
|
for j in arg_list_from_node(i): |
|
|
|
|
if isinstance(j, mparser.StringNode): |
|
|
|
|
if isinstance(j, StringNode): |
|
|
|
|
src_list += [j.value] |
|
|
|
|
test_data = { |
|
|
|
|
'name': target['name'], |
|
|
|
@ -566,20 +608,29 @@ class Rewriter: |
|
|
|
|
|
|
|
|
|
def apply_changes(self): |
|
|
|
|
assert(all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'subdir') for x in self.modefied_nodes)) |
|
|
|
|
assert(all(isinstance(x, (mparser.ArrayNode, mparser.FunctionNode)) for x in self.modefied_nodes)) |
|
|
|
|
assert(all(hasattr(x, 'lineno') and hasattr(x, 'colno') and hasattr(x, 'subdir') for x in self.to_remove_nodes)) |
|
|
|
|
assert(all(isinstance(x, (ArrayNode, FunctionNode)) for x in self.modefied_nodes)) |
|
|
|
|
assert(all(isinstance(x, (ArrayNode, AssignmentNode, FunctionNode)) for x in self.to_remove_nodes)) |
|
|
|
|
# Sort based on line and column in reversed order |
|
|
|
|
work_nodes = list(sorted(self.modefied_nodes, key=lambda x: x.lineno * 1000 + x.colno, reverse=True)) |
|
|
|
|
work_nodes = [{'node': x, 'action': 'modify'} for x in self.modefied_nodes] |
|
|
|
|
work_nodes += [{'node': x, 'action': 'rm'} for x in self.to_remove_nodes] |
|
|
|
|
work_nodes = list(sorted(work_nodes, key=lambda x: x['node'].lineno * 1000 + x['node'].colno, reverse=True)) |
|
|
|
|
work_nodes += [{'node': x, 'action': 'add'} for x in self.to_add_nodes] |
|
|
|
|
|
|
|
|
|
# Generating the new replacement string |
|
|
|
|
str_list = [] |
|
|
|
|
for i in work_nodes: |
|
|
|
|
new_data = '' |
|
|
|
|
if i['action'] == 'modify' or i['action'] == 'add': |
|
|
|
|
printer = AstPrinter() |
|
|
|
|
i.accept(printer) |
|
|
|
|
i['node'].accept(printer) |
|
|
|
|
printer.post_process() |
|
|
|
|
new_data = printer.result.strip() |
|
|
|
|
data = { |
|
|
|
|
'file': os.path.join(i.subdir, environment.build_filename), |
|
|
|
|
'str': printer.result.strip(), |
|
|
|
|
'node': i |
|
|
|
|
'file': os.path.join(i['node'].subdir, environment.build_filename), |
|
|
|
|
'str': new_data, |
|
|
|
|
'node': i['node'], |
|
|
|
|
'action': i['action'] |
|
|
|
|
} |
|
|
|
|
str_list += [data] |
|
|
|
|
|
|
|
|
@ -590,6 +641,10 @@ class Rewriter: |
|
|
|
|
continue |
|
|
|
|
fpath = os.path.realpath(os.path.join(self.sourcedir, i['file'])) |
|
|
|
|
fdata = '' |
|
|
|
|
# Create an empty file if it does not exist |
|
|
|
|
if not os.path.exists(fpath): |
|
|
|
|
with open(fpath, 'w'): |
|
|
|
|
pass |
|
|
|
|
with open(fpath, 'r') as fp: |
|
|
|
|
fdata = fp.read() |
|
|
|
|
|
|
|
|
@ -608,7 +663,7 @@ class Rewriter: |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
# Replace in source code |
|
|
|
|
for i in str_list: |
|
|
|
|
def remove_node(i): |
|
|
|
|
offsets = files[i['file']]['offsets'] |
|
|
|
|
raw = files[i['file']]['raw'] |
|
|
|
|
node = i['node'] |
|
|
|
@ -616,10 +671,10 @@ class Rewriter: |
|
|
|
|
col = node.colno |
|
|
|
|
start = offsets[line] + col |
|
|
|
|
end = start |
|
|
|
|
if isinstance(node, mparser.ArrayNode): |
|
|
|
|
if isinstance(node, ArrayNode): |
|
|
|
|
if raw[end] != '[': |
|
|
|
|
mlog.warning('Internal error: expected "[" at {}:{} but got "{}"'.format(line, col, raw[end])) |
|
|
|
|
continue |
|
|
|
|
return |
|
|
|
|
counter = 1 |
|
|
|
|
while counter > 0: |
|
|
|
|
end += 1 |
|
|
|
@ -628,7 +683,8 @@ class Rewriter: |
|
|
|
|
elif raw[end] == ']': |
|
|
|
|
counter -= 1 |
|
|
|
|
end += 1 |
|
|
|
|
elif isinstance(node, mparser.FunctionNode): |
|
|
|
|
|
|
|
|
|
elif isinstance(node, FunctionNode): |
|
|
|
|
while raw[end] != '(': |
|
|
|
|
end += 1 |
|
|
|
|
end += 1 |
|
|
|
@ -640,8 +696,26 @@ class Rewriter: |
|
|
|
|
elif raw[end] == ')': |
|
|
|
|
counter -= 1 |
|
|
|
|
end += 1 |
|
|
|
|
|
|
|
|
|
# Only removal is supported for assignments |
|
|
|
|
elif isinstance(node, AssignmentNode) and i['action'] == 'rm': |
|
|
|
|
if isinstance(node.value, (ArrayNode, FunctionNode)): |
|
|
|
|
remove_node({'file': i['file'], 'str': '', 'node': node.value, 'action': 'rm'}) |
|
|
|
|
raw = files[i['file']]['raw'] |
|
|
|
|
while raw[end] != '=': |
|
|
|
|
end += 1 |
|
|
|
|
end += 1 # Handle the '=' |
|
|
|
|
while raw[end] in [' ', '\n', '\t']: |
|
|
|
|
end += 1 |
|
|
|
|
|
|
|
|
|
raw = files[i['file']]['raw'] = raw[:start] + i['str'] + raw[end:] |
|
|
|
|
|
|
|
|
|
for i in str_list: |
|
|
|
|
if i['action'] in ['modify', 'rm']: |
|
|
|
|
remove_node(i) |
|
|
|
|
elif i['action'] in ['add']: |
|
|
|
|
files[i['file']]['raw'] += i['str'] + '\n' |
|
|
|
|
|
|
|
|
|
# Write the files back |
|
|
|
|
for key, val in files.items(): |
|
|
|
|
mlog.log('Rewriting', mlog.yellow(key)) |
|
|
|
|