|
|
|
@ -71,15 +71,224 @@ class RequiredKeys: |
|
|
|
|
|
|
|
|
|
return wrapped |
|
|
|
|
|
|
|
|
|
class MTypeBase: |
|
|
|
|
def __init__(self, node: mparser.BaseNode): |
|
|
|
|
if node is None: |
|
|
|
|
self.node = self._new_node() |
|
|
|
|
else: |
|
|
|
|
self.node = node |
|
|
|
|
self.node_type = None |
|
|
|
|
for i in self.supported_nodes(): |
|
|
|
|
if isinstance(self.node, i): |
|
|
|
|
self.node_type = i |
|
|
|
|
|
|
|
|
|
def _new_node(self): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
return mparser.BaseNode() |
|
|
|
|
|
|
|
|
|
def can_modify(self): |
|
|
|
|
return self.node_type is not None |
|
|
|
|
|
|
|
|
|
def get_node(self): |
|
|
|
|
return self.node |
|
|
|
|
|
|
|
|
|
def supported_nodes(self): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
def set_value(self, value): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
mlog.warning('Cannot set the value of type', mlog.bold(type(self).__name__), '--> skipping') |
|
|
|
|
|
|
|
|
|
def add_value(self, value): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
mlog.warning('Cannot add a value of type', mlog.bold(type(self).__name__), '--> skipping') |
|
|
|
|
|
|
|
|
|
def remove_value(self, value): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
mlog.warning('Cannot remove a value of type', mlog.bold(type(self).__name__), '--> skipping') |
|
|
|
|
|
|
|
|
|
class MTypeStr(MTypeBase): |
|
|
|
|
def __init__(self, node: mparser.BaseNode): |
|
|
|
|
super().__init__(node) |
|
|
|
|
|
|
|
|
|
def _new_node(self): |
|
|
|
|
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, '')) |
|
|
|
|
|
|
|
|
|
def supported_nodes(self): |
|
|
|
|
return [mparser.StringNode] |
|
|
|
|
|
|
|
|
|
def set_value(self, value): |
|
|
|
|
self.node.value = str(value) |
|
|
|
|
|
|
|
|
|
class MTypeBool(MTypeBase): |
|
|
|
|
def __init__(self, node: mparser.BaseNode): |
|
|
|
|
super().__init__(node) |
|
|
|
|
|
|
|
|
|
def _new_node(self): |
|
|
|
|
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, False)) |
|
|
|
|
|
|
|
|
|
def supported_nodes(self): |
|
|
|
|
return [mparser.BooleanNode] |
|
|
|
|
|
|
|
|
|
def set_value(self, value): |
|
|
|
|
self.node.value = bool(value) |
|
|
|
|
|
|
|
|
|
class MTypeID(MTypeBase): |
|
|
|
|
def __init__(self, node: mparser.BaseNode): |
|
|
|
|
super().__init__(node) |
|
|
|
|
|
|
|
|
|
def _new_node(self): |
|
|
|
|
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, '')) |
|
|
|
|
|
|
|
|
|
def supported_nodes(self): |
|
|
|
|
return [mparser.IdNode] |
|
|
|
|
|
|
|
|
|
def set_value(self, value): |
|
|
|
|
self.node.value = str(value) |
|
|
|
|
|
|
|
|
|
class MTypeList(MTypeBase): |
|
|
|
|
def __init__(self, node: mparser.BaseNode): |
|
|
|
|
super().__init__(node) |
|
|
|
|
|
|
|
|
|
def _new_node(self): |
|
|
|
|
return mparser.ArrayNode(mparser.ArgumentNode(mparser.Token('', '', 0, 0, 0, None, '')), 0, 0) |
|
|
|
|
|
|
|
|
|
def _new_element_node(self, value): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
return mparser.BaseNode() |
|
|
|
|
|
|
|
|
|
def _ensure_array_node(self): |
|
|
|
|
if not isinstance(self.node, mparser.ArrayNode): |
|
|
|
|
tmp = self.node |
|
|
|
|
self.node = self._new_node() |
|
|
|
|
self.node.args.arguments += [tmp] |
|
|
|
|
|
|
|
|
|
def _check_is_equal(self, node, value): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def get_node(self): |
|
|
|
|
if isinstance(self.node, mparser.ArrayNode): |
|
|
|
|
if len(self.node.args.arguments) == 1: |
|
|
|
|
return self.node.args.arguments[0] |
|
|
|
|
return self.node |
|
|
|
|
|
|
|
|
|
def supported_element_nodes(self): |
|
|
|
|
# Overwrite in derived class |
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
def supported_nodes(self): |
|
|
|
|
return [mparser.ArrayNode] + self.supported_element_nodes() |
|
|
|
|
|
|
|
|
|
def set_value(self, value): |
|
|
|
|
if not isinstance(value, list): |
|
|
|
|
value = [value] |
|
|
|
|
self._ensure_array_node() |
|
|
|
|
self.node.args.arguments = [] # Remove all current nodes |
|
|
|
|
for i in value: |
|
|
|
|
self.node.args.arguments += [self._new_element_node(i)] |
|
|
|
|
|
|
|
|
|
def add_value(self, value): |
|
|
|
|
if not isinstance(value, list): |
|
|
|
|
value = [value] |
|
|
|
|
self._ensure_array_node() |
|
|
|
|
for i in value: |
|
|
|
|
self.node.args.arguments += [self._new_element_node(i)] |
|
|
|
|
|
|
|
|
|
def remove_value(self, value): |
|
|
|
|
def check_remove_node(node): |
|
|
|
|
for j in value: |
|
|
|
|
if self._check_is_equal(i, j): |
|
|
|
|
return True |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
if not isinstance(value, list): |
|
|
|
|
value = [value] |
|
|
|
|
self._ensure_array_node() |
|
|
|
|
removed_list = [] |
|
|
|
|
for i in self.node.args.arguments: |
|
|
|
|
if not check_remove_node(i): |
|
|
|
|
removed_list += [i] |
|
|
|
|
self.node.args.arguments = removed_list |
|
|
|
|
|
|
|
|
|
class MtypeStrList(MTypeList): |
|
|
|
|
def __init__(self, node: mparser.BaseNode): |
|
|
|
|
super().__init__(node) |
|
|
|
|
|
|
|
|
|
def _new_element_node(self, value): |
|
|
|
|
return mparser.StringNode(mparser.Token('', '', 0, 0, 0, None, str(value))) |
|
|
|
|
|
|
|
|
|
def _check_is_equal(self, node, value): |
|
|
|
|
if isinstance(node, mparser.StringNode): |
|
|
|
|
return node.value == value |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def supported_element_nodes(self): |
|
|
|
|
return [mparser.StringNode] |
|
|
|
|
|
|
|
|
|
class MTypeIDList(MTypeList): |
|
|
|
|
def __init__(self, node: mparser.BaseNode): |
|
|
|
|
super().__init__(node) |
|
|
|
|
|
|
|
|
|
def _new_element_node(self, value): |
|
|
|
|
return mparser.IdNode(mparser.Token('', '', 0, 0, 0, None, str(value))) |
|
|
|
|
|
|
|
|
|
def _check_is_equal(self, node, value): |
|
|
|
|
if isinstance(node, mparser.IdNode): |
|
|
|
|
return node.value == value |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def supported_element_nodes(self): |
|
|
|
|
return [mparser.IdNode] |
|
|
|
|
|
|
|
|
|
rewriter_keys = { |
|
|
|
|
'kwargs': { |
|
|
|
|
'function': (str, None, None), |
|
|
|
|
'id': (str, None, None), |
|
|
|
|
'operation': (str, None, ['set', 'delete', 'add', 'remove', 'info']), |
|
|
|
|
'kwargs': (dict, {}, None) |
|
|
|
|
}, |
|
|
|
|
'target': { |
|
|
|
|
'target': (str, None, None), |
|
|
|
|
'operation': (str, None, ['src_add', 'src_rm', 'test']), |
|
|
|
|
'operation': (str, None, ['src_add', 'src_rm', 'info']), |
|
|
|
|
'sources': (list, [], None), |
|
|
|
|
'debug': (bool, False, None) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rewriter_func_kwargs = { |
|
|
|
|
'dependency': { |
|
|
|
|
'language': MTypeStr, |
|
|
|
|
'method': MTypeStr, |
|
|
|
|
'native': MTypeBool, |
|
|
|
|
'not_found_message': MTypeStr, |
|
|
|
|
'required': MTypeBool, |
|
|
|
|
'static': MTypeBool, |
|
|
|
|
'version': MtypeStrList, |
|
|
|
|
'modules': MtypeStrList |
|
|
|
|
}, |
|
|
|
|
'target': { |
|
|
|
|
'build_by_default': MTypeBool, |
|
|
|
|
'build_rpath': MTypeStr, |
|
|
|
|
'dependencies': MTypeIDList, |
|
|
|
|
'gui_app': MTypeBool, |
|
|
|
|
'link_with': MTypeIDList, |
|
|
|
|
'export_dynamic': MTypeBool, |
|
|
|
|
'implib': MTypeBool, |
|
|
|
|
'install': MTypeBool, |
|
|
|
|
'install_dir': MTypeStr, |
|
|
|
|
'install_rpath': MTypeStr, |
|
|
|
|
'pie': MTypeBool |
|
|
|
|
}, |
|
|
|
|
'project': { |
|
|
|
|
'meson_version': MTypeStr, |
|
|
|
|
'license': MtypeStrList, |
|
|
|
|
'subproject_dir': MTypeStr, |
|
|
|
|
'version': MTypeStr |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class Rewriter: |
|
|
|
|
def __init__(self, sourcedir: str, generator: str = 'ninja'): |
|
|
|
|
self.sourcedir = sourcedir |
|
|
|
@ -87,8 +296,10 @@ class Rewriter: |
|
|
|
|
self.id_generator = AstIDGenerator() |
|
|
|
|
self.modefied_nodes = [] |
|
|
|
|
self.functions = { |
|
|
|
|
'kwargs': self.process_kwargs, |
|
|
|
|
'target': self.process_target, |
|
|
|
|
} |
|
|
|
|
self.info_dump = None |
|
|
|
|
|
|
|
|
|
def analyze_meson(self): |
|
|
|
|
mlog.log('Analyzing meson file:', mlog.bold(os.path.join(self.sourcedir, environment.build_filename))) |
|
|
|
@ -98,11 +309,152 @@ class Rewriter: |
|
|
|
|
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: |
|
|
|
|
self.info_dump = {} |
|
|
|
|
if cmd_type not in self.info_dump: |
|
|
|
|
self.info_dump[cmd_type] = {} |
|
|
|
|
self.info_dump[cmd_type][cmd_id] = data |
|
|
|
|
|
|
|
|
|
def print_info(self): |
|
|
|
|
if self.info_dump is None: |
|
|
|
|
return |
|
|
|
|
# Wrap the dump in magic strings |
|
|
|
|
print('!!==JSON DUMP: BEGIN==!!') |
|
|
|
|
print(json.dumps(self.info_dump, indent=2)) |
|
|
|
|
print('!!==JSON DUMP: END==!!') |
|
|
|
|
|
|
|
|
|
def find_target(self, target: str): |
|
|
|
|
for i in self.interpreter.targets: |
|
|
|
|
if target == i['name'] or target == i['id']: |
|
|
|
|
return i |
|
|
|
|
return None |
|
|
|
|
def check_list(name: str): |
|
|
|
|
for i in self.interpreter.targets: |
|
|
|
|
if name == i['name'] or name == i['id']: |
|
|
|
|
return i |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
tgt = check_list(target) |
|
|
|
|
if tgt is not None: |
|
|
|
|
return tgt |
|
|
|
|
|
|
|
|
|
# Check the assignments |
|
|
|
|
if target in self.interpreter.assignments: |
|
|
|
|
node = self.interpreter.assignments[target][0] |
|
|
|
|
if isinstance(node, mparser.FunctionNode): |
|
|
|
|
if node.func_name in ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries']: |
|
|
|
|
name = self.interpreter.flatten_args(node.args)[0] |
|
|
|
|
tgt = check_list(name) |
|
|
|
|
|
|
|
|
|
return tgt |
|
|
|
|
|
|
|
|
|
def find_dependency(self, dependency: str): |
|
|
|
|
def check_list(name: str): |
|
|
|
|
for i in self.interpreter.dependencies: |
|
|
|
|
if name == i['name']: |
|
|
|
|
return i |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
dep = check_list(dependency) |
|
|
|
|
if dep is not None: |
|
|
|
|
return dep |
|
|
|
|
|
|
|
|
|
# Check the assignments |
|
|
|
|
if dependency in self.interpreter.assignments: |
|
|
|
|
node = self.interpreter.assignments[dependency][0] |
|
|
|
|
if isinstance(node, mparser.FunctionNode): |
|
|
|
|
if node.func_name in ['dependency']: |
|
|
|
|
name = self.interpreter.flatten_args(node.args)[0] |
|
|
|
|
dep = check_list(name) |
|
|
|
|
|
|
|
|
|
return dep |
|
|
|
|
|
|
|
|
|
@RequiredKeys(rewriter_keys['kwargs']) |
|
|
|
|
def process_kwargs(self, cmd): |
|
|
|
|
mlog.log('Processing function type', mlog.bold(cmd['function']), 'with id', mlog.cyan("'" + cmd['id'] + "'")) |
|
|
|
|
if cmd['function'] not in rewriter_func_kwargs: |
|
|
|
|
mlog.error('Unknown function type {} --> skipping'.format(cmd['function'])) |
|
|
|
|
return |
|
|
|
|
kwargs_def = rewriter_func_kwargs[cmd['function']] |
|
|
|
|
|
|
|
|
|
# Find the function node to modify |
|
|
|
|
node = None |
|
|
|
|
arg_node = None |
|
|
|
|
if cmd['function'] == 'project': |
|
|
|
|
node = self.interpreter.project_node |
|
|
|
|
arg_node = node.args |
|
|
|
|
elif cmd['function'] == 'target': |
|
|
|
|
tmp = self.find_target(cmd['id']) |
|
|
|
|
if tmp: |
|
|
|
|
node = tmp['node'] |
|
|
|
|
arg_node = node.args |
|
|
|
|
elif cmd['function'] == 'dependency': |
|
|
|
|
tmp = self.find_dependency(cmd['id']) |
|
|
|
|
if tmp: |
|
|
|
|
node = tmp['node'] |
|
|
|
|
arg_node = node.args |
|
|
|
|
if not node: |
|
|
|
|
mlog.error('Unable to find the function node') |
|
|
|
|
assert(isinstance(node, mparser.FunctionNode)) |
|
|
|
|
assert(isinstance(arg_node, mparser.ArgumentNode)) |
|
|
|
|
|
|
|
|
|
# Print kwargs info |
|
|
|
|
if cmd['operation'] == 'info': |
|
|
|
|
info_data = {} |
|
|
|
|
for key, val in arg_node.kwargs.items(): |
|
|
|
|
info_data[key] = None |
|
|
|
|
if isinstance(val, mparser.ElementaryNode): |
|
|
|
|
info_data[key] = val.value |
|
|
|
|
elif isinstance(val, mparser.ArrayNode): |
|
|
|
|
data_list = [] |
|
|
|
|
for i in val.args.arguments: |
|
|
|
|
element = None |
|
|
|
|
if isinstance(i, mparser.ElementaryNode): |
|
|
|
|
element = i.value |
|
|
|
|
data_list += [element] |
|
|
|
|
info_data[key] = data_list |
|
|
|
|
|
|
|
|
|
self.add_info('kwargs', '{}#{}'.format(cmd['function'], cmd['id']), info_data) |
|
|
|
|
return # Nothing else to do |
|
|
|
|
|
|
|
|
|
# Modify the kwargs |
|
|
|
|
num_changed = 0 |
|
|
|
|
for key, val in cmd['kwargs'].items(): |
|
|
|
|
if key not in kwargs_def: |
|
|
|
|
mlog.error('Cannot modify unknown kwarg --> skipping', mlog.bold(key)) |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
# Remove the key from the kwargs |
|
|
|
|
if cmd['operation'] == 'delete': |
|
|
|
|
if key in arg_node.kwargs: |
|
|
|
|
mlog.log(' -- Deleting', mlog.bold(key), 'from the kwargs') |
|
|
|
|
del arg_node.kwargs[key] |
|
|
|
|
num_changed += 1 |
|
|
|
|
else: |
|
|
|
|
mlog.log(' -- Key', mlog.bold(key), 'is already deleted') |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
if key not in arg_node.kwargs: |
|
|
|
|
arg_node.kwargs[key] = None |
|
|
|
|
modifyer = kwargs_def[key](arg_node.kwargs[key]) |
|
|
|
|
if not modifyer.can_modify(): |
|
|
|
|
mlog.log(' -- Skipping', mlog.bold(key), 'because it is to complex to modify') |
|
|
|
|
|
|
|
|
|
# Apply the operation |
|
|
|
|
val_str = str(val) |
|
|
|
|
if cmd['operation'] == 'set': |
|
|
|
|
mlog.log(' -- Setting', mlog.bold(key), 'to', mlog.yellow(val_str)) |
|
|
|
|
modifyer.set_value(val) |
|
|
|
|
elif cmd['operation'] == 'add': |
|
|
|
|
mlog.log(' -- Adding', mlog.yellow(val_str), 'to', mlog.bold(key)) |
|
|
|
|
modifyer.add_value(val) |
|
|
|
|
elif cmd['operation'] == 'remove': |
|
|
|
|
mlog.log(' -- Removing', mlog.yellow(val_str), 'from', mlog.bold(key)) |
|
|
|
|
modifyer.remove_value(val) |
|
|
|
|
|
|
|
|
|
# Write back the result |
|
|
|
|
arg_node.kwargs[key] = modifyer.get_node() |
|
|
|
|
num_changed += 1 |
|
|
|
|
|
|
|
|
|
if num_changed > 0 and node not in self.modefied_nodes: |
|
|
|
|
self.modefied_nodes += [node] |
|
|
|
|
|
|
|
|
|
@RequiredKeys(rewriter_keys['target']) |
|
|
|
|
def process_target(self, cmd): |
|
|
|
@ -191,7 +543,7 @@ class Rewriter: |
|
|
|
|
if root not in self.modefied_nodes: |
|
|
|
|
self.modefied_nodes += [root] |
|
|
|
|
|
|
|
|
|
elif cmd['operation'] == 'test': |
|
|
|
|
elif cmd['operation'] == 'info': |
|
|
|
|
# List all sources in the target |
|
|
|
|
src_list = [] |
|
|
|
|
for i in target['sources']: |
|
|
|
@ -202,7 +554,7 @@ class Rewriter: |
|
|
|
|
'name': target['name'], |
|
|
|
|
'sources': src_list |
|
|
|
|
} |
|
|
|
|
mlog.log(' !! target {}={}'.format(target['id'], json.dumps(test_data))) |
|
|
|
|
self.add_info('target', target['id'], test_data) |
|
|
|
|
|
|
|
|
|
def process(self, cmd): |
|
|
|
|
if 'type' not in cmd: |
|
|
|
@ -314,4 +666,5 @@ def run(options): |
|
|
|
|
rewriter.process(i) |
|
|
|
|
|
|
|
|
|
rewriter.apply_changes() |
|
|
|
|
rewriter.print_info() |
|
|
|
|
return 0 |
|
|
|
|