1#!/usr/bin/env python3 2# 3# Copyright 2019 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""It is an AIDEGen sub task: generate the .project file for Eclipse.""" 18 19import os 20 21from aidegen import constant 22from aidegen import templates 23from aidegen.lib import common_util 24from aidegen.lib import project_file_gen 25 26 27class EclipseConf(project_file_gen.ProjectFileGenerator): 28 """Class to generate project file under the module path for Eclipse. 29 30 Attributes: 31 module_abspath: The absolute path of the target project. 32 module_relpath: The relative path of the target project. 33 module_name: The name of the target project. 34 jar_module_paths: A dict records a mapping of jar file and module path. 35 r_java_paths: A list contains the relative folder paths of the R.java 36 files. 37 project_file: The absolute path of .project file. 38 project_content: A string ready to be written into project_file. 39 src_paths: A list contains the project's source paths. 40 classpath_file: The absolute path of .classpath file. 41 classpath_content: A string ready to be written into classpath_file. 42 """ 43 # Constants of .project file 44 _PROJECT_LINK = (' <link><name>{}</name><type>2</type>' 45 '<location>{}</location></link>\n') 46 _PROJECT_FILENAME = '.project' 47 _OUTPUT_BIN_SYMBOLIC_NAME = 'bin' 48 49 # constants of .classpath file 50 _CLASSPATH_SRC_ENTRY = ' <classpathentry kind="src" path="{}"/>\n' 51 _EXCLUDE_ANDROID_BP_ENTRY = (' <classpathentry excluding="Android.bp" ' 52 'kind="src" path="{}"/>\n') 53 _CLASSPATH_LIB_ENTRY = (' <classpathentry exported="true" kind="lib" ' 54 'path="{}" sourcepath="{}"/>\n') 55 _CLASSPATH_FILENAME = '.classpath' 56 57 def __init__(self, project): 58 """Initialize class. 59 60 Args: 61 project: A ProjectInfo instance. 62 """ 63 super().__init__(project) 64 self.module_abspath = project.project_absolute_path 65 self.module_relpath = project.project_relative_path 66 self.module_name = project.module_name 67 self.jar_module_paths = project.source_path['jar_module_path'] 68 self.r_java_paths = list(project.source_path['r_java_path']) 69 # Related value for generating .project. 70 self.project_file = os.path.join(self.module_abspath, 71 self._PROJECT_FILENAME) 72 self.project_content = '' 73 # Related value for generating .classpath. 74 self.src_paths = list(project.source_path['source_folder_path']) 75 self.src_paths.extend(project.source_path['test_folder_path']) 76 self.classpath_file = os.path.join(self.module_abspath, 77 self._CLASSPATH_FILENAME) 78 self.classpath_content = '' 79 80 def _gen_r_link(self): 81 """Generate the link resources of the R paths. 82 83 E.g. 84 <link> 85 <name>dependencies/out/target/common/R</name> 86 <type>2</type> 87 <location>{ANDROID_ROOT_PATH}/out/target/common/R</location> 88 </link> 89 90 Returns: A set contains R paths link resources strings. 91 """ 92 return {self._gen_link(r_path) for r_path in self.r_java_paths} 93 94 def _gen_src_links(self, relpaths): 95 """Generate the link resources from relpaths. 96 97 The link resource is linked to a folder outside the module's path. It 98 cannot be a folder under the module's path. 99 100 Args: 101 relpaths: A list of module paths which are relative to 102 ANDROID_BUILD_TOP. 103 e.g. ['relpath/to/module1', 'relpath/to/module2', ...] 104 105 Returns: A set includes all unique link resources. 106 """ 107 src_links = set() 108 for src_path in relpaths: 109 if not common_util.is_source_under_relative_path( 110 src_path, self.module_relpath): 111 src_links.add(self._gen_link(src_path)) 112 return src_links 113 114 @classmethod 115 def _gen_link(cls, relpath): 116 """Generate a link resource from a relative path. 117 118 E.g. 119 <link> 120 <name>dependencies/path/to/relpath</name> 121 <type>2</type> 122 <location>/absolute/path/to/relpath</location> 123 </link> 124 125 Args: 126 relpath: A string of a relative path to Android_BUILD_TOP. 127 128 Returns: A string of link resource. 129 """ 130 alias_name = os.path.join(constant.KEY_DEPENDENCIES, relpath) 131 abs_path = os.path.join(common_util.get_android_root_dir(), relpath) 132 return cls._PROJECT_LINK.format(alias_name, abs_path) 133 134 def _gen_bin_link(self): 135 """Generate the link resource of the bin folder. 136 137 The bin folder will be set as default in the module's root path. But 138 doing so causes issues with the android build system. We should instead 139 move the bin under the out folder. 140 For example: 141 <link> 142 <name>bin</name> 143 <type>2</type> 144 <location>/home/user/aosp/out/Eclipse/framework</location> 145 </link> 146 147 Returns: A set includes a link resource of the bin folder. 148 """ 149 real_bin_path = os.path.join(common_util.get_android_root_dir(), 150 common_util.get_android_out_dir(), 151 constant.IDE_ECLIPSE, 152 self.module_name) 153 if not os.path.exists(real_bin_path): 154 os.makedirs(real_bin_path) 155 return {self._PROJECT_LINK.format(self._OUTPUT_BIN_SYMBOLIC_NAME, 156 real_bin_path)} 157 158 def _get_other_src_folders(self): 159 """Get the source folders outside the module's path. 160 161 Some source folders are generated by build system and placed under the 162 out folder. They also need to be set as link resources in Eclipse. 163 164 Returns: A list of source folder paths. 165 """ 166 return [p for p in self.src_paths 167 if not common_util.is_source_under_relative_path( 168 p, self.module_relpath)] 169 170 def _create_project_content(self): 171 """Create the project file .project under the module.""" 172 # links is a set to save unique link resources. 173 links = self._gen_src_links(self.jar_module_paths.values()) 174 links.update(self._gen_src_links(self._get_other_src_folders())) 175 links.update(self._gen_r_link()) 176 links.update(self._gen_bin_link()) 177 self.project_content = templates.ECLIPSE_PROJECT_XML.format( 178 PROJECTNAME=self.module_name.replace('/', '_'), 179 LINKEDRESOURCES=''.join(sorted(list(links)))) 180 181 def _gen_r_path_entries(self): 182 """Generate the class path entries for the R paths. 183 184 E.g. 185 <classpathentry kind="src" 186 path="dependencies/out/target/common/R"/> 187 <classpathentry kind="src" 188 path="dependencies/out/soong/.intermediates/packages/apps/ 189 Settings/Settings/android_common/gen/aapt2/R"/> 190 191 Returns: A list of the R path's class path entry. 192 """ 193 r_entry_list = [] 194 for r_path in self.r_java_paths: 195 alias_path = os.path.join(constant.KEY_DEPENDENCIES, r_path) 196 r_entry_list.append(self._CLASSPATH_SRC_ENTRY.format(alias_path)) 197 return r_entry_list 198 199 def _gen_src_path_entries(self): 200 """Generate the class path entries from srcs. 201 202 If the Android.bp file exists, generate the following content in 203 .classpath file to avoid copying the Android.bp to the bin folder under 204 current project directory. 205 <classpathentry excluding="Android.bp" kind="src" 206 path="clearcut_client"/> 207 208 If the source folder is under the module's path, revise the source 209 folder path as a relative path to the module's path. 210 E.g. 211 The source folder paths list: 212 ['packages/apps/Settings/src', 213 'packages/apps/Settings/tests/robotests/src', 214 'packages/apps/Settings/tests/uitests/src', 215 'packages/apps/Settings/tests/unit/src' 216 ] 217 It will generate the related <classpathentry> list: 218 ['<classpathentry kind="src" path="src"/>', 219 '<classpathentry kind="src" path="tests/robotests/src"/>', 220 '<classpathentry kind="src" path="tests/uitests/src"/>', 221 '<classpathentry kind="src" path="tests/unit/src"/>' 222 ] 223 224 Some source folders are not under the module's path: 225 e.g. out/path/src 226 The class path entry would be: 227 <classpathentry kind="src" path="dependencies/out/path/src"/> 228 229 Returns: A list of source folders' class path entries. 230 """ 231 src_path_entries = [] 232 for src in self.src_paths: 233 src_abspath = os.path.join(common_util.get_android_root_dir(), src) 234 if common_util.is_source_under_relative_path( 235 src, self.module_relpath): 236 src = src.replace(self.module_relpath, '').strip(os.sep) 237 else: 238 src = os.path.join(constant.KEY_DEPENDENCIES, src) 239 if common_util.exist_android_bp(src_abspath): 240 src_path_entries.append( 241 self._EXCLUDE_ANDROID_BP_ENTRY.format(src)) 242 else: 243 src_path_entries.append(self._CLASSPATH_SRC_ENTRY.format(src)) 244 return src_path_entries 245 246 def _gen_jar_path_entries(self): 247 """Generate the jar files' class path entries. 248 249 The self.jar_module_paths is a dictionary. 250 e.g. 251 {'/abspath/to/the/file.jar': 'relpath/to/the/module'} 252 This method will generate the <classpathentry> for each jar file. 253 The format of <classpathentry> looks like: 254 <classpathentry exported="true" kind="lib" 255 path="/abspath/to/the/file.jar" 256 sourcepath="dependencies/relpath/to/the/module"/> 257 258 Returns: A list of jar files' class path entries. 259 """ 260 jar_entries = [] 261 for jar_relpath, module_relpath in self.jar_module_paths.items(): 262 jar_abspath = os.path.join(common_util.get_android_root_dir(), 263 jar_relpath) 264 alias_module_path = os.path.join(constant.KEY_DEPENDENCIES, 265 module_relpath) 266 jar_entries.append(self._CLASSPATH_LIB_ENTRY.format( 267 jar_abspath, alias_module_path)) 268 return jar_entries 269 270 def _gen_bin_dir_entry(self): 271 """Generate the class path entry of the bin folder. 272 273 Returns: A list has a class path entry of the bin folder. 274 """ 275 return [self._CLASSPATH_SRC_ENTRY.format(self._OUTPUT_BIN_SYMBOLIC_NAME) 276 ] 277 278 def _create_classpath_content(self): 279 """Create the project file .classpath under the module.""" 280 src_entries = self._gen_src_path_entries() 281 src_entries.extend(self._gen_r_path_entries()) 282 src_entries.extend(self._gen_bin_dir_entry()) 283 jar_entries = self._gen_jar_path_entries() 284 self.classpath_content = templates.ECLIPSE_CLASSPATH_XML.format( 285 SRC=''.join(sorted(src_entries)), 286 LIB=''.join(sorted(jar_entries))) 287 288 def generate_project_file(self): 289 """Generate .project file of the target module.""" 290 self._create_project_content() 291 common_util.file_generate(self.project_file, self.project_content) 292 293 def generate_classpath_file(self): 294 """Generate .classpath file of the target module.""" 295 self._create_classpath_content() 296 common_util.file_generate(self.classpath_file, self.classpath_content) 297 298 @classmethod 299 def generate_ide_project_files(cls, projects): 300 """Generate Eclipse project files by a list of ProjectInfo instances. 301 302 Args: 303 projects: A list of ProjectInfo instances. 304 """ 305 for project in projects: 306 eclipse_configure = EclipseConf(project) 307 eclipse_configure.generate_project_file() 308 eclipse_configure.generate_classpath_file() 309