Merge "AIDEGen: Write the link resources info into .project file."
diff --git a/aidegen/lib/eclipse_project_file_gen.py b/aidegen/lib/eclipse_project_file_gen.py
new file mode 100644
index 0000000..7c6efa4
--- /dev/null
+++ b/aidegen/lib/eclipse_project_file_gen.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""It is an AIDEGen sub task: generate the .project file for Eclipse."""
+
+import os
+
+from aidegen import constant
+from aidegen.lib import common_util
+
+
+class EclipseConf():
+    """Class to generate project file under the module path for Eclipse.
+
+    Attributes:
+        module_abspath: The absolute path of the target project.
+        module_name: The name of the target project.
+        jar_module_paths: A dict records a mapping of jar file and module path.
+        r_java_paths: A list contains the relative folder paths of the R.java
+                      files.
+        project_file: The absolutely path of .project file.
+        template_project_content: A string of a template project_file content.
+        project_content: A string ready to be written into project_file.
+    """
+    # Constants of .project file
+    _TEMPLATE_PROJECT_FILE = os.path.join(constant.AIDEGEN_ROOT_PATH,
+                                          'templates/eclipse/project.xml')
+    _PROJECT_LINK = ('                <link><name>{}</name><type>2</type>'
+                     '<location>{}</location></link>\n')
+    _PROJECT_FILENAME = '.project'
+
+    def __init__(self, project):
+        """Initialize class.
+
+        Args:
+            project: A ProjectInfo instance.
+        """
+        self.module_abspath = project.project_absolute_path
+        self.module_name = project.module_name
+        self.jar_module_paths = project.source_path['jar_module_path']
+        self.r_java_paths = list(project.source_path['r_java_path'])
+        # Related value for generating .project.
+        self.project_file = os.path.join(self.module_abspath,
+                                         self._PROJECT_FILENAME)
+        self.template_project_content = common_util.read_file_content(
+            self._TEMPLATE_PROJECT_FILE)
+        self.project_content = ''
+
+    def _gen_r_link(self):
+        """Generate the link resources of the R paths.
+
+        E.g.
+            <link>
+                <name>dependencies/out/target/common/R</name>
+                <type>2</type>
+                <location>{ANDROID_ROOT_PATH}/out/target/common/R</location>
+            </link>
+
+        Returns: A set contains R paths link resources strings.
+        """
+        return {self._gen_link(rpath) for rpath in self.r_java_paths}
+
+    def _gen_src_links(self, relpaths):
+        """Generate the link resources from relpaths.
+
+        Args:
+            relpaths: A list of module paths which are relative to
+                      ANDROID_BUILD_TOP.
+                      e.g. ['relpath/to/module1', 'relpath/to/module2', ...]
+
+        Returns: A set includes all unique link resources.
+        """
+        return {self._gen_link(relpath) for relpath in relpaths}
+
+    @classmethod
+    def _gen_link(cls, relpath):
+        """Generate a link resource from a relative path.
+
+         E.g.
+            <link>
+                <name>dependencies/path/to/relpath</name>
+                <type>2</type>
+                <location>/absolute/path/to/relpath</location>
+            </link>
+
+        Args:
+            relpath: A string of a relative path to Android_BUILD_TOP.
+
+        Returns: A string of link resource.
+        """
+        alias_name = os.path.join(constant.KEY_DEP, relpath)
+        abs_path = os.path.join(constant.ANDROID_ROOT_PATH, relpath)
+        return cls._PROJECT_LINK.format(alias_name, abs_path)
+
+    def _create_project_content(self):
+        """Create the project file .project under the module."""
+        # links is a set to save unique link resources.
+        links = self._gen_src_links(self.jar_module_paths.values())
+        links.update(self._gen_r_link())
+        self.project_content = self.template_project_content.format(
+            PROJECTNAME=self.module_name,
+            LINKEDRESOURCES=''.join(sorted(list(links))))
+
+    def generate_project_file(self):
+        """Generate .project file of the target module."""
+        self._create_project_content()
+        common_util.file_generate(self.project_file, self.project_content)
diff --git a/aidegen/lib/eclipse_project_file_gen_unittest.py b/aidegen/lib/eclipse_project_file_gen_unittest.py
new file mode 100644
index 0000000..e63d89b
--- /dev/null
+++ b/aidegen/lib/eclipse_project_file_gen_unittest.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittests for generate project file of Eclipse."""
+
+import os
+import unittest
+from unittest import mock
+
+from aidegen import constant
+from aidegen import unittest_constants
+from aidegen.lib.common_util import read_file_content
+from aidegen.lib.eclipse_project_file_gen import EclipseConf
+
+
+# pylint: disable=protected-access
+class EclipseConfUnittests(unittest.TestCase):
+    """Unit tests for generate_project_file.py"""
+    _ROOT_PATH = '/android/root'
+    _PROJECT_RELPATH = 'module/path'
+    _PROJECT_ABSPATH = os.path.join(_ROOT_PATH, _PROJECT_RELPATH)
+    _PROJECT_NAME = 'test'
+    _LINK_TEMPLATE = ('                <link><name>%s</name><type>2</type>'
+                      '<location>%s</location></link>\n')
+    _TEST_DATA_PATH = unittest_constants.TEST_DATA_PATH
+    _PROJECT_SAMPLE = os.path.join(_TEST_DATA_PATH, 'eclipse.project')
+
+    def test_gen_link(self):
+        """Test get_link return a correct link resource config."""
+        constant.ANDROID_ROOT_PATH = self._ROOT_PATH
+        name = os.path.join(constant.KEY_DEP, self._PROJECT_RELPATH)
+        expected_link = self._LINK_TEMPLATE % (name, self._PROJECT_ABSPATH)
+        generated_link = EclipseConf._gen_link(self._PROJECT_RELPATH)
+        self.assertEqual(generated_link, expected_link)
+
+    @mock.patch('aidegen.lib.project_info.ProjectInfo')
+    def test_create_project_content(self, mock_project_info):
+        """Test _create_project_content."""
+        constant.ANDROID_ROOT_PATH = self._ROOT_PATH
+        mock_project_info.project_absolute_path = self._PROJECT_ABSPATH
+        mock_project_info.module_name = self._PROJECT_NAME
+        mock_project_info.source_path = {
+            'jar_module_path': {
+                '': self._PROJECT_RELPATH
+            },
+            'r_java_path': {constant.CENTRAL_R_PATH}
+        }
+        expected_content = read_file_content(self._PROJECT_SAMPLE)
+        eclipse_config = EclipseConf(mock_project_info)
+        eclipse_config._create_project_content()
+        generated_content = eclipse_config.project_content
+        self.assertEqual(generated_content, expected_content)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/aidegen/lib/project_file_gen.py b/aidegen/lib/project_file_gen.py
index 108da52..5cadfbc 100644
--- a/aidegen/lib/project_file_gen.py
+++ b/aidegen/lib/project_file_gen.py
@@ -30,6 +30,7 @@
 
 from aidegen import constant
 from aidegen.lib import common_util
