sourcedr: Add build dependency mapper
This commit adds `sourcedr-map` command which can map source file
review results to compiled binaries.
Test: ./sourcedr/functional_tests.py
Change-Id: I2ea69c982c8096be573174551075872722c13790
diff --git a/vndk/tools/source-deps-reviewer/setup.py b/vndk/tools/source-deps-reviewer/setup.py
index 0b6759e..fee747c 100755
--- a/vndk/tools/source-deps-reviewer/setup.py
+++ b/vndk/tools/source-deps-reviewer/setup.py
@@ -26,6 +26,7 @@
entry_points={
'console_scripts': [
'sourcedr = sourcedr.server:main',
+ 'sourcedr-map = sourcedr.map:main'
],
}
)
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py b/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py
index 630ced4..9305228 100755
--- a/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py
+++ b/vndk/tools/source-deps-reviewer/sourcedr/functional_tests.py
@@ -1,6 +1,11 @@
#!/usr/bin/env python3
+from sourcedr import data_utils
from sourcedr.data_utils import data_path, load_data, remove_data
+from sourcedr.map import (
+ load_build_dep_file_from_path, load_review_data,
+ link_build_dep_and_review_data,
+)
from sourcedr.preprocess import CodeSearch
from sourcedr.server import app, args
@@ -62,7 +67,7 @@
self.assertEqual(codes, cdata[test_arg][1])
def test_save_all(self):
- label = os.path.abspath('sourcedr//test/dlopen/test.c')
+ label = os.path.abspath('sourcedr/test/dlopen/test.c')
label += ':10: handle = dlopen("libm.so.6", RTLD_LAZY);'
test_arg = {
'label': label,
@@ -74,5 +79,45 @@
self.assertEqual(['this_is_a_test.so'], cdata[test_arg['label']][0])
self.assertEqual(['arr_0', 'arr_1'], cdata[test_arg['label']][1])
+
+class MapTest(unittest.TestCase):
+ def setUp(self):
+ # TODO: Remove this global variable hacks after refactoring process.
+ self.old_data_path = data_utils.data_path
+ data_utils.data_path = 'sourcedr/test/map/data.json'
+
+ def tearDown(self):
+ # TODO: Remove this global variable hacks after refactoring process.
+ data_utils.data_path = self.old_data_path
+
+ def test_load_build_dep_file(self):
+ dep = load_build_dep_file_from_path('sourcedr/test/map/build_dep.json')
+
+ self.assertIn('liba.so', dep)
+ self.assertIn('libb.so', dep)
+ self.assertIn('libc.so', dep)
+
+ self.assertSetEqual({'a.h', 'a1.c', 'a1.o', 'a2.c', 'a2.o'}, dep['liba.so'])
+ self.assertSetEqual({'a.h', 'b.c', 'b.o'}, dep['libb.so'])
+ self.assertSetEqual(set(), dep['libc.so'])
+
+ def test_load_review_data(self):
+ data = load_review_data()
+ self.assertIn('a.h', data)
+ self.assertEqual(['libx.so'], data['a.h'])
+
+ def test_link_build_dep_and_review_data(self):
+ dep = load_build_dep_file_from_path('sourcedr/test/map/build_dep.json')
+ data = load_review_data()
+ result = link_build_dep_and_review_data(dep, data)
+
+ self.assertIn('liba.so', result)
+ self.assertIn('libb.so', result)
+ self.assertIn('libc.so', result)
+
+ self.assertEqual(['libx.so'], result['liba.so'])
+ self.assertEqual(['libx.so'], result['libb.so'])
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/map.py b/vndk/tools/source-deps-reviewer/sourcedr/map.py
new file mode 100755
index 0000000..bd8da05
--- /dev/null
+++ b/vndk/tools/source-deps-reviewer/sourcedr/map.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+
+"""This command maps source file review results to compiled binaries.
+"""
+
+from sourcedr.data_utils import load_data
+
+import argparse
+import collections
+import json
+import os
+
+
+def load_build_dep_file(fp):
+ graph = json.load(fp)
+
+ # Collect all shared libraries
+ shared_libs = set()
+ for key, value in graph.items():
+ if key.split('.')[-1] == 'so':
+ shared_libs.add(key)
+ for v in value:
+ if v.split('.')[-1] == 'so':
+ shared_libs.add(v)
+
+ # Collect transitive closures
+ dep = {}
+ for s in shared_libs:
+ visited = set()
+ stack = [s]
+ while stack:
+ v = stack.pop()
+ if v not in visited:
+ visited.add(v)
+ try:
+ stack.extend(x for x in graph[v]
+ if x not in visited and not x.endswith('.so')
+ and not x.endswith('.toc'))
+ except KeyError:
+ pass
+ visited.remove(s)
+ dep[s] = visited
+
+ return dep
+
+
+def load_build_dep_file_from_path(path):
+ with open(path, 'r') as fp:
+ return load_build_dep_file(fp)
+
+
+def load_review_data():
+ table = collections.defaultdict(list)
+ data = load_data()
+ for key, item in data.items():
+ table[key.split(':')[0]] += item[0]
+ return table
+
+
+def link_build_dep_and_review_data(dep, table):
+ res = collections.defaultdict(list)
+ for out, ins in dep.items():
+ try:
+ res[out] += table[out]
+ except KeyError:
+ pass
+
+ for in_file in ins:
+ try:
+ res[out] += table[in_file]
+ except KeyError:
+ pass
+ return res
+
+
+def main():
+ # Parse arguments
+ parser = argparse.ArgumentParser()
+ parser.add_argument('input', help='Build dependency file')
+ parser.add_argument('-o', '--output', required=True)
+ args = parser.parse_args()
+
+ # Load build dependency file
+ try:
+ dep = load_build_dep_file_from_path(args.input)
+ except IOError:
+ print('error: Failed to open build dependency file:', args.input,
+ file=sys.stderr)
+ sys.exit(1)
+
+ # Load review data
+ table = load_review_data()
+
+ # Link build dependency file and review data
+ res = link_build_dep_and_review_data(dep, table)
+
+ # Write the output file
+ with open(args.output, 'w') as f:
+ json.dump(res, f, sort_keys=True, indent=4)
+
+if __name__ == '__main__':
+ main()
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/test/map/build_dep.json b/vndk/tools/source-deps-reviewer/sourcedr/test/map/build_dep.json
new file mode 100644
index 0000000..d60fa89
--- /dev/null
+++ b/vndk/tools/source-deps-reviewer/sourcedr/test/map/build_dep.json
@@ -0,0 +1,8 @@
+{
+ "liba.so": ["libb.so", "libc.so", "a1.o", "a2.o"],
+ "libb.so": ["b.o"],
+ "libc.so": [],
+ "a1.o": ["a.h", "a1.c"],
+ "a2.o": ["a.h", "a2.c"],
+ "b.o": ["a.h", "b.c"]
+}
diff --git a/vndk/tools/source-deps-reviewer/sourcedr/test/map/data.json b/vndk/tools/source-deps-reviewer/sourcedr/test/map/data.json
new file mode 100644
index 0000000..0d1bfe2
--- /dev/null
+++ b/vndk/tools/source-deps-reviewer/sourcedr/test/map/data.json
@@ -0,0 +1,6 @@
+{
+ "a.h:2:dlopen(\"libx.so\",": [
+ ["libx.so"],
+ ["a.h:2:dlopen(\"libx.so\","]
+ ]
+}