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 absolutely 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 absolutely 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 # constans 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 58 def __init__(self, project): 59 """Initialize class. 60 61 Args: 62 project: A ProjectInfo instance. 63 """ 64 super().__init__(project) 65 self.module_abspath = project.project_absolute_path 66 self.module_relpath = project.project_relative_path 67 self.module_name = project.module_name 68 self.jar_module_paths = project.source_path['jar_module_path'] 69 self.r_java_paths = list(project.source_path['r_java_path']) 70 # Related value for generating .project. 71 self.project_file = os.path.join(self.module_abspath, 72 self._PROJECT_FILENAME) 73 self.project_content = '' 74 # Related value for generating .classpath. 75 self.src_paths = list(project.source_path['source_folder_path']) 76 self.src_paths.extend(project.source_path['test_folder_path']) 77 self.classpath_file = os.path.join(self.module_abspath, 78 self._CLASSPATH_FILENAME) 79 self.classpath_content = '' 80 81 def _gen_r_link(self): 82 """Generate the link resources of the R paths. 83 84 E.g. 85 <link> 86 <name>dependencies/out/target/common/R</name> 87 <type>2</type> 88 <location>{ANDROID_ROOT_PATH}/out/target/common/R</location> 89 </link> 90 91 Returns: A set contains R paths link resources strings. 92 """ 93 return {self._gen_link(r_path) for r_path in self.r_java_paths} 94 95 def _gen_src_links(self, relpaths): 96 """Generate the link resources from relpaths. 97 98 The link resource is linked to a folder outside the module's path. It 99 cannot be a folder under the module's path. 100 101 Args: 102 relpaths: A list of module paths which are relative to 103 ANDROID_BUILD_TOP. 104 e.g. ['relpath/to/module1', 'relpath/to/module2', ...] 105 106 Returns: A set includes all unique link resources. 107 """ 108 src_links = set() 109 for src_path in relpaths: 110 if not common_util.is_source_under_relative_path( 111 src_path, self.module_relpath): 112 src_links.add(self._gen_link(src_path)) 113 return src_links 114 115 @classmethod 116 def _gen_link(cls, relpath): 117 """Generate a link resource from a relative path. 118 119 E.g. 120 <link> 121 <name>dependencies/path/to/relpath</name> 122 <type>2</type> 123 <location>/absolute/path/to/relpath</location> 124 </link> 125 126 Args: 127 relpath: A string of a relative path to Android_BUILD_TOP. 128 129 Returns: A string of link resource. 130 """ 131 alias_name = os.path.join(constant.KEY_DEPENDENCIES, relpath) 132 abs_path = os.path.join(common_util.get_android_root_dir(), relpath) 133 return cls._PROJECT_LINK.format(alias_name, abs_path) 134 135 def _gen_bin_link(self): 136 """Generate the link resource of the bin folder. 137 138 The bin folder will be set as default in the module's root path. But 139 doing so causes issues with the android build system. We should instead 140 move the bin under the out folder. 141 For example: 142 <link> 143 <name>bin</name> 144 <type>2</type> 145 <location>/home/user/aosp/out/Eclipse/framework</location> 146 </link> 147 148 Returns: A set includes a link resource of the bin folder. 149 """ 150 real_bin_path = os.path.join(common_util.get_android_root_dir(), 151 common_util.get_android_out_dir(), 152 constant.IDE_ECLIPSE, 153 self.module_name) 154 if not os.path.exists(real_bin_path): 155 os.makedirs(real_bin_path) 156 return {self._PROJECT_LINK.format(self._OUTPUT_BIN_SYMBOLIC_NAME, 157 real_bin_path)} 158 159 def _get_other_src_folders(self): 160 """Get the source folders outside the module's path. 161 162 Some source folders are generated by build system and placed under the 163 out folder. They also need to be set as link resources in Eclipse. 164 165 Returns: A list of source folder paths. 166 """ 167 return [p for p in self.src_paths 168 if not common_util.is_source_under_relative_path( 169 p, self.module_relpath)] 170 171 def _create_project_content(self): 172 """Create the project file .project under the module.""" 173 # links is a set to save unique link resources. 174 links = self._gen_src_links(self.jar_module_paths.values()) 175 links.update(self._gen_src_links(self._get_other_src_folders())) 176 links.update(self._gen_r_link()) 177 links.update(self._gen_bin_link()) 178 self.project_content = templates.ECLIPSE_PROJECT_XML.format( 179 PROJECTNAME=self.module_name.replace('/', '_'), 180 LINKEDRESOURCES=''.join(sorted(list(links)))) 181 182 def _gen_r_path_entries(self): 183 """Generate the class path entries for the R paths. 184 185 E.g. 186 <classpathentry kind="src" 187 path="dependencies/out/target/common/R"/> 188 <classpathentry kind="src" 189 path="dependencies/out/soong/.intermediates/packages/apps/ 190 Settings/Settings/android_common/gen/aapt2/R"/> 191 192 Returns: A list of the R path's class path entry. 193 """ 194 r_entry_list = [] 195 for r_path in self.r_java_paths: 196 alias_path = os.path.join(constant.KEY_DEPENDENCIES, r_path) 197 r_entry_list.append(self._CLASSPATH_SRC_ENTRY.format(alias_path)) 198 return r_entry_list 199 200 def _gen_src_path_entries(self): 201 """Generate the class path entries from srcs. 202 203 If the Android.bp file exists, generate the following content in 204 .classpath file to avoid copying the Android.bp to the bin folder under 205 current project directory. 206 <classpathentry excluding="Android.bp" kind="src" 207 path="clearcut_client"/> 208 209 If the source folder is under the module's path, revise the source 210 folder path as a relative path to the module's path. 211 E.g. 212 The source folder paths list: 213 ['packages/apps/Settings/src', 214 'packages/apps/Settings/tests/robotests/src', 215 'packages/apps/Settings/tests/uitests/src', 216 'packages/apps/Settings/tests/unit/src' 217 ] 218 It will generate the related <classpathentry> list: 219 ['<classpathentry kind="src" path="src"/>', 220 '<classpathentry kind="src" path="tests/robotests/src"/>', 221 '<classpathentry kind="src" path="tests/uitests/src"/>', 222 '<classpathentry kind="src" path="tests/unit/src"/>' 223 ] 224 225 Some source folders are not under the module's path: 226 e.g. out/path/src 227 The class path entry would be: 228 <classpathentry kind="src" path="dependencies/out/path/src"/> 229 230 Returns: A list of source folders' class path entries. 231 """ 232 src_path_entries = [] 233 for src in self.src_paths: 234 src_abspath = os.path.join(common_util.get_android_root_dir(), src) 235 if common_util.is_source_under_relative_path( 236 src, self.module_relpath): 237 src = src.replace(self.module_relpath, '').strip(os.sep) 238 else: 239 src = os.path.join(constant.KEY_DEPENDENCIES, src) 240 if common_util.exist_android_bp(src_abspath): 241 src_path_entries.append( 242 self._EXCLUDE_ANDROID_BP_ENTRY.format(src)) 243 else: 244 src_path_entries.append(self._CLASSPATH_SRC_ENTRY.format(src)) 245 return src_path_entries 246 247 def _gen_jar_path_entries(self): 248 """Generate the jar files' class path entries. 249 250 The self.jar_module_paths is a dictionary. 251 e.g. 252 {'/abspath/to/the/file.jar': 'relpath/to/the/module'} 253 This method will generate the <classpathentry> for each jar file. 254 The format of <classpathentry> looks like: 255 <classpathentry exported="true" kind="lib" 256 path="/abspath/to/the/file.jar" 257 sourcepath="dependencies/relpath/to/the/module"/> 258 259 Returns: A list of jar files' class path entries. 260 """ 261 jar_entries = [] 262 for jar_relpath, module_relpath in self.jar_module_paths.items(): 263 jar_abspath = os.path.join(common_util.get_android_root_dir(), 264 jar_relpath) 265 alias_module_path = os.path.join(constant.KEY_DEPENDENCIES, 266 module_relpath) 267 jar_entries.append(self._CLASSPATH_LIB_ENTRY.format( 268 jar_abspath, alias_module_path)) 269 return jar_entries 270 271 def _gen_bin_dir_entry(self): 272 """Generate the class path entry of the bin folder. 273 274 Returns: A list has a class path entry of the bin folder. 275 """ 276 return [self._CLASSPATH_SRC_ENTRY.format(self._OUTPUT_BIN_SYMBOLIC_NAME) 277 ] 278 279 def _create_classpath_content(self): 280 """Create the project file .classpath under the module.""" 281 src_entries = self._gen_src_path_entries() 282 src_entries.extend(self._gen_r_path_entries()) 283 src_entries.extend(self._gen_bin_dir_entry()) 284 jar_entries = self._gen_jar_path_entries() 285 self.classpath_content = templates.ECLIPSE_CLASSPATH_XML.format( 286 SRC=''.join(sorted(src_entries)), 287 LIB=''.join(sorted(jar_entries))) 288 289 def generate_project_file(self): 290 """Generate .project file of the target module.""" 291 self._create_project_content() 292 common_util.file_generate(self.project_file, self.project_content) 293 294 def generate_classpath_file(self): 295 """Generate .classpath file of the target module.""" 296 self._create_classpath_content() 297 common_util.file_generate(self.classpath_file, self.classpath_content) 298 299 @classmethod 300 def generate_ide_project_files(cls, projects): 301 """Generate Eclipse project files by a list of ProjectInfo instances. 302 303 Args: 304 projects: A list of ProjectInfo instances. 305 """ 306 for project in projects: 307 eclipse_configure = EclipseConf(project) 308 eclipse_configure.generate_project_file() 309 eclipse_configure.generate_classpath_file() 310