summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2019-01-22 16:00:10 +0100
committerDaniel Mensinger <daniel@mensinger-ka.de>2019-01-22 16:41:25 +0100
commitb7c6f3ec72c831c2af20eb5320d5f51b52d79227 (patch)
treec7d77532371251aefcdbe45cfb74113337340375
parente089eb7665ca9dfb28e740829652add666f4a2f0 (diff)
downloadmeson-b7c6f3ec72c831c2af20eb5320d5f51b52d79227.tar.gz
Can now rewrite files
-rw-r--r--mesonbuild/ast/printer.py16
-rw-r--r--mesonbuild/rewriter.py86
-rwxr-xr-xrun_unittests.py14
-rw-r--r--test cases/rewrite/1 basic/addSrc.json6
-rw-r--r--test cases/rewrite/1 basic/meson.build2
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'])