AIDEgen: Error handling no related module path

Bug: 118787164
Test: 1. $ cd tools/tradefederation
      2. $ aidegen
      3. ERROR: No modules defined at tools/tradefederation.

Change-Id: Ia2820f3ca7b8f96203721539f53cb5d57b550fd4
diff --git a/aidegen/aidegen_main.py b/aidegen/aidegen_main.py
index 04bb780..dc8abec 100644
--- a/aidegen/aidegen_main.py
+++ b/aidegen/aidegen_main.py
@@ -92,6 +92,27 @@
     logging.basicConfig(level=level, format=log_format, datefmt=datefmt)
 
 
+def _check_module_exists(target, module_info):
+    """Check if module or project path exists inside source tree.
+
+    Args:
+        target: A string user input from command line. It could be
+                several cases such as:
+                1. Module name, e.g. Settings
+                2. Module path, e.g. packages/apps/Settings
+                3. Relative path, e.g. ../../packages/apps/Settings
+                4. Current directory, e.g. . or no argument
+        module_info: A ModuleInfo class contains data of module-info.json.
+    """
+    rel_path, abs_path = ProjectInfo.get_related_path(target, module_info)
+    if not abs_path.startswith(ProjectInfo.android_root_path):
+        logging.error('%s is outside android root.', abs_path)
+        sys.exit(1)
+    if not module_info.get_module_names(rel_path):
+        logging.error('No modules defined at %s.', rel_path)
+        sys.exit(1)
+
+
 @time_logged
 def main(argv):
     """Main entry.
@@ -103,9 +124,9 @@
     """
     args = _parse_args(argv)
     _configure_logging(args.verbose)
-    module_util_obj = ModuleInfoUtil()
-    project = ProjectInfo(args, module_util_obj.atest_module_info)
-    project.modules_info = module_util_obj.generate_module_info_json(
+    _check_module_exists(args.target, ModuleInfoUtil.atest_module_info)
+    project = ProjectInfo(args.target, ModuleInfoUtil.atest_module_info)
+    project.modules_info = ModuleInfoUtil().generate_module_info_json(
         project, args.verbose)
     project.dep_modules = project.get_dep_modules()
     locate_source(project, args.verbose)
diff --git a/aidegen/lib/module_info_util.py b/aidegen/lib/module_info_util.py
index 23e7557..e4ac543 100644
--- a/aidegen/lib/module_info_util.py
+++ b/aidegen/lib/module_info_util.py
@@ -61,13 +61,7 @@
     """Class offers a merged dictionary of both mk and bp json files and
     fast/easy lookup for Module related details."""
 
-    def __init__(self):
-        self._atest_module_info = module_info.ModuleInfo()
-
-    @property
-    def atest_module_info(self):
-        """Return Atest module info instance."""
-        return self._atest_module_info
+    atest_module_info = module_info.ModuleInfo()
 
     @time_logged
     def generate_module_info_json(self, project, verbose):
@@ -86,7 +80,7 @@
             A merged json dictionary.
         """
         _build_target(project, verbose)
-        mk_dict = self._atest_module_info.name_to_module_info
+        mk_dict = self.atest_module_info.name_to_module_info
         bp_dict = _get_soong_build_json_dict()
         return _merge_json(mk_dict, bp_dict)
 
diff --git a/aidegen/lib/project_info.py b/aidegen/lib/project_info.py
index 9d0cce1..15bc980 100644
--- a/aidegen/lib/project_info.py
+++ b/aidegen/lib/project_info.py
@@ -28,8 +28,10 @@
 class ProjectInfo():
     """Project information.
 
-    Attributes:
+    Class attributes:
         android_root_path: The path to android source root.
+
+    Attributes:
         project_absolute_path: The absolute path to the project.
         project_relative_path: The relative path to the project by
                                android_root_path.
@@ -41,6 +43,8 @@
                      project_module_names.
     """
 
+    android_root_path = os.environ.get(constants.ANDROID_BUILD_TOP)
+
     def __init__(self, args, module_info):
         """ProjectInfo initialize.
 
@@ -50,15 +54,10 @@
                   project path.
             module_info: A ModuleInfo class contains data of module-info.json.
         """
-        self.android_root_path = os.environ.get(constants.ANDROID_BUILD_TOP)
         # TODO: Find the closest parent module if no modules defined at project
         #       path.
-        rel_path, abs_path = self._get_related_path(args.target, module_info)
-        assert abs_path.startswith(self.android_root_path), (
-            'The path is outside android root: %s.' % abs_path)
+        rel_path, abs_path = self.get_related_path(args.target, module_info)
         self.project_module_names = module_info.get_module_names(rel_path)
-        assert self.project_module_names, (
-            'No modules defined at %s.' % rel_path)
         self.project_relative_path = rel_path
         self.project_absolute_path = abs_path
         self.modules_info = {}
@@ -83,11 +82,12 @@
             'jar_path': set()
         }
 
-    def _get_related_path(self, target, module_info):
+    @classmethod
+    def get_related_path(cls, target, module_info):
         """Get the relative and absolute paths of target from module-info.
 
         Args:
-            target: A list of string user input from command line. Could be
+            target: A string user input from command line. It could be
                     several cases such as:
                     1. Module name. e.g. Settings
                     2. Module path. e.g. packages/apps/Settings
@@ -101,21 +101,21 @@
         """
         if target:
             # User inputs a module name.
-            if module_info.is_module(target[0]):
-                rel_path = module_info.get_paths(target[0])[0]
-                abs_path = os.path.join(self.android_root_path, rel_path)
+            if module_info.is_module(target):
+                rel_path = module_info.get_paths(target)[0]
+                abs_path = os.path.join(cls.android_root_path, rel_path)
             # User inputs a module path.
-            elif module_info.get_module_names(target[0]):
-                rel_path = target[0]
-                abs_path = os.path.join(self.android_root_path, rel_path)
+            elif module_info.get_module_names(target):
+                rel_path = target
+                abs_path = os.path.join(cls.android_root_path, rel_path)
             # User inputs a relative path of current directory.
             else:
-                abs_path = os.path.abspath(os.path.join(os.getcwd(), target[0]))
-                rel_path = os.path.relpath(abs_path, self.android_root_path)
+                abs_path = os.path.abspath(os.path.join(os.getcwd(), target))
+                rel_path = os.path.relpath(abs_path, cls.android_root_path)
         else:
             # User doesn't input.
             abs_path = os.getcwd()
-            rel_path = os.path.relpath(abs_path, self.android_root_path)
+            rel_path = os.path.relpath(abs_path, cls.android_root_path)
         return rel_path, abs_path
 
     def set_modules_under_project_path(self):