vndk-def: Add --system-dir-ignored option

This commit adds --system-dir-ignored option so that the user can
ignore /system/vendor directory and run the following command on some
devices:

    python3 vndk_definition_tool.py vndk \
      --system ${ANDROID_PRODUCT_OUT}/system \
      --system-dir-ignored vendor \
      --vendor ${ANDROID_PRODUCT_OUT}/vendor

Test: ./tests/run.py
Test: Add --system-dir-ignored to make sure /system/vendor on some
devices are not treated as system modules.

Change-Id: I45a82bf81f2e2a6b34c146310f53d47dfe2313c3
diff --git a/vndk/tools/definition-tool/README.md b/vndk/tools/definition-tool/README.md
index 37363fc..acf5824 100644
--- a/vndk/tools/definition-tool/README.md
+++ b/vndk/tools/definition-tool/README.md
@@ -61,6 +61,19 @@
     PRODUCT_PACKAGES += libexample4.vndk-ext
 
 
+## Ignore Subdirectories
+
+Some devices keep their vendor modules in `/system/vendor`.  To run VNDK
+definition tool for those devices, we have to skip `/system/vendor` and specify
+it with `--vendor` option.  For example:
+
+    python3 vndk_definition_tool.py vndk \
+        --system ${ANDROID_PRODUCT_OUT}/system \
+        --system-dir-igored vendor \
+        --vendor ${ANDROID_PRODUCT_OUT}/system/vendor \
+        # ...
+
+
 ## Implicit Dependencies
 
 If there are implicit dependencies, such as `dlopen()`, we can specify them in
diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py
index 46c587f..aeefd2c 100755
--- a/vndk/tools/definition-tool/vndk_definition_tool.py
+++ b/vndk/tools/definition-tool/vndk_definition_tool.py
@@ -948,15 +948,20 @@
         return re.compile('|'.join(patts))
 
     def add_executables_in_dir(self, partition_name, partition, root,
-                               alter_partition, alter_subdirs, scan_elf_files):
+                               alter_partition, alter_subdirs, ignored_subdirs,
+                               scan_elf_files):
         root = os.path.abspath(root)
         prefix_len = len(root) + 1
 
         if alter_subdirs:
             alter_patt = ELFLinker._compile_path_matcher(root, alter_subdirs)
+        if ignored_subdirs:
+            ignored_patt = ELFLinker._compile_path_matcher(root, ignored_subdirs)
 
         for path, elf in scan_elf_files(root):
             short_path = os.path.join('/', partition_name, path[prefix_len:])
+            if ignored_subdirs and ignored_patt.match(path):
+                continue
             if alter_subdirs and alter_patt.match(path):
                 self.add_lib(alter_partition, short_path, elf)
             else:
@@ -1488,20 +1493,23 @@
 
     @staticmethod
     def _create_internal(scan_elf_files, system_dirs, system_dirs_as_vendor,
-                         vendor_dirs, vendor_dirs_as_system, extra_deps,
-                         generic_refs):
+                         system_dirs_ignored, vendor_dirs,
+                         vendor_dirs_as_system, vendor_dirs_ignored,
+                         extra_deps, generic_refs):
         graph = ELFLinker()
 
         if system_dirs:
             for path in system_dirs:
                 graph.add_executables_in_dir('system', PT_SYSTEM, path,
                                              PT_VENDOR, system_dirs_as_vendor,
+                                             system_dirs_ignored,
                                              scan_elf_files)
 
         if vendor_dirs:
             for path in vendor_dirs:
                 graph.add_executables_in_dir('vendor', PT_VENDOR, path,
                                              PT_SYSTEM, vendor_dirs_as_system,
+                                             vendor_dirs_ignored,
                                              scan_elf_files)
 
         if extra_deps:
@@ -1513,11 +1521,14 @@
         return graph
 
     @staticmethod
-    def create(system_dirs=None, system_dirs_as_vendor=None, vendor_dirs=None,
-               vendor_dirs_as_system=None, extra_deps=None, generic_refs=None):
+    def create(system_dirs=None, system_dirs_as_vendor=None,
+               system_dirs_ignored=None, vendor_dirs=None,
+               vendor_dirs_as_system=None, vendor_dirs_ignored=None,
+               extra_deps=None, generic_refs=None):
         return ELFLinker._create_internal(
-                scan_elf_files, system_dirs, system_dirs_as_vendor, vendor_dirs,
-                vendor_dirs_as_system, extra_deps, generic_refs)
+                scan_elf_files, system_dirs, system_dirs_as_vendor,
+                system_dirs_ignored, vendor_dirs, vendor_dirs_as_system,
+                vendor_dirs_ignored, extra_deps, generic_refs)
 
     @staticmethod
     def create_from_dump(system_dirs=None, system_dirs_as_vendor=None,
@@ -1673,10 +1684,18 @@
                 help='sub directory of system partition that has vendor files')
 
         parser.add_argument(
+                '--system-dir-ignored', action='append',
+                help='sub directory of system partition that must be ignored')
+
+        parser.add_argument(
                 '--vendor-dir-as-system', action='append',
                 help='sub directory of vendor partition that has system files')
 
         parser.add_argument(
+                '--vendor-dir-ignored', action='append',
+                help='sub directory of vendor partition that must be ignored')
+
+        parser.add_argument(
                 '--load-generic-refs',
                 help='compare with generic reference symbols')
 