+from aidegen.lib.eclipse_project_file_gen import EclipseConf
 
 # FACET_SECTION is a part of iml, which defines the framework of the project.
 _FACET_SECTION = '''\
@@ -90,9 +91,6 @@
 _ECLIP_EXTENSION = '.classpath'
 _ECLIP_SRC_TOKEN = '@SRC@'
 _ECLIP_LIB_TOKEN = '@LIB@'
-_ECLIP_PROJECT_PATH = os.path.join(_ROOT_DIR, 'templates/eclipse/project.xml')
-_ECLIP_PROJECT_NAME_TOKEN = '@PROJECTNAME@'
-_ECLIP_PROJECT_EXTENSION = '.project'
 
 # b/121256503: Prevent duplicated iml names from breaking IDEA.
 # Use a map to cache in-using(already used) iml project file names.
@@ -200,18 +198,19 @@
 
 
 def _generate_eclipse_project_file(project_info):
-    """Generates Eclipse project file.
+    """Generates Eclipse project files.
 
     Args:
         project_info: ProjectInfo instance.
     """
-    module_path = project_info.project_absolute_path
-    module_name = get_unique_iml_name(module_path)
-    _generate_eclipse_project(module_name, module_path)
+    eclipse_configure = EclipseConf(project_info)
+    eclipse_configure.generate_project_file()
     source_dict = dict.fromkeys(
         list(project_info.source_path['source_folder_path']), False)
     source_dict.update(
         dict.fromkeys(list(project_info.source_path['test_folder_path']), True))
+    source_dict.update(
+        dict.fromkeys(list(project_info.source_path['r_java_path']), True))
     project_info.iml_path = _generate_classpath(
         project_info.project_absolute_path, list(sorted(source_dict)),
         list(project_info.source_path['jar_path']))
@@ -514,19 +513,6 @@
     return classpath_path
 
 
-def _generate_eclipse_project(project_name, module_path):
-    """Generate .project file of Eclipse.
-
-    Args:
-        project_name: A string of the project name.
-        module_path: Absolute path of the module.
-    """
-    template = common_util.read_file_content(_ECLIP_PROJECT_PATH)
-    template = template.replace(_ECLIP_PROJECT_NAME_TOKEN, project_name)
-    eclipse_project = os.path.join(module_path, _ECLIP_PROJECT_EXTENSION)
-    common_util.file_generate(eclipse_project, template)
-
-
 def _get_dependencies_name(module_name):
     """Get module's dependencies iml name which will be written in module.xml.
 
diff --git a/aidegen/lib/project_info.py b/aidegen/lib/project_info.py
index 8753519..9c97800 100644
--- a/aidegen/lib/project_info.py
+++ b/aidegen/lib/project_info.py
@@ -72,6 +72,8 @@
                      jar_path: A set contains the jar file paths.
                      jar_module_path: A dictionary contains the jar file and
                                       the module's path mapping.
+                     r_java_path: A set contains the relative path to the
+                                  R.java files.
     """
 
     modules_info = {}
@@ -88,7 +90,7 @@
                     the given target has a higher priority than project path.
         """
         rel_path, abs_path = get_related_paths(module_info, target)
-        target = self._get_target_name(target, abs_path)
+        self.module_name = self._get_target_name(target, abs_path)
         self.project_module_names = set(module_info.get_module_names(rel_path))
         self.project_relative_path = rel_path
         self.project_absolute_path = abs_path
@@ -97,7 +99,7 @@
         self._init_source_path()
         self.dep_modules = self.get_dep_modules()
         self._filter_out_modules()
-        self._display_convert_make_files_message(module_info, target)
+        self._display_convert_make_files_message(module_info, self.module_name)
 
     def _set_default_modues(self):
         """Append default hard-code modules, source paths and jar files.
@@ -120,7 +122,8 @@
             'source_folder_path': set(),
             'test_folder_path': set(),
             'jar_path': set(),
-            'jar_module_path': dict()
+            'jar_module_path': dict(),
+            'r_java_path': set()
         }
 
     def _display_convert_make_files_message(self, module_info, target):
diff --git a/aidegen/lib/source_locator.py b/aidegen/lib/source_locator.py
index 755f898..3064a1e 100644
--- a/aidegen/lib/source_locator.py
+++ b/aidegen/lib/source_locator.py
@@ -128,6 +128,7 @@
         module.locate_sources_path()
         dependencies['source_folder_path'].update(module.src_dirs)
         dependencies['test_folder_path'].update(module.test_dirs)
+        dependencies['r_java_path'].update(module.r_java_paths)
         _append_jars_as_dependencies(dependencies, module)
         if module.build_targets:
             rebuild_targets |= module.build_targets
@@ -253,6 +254,7 @@
         self.src_dirs = set()
         self.test_dirs = set()
         self.jar_files = set()
+        self.r_java_paths = set()
         self.referenced_by_jar = False
         self.build_targets = set()
         self.missing_jars = set()
@@ -298,9 +300,9 @@
                 self.build_targets.add(_AAPT2_SRCJAR % self.module_name)
             # In case the central R folder been deleted, uses the intermediate
             # folder as the dependency to R.java.
-            self.src_dirs.add(r_src_dir)
+            self.r_java_paths.add(r_src_dir)
         # Add the central R as a default source folder.
-        self.src_dirs.add(constant.CENTRAL_R_PATH)
+        self.r_java_paths.add(constant.CENTRAL_R_PATH)
 
     def _init_module_path(self):
         """Inintialize self.module_path."""
diff --git a/aidegen/lib/source_locator_unittest.py b/aidegen/lib/source_locator_unittest.py
index 97fd8c9..f1823a3 100644
--- a/aidegen/lib/source_locator_unittest.py
+++ b/aidegen/lib/source_locator_unittest.py
@@ -150,10 +150,10 @@
         """Test locate_sources_path handling."""
         # Test collect source path.
         module_info = dict(_MODULE_INFO)
-        result_src_list = set(['packages/apps/test/src/main/java',
-                               constant.CENTRAL_R_PATH])
+        result_src_list = set(['packages/apps/test/src/main/java'])
         result_test_list = set(['packages/apps/test/tests'])
         result_jar_list = set()
+        result_r_path = set([constant.CENTRAL_R_PATH])
         constant.ANDROID_ROOT_PATH = uc.TEST_DATA_PATH
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 _MODULE_DEPTH)
@@ -161,6 +161,7 @@
         self.assertEqual(module_data.src_dirs, result_src_list)
         self.assertEqual(module_data.test_dirs, result_test_list)
         self.assertEqual(module_data.jar_files, result_jar_list)
+        self.assertEqual(module_data.r_java_paths, result_r_path)
 
         # Test find jar files.
         jar_file = ('out/soong/.intermediates/packages/apps/test/test/'
@@ -219,32 +220,34 @@
         depth_by_source = 2
         module_info = dict(_MODULE_INFO)
         module_info['depth'] = 2
-        result_src_list = set(['packages/apps/test/src/main/java',
-                               constant.CENTRAL_R_PATH])
+        result_src_list = set(['packages/apps/test/src/main/java'])
         result_test_list = set(['packages/apps/test/tests'])
         result_jar_list = set()
+        result_r_path = set([constant.CENTRAL_R_PATH])
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 depth_by_source)
         module_data.locate_sources_path()
         self.assertEqual(module_data.src_dirs, result_src_list)
         self.assertEqual(module_data.test_dirs, result_test_list)
         self.assertEqual(module_data.jar_files, result_jar_list)
+        self.assertEqual(module_data.r_java_paths, result_r_path)
 
         # Test find source folder when module's depth smaller than the --depth
         # value from command line.
         depth_by_source = 3
         module_info = dict(_MODULE_INFO)
         module_info['depth'] = 2
-        result_src_list = set(['packages/apps/test/src/main/java',
-                               constant.CENTRAL_R_PATH])
+        result_src_list = set(['packages/apps/test/src/main/java'])
         result_test_list = set(['packages/apps/test/tests'])
         result_jar_list = set()
+        result_r_path = set([constant.CENTRAL_R_PATH])
         module_data = source_locator.ModuleData(_MODULE_NAME, module_info,
                                                 depth_by_source)
         module_data.locate_sources_path()
         self.assertEqual(module_data.src_dirs, result_src_list)
         self.assertEqual(module_data.test_dirs, result_test_list)
         self.assertEqual(module_data.jar_files, result_jar_list)
+        self.assertEqual(module_data.r_java_paths, result_r_path)
 
     @mock.patch('aidegen.lib.project_info.ProjectInfo')
     @mock.patch('atest.atest_utils.build')
@@ -267,6 +270,7 @@
             'test_folder_path': set(),
             'jar_path': set(),
             'jar_module_path': dict(),
+            'r_java_path': set(),
         }
         # Show warning when the jar not exists after build the module.
         result_jar = set()
@@ -290,13 +294,15 @@
             shutil.rmtree(test_root_path)
 
         # Test collects source and test folders.
-        result_source = set(['packages/apps/test/src/main/java',
-                             constant.CENTRAL_R_PATH])
+        result_source = set(['packages/apps/test/src/main/java'])
         result_test = set(['packages/apps/test/tests'])
+        result_r_path = set([constant.CENTRAL_R_PATH])
         self.assertEqual(mock_project_info.source_path['source_folder_path'],
                          result_source)
         self.assertEqual(mock_project_info.source_path['test_folder_path'],
                          result_test)
+        self.assertEqual(mock_project_info.source_path['r_java_path'],
+                         result_r_path)
 
         # Test loading jar from dependencies parameter.
         default_jar = os.path.join(_MODULE_PATH, 'test.jar')
diff --git a/aidegen/templates/eclipse/project.xml b/aidegen/templates/eclipse/project.xml
index 2b19250..a41fa48 100644
--- a/aidegen/templates/eclipse/project.xml
+++ b/aidegen/templates/eclipse/project.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-        <name>@PROJECTNAME@</name>
+        <name>{PROJECTNAME}</name>
         <comment></comment>
         <projects>
         </projects>
@@ -14,4 +14,7 @@
         <natures>
                 <nature>org.eclipse.jdt.core.javanature</nature>
         </natures>
+        <linkedResources>
+{LINKEDRESOURCES}
+        </linkedResources>
 </projectDescription>
diff --git a/aidegen/test_data/eclipse.project b/aidegen/test_data/eclipse.project
new file mode 100644
index 0000000..d5ca893
--- /dev/null
+++ b/aidegen/test_data/eclipse.project
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+        <name>test</name>
+        <comment></comment>
+        <projects>
+        </projects>
+        <buildSpec>
+                <buildCommand>
+                        <name>org.eclipse.jdt.core.javabuilder</name>
+                        <arguments>
+                        </arguments>
+                </buildCommand>
+        </buildSpec>
+        <natures>
+                <nature>org.eclipse.jdt.core.javanature</nature>
+        </natures>
+        <linkedResources>
+                <link><name>dependencies/module/path</name><type>2</type><location>/android/root/module/path</location></link>
+                <link><name>dependencies/out/target/common/R</name><type>2</type><location>/android/root/out/target/common/R</location></link>
+
+        </linkedResources>
+</projectDescription>