1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.tool.apibuilder; 18 19 import com.android.car.tool.data.ParsedData; 20 21 import java.io.File; 22 import java.io.IOException; 23 import java.nio.charset.StandardCharsets; 24 import java.nio.file.Files; 25 import java.nio.file.Path; 26 import java.nio.file.Paths; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.List; 30 31 public final class GenerateAPI { 32 33 private static final boolean DBG = false; 34 35 private static final String ANDROID_BUILD_TOP = "ANDROID_BUILD_TOP"; 36 private static final String CAR_API_PATH = 37 "/packages/services/Car/car-lib/src/android/car"; 38 private static final String CAR_SERVICE_PATH = 39 "/packages/services/Car/service/src"; 40 private static final String CAR_BUILT_IN_API_PATH = 41 "/packages/services/Car/car-builtin-lib/src/android/car/builtin"; 42 private static final String CSHS_PATH = 43 "/frameworks/opt/car/services/builtInServices/src"; 44 private static final String CAR_API_ANNOTATION_TEST_FILE = 45 "/packages/services/Car/tests/carservice_unit_test/res/raw/car_api_classes.txt"; 46 private static final String CAR_BUILT_IN_ANNOTATION_TEST_FILE = 47 "/packages/services/Car/tests/carservice_unit_test/res/raw/" 48 + "car_built_in_api_classes.txt"; 49 private static final String CAR_HIDDEN_API_FILE = 50 "/packages/services/Car/tests/carservice_unit_test/res/raw/" 51 + "car_hidden_apis.txt"; 52 private static final String CAR_ADDEDINORBEFORE_API_FILE = 53 "/packages/services/Car/tests/carservice_unit_test/res/raw/" 54 + "car_addedinorbefore_apis.txt"; 55 private static final String CSHS_NON_HIDDEN_CLASSES_FILE = 56 "/frameworks/opt/car/services/builtInServices/tests/res/raw/CSHS_classes.txt"; 57 58 private static final String PRINT_CLASSES = "--print-classes"; 59 private static final String PRINT_NON_HIDDEN_CLASSES_CSHS = "--print-non-hidden-classes-CSHS"; 60 private static final String UPDATE_CLASSES = "--update-classes"; 61 private static final String UPDATE_NON_HIDDEN_CLASSES_CSHS = "--update-non-hidden-classes-CSHS"; 62 private static final String PRINT_HIDDEN_APIS = "--print-hidden-apis"; 63 private static final String PRINT_HIDDEN_APIS_WITH_CONSTR = "--print-hidden-apis-with-constr"; 64 private static final String UPDATE_HIDDEN_APIS = "--update-hidden-apis"; 65 private static final String PRINT_ALL_APIS = "--print-all-apis"; 66 private static final String PRINT_ALL_APIS_WITH_CONSTR = "--print-all-apis-with-constr"; 67 private static final String UPDATE_APIS_WITH_ADDEDINORBEFORE = 68 "--update-apis-with-addedinorbefore"; 69 private static final String PLATFORM_VERSION_ASSERTION_CHECK = 70 "--platform-version-assertion-check"; 71 private static final String PRINT_ALL_APIS_WITH_CAR_VERSION = 72 "--print-all-apis-with-car-version"; 73 private static final String PRINT_INCORRECT_REQUIRES_API_USAGE_IN_CAR_SERVICE = 74 "--print-incorrect-requires-api-usage-in-car-service"; 75 private static final String PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CAR_BUILT_IN = 76 "--print-addedin-without-requires-api-in-car-built-in"; 77 private static final String PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CSHS = 78 "--print-addedin-without-requires-api-in-CSHS"; 79 private static final String ROOT_DIR = "--root-dir"; 80 main(final String[] args)81 public static void main(final String[] args) throws Exception { 82 try { 83 if (args.length == 0) { 84 printHelp(); 85 return; 86 } 87 88 boolean runForCSHS = false; 89 boolean printClasses = false; 90 boolean printNonHiddenClassesCSHS = false; 91 boolean updateClasses = false; 92 boolean updateNonHiddenClassesCSHS = false; 93 boolean printHiddenApis = false; 94 boolean printHiddenApisWithConstr = false; 95 boolean updateHiddenApis = false; 96 boolean printAllApis = false; 97 boolean printAllApisWithConstr = false; 98 boolean updateApisWithAddedinorbefore = false; 99 boolean platformVersionCheck = false; 100 boolean printAllApisWithCarVersion = false; 101 boolean printIncorrectRequiresApiUsageInCarService = false; 102 boolean printAddedinWithoutRequiresApiInCarBuiltIn = false; 103 boolean printAddedinWithoutRequiresApiInCSHS = false; 104 String rootDir = System.getenv(ANDROID_BUILD_TOP); 105 // If print request is more than one. Use marker to separate data. This would be useful 106 // for executing multiple requests in one go. 107 int printRequests = 0; 108 109 for (int i = 0; i < args.length; i++) { 110 String arg = args[i]; 111 switch (arg) { 112 case PRINT_CLASSES: 113 printClasses = true; 114 printRequests++; 115 break; 116 case PRINT_NON_HIDDEN_CLASSES_CSHS: 117 printNonHiddenClassesCSHS = true; 118 runForCSHS = true; 119 printRequests++; 120 break; 121 case UPDATE_CLASSES: 122 updateClasses = true; 123 break; 124 case UPDATE_NON_HIDDEN_CLASSES_CSHS: 125 updateNonHiddenClassesCSHS = true; 126 runForCSHS = true; 127 break; 128 case PRINT_HIDDEN_APIS: 129 printHiddenApis = true; 130 printRequests++; 131 break; 132 case PRINT_HIDDEN_APIS_WITH_CONSTR: 133 printHiddenApisWithConstr = true; 134 printRequests++; 135 break; 136 case UPDATE_HIDDEN_APIS: 137 updateHiddenApis = true; 138 break; 139 case PRINT_ALL_APIS: 140 printAllApis = true; 141 printRequests++; 142 break; 143 case PRINT_ALL_APIS_WITH_CONSTR: 144 printAllApisWithConstr = true; 145 printRequests++; 146 break; 147 case UPDATE_APIS_WITH_ADDEDINORBEFORE: 148 updateApisWithAddedinorbefore = true; 149 break; 150 case ROOT_DIR: 151 rootDir = args[++i]; 152 break; 153 case PLATFORM_VERSION_ASSERTION_CHECK: 154 platformVersionCheck = true; 155 break; 156 case PRINT_ALL_APIS_WITH_CAR_VERSION: 157 printAllApisWithCarVersion = true; 158 break; 159 case PRINT_INCORRECT_REQUIRES_API_USAGE_IN_CAR_SERVICE: 160 printIncorrectRequiresApiUsageInCarService = true; 161 break; 162 case PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CAR_BUILT_IN: 163 printAddedinWithoutRequiresApiInCarBuiltIn = true; 164 break; 165 case PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CSHS: 166 printAddedinWithoutRequiresApiInCSHS = true; 167 runForCSHS = true; 168 printRequests++; 169 break; 170 default: 171 System.out.println("Incorrect Arguments."); 172 printHelp(); 173 return; 174 } 175 } 176 177 // rootDir must be set. 178 if (rootDir == null || rootDir.isEmpty()) { 179 System.out.println("Root dir not set."); 180 printHelp(); 181 return; 182 } 183 184 // Do CarServiceHelperService related stuff here 185 if (runForCSHS) { 186 List<File> allJavaFiles_CSHS = getAllFiles(new File(rootDir + CSHS_PATH)); 187 ParsedData parsedDataCSHS = new ParsedData(); 188 ParsedDataBuilder.populateParsedData(allJavaFiles_CSHS, parsedDataCSHS); 189 if (printNonHiddenClassesCSHS) { 190 printMarker(printRequests, PRINT_NON_HIDDEN_CLASSES_CSHS); 191 print(ParsedDataHelper.getNonHiddenClassNamesOnly(parsedDataCSHS)); 192 } 193 if (printAddedinWithoutRequiresApiInCSHS) { 194 printMarker(printRequests, PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CSHS); 195 print(ParsedDataHelper.getIncorrectRequiresApi(parsedDataCSHS)); 196 } 197 if (updateNonHiddenClassesCSHS) { 198 write(rootDir + CSHS_NON_HIDDEN_CLASSES_FILE, 199 ParsedDataHelper.getNonHiddenClassNamesOnly(parsedDataCSHS)); 200 } 201 return; 202 } 203 204 List<File> allJavaFiles_carLib = getAllFiles(new File(rootDir + CAR_API_PATH)); 205 List<File> allJavaFiles_carBuiltInLib = getAllFiles( 206 new File(rootDir + CAR_BUILT_IN_API_PATH)); 207 208 ParsedData parsedDataCarLib = new ParsedData(); 209 ParsedData parsedDataCarBuiltinLib = new ParsedData(); 210 ParsedDataBuilder.populateParsedData(allJavaFiles_carLib, parsedDataCarLib); 211 ParsedDataBuilder.populateParsedData(allJavaFiles_carBuiltInLib, 212 parsedDataCarBuiltinLib); 213 214 if (printClasses) { 215 printMarker(printRequests, PRINT_CLASSES); 216 print(ParsedDataHelper.getClassNamesOnly(parsedDataCarLib)); 217 print(ParsedDataHelper.getClassNamesOnly(parsedDataCarBuiltinLib)); 218 } 219 220 if (updateClasses) { 221 write(rootDir + CAR_API_ANNOTATION_TEST_FILE, 222 ParsedDataHelper.getClassNamesOnly(parsedDataCarLib)); 223 write(rootDir + CAR_BUILT_IN_ANNOTATION_TEST_FILE, 224 ParsedDataHelper.getClassNamesOnly(parsedDataCarBuiltinLib)); 225 } 226 227 if (updateHiddenApis) { 228 write(rootDir + CAR_HIDDEN_API_FILE, 229 ParsedDataHelper.getHiddenApisOnly(parsedDataCarLib)); 230 } 231 232 if (printHiddenApis) { 233 printMarker(printRequests, PRINT_HIDDEN_APIS); 234 print(ParsedDataHelper.getHiddenApisOnly(parsedDataCarLib)); 235 } 236 237 if (printHiddenApisWithConstr) { 238 printMarker(printRequests, PRINT_HIDDEN_APIS_WITH_CONSTR); 239 print(ParsedDataHelper.getHiddenApisWithHiddenConstructor(parsedDataCarLib)); 240 } 241 242 if (printAllApis) { 243 printMarker(printRequests, PRINT_ALL_APIS); 244 print(ParsedDataHelper.getAllApis(parsedDataCarLib)); 245 } 246 247 if (printAllApisWithConstr) { 248 printMarker(printRequests, PRINT_ALL_APIS_WITH_CONSTR); 249 print(ParsedDataHelper.getAllApisWithConstructor(parsedDataCarLib)); 250 } 251 252 if (updateApisWithAddedinorbefore) { 253 write(rootDir + CAR_ADDEDINORBEFORE_API_FILE, 254 ParsedDataHelper.getAddedInOrBeforeApisOnly(parsedDataCarLib)); 255 } 256 if (platformVersionCheck) { 257 printMarker(printRequests, PLATFORM_VERSION_ASSERTION_CHECK); 258 print(ParsedDataHelper.checkAssertPlatformVersionAtLeast(parsedDataCarLib)); 259 } 260 if (printAllApisWithCarVersion) { 261 printMarker(printRequests, PRINT_ALL_APIS_WITH_CAR_VERSION); 262 print(ParsedDataHelper.getApisWithVersion(parsedDataCarLib)); 263 print(ParsedDataHelper.getApisWithVersion(parsedDataCarBuiltinLib)); 264 } 265 if (printIncorrectRequiresApiUsageInCarService) { 266 printMarker(printRequests, PRINT_INCORRECT_REQUIRES_API_USAGE_IN_CAR_SERVICE); 267 List<File> allJavaFiles_CarService = 268 getAllFiles(new File(rootDir + CAR_SERVICE_PATH)); 269 ParsedData parsedDataCarService = new ParsedData(); 270 ParsedDataBuilder.populateParsedData(allJavaFiles_CarService, parsedDataCarService); 271 print(ParsedDataHelper.getIncorrectRequiresApiUsage(parsedDataCarService)); 272 } 273 if (printAddedinWithoutRequiresApiInCarBuiltIn) { 274 printMarker(printRequests, PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CAR_BUILT_IN); 275 print(ParsedDataHelper.getIncorrectRequiresApi(parsedDataCarBuiltinLib)); 276 } 277 } catch (Exception e) { 278 throw e; 279 } 280 } 281 printMarker(int printRequests, String request)282 private static void printMarker(int printRequests, String request) { 283 if (printRequests > 1) { 284 // Should not change this marker since it would break the other script. 285 System.out.println("Start-" + request); 286 } 287 } 288 print(List<String> data)289 private static void print(List<String> data) { 290 for (String string : data) { 291 System.out.println(string); 292 } 293 } 294 write(String filePath, List<String> data)295 private static void write(String filePath, List<String> data) throws IOException { 296 Collections.sort(data); 297 Path file = Paths.get(filePath); 298 Files.write(file, data, StandardCharsets.UTF_8); 299 } 300 printHelp()301 private static void printHelp() { 302 System.out.println("**** Help ****"); 303 System.out.println("At least one argument is required. Supported arguments - "); 304 System.out.println(PRINT_CLASSES + " prints the list of valid class and" 305 + " interfaces."); 306 System.out.println(UPDATE_CLASSES + " updates the test file with the list" 307 + " of valid class and interfaces. These files are updated" 308 + " tests/carservice_unit_test/res/raw/car_api_classes.txt and" 309 + " tests/carservice_unit_test/res/raw/car_built_in_api_classes.txt"); 310 System.out.println(PRINT_HIDDEN_APIS + " prints hidden api list."); 311 System.out.println(PRINT_HIDDEN_APIS_WITH_CONSTR + " generates hidden api list with" 312 + " hidden constructors."); 313 System.out.println(UPDATE_HIDDEN_APIS + " generates hidden api list. " 314 + "Results would be updated in " + CAR_HIDDEN_API_FILE); 315 System.out.println(PRINT_ALL_APIS + " prints all apis"); 316 System.out.println(PRINT_ALL_APIS + " prints all apis and constructors."); 317 System.out.println(UPDATE_APIS_WITH_ADDEDINORBEFORE 318 + " generates the api list that contains the @AddedInOrBefore annotation. " 319 + "Results would be updated in " + CAR_ADDEDINORBEFORE_API_FILE); 320 System.out.println(PLATFORM_VERSION_ASSERTION_CHECK 321 + " : Iterates through APIs to ensure that APIs added after TIRAMISU_x have call " 322 + "assertPlatformVersionAtLeast with the correct minPlatformVersion."); 323 System.out.println(PRINT_ALL_APIS_WITH_CAR_VERSION 324 + " : Prints a list of all apis along with their min car version."); 325 System.out.println("Second argument is value of Git Root Directory. By default, " 326 + "it is environment variable ANDROID_BUILD_TOP. If environment variable is not set" 327 + "then provide using" + ROOT_DIR + " <directory>"); 328 } 329 getAllFiles(File folderName)330 private static List<File> getAllFiles(File folderName) { 331 List<File> allFiles = new ArrayList<>(); 332 File[] files = folderName.listFiles(); 333 334 for (int i = 0; i < files.length; i++) { 335 if (files[i].isFile() && files[i].getName().endsWith(".java")) { 336 if (DBG) { 337 System.out.printf("File added %s\n", files[i]); 338 } 339 allFiles.add(files[i]); 340 } 341 if (files[i].isDirectory()) { 342 allFiles.addAll(getAllFiles(files[i])); 343 } 344 } 345 // List files doesn't guarantee fixed order on all systems. It is better to sort the list. 346 Collections.sort(allFiles); 347 return allFiles; 348 } 349 } 350