@@ -1692,14 +1711,6 @@
                                                      '/system')
         return None
 
-
-class VNDKCommandBase(ELFGraphCommand):
-    def add_argparser_options(self, parser):
-        super(VNDKCommandBase, self).add_argparser_options(parser)
-
-        parser.add_argument('--no-default-dlopen-deps', action='store_true',
-                help='do not add default dlopen dependencies')
-
     def _check_arg_dir_exists(self, arg_name, dirs):
         for path in dirs:
             if not os.path.exists(path):
@@ -1716,17 +1727,33 @@
         self._check_arg_dir_exists('--vendor', args.vendor)
 
     def create_from_args(self, args):
-        """Create all essential data structures for VNDK computation."""
-
         self.check_dirs_from_args(args)
 
         generic_refs = self.get_generic_refs_from_args(args)
 
         graph = ELFLinker.create(args.system, args.system_dir_as_vendor,
+                                 args.system_dir_ignored,
                                  args.vendor, args.vendor_dir_as_system,
+                                 args.vendor_dir_ignored,
                                  args.load_extra_deps,
                                  generic_refs=generic_refs)
 
+        return (generic_refs, graph)
+
+
+class VNDKCommandBase(ELFGraphCommand):
+    def add_argparser_options(self, parser):
+        super(VNDKCommandBase, self).add_argparser_options(parser)
+
+        parser.add_argument('--no-default-dlopen-deps', action='store_true',
+                help='do not add default dlopen dependencies')
+
+    def create_from_args(self, args):
+        """Create all essential data structures for VNDK computation."""
+
+        generic_refs, graph = \
+                super(VNDKCommandBase, self).create_from_args(args)
+
         if not args.no_default_dlopen_deps:
             script_dir = os.path.dirname(os.path.abspath(__file__))
             minimum_dlopen_deps = os.path.join(script_dir, 'datasets',
@@ -1921,9 +1948,7 @@
         super(VNDKCapCommand, self).add_argparser_options(parser)
 
     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)
+        generic_refs, graph = self.create_from_args(args)
 
         banned_libs = BannedLibDict.create_default()
 
@@ -1954,9 +1979,7 @@
                 help='print symbols')
 
     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)
+        generic_refs, graph = self.create_from_args(args)
 
         results = []
         for partition in range(NUM_PARTITIONS):
@@ -2012,9 +2035,7 @@
                             help='exclude ndk libraries')
 
     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)
+        generic_refs, graph = self.create_from_args(args)
 
         # Find root/excluded libraries by their paths.
         def report_error(path):
@@ -2299,9 +2320,7 @@
         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)
+        generic_refs, graph = self.create_from_args(args)
 
         tagged_paths = TaggedPathDict.create_from_csv_path(args.tag_file)
         tagged_libs = TaggedLibDict.create_from_graph(graph, tagged_paths)
@@ -2378,9 +2397,7 @@
         return data, violate_libs
 
     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)
+        generic_refs, graph = self.create_from_args(args)
 
         tagged_paths = TaggedPathDict.create_from_csv_path(args.tag_file)
         data, violate_libs = self._get_dep_graph(graph, tagged_paths)
@@ -2410,9 +2427,7 @@
         super(VNDKSPCommand, self).add_argparser_options(parser)
 
     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)
+        generic_refs, graph = self.create_from_args(args)
 
         vndk_sp = graph.compute_predefined_vndk_sp()
         for lib in sorted_lib_path_list(vndk_sp):
@@ -2432,12 +2447,7 @@
         super(SpLibCommand, self).add_argparser_options(parser)
 
     def main(self, args):
-        generic_refs = self.get_generic_refs_from_args(args)
-
-        graph = ELFLinker.create(args.system, args.system_dir_as_vendor,
-                                 args.vendor, args.vendor_dir_as_system,
-                                 args.load_extra_deps)
-
+        generic_refs, graph = self.create_from_args(args)
         print_sp_lib(graph.compute_sp_lib(generic_refs))
         return 0