diff options
author | Daniel Mensinger <daniel@mensinger-ka.de> | 2019-01-22 16:00:10 +0100 |
---|---|---|
committer | Daniel Mensinger <daniel@mensinger-ka.de> | 2019-01-22 16:41:25 +0100 |
commit | b7c6f3ec72c831c2af20eb5320d5f51b52d79227 (patch) | |
tree | c7d77532371251aefcdbe45cfb74113337340375 | |
parent | e089eb7665ca9dfb28e740829652add666f4a2f0 (diff) | |
download | meson-b7c6f3ec72c831c2af20eb5320d5f51b52d79227.tar.gz |
Can now rewrite files
-rw-r--r-- | mesonbuild/ast/printer.py | 16 | ||||
-rw-r--r-- | mesonbuild/rewriter.py | 86 | ||||
-rwxr-xr-x | run_unittests.py | 14 | ||||
-rw-r--r-- | test cases/rewrite/1 basic/addSrc.json | 6 | ||||
-rw-r--r-- | test cases/rewrite/1 basic/meson.build | 2 |
5 files changed, 120 insertions, 4 deletions
diff --git a/mesonbuild/ast/printer.py b/mesonbuild/ast/printer.py index aab5a301f..1f5814676 100644 --- a/mesonbuild/ast/printer.py +++ b/mesonbuild/ast/printer.py @@ -17,6 +17,7 @@ from .. import mparser from . import AstVisitor +import re arithmic_map = { 'add': '+', @@ -33,11 +34,18 @@ class AstPrinter(AstVisitor): self.arg_newline_cutoff = arg_newline_cutoff self.ci = '' self.is_newline = True + self.last_level = 0 + + def post_process(self): + self.result = re.sub(r'\s+\n', '\n', self.result) def append(self, data: str, node: mparser.BaseNode): level = 0 if node and hasattr(node, 'level'): level = node.level + else: + level = self.last_level + self.last_level = level if self.is_newline: self.result += ' ' * (level * self.indent) self.result += data @@ -179,13 +187,17 @@ class AstPrinter(AstVisitor): self.newline() for i in node.arguments: i.accept(self) - self.append(',', node) + self.append(', ', node) if break_args: self.newline() for key, val in node.kwargs.items(): self.append(key, node) self.appendS(':', node) val.accept(self) - self.append(',', node) + self.append(', ', node) if break_args: self.newline() + if break_args: + self.result = re.sub(r', \n$', '\n', self.result) + else: + self.result = re.sub(r', $', '', self.result) diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index 6a1176776..dfed1c63e 100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py @@ -219,6 +219,90 @@ class Rewriter: .format(cmd['type'], list(self.functions.keys()))) self.functions[cmd['type']](cmd) + 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)) + # 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)) + + # Generating the new replacement string + str_list = [] + for i in work_nodes: + printer = AstPrinter() + i.accept(printer) + printer.post_process() + data = { + 'file': os.path.join(i.subdir, environment.build_filename), + 'str': printer.result.strip(), + 'node': i + } + str_list += [data] + + # Load build files + files = {} + for i in str_list: + if i['file'] in files: + continue + fpath = os.path.realpath(os.path.join(self.sourcedir, i['file'])) + fdata = '' + with open(fpath, 'r') as fp: + fdata = fp.read() + + # Generate line offsets numbers + m_lines = fdata.splitlines(True) + offset = 0 + line_offsets = [] + for j in m_lines: + line_offsets += [offset] + offset += len(j) + + files[i['file']] = { + 'path': fpath, + 'raw': fdata, + 'offsets': line_offsets + } + + # Replace in source code + for i in str_list: + offsets = files[i['file']]['offsets'] + raw = files[i['file']]['raw'] + node = i['node'] + line = node.lineno - 1 + col = node.colno + start = offsets[line]+col + end = start + if isinstance(node, mparser.ArrayNode): + if raw[end] != '[': + mlog.warning('Internal error: expected "[" at {}:{} but got "{}"'.format(line, col, raw[end])) + continue + counter = 1 + while counter > 0: + end += 1 + if raw[end] == '[': + counter += 1 + elif raw[end] == ']': + counter -= 1 + end += 1 + elif isinstance(node, mparser.FunctionNode): + while raw[end] != '(': + end += 1 + end += 1 + counter = 1 + while counter > 0: + end += 1 + if raw[end] == '(': + counter += 1 + elif raw[end] == ')': + counter -= 1 + end += 1 + raw = files[i['file']]['raw'] = raw[:start] + i['str'] + raw[end:] + + # Write the files back + for key, val in files.items(): + mlog.log('Rewriting', mlog.yellow(key)) + with open(val['path'], 'w') as fp: + fp.write(val['raw']) + def run(options): rewriter = Rewriter(options.sourcedir) rewriter.analyze_meson() @@ -235,4 +319,6 @@ def run(options): if not isinstance(i, object): raise TypeError('Command is not an object') rewriter.process(i) + + rewriter.apply_changes() return 0 diff --git a/run_unittests.py b/run_unittests.py index 9087a9c9d..c8d7f7a04 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -4972,10 +4972,10 @@ class RewriterTests(BasePlatformTests): expected = { 'target': { 'trivialprog1@exe': {'name': 'trivialprog1', 'sources': ['main.cpp', 'fileA.cpp', 'a1.cpp', 'a2.cpp', 'a6.cpp']}, - 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['fileB.cpp', 'fileC.cpp']}, + 'trivialprog2@exe': {'name': 'trivialprog2', 'sources': ['fileB.cpp', 'fileC.cpp', 'a7.cpp']}, 'trivialprog3@exe': {'name': 'trivialprog3', 'sources': ['main.cpp', 'fileA.cpp', 'a5.cpp']}, 'trivialprog4@exe': {'name': 'trivialprog4', 'sources': ['main.cpp', 'a5.cpp', 'fileA.cpp']}, - 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['main.cpp', 'a3.cpp', 'fileB.cpp', 'fileC.cpp']}, + 'trivialprog5@exe': {'name': 'trivialprog5', 'sources': ['main.cpp', 'a3.cpp', 'fileB.cpp', 'fileC.cpp', 'a7.cpp']}, 'trivialprog6@exe': {'name': 'trivialprog6', 'sources': ['main.cpp', 'fileA.cpp', 'a4.cpp']}, 'trivialprog7@exe': {'name': 'trivialprog7', 'sources': ['fileB.cpp', 'fileC.cpp', 'main.cpp', 'fileA.cpp', 'a1.cpp', 'a2.cpp', 'a6.cpp']}, 'trivialprog8@exe': {'name': 'trivialprog8', 'sources': ['main.cpp', 'fileA.cpp', 'a1.cpp', 'a2.cpp', 'a6.cpp']}, @@ -4984,6 +4984,11 @@ class RewriterTests(BasePlatformTests): } self.assertDictEqual(out, expected) + # Check the written file + out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json')) + out = self.extract_test_data(out) + self.assertDictEqual(out, expected) + def test_target_remove_sources(self): self.prime('1 basic') out = self.rewrite(self.builddir, os.path.join(self.builddir, 'rmSrc.json')) @@ -5003,6 +5008,11 @@ class RewriterTests(BasePlatformTests): } self.assertDictEqual(out, expected) + # Check the written file + out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json')) + out = self.extract_test_data(out) + self.assertDictEqual(out, expected) + class NativeFileTests(BasePlatformTests): def setUp(self): diff --git a/test cases/rewrite/1 basic/addSrc.json b/test cases/rewrite/1 basic/addSrc.json index 6d8c5996c..1a504bff2 100644 --- a/test cases/rewrite/1 basic/addSrc.json +++ b/test cases/rewrite/1 basic/addSrc.json @@ -7,6 +7,12 @@ }, { "type": "target", + "target": "trivialprog2", + "operation": "src_add", + "sources": ["a7.cpp"] + }, + { + "type": "target", "target": "trivialprog3", "operation": "src_add", "sources": ["a5.cpp"] diff --git a/test cases/rewrite/1 basic/meson.build b/test cases/rewrite/1 basic/meson.build index 3e092ee60..1bed0e18d 100644 --- a/test cases/rewrite/1 basic/meson.build +++ b/test cases/rewrite/1 basic/meson.build @@ -5,6 +5,8 @@ src2 = files(['fileB.cpp', 'fileC.cpp']) src3 = src1 src4 = [src3] +# Magic comment + exe1 = executable('trivialprog1', src1) exe2 = executable('trivialprog2', [src2]) exe3 = executable('trivialprog3', ['main.cpp', 'fileA.cpp']) |