1*c2e18aaaSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*c2e18aaaSAndroid Build Coastguard Worker# 3*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2020 - The Android Open Source Project 4*c2e18aaaSAndroid Build Coastguard Worker# 5*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*c2e18aaaSAndroid Build Coastguard Worker# 9*c2e18aaaSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*c2e18aaaSAndroid Build Coastguard Worker# 11*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License. 16*c2e18aaaSAndroid Build Coastguard Worker 17*c2e18aaaSAndroid Build Coastguard Worker"""Creates the iml file for each module. 18*c2e18aaaSAndroid Build Coastguard Worker 19*c2e18aaaSAndroid Build Coastguard WorkerThis class is used to create the iml file for each module. So far, only generate 20*c2e18aaaSAndroid Build Coastguard Workerthe create_srcjar() for the framework-all module. 21*c2e18aaaSAndroid Build Coastguard Worker 22*c2e18aaaSAndroid Build Coastguard WorkerUsage example: 23*c2e18aaaSAndroid Build Coastguard Worker modules_info = project_info.ProjectInfo.modules_info 24*c2e18aaaSAndroid Build Coastguard Worker mod_info = modules_info.name_to_module_info['module'] 25*c2e18aaaSAndroid Build Coastguard Worker iml = IMLGenerator(mod_info) 26*c2e18aaaSAndroid Build Coastguard Worker iml.create() 27*c2e18aaaSAndroid Build Coastguard Worker""" 28*c2e18aaaSAndroid Build Coastguard Worker 29*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import absolute_import 30*c2e18aaaSAndroid Build Coastguard Worker 31*c2e18aaaSAndroid Build Coastguard Workerimport logging 32*c2e18aaaSAndroid Build Coastguard Workerimport os 33*c2e18aaaSAndroid Build Coastguard Worker 34*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import constant 35*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import templates 36*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import common_util 37*c2e18aaaSAndroid Build Coastguard Worker 38*c2e18aaaSAndroid Build Coastguard Worker 39*c2e18aaaSAndroid Build Coastguard Workerclass IMLGenerator: 40*c2e18aaaSAndroid Build Coastguard Worker """Creates the iml file for each module. 41*c2e18aaaSAndroid Build Coastguard Worker 42*c2e18aaaSAndroid Build Coastguard Worker Class attributes: 43*c2e18aaaSAndroid Build Coastguard Worker _USED_NAME_CACHE: A dict to cache already used iml project file names 44*c2e18aaaSAndroid Build Coastguard Worker and prevent duplicated iml names from breaking IDEA. 45*c2e18aaaSAndroid Build Coastguard Worker 46*c2e18aaaSAndroid Build Coastguard Worker Attributes: 47*c2e18aaaSAndroid Build Coastguard Worker _mod_info: A dictionary of the module's data from module-info.json. 48*c2e18aaaSAndroid Build Coastguard Worker _android_root: A string ot the Android root's absolute path. 49*c2e18aaaSAndroid Build Coastguard Worker _mod_path: A string of the module's absolute path. 50*c2e18aaaSAndroid Build Coastguard Worker _iml_path: A string of the module's iml absolute path. 51*c2e18aaaSAndroid Build Coastguard Worker _facet: A string of the facet setting. 52*c2e18aaaSAndroid Build Coastguard Worker _excludes: A string of the exclude relative paths. 53*c2e18aaaSAndroid Build Coastguard Worker _srcs: A string of the source urls. 54*c2e18aaaSAndroid Build Coastguard Worker _jars: A list of the jar urls. 55*c2e18aaaSAndroid Build Coastguard Worker _srcjars: A list of srcjar urls. 56*c2e18aaaSAndroid Build Coastguard Worker _deps: A list of the dependency module urls. 57*c2e18aaaSAndroid Build Coastguard Worker """ 58*c2e18aaaSAndroid Build Coastguard Worker # b/121256503: Prevent duplicated iml names from breaking IDEA. 59*c2e18aaaSAndroid Build Coastguard Worker # Use a map to cache in-using(already used) iml project file names. 60*c2e18aaaSAndroid Build Coastguard Worker USED_NAME_CACHE = {} 61*c2e18aaaSAndroid Build Coastguard Worker 62*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, mod_info): 63*c2e18aaaSAndroid Build Coastguard Worker """Initializes IMLGenerator. 64*c2e18aaaSAndroid Build Coastguard Worker 65*c2e18aaaSAndroid Build Coastguard Worker Args: 66*c2e18aaaSAndroid Build Coastguard Worker mod_info: A dictionary of the module's data from module-info.json. 67*c2e18aaaSAndroid Build Coastguard Worker """ 68*c2e18aaaSAndroid Build Coastguard Worker self._mod_info = mod_info 69*c2e18aaaSAndroid Build Coastguard Worker self._android_root = common_util.get_android_root_dir() 70*c2e18aaaSAndroid Build Coastguard Worker self._mod_path = os.path.join(self._android_root, 71*c2e18aaaSAndroid Build Coastguard Worker mod_info[constant.KEY_PATH][0]) 72*c2e18aaaSAndroid Build Coastguard Worker self._iml_path = os.path.join(self._mod_path, 73*c2e18aaaSAndroid Build Coastguard Worker mod_info[constant.KEY_IML_NAME] + '.iml') 74*c2e18aaaSAndroid Build Coastguard Worker self._facet = '' 75*c2e18aaaSAndroid Build Coastguard Worker self._excludes = '' 76*c2e18aaaSAndroid Build Coastguard Worker self._srcs = '' 77*c2e18aaaSAndroid Build Coastguard Worker self._jars = [] 78*c2e18aaaSAndroid Build Coastguard Worker self._srcjars = [] 79*c2e18aaaSAndroid Build Coastguard Worker self._deps = [] 80*c2e18aaaSAndroid Build Coastguard Worker 81*c2e18aaaSAndroid Build Coastguard Worker @classmethod 82*c2e18aaaSAndroid Build Coastguard Worker def get_unique_iml_name(cls, abs_module_path): 83*c2e18aaaSAndroid Build Coastguard Worker """Create a unique iml name if needed. 84*c2e18aaaSAndroid Build Coastguard Worker 85*c2e18aaaSAndroid Build Coastguard Worker If the name of last sub folder is used already, prefixing it with prior 86*c2e18aaaSAndroid Build Coastguard Worker sub folder names as a candidate name. If finally, it's unique, storing 87*c2e18aaaSAndroid Build Coastguard Worker in USED_NAME_CACHE as: { abs_module_path:unique_name }. The cts case 88*c2e18aaaSAndroid Build Coastguard Worker and UX of IDE view are the main reasons why using module path strategy 89*c2e18aaaSAndroid Build Coastguard Worker but not name of module directly. Following is the detailed strategy: 90*c2e18aaaSAndroid Build Coastguard Worker 1. While loop composes a sensible and shorter name, by checking unique 91*c2e18aaaSAndroid Build Coastguard Worker to finish the loop and finally add to cache. 92*c2e18aaaSAndroid Build Coastguard Worker Take ['cts', 'tests', 'app', 'ui'] an example, if 'ui' isn't 93*c2e18aaaSAndroid Build Coastguard Worker occupied, use it, else try 'cts_ui', then 'cts_app_ui', the worst 94*c2e18aaaSAndroid Build Coastguard Worker case is whole three candidate names are occupied already. 95*c2e18aaaSAndroid Build Coastguard Worker 2. 'Else' for that while stands for no suitable name generated, so 96*c2e18aaaSAndroid Build Coastguard Worker trying 'cts_tests_app_ui' directly. If it's still non unique, e.g., 97*c2e18aaaSAndroid Build Coastguard Worker module path cts/xxx/tests/app/ui occupied that name already, 98*c2e18aaaSAndroid Build Coastguard Worker appending increasing sequence number to get a unique name. 99*c2e18aaaSAndroid Build Coastguard Worker 100*c2e18aaaSAndroid Build Coastguard Worker Args: 101*c2e18aaaSAndroid Build Coastguard Worker abs_module_path: The absolute module path string. 102*c2e18aaaSAndroid Build Coastguard Worker 103*c2e18aaaSAndroid Build Coastguard Worker Return: 104*c2e18aaaSAndroid Build Coastguard Worker String: A unique iml name. 105*c2e18aaaSAndroid Build Coastguard Worker """ 106*c2e18aaaSAndroid Build Coastguard Worker if abs_module_path in cls.USED_NAME_CACHE: 107*c2e18aaaSAndroid Build Coastguard Worker return cls.USED_NAME_CACHE[abs_module_path] 108*c2e18aaaSAndroid Build Coastguard Worker 109*c2e18aaaSAndroid Build Coastguard Worker uniq_name = abs_module_path.strip(os.sep).split(os.sep)[-1] 110*c2e18aaaSAndroid Build Coastguard Worker 111*c2e18aaaSAndroid Build Coastguard Worker if any(uniq_name == name for name in cls.USED_NAME_CACHE.values()): 112*c2e18aaaSAndroid Build Coastguard Worker parent_path = os.path.relpath(abs_module_path, 113*c2e18aaaSAndroid Build Coastguard Worker common_util.get_android_root_dir()) 114*c2e18aaaSAndroid Build Coastguard Worker sub_folders = parent_path.split(os.sep) 115*c2e18aaaSAndroid Build Coastguard Worker zero_base_index = len(sub_folders) - 1 116*c2e18aaaSAndroid Build Coastguard Worker # Start compose a sensible, shorter and unique name. 117*c2e18aaaSAndroid Build Coastguard Worker while zero_base_index > 0: 118*c2e18aaaSAndroid Build Coastguard Worker uniq_name = '_'.join( 119*c2e18aaaSAndroid Build Coastguard Worker [sub_folders[0], '_'.join(sub_folders[zero_base_index:])]) 120*c2e18aaaSAndroid Build Coastguard Worker zero_base_index = zero_base_index - 1 121*c2e18aaaSAndroid Build Coastguard Worker if uniq_name not in cls.USED_NAME_CACHE.values(): 122*c2e18aaaSAndroid Build Coastguard Worker break 123*c2e18aaaSAndroid Build Coastguard Worker else: 124*c2e18aaaSAndroid Build Coastguard Worker # For full source tree case, there are 2 path cases w/wo "/". 125*c2e18aaaSAndroid Build Coastguard Worker # In the 2nd run, if abs_module_path is root dir, 126*c2e18aaaSAndroid Build Coastguard Worker # it will use uniq_name directly. 127*c2e18aaaSAndroid Build Coastguard Worker if parent_path == ".": 128*c2e18aaaSAndroid Build Coastguard Worker pass 129*c2e18aaaSAndroid Build Coastguard Worker else: 130*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/133393638): To handle several corner cases. 131*c2e18aaaSAndroid Build Coastguard Worker uniq_name_base = parent_path.strip(os.sep).replace(os.sep, 132*c2e18aaaSAndroid Build Coastguard Worker '_') 133*c2e18aaaSAndroid Build Coastguard Worker i = 0 134*c2e18aaaSAndroid Build Coastguard Worker uniq_name = uniq_name_base 135*c2e18aaaSAndroid Build Coastguard Worker while uniq_name in cls.USED_NAME_CACHE.values(): 136*c2e18aaaSAndroid Build Coastguard Worker i = i + 1 137*c2e18aaaSAndroid Build Coastguard Worker uniq_name = '_'.join([uniq_name_base, str(i)]) 138*c2e18aaaSAndroid Build Coastguard Worker cls.USED_NAME_CACHE[abs_module_path] = uniq_name 139*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Unique name for module path of %s is %s.', 140*c2e18aaaSAndroid Build Coastguard Worker abs_module_path, uniq_name) 141*c2e18aaaSAndroid Build Coastguard Worker return uniq_name 142*c2e18aaaSAndroid Build Coastguard Worker 143*c2e18aaaSAndroid Build Coastguard Worker @property 144*c2e18aaaSAndroid Build Coastguard Worker def iml_path(self): 145*c2e18aaaSAndroid Build Coastguard Worker """Gets the iml path.""" 146*c2e18aaaSAndroid Build Coastguard Worker return self._iml_path 147*c2e18aaaSAndroid Build Coastguard Worker 148*c2e18aaaSAndroid Build Coastguard Worker def create(self, content_type): 149*c2e18aaaSAndroid Build Coastguard Worker """Creates the iml file. 150*c2e18aaaSAndroid Build Coastguard Worker 151*c2e18aaaSAndroid Build Coastguard Worker Create the iml file with specific part of sources. 152*c2e18aaaSAndroid Build Coastguard Worker e.g. 153*c2e18aaaSAndroid Build Coastguard Worker { 154*c2e18aaaSAndroid Build Coastguard Worker 'srcs': True, 155*c2e18aaaSAndroid Build Coastguard Worker 'dependencies': True, 156*c2e18aaaSAndroid Build Coastguard Worker } 157*c2e18aaaSAndroid Build Coastguard Worker 158*c2e18aaaSAndroid Build Coastguard Worker Args: 159*c2e18aaaSAndroid Build Coastguard Worker content_type: A dict to set which part of sources will be created. 160*c2e18aaaSAndroid Build Coastguard Worker """ 161*c2e18aaaSAndroid Build Coastguard Worker if content_type.get(constant.KEY_SRCS, None): 162*c2e18aaaSAndroid Build Coastguard Worker self._generate_srcs() 163*c2e18aaaSAndroid Build Coastguard Worker if content_type.get(constant.KEY_DEP_SRCS, None): 164*c2e18aaaSAndroid Build Coastguard Worker self._generate_dep_srcs() 165*c2e18aaaSAndroid Build Coastguard Worker if content_type.get(constant.KEY_JARS, None): 166*c2e18aaaSAndroid Build Coastguard Worker self._generate_jars() 167*c2e18aaaSAndroid Build Coastguard Worker if content_type.get(constant.KEY_SRCJARS, None): 168*c2e18aaaSAndroid Build Coastguard Worker self._generate_srcjars() 169*c2e18aaaSAndroid Build Coastguard Worker if content_type.get(constant.KEY_DEPENDENCIES, None): 170*c2e18aaaSAndroid Build Coastguard Worker self._generate_dependencies() 171*c2e18aaaSAndroid Build Coastguard Worker 172*c2e18aaaSAndroid Build Coastguard Worker if self._srcs or self._jars or self._srcjars or self._deps: 173*c2e18aaaSAndroid Build Coastguard Worker self._create_iml() 174*c2e18aaaSAndroid Build Coastguard Worker 175*c2e18aaaSAndroid Build Coastguard Worker def _generate_facet(self): 176*c2e18aaaSAndroid Build Coastguard Worker """Generates the facet when the AndroidManifest.xml exists.""" 177*c2e18aaaSAndroid Build Coastguard Worker if os.path.exists(os.path.join(self._mod_path, 178*c2e18aaaSAndroid Build Coastguard Worker constant.ANDROID_MANIFEST)): 179*c2e18aaaSAndroid Build Coastguard Worker self._facet = templates.FACET 180*c2e18aaaSAndroid Build Coastguard Worker 181*c2e18aaaSAndroid Build Coastguard Worker def _generate_srcs(self): 182*c2e18aaaSAndroid Build Coastguard Worker """Generates the source urls of the project's iml file.""" 183*c2e18aaaSAndroid Build Coastguard Worker srcs = [] 184*c2e18aaaSAndroid Build Coastguard Worker framework_srcs = [] 185*c2e18aaaSAndroid Build Coastguard Worker for src in self._mod_info.get(constant.KEY_SRCS, []): 186*c2e18aaaSAndroid Build Coastguard Worker if constant.FRAMEWORK_PATH in src: 187*c2e18aaaSAndroid Build Coastguard Worker framework_srcs.append(templates.SOURCE.format( 188*c2e18aaaSAndroid Build Coastguard Worker SRC=os.path.join(self._android_root, src), 189*c2e18aaaSAndroid Build Coastguard Worker IS_TEST='false')) 190*c2e18aaaSAndroid Build Coastguard Worker continue 191*c2e18aaaSAndroid Build Coastguard Worker srcs.append(templates.SOURCE.format( 192*c2e18aaaSAndroid Build Coastguard Worker SRC=os.path.join(self._android_root, src), 193*c2e18aaaSAndroid Build Coastguard Worker IS_TEST='false')) 194*c2e18aaaSAndroid Build Coastguard Worker for test in self._mod_info.get(constant.KEY_TESTS, []): 195*c2e18aaaSAndroid Build Coastguard Worker if constant.FRAMEWORK_PATH in test: 196*c2e18aaaSAndroid Build Coastguard Worker framework_srcs.append(templates.SOURCE.format( 197*c2e18aaaSAndroid Build Coastguard Worker SRC=os.path.join(self._android_root, test), 198*c2e18aaaSAndroid Build Coastguard Worker IS_TEST='true')) 199*c2e18aaaSAndroid Build Coastguard Worker continue 200*c2e18aaaSAndroid Build Coastguard Worker srcs.append(templates.SOURCE.format( 201*c2e18aaaSAndroid Build Coastguard Worker SRC=os.path.join(self._android_root, test), 202*c2e18aaaSAndroid Build Coastguard Worker IS_TEST='true')) 203*c2e18aaaSAndroid Build Coastguard Worker self._excludes = self._mod_info.get(constant.KEY_EXCLUDES, '') 204*c2e18aaaSAndroid Build Coastguard Worker 205*c2e18aaaSAndroid Build Coastguard Worker # For solving duplicate package name, frameworks/base will be higher 206*c2e18aaaSAndroid Build Coastguard Worker # priority. 207*c2e18aaaSAndroid Build Coastguard Worker srcs = sorted(framework_srcs) + sorted(srcs) 208*c2e18aaaSAndroid Build Coastguard Worker self._srcs = templates.CONTENT.format(MODULE_PATH=self._mod_path, 209*c2e18aaaSAndroid Build Coastguard Worker EXCLUDES=self._excludes, 210*c2e18aaaSAndroid Build Coastguard Worker SOURCES=''.join(srcs)) 211*c2e18aaaSAndroid Build Coastguard Worker 212*c2e18aaaSAndroid Build Coastguard Worker def _generate_dep_srcs(self): 213*c2e18aaaSAndroid Build Coastguard Worker """Generates the source urls of the dependencies.iml.""" 214*c2e18aaaSAndroid Build Coastguard Worker srcs = [] 215*c2e18aaaSAndroid Build Coastguard Worker for src in self._mod_info.get(constant.KEY_SRCS, []): 216*c2e18aaaSAndroid Build Coastguard Worker srcs.append(templates.OTHER_SOURCE.format( 217*c2e18aaaSAndroid Build Coastguard Worker SRC=os.path.join(self._android_root, src), 218*c2e18aaaSAndroid Build Coastguard Worker IS_TEST='false')) 219*c2e18aaaSAndroid Build Coastguard Worker for test in self._mod_info.get(constant.KEY_TESTS, []): 220*c2e18aaaSAndroid Build Coastguard Worker srcs.append(templates.OTHER_SOURCE.format( 221*c2e18aaaSAndroid Build Coastguard Worker SRC=os.path.join(self._android_root, test), 222*c2e18aaaSAndroid Build Coastguard Worker IS_TEST='true')) 223*c2e18aaaSAndroid Build Coastguard Worker self._srcs = ''.join(sorted(srcs)) 224*c2e18aaaSAndroid Build Coastguard Worker 225*c2e18aaaSAndroid Build Coastguard Worker def _generate_jars(self): 226*c2e18aaaSAndroid Build Coastguard Worker """Generates the jar urls.""" 227*c2e18aaaSAndroid Build Coastguard Worker for jar in self._mod_info.get(constant.KEY_JARS, []): 228*c2e18aaaSAndroid Build Coastguard Worker self._jars.append(templates.JAR.format( 229*c2e18aaaSAndroid Build Coastguard Worker JAR=os.path.join(self._android_root, jar))) 230*c2e18aaaSAndroid Build Coastguard Worker 231*c2e18aaaSAndroid Build Coastguard Worker def _generate_srcjars(self): 232*c2e18aaaSAndroid Build Coastguard Worker """Generates the srcjar urls.""" 233*c2e18aaaSAndroid Build Coastguard Worker for srcjar in self._mod_info.get(constant.KEY_SRCJARS, []): 234*c2e18aaaSAndroid Build Coastguard Worker self._srcjars.append(templates.SRCJAR.format( 235*c2e18aaaSAndroid Build Coastguard Worker SRCJAR=os.path.join(self._android_root, srcjar))) 236*c2e18aaaSAndroid Build Coastguard Worker 237*c2e18aaaSAndroid Build Coastguard Worker def _generate_dependencies(self): 238*c2e18aaaSAndroid Build Coastguard Worker """Generates the dependency module urls.""" 239*c2e18aaaSAndroid Build Coastguard Worker for dep in self._mod_info.get(constant.KEY_DEPENDENCIES, []): 240*c2e18aaaSAndroid Build Coastguard Worker self._deps.append(templates.DEPENDENCIES.format(MODULE=dep)) 241*c2e18aaaSAndroid Build Coastguard Worker 242*c2e18aaaSAndroid Build Coastguard Worker def _create_iml(self): 243*c2e18aaaSAndroid Build Coastguard Worker """Creates the iml file.""" 244*c2e18aaaSAndroid Build Coastguard Worker content = templates.IML.format(FACET=self._facet, 245*c2e18aaaSAndroid Build Coastguard Worker SOURCES=self._srcs, 246*c2e18aaaSAndroid Build Coastguard Worker JARS=''.join(self._jars), 247*c2e18aaaSAndroid Build Coastguard Worker SRCJARS=''.join(self._srcjars), 248*c2e18aaaSAndroid Build Coastguard Worker DEPENDENCIES=''.join(self._deps)) 249*c2e18aaaSAndroid Build Coastguard Worker common_util.file_generate(self._iml_path, content) 250