Merge "vndk-def: Add vendor module dependency check tool"
diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py
index bbe0b74..40295fc 100755
--- a/vndk/tools/definition-tool/vndk_definition_tool.py
+++ b/vndk/tools/definition-tool/vndk_definition_tool.py
@@ -4,6 +4,7 @@
 
 import argparse
 import collections
+import csv
 import itertools
 import json
 import os
@@ -2180,6 +2181,127 @@
         return 0
 
 
+TAGGED_LIB_DICT_FIELDS = ('ll_ndk', 'sp_ndk', 'sp_ndk_indirect', 'hl_ndk',
+                          'vndk_sp', 'vndk', 'vndk_indirect', 'fwk_only')
+
+TaggedLibDict = collections.namedtuple('TaggedLibDict', TAGGED_LIB_DICT_FIELDS)
+
+class CheckDepCommand(ELFGraphCommand):
+    def __init__(self):
+        super(CheckDepCommand, self).__init__(
+                'check-dep', help='Check the eligible dependencies')
+
+    def add_argparser_options(self, parser):
+        super(CheckDepCommand, self).add_argparser_options(parser)
+
+        parser.add_argument('--tag-file', required=True)
+
+    def _load_tag_file(self, tag_file_path):
+        res = TaggedLibDict(set(), set(), set(), set(), set(), set(), set(),
+                            set())
+
+        mapping = {
+            'll-ndk': res.ll_ndk,
+            'sp-ndk': res.sp_ndk,
+            'sp-ndk-indirect': res.sp_ndk_indirect,
+            'hl-ndk': res.hl_ndk,
+            'vndk-sp-hal': res.vndk_sp,
+            'vndk-sp-both': res.vndk_sp,
+            'vndk': res.vndk,
+            'vndk-indirect': res.vndk_indirect,
+            'fwk-only': res.fwk_only,
+            'remove': res.fwk_only,
+        }
+
+        with open(tag_file_path, 'r') as tag_file:
+            csv_reader = csv.reader(tag_file)
+            for lib_name, tag in csv_reader:
+                mapping[tag.lower()].add(lib_name)
+
+        return res
+
+    def _get_tagged_libs(self, graph, tags):
+        res = TaggedLibDict(set(), set(), set(), set(), set(), set(), set(),
+                            set())
+        for lib in graph.lib_pt[PT_SYSTEM].values():
+            lib_name = os.path.basename(lib.path)
+            for i, lib_set in enumerate(tags):
+                if lib_name in lib_set:
+                    res[i].add(lib)
+        return res
+
+    def _check_eligible_vndk_dep(self, graph, tagged_libs):
+        """Check whether eligible sets are self-contained."""
+        num_errors = 0
+
+        indirect_libs = (tagged_libs.sp_ndk_indirect)
+
+        eligible_libs = (tagged_libs.ll_ndk | tagged_libs.sp_ndk | \
+                         tagged_libs.vndk_sp | \
+                         tagged_libs.vndk | tagged_libs.vndk_indirect)
+
+        # Check eligible vndk is self-contained.
+        for lib in eligible_libs:
+            for dep in lib.deps:
+                if dep not in eligible_libs and dep not in indirect_libs:
+                    print('error: eligible-lib: {}: eligible lib "{}" should '
+                          'not depend on non-eligible lib "{}".'
+                          .format(lib.path, lib.path, dep.path),
+                          file=sys.stderr)
+                    num_errors += 1
+
+        # Check the libbinder dependencies.
+        for lib in eligible_libs:
+            for dep in lib.deps:
+                if os.path.basename(dep.path) == 'libbinder.so':
+                    print('error: eligible-lib: {}: eligible lib "{}" should '
+                          'not depend on libbinder.so.'
+                          .format(lib.path, lib.path),
+                          file=sys.stderr)
+                    num_errors += 1
+
+        return num_errors
+
+    def _check_vendor_dep(self, graph, tagged_libs):
+        """Check whether vendor libs are depending on non-eligible libs."""
+        num_errors = 0
+
+        vendor_libs = graph.lib_pt[PT_VENDOR].values()
+        for lib in vendor_libs:
+            print('debug1:', lib.path)
+
+        eligible_libs = (tagged_libs.ll_ndk | tagged_libs.sp_ndk | \
+                         tagged_libs.vndk_sp | \
+                         tagged_libs.vndk | tagged_libs.vndk_indirect)
+
+        for lib in eligible_libs:
+            print('debug:', lib.path)
+
+        for lib in vendor_libs:
+            for dep in lib.deps:
+                if dep not in vendor_libs and dep not in eligible_libs:
+                    print('error: vendor-lib: {}: vendor lib "{}" depends on '
+                          'non-eligible lib "{}".'
+                          .format(lib.path, lib.path, dep.path),
+                          file=sys.stderr)
+                    num_errors += 1
+
+        return num_errors
+
+    def main(self, args):
+        graph = ELFLinker.create(args.system, args.system_dir_as_vendor,
+                                 args.vendor, args.vendor_dir_as_system,
+                                 args.load_extra_deps)
+
+        tags = self._load_tag_file(args.tag_file)
+        tagged_libs = self._get_tagged_libs(graph, tags)
+
+        num_errors = self._check_eligible_vndk_dep(graph, tagged_libs)
+        num_errors += self._check_vendor_dep(graph, tagged_libs)
+
+        return 0 if num_errors == 0 else 1
+
+
 class VNDKStableCommand(ELFGraphCommand):
     def __init__(self):
         super(VNDKStableCommand, self).__init__(
@@ -2241,6 +2363,7 @@
     register_subcmd(DepsCommand())
     register_subcmd(DepsClosureCommand())
     register_subcmd(DepsInsightCommand())
+    register_subcmd(CheckDepCommand())
     register_subcmd(SpLibCommand())
     register_subcmd(VNDKStableCommand())