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"""Config Android SDK information. 18*c2e18aaaSAndroid Build Coastguard Worker 19*c2e18aaaSAndroid Build Coastguard WorkerIn order to create the configuration of Android SDK in IntelliJ automatically, 20*c2e18aaaSAndroid Build Coastguard Workerparses the Android SDK information from the Android SDK path. 21*c2e18aaaSAndroid Build Coastguard Worker 22*c2e18aaaSAndroid Build Coastguard WorkerUsage example: 23*c2e18aaaSAndroid Build Coastguard Worker android_sdk = AndroidSDK() 24*c2e18aaaSAndroid Build Coastguard Worker android_sdk.path_analysis(default_sdk_path) 25*c2e18aaaSAndroid Build Coastguard Worker api_level = android_sdk.max_api_level 26*c2e18aaaSAndroid Build Coastguard Worker android_sdk_path = android_sdk.android_sdk_path 27*c2e18aaaSAndroid Build Coastguard Worker platform_mapping = android_sdk.platform_mapping 28*c2e18aaaSAndroid Build Coastguard Worker""" 29*c2e18aaaSAndroid Build Coastguard Worker 30*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import absolute_import 31*c2e18aaaSAndroid Build Coastguard Worker 32*c2e18aaaSAndroid Build Coastguard Workerimport glob 33*c2e18aaaSAndroid Build Coastguard Workerimport os 34*c2e18aaaSAndroid Build Coastguard Workerimport re 35*c2e18aaaSAndroid Build Coastguard Worker 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 AndroidSDK: 40*c2e18aaaSAndroid Build Coastguard Worker """Configures API level from the Android SDK path. 41*c2e18aaaSAndroid Build Coastguard Worker 42*c2e18aaaSAndroid Build Coastguard Worker Attributes: 43*c2e18aaaSAndroid Build Coastguard Worker _android_sdk_path: The path to the Android SDK, 44*c2e18aaaSAndroid Build Coastguard Worker None if the Android SDK doesn't exist. 45*c2e18aaaSAndroid Build Coastguard Worker _max_api_level: An integer, the max API level in the platforms folder. 46*c2e18aaaSAndroid Build Coastguard Worker _max_code_name: A string, the code name of the max API level. 47*c2e18aaaSAndroid Build Coastguard Worker _max_folder_name: A string, the folder name corresponding 48*c2e18aaaSAndroid Build Coastguard Worker to the max API level. 49*c2e18aaaSAndroid Build Coastguard Worker _platform_mapping: A dictionary of Android platform versions mapping to 50*c2e18aaaSAndroid Build Coastguard Worker the API level and the Android version code name. e.g. 51*c2e18aaaSAndroid Build Coastguard Worker { 52*c2e18aaaSAndroid Build Coastguard Worker 'android-29': { 53*c2e18aaaSAndroid Build Coastguard Worker 'api_level': 29, 54*c2e18aaaSAndroid Build Coastguard Worker 'code_name': '29', 55*c2e18aaaSAndroid Build Coastguard Worker }, 56*c2e18aaaSAndroid Build Coastguard Worker 'android-Q': { 57*c2e18aaaSAndroid Build Coastguard Worker 'api_level': 29, 58*c2e18aaaSAndroid Build Coastguard Worker 'code_name': 'Q', 59*c2e18aaaSAndroid Build Coastguard Worker } 60*c2e18aaaSAndroid Build Coastguard Worker } 61*c2e18aaaSAndroid Build Coastguard Worker """ 62*c2e18aaaSAndroid Build Coastguard Worker 63*c2e18aaaSAndroid Build Coastguard Worker _API_LEVEL = 'api_level' 64*c2e18aaaSAndroid Build Coastguard Worker _CODE_NAME = 'code_name' 65*c2e18aaaSAndroid Build Coastguard Worker _FOLDER_NAME = 'folder_name' 66*c2e18aaaSAndroid Build Coastguard Worker _RE_API_LEVEL = re.compile( 67*c2e18aaaSAndroid Build Coastguard Worker r'AndroidVersion\.ApiLevel=(?P<api_level>[\d]+)') 68*c2e18aaaSAndroid Build Coastguard Worker _RE_CODE_NAME = re.compile( 69*c2e18aaaSAndroid Build Coastguard Worker r'AndroidVersion\.CodeName=(?P<code_name>[a-zA-Z]+)') 70*c2e18aaaSAndroid Build Coastguard Worker _GLOB_PROPERTIES_FILE = os.path.join('platforms', 'android-*', 71*c2e18aaaSAndroid Build Coastguard Worker 'source.properties') 72*c2e18aaaSAndroid Build Coastguard Worker _INPUT_QUERY_TIMES = 3 73*c2e18aaaSAndroid Build Coastguard Worker _ENTER_ANDROID_SDK_PATH = ('\nThe Android SDK folder:{} doesn\'t exist. ' 74*c2e18aaaSAndroid Build Coastguard Worker 'The debug function "Attach debugger to Android ' 75*c2e18aaaSAndroid Build Coastguard Worker 'process" is disabled without Android SDK in ' 76*c2e18aaaSAndroid Build Coastguard Worker 'IntelliJ or Android Studio. Please set it up ' 77*c2e18aaaSAndroid Build Coastguard Worker 'to enable the function. \nPlease enter the ' 78*c2e18aaaSAndroid Build Coastguard Worker 'absolute path to Android SDK:') 79*c2e18aaaSAndroid Build Coastguard Worker _WARNING_NO_ANDROID_SDK = ('Please install the Android SDK, otherwise the ' 80*c2e18aaaSAndroid Build Coastguard Worker 'debug function "Attach debugger to Android ' 81*c2e18aaaSAndroid Build Coastguard Worker 'process" cannot be enabled in IntelliJ or ' 82*c2e18aaaSAndroid Build Coastguard Worker 'Android Studio.') 83*c2e18aaaSAndroid Build Coastguard Worker 84*c2e18aaaSAndroid Build Coastguard Worker def __init__(self): 85*c2e18aaaSAndroid Build Coastguard Worker """Initializes AndroidSDK.""" 86*c2e18aaaSAndroid Build Coastguard Worker self._max_api_level = 0 87*c2e18aaaSAndroid Build Coastguard Worker self._max_code_name = None 88*c2e18aaaSAndroid Build Coastguard Worker self._max_folder_name = None 89*c2e18aaaSAndroid Build Coastguard Worker self._platform_mapping = {} 90*c2e18aaaSAndroid Build Coastguard Worker self._android_sdk_path = None 91*c2e18aaaSAndroid Build Coastguard Worker 92*c2e18aaaSAndroid Build Coastguard Worker @property 93*c2e18aaaSAndroid Build Coastguard Worker def max_api_level(self): 94*c2e18aaaSAndroid Build Coastguard Worker """Gets the max API level.""" 95*c2e18aaaSAndroid Build Coastguard Worker return self._max_api_level 96*c2e18aaaSAndroid Build Coastguard Worker 97*c2e18aaaSAndroid Build Coastguard Worker @property 98*c2e18aaaSAndroid Build Coastguard Worker def max_code_name(self): 99*c2e18aaaSAndroid Build Coastguard Worker """Gets the max code name.""" 100*c2e18aaaSAndroid Build Coastguard Worker return self._max_code_name 101*c2e18aaaSAndroid Build Coastguard Worker 102*c2e18aaaSAndroid Build Coastguard Worker @property 103*c2e18aaaSAndroid Build Coastguard Worker def max_folder_name(self): 104*c2e18aaaSAndroid Build Coastguard Worker """Gets the max folder name.""" 105*c2e18aaaSAndroid Build Coastguard Worker return self._max_folder_name 106*c2e18aaaSAndroid Build Coastguard Worker 107*c2e18aaaSAndroid Build Coastguard Worker @property 108*c2e18aaaSAndroid Build Coastguard Worker def platform_mapping(self): 109*c2e18aaaSAndroid Build Coastguard Worker """Gets the Android platform mapping.""" 110*c2e18aaaSAndroid Build Coastguard Worker return self._platform_mapping 111*c2e18aaaSAndroid Build Coastguard Worker 112*c2e18aaaSAndroid Build Coastguard Worker @property 113*c2e18aaaSAndroid Build Coastguard Worker def android_sdk_path(self): 114*c2e18aaaSAndroid Build Coastguard Worker """Gets the Android SDK path.""" 115*c2e18aaaSAndroid Build Coastguard Worker return self._android_sdk_path 116*c2e18aaaSAndroid Build Coastguard Worker 117*c2e18aaaSAndroid Build Coastguard Worker def _parse_max_api_level(self): 118*c2e18aaaSAndroid Build Coastguard Worker """Parses the max API level from self._platform_mapping. 119*c2e18aaaSAndroid Build Coastguard Worker 120*c2e18aaaSAndroid Build Coastguard Worker Returns: 121*c2e18aaaSAndroid Build Coastguard Worker An integer of API level and 0 means no Android platform exists. 122*c2e18aaaSAndroid Build Coastguard Worker """ 123*c2e18aaaSAndroid Build Coastguard Worker return max( 124*c2e18aaaSAndroid Build Coastguard Worker [v[self._API_LEVEL] for v in self._platform_mapping.values()], 125*c2e18aaaSAndroid Build Coastguard Worker default=0) 126*c2e18aaaSAndroid Build Coastguard Worker 127*c2e18aaaSAndroid Build Coastguard Worker def _parse_max_code_name(self): 128*c2e18aaaSAndroid Build Coastguard Worker """Parses the max code name from self._platform_mapping. 129*c2e18aaaSAndroid Build Coastguard Worker 130*c2e18aaaSAndroid Build Coastguard Worker Returns: 131*c2e18aaaSAndroid Build Coastguard Worker A string of code name. 132*c2e18aaaSAndroid Build Coastguard Worker """ 133*c2e18aaaSAndroid Build Coastguard Worker code_name = '' 134*c2e18aaaSAndroid Build Coastguard Worker for data in self._platform_mapping.values(): 135*c2e18aaaSAndroid Build Coastguard Worker if (data[self._API_LEVEL] == self._max_api_level 136*c2e18aaaSAndroid Build Coastguard Worker and data[self._CODE_NAME] > code_name): 137*c2e18aaaSAndroid Build Coastguard Worker code_name = data[self._CODE_NAME] 138*c2e18aaaSAndroid Build Coastguard Worker return code_name 139*c2e18aaaSAndroid Build Coastguard Worker 140*c2e18aaaSAndroid Build Coastguard Worker def _get_max_folder_name(self): 141*c2e18aaaSAndroid Build Coastguard Worker """Gets the max folder name from self._platform_mapping. 142*c2e18aaaSAndroid Build Coastguard Worker 143*c2e18aaaSAndroid Build Coastguard Worker Returns: 144*c2e18aaaSAndroid Build Coastguard Worker A string of the folder name corresponding to the max API level. 145*c2e18aaaSAndroid Build Coastguard Worker """ 146*c2e18aaaSAndroid Build Coastguard Worker folder_name = '' 147*c2e18aaaSAndroid Build Coastguard Worker for platform, data in self._platform_mapping.items(): 148*c2e18aaaSAndroid Build Coastguard Worker if (data[self._API_LEVEL] == self.max_api_level 149*c2e18aaaSAndroid Build Coastguard Worker and data[self._CODE_NAME] == self._max_code_name): 150*c2e18aaaSAndroid Build Coastguard Worker folder_name = platform 151*c2e18aaaSAndroid Build Coastguard Worker break 152*c2e18aaaSAndroid Build Coastguard Worker return folder_name 153*c2e18aaaSAndroid Build Coastguard Worker 154*c2e18aaaSAndroid Build Coastguard Worker def _parse_api_info(self, properties_file): 155*c2e18aaaSAndroid Build Coastguard Worker """Parses the API information from the source.properties file. 156*c2e18aaaSAndroid Build Coastguard Worker 157*c2e18aaaSAndroid Build Coastguard Worker For the preview platform like android-Q, the source.properties file 158*c2e18aaaSAndroid Build Coastguard Worker contains two properties named AndroidVersion.ApiLevel, API level of 159*c2e18aaaSAndroid Build Coastguard Worker the platform, and AndroidVersion.CodeName such as Q, the code name of 160*c2e18aaaSAndroid Build Coastguard Worker the platform. 161*c2e18aaaSAndroid Build Coastguard Worker However, the formal platform like android-29, there is no property 162*c2e18aaaSAndroid Build Coastguard Worker AndroidVersion.CodeName. 163*c2e18aaaSAndroid Build Coastguard Worker 164*c2e18aaaSAndroid Build Coastguard Worker Args: 165*c2e18aaaSAndroid Build Coastguard Worker properties_file: A path of the source.properties file. 166*c2e18aaaSAndroid Build Coastguard Worker 167*c2e18aaaSAndroid Build Coastguard Worker Returns: 168*c2e18aaaSAndroid Build Coastguard Worker A tuple contains the API level and Code name of the 169*c2e18aaaSAndroid Build Coastguard Worker source.properties file. 170*c2e18aaaSAndroid Build Coastguard Worker API level: An integer of the platform, e.g. 29. 171*c2e18aaaSAndroid Build Coastguard Worker Code name: A string, e.g. 29 or Q. 172*c2e18aaaSAndroid Build Coastguard Worker """ 173*c2e18aaaSAndroid Build Coastguard Worker api_level = 0 174*c2e18aaaSAndroid Build Coastguard Worker properties = common_util.read_file_content(properties_file) 175*c2e18aaaSAndroid Build Coastguard Worker match_api_level = self._RE_API_LEVEL.search(properties) 176*c2e18aaaSAndroid Build Coastguard Worker if match_api_level: 177*c2e18aaaSAndroid Build Coastguard Worker api_level = match_api_level.group(self._API_LEVEL) 178*c2e18aaaSAndroid Build Coastguard Worker match_code_name = self._RE_CODE_NAME.search(properties) 179*c2e18aaaSAndroid Build Coastguard Worker if match_code_name: 180*c2e18aaaSAndroid Build Coastguard Worker code_name = match_code_name.group(self._CODE_NAME) 181*c2e18aaaSAndroid Build Coastguard Worker else: 182*c2e18aaaSAndroid Build Coastguard Worker code_name = api_level 183*c2e18aaaSAndroid Build Coastguard Worker return api_level, code_name 184*c2e18aaaSAndroid Build Coastguard Worker 185*c2e18aaaSAndroid Build Coastguard Worker def _gen_platform_mapping(self, path): 186*c2e18aaaSAndroid Build Coastguard Worker """Generates the Android platforms mapping. 187*c2e18aaaSAndroid Build Coastguard Worker 188*c2e18aaaSAndroid Build Coastguard Worker Args: 189*c2e18aaaSAndroid Build Coastguard Worker path: A string, the absolute path of Android SDK. 190*c2e18aaaSAndroid Build Coastguard Worker 191*c2e18aaaSAndroid Build Coastguard Worker Returns: 192*c2e18aaaSAndroid Build Coastguard Worker True when successful generates platform mapping, otherwise False. 193*c2e18aaaSAndroid Build Coastguard Worker """ 194*c2e18aaaSAndroid Build Coastguard Worker prop_files = glob.glob(os.path.join(path, self._GLOB_PROPERTIES_FILE)) 195*c2e18aaaSAndroid Build Coastguard Worker for prop_file in prop_files: 196*c2e18aaaSAndroid Build Coastguard Worker api_level, code_name = self._parse_api_info(prop_file) 197*c2e18aaaSAndroid Build Coastguard Worker if not api_level: 198*c2e18aaaSAndroid Build Coastguard Worker continue 199*c2e18aaaSAndroid Build Coastguard Worker platform = os.path.basename(os.path.dirname(prop_file)) 200*c2e18aaaSAndroid Build Coastguard Worker self._platform_mapping[platform] = { 201*c2e18aaaSAndroid Build Coastguard Worker self._API_LEVEL: int(api_level), 202*c2e18aaaSAndroid Build Coastguard Worker self._CODE_NAME: code_name 203*c2e18aaaSAndroid Build Coastguard Worker } 204*c2e18aaaSAndroid Build Coastguard Worker return bool(self._platform_mapping) 205*c2e18aaaSAndroid Build Coastguard Worker 206*c2e18aaaSAndroid Build Coastguard Worker def is_android_sdk_path(self, path): 207*c2e18aaaSAndroid Build Coastguard Worker """Checks if the Android SDK path is correct. 208*c2e18aaaSAndroid Build Coastguard Worker 209*c2e18aaaSAndroid Build Coastguard Worker Confirm the Android SDK path is correct by checking if it has 210*c2e18aaaSAndroid Build Coastguard Worker platform versions. 211*c2e18aaaSAndroid Build Coastguard Worker 212*c2e18aaaSAndroid Build Coastguard Worker Args: 213*c2e18aaaSAndroid Build Coastguard Worker path: A string, the path of Android SDK user input. 214*c2e18aaaSAndroid Build Coastguard Worker 215*c2e18aaaSAndroid Build Coastguard Worker Returns: 216*c2e18aaaSAndroid Build Coastguard Worker True when get a platform version, otherwise False. 217*c2e18aaaSAndroid Build Coastguard Worker """ 218*c2e18aaaSAndroid Build Coastguard Worker if self._gen_platform_mapping(path): 219*c2e18aaaSAndroid Build Coastguard Worker self._android_sdk_path = path 220*c2e18aaaSAndroid Build Coastguard Worker self._max_api_level = self._parse_max_api_level() 221*c2e18aaaSAndroid Build Coastguard Worker self._max_code_name = self._parse_max_code_name() 222*c2e18aaaSAndroid Build Coastguard Worker self._max_folder_name = self._get_max_folder_name() 223*c2e18aaaSAndroid Build Coastguard Worker return True 224*c2e18aaaSAndroid Build Coastguard Worker return False 225*c2e18aaaSAndroid Build Coastguard Worker 226*c2e18aaaSAndroid Build Coastguard Worker def path_analysis(self, sdk_path): 227*c2e18aaaSAndroid Build Coastguard Worker """Analyses the Android SDK path. 228*c2e18aaaSAndroid Build Coastguard Worker 229*c2e18aaaSAndroid Build Coastguard Worker Confirm the path is an Android SDK folder. If it's not correct, ask user 230*c2e18aaaSAndroid Build Coastguard Worker to enter a new one. Skip asking when enter nothing. 231*c2e18aaaSAndroid Build Coastguard Worker 232*c2e18aaaSAndroid Build Coastguard Worker Args: 233*c2e18aaaSAndroid Build Coastguard Worker sdk_path: A string, the path of Android SDK. 234*c2e18aaaSAndroid Build Coastguard Worker 235*c2e18aaaSAndroid Build Coastguard Worker Returns: 236*c2e18aaaSAndroid Build Coastguard Worker True when get an Android SDK path, otherwise False. 237*c2e18aaaSAndroid Build Coastguard Worker """ 238*c2e18aaaSAndroid Build Coastguard Worker for _ in range(self._INPUT_QUERY_TIMES): 239*c2e18aaaSAndroid Build Coastguard Worker if self.is_android_sdk_path(sdk_path): 240*c2e18aaaSAndroid Build Coastguard Worker return True 241*c2e18aaaSAndroid Build Coastguard Worker sdk_path = input(common_util.COLORED_FAIL( 242*c2e18aaaSAndroid Build Coastguard Worker self._ENTER_ANDROID_SDK_PATH.format(sdk_path))) 243*c2e18aaaSAndroid Build Coastguard Worker if not sdk_path: 244*c2e18aaaSAndroid Build Coastguard Worker break 245*c2e18aaaSAndroid Build Coastguard Worker print('\n{} {}\n'.format(common_util.COLORED_INFO('Warning:'), 246*c2e18aaaSAndroid Build Coastguard Worker self._WARNING_NO_ANDROID_SDK)) 247*c2e18aaaSAndroid Build Coastguard Worker return False 248