1 /* <lambda>null2 * Copyright (C) 2025 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.tools.metalava.cli.historical 18 19 import com.android.tools.metalava.ARG_CONFIG_FILE 20 import com.android.tools.metalava.ConfigFileOptions 21 import com.android.tools.metalava.OptionsDelegate 22 import com.android.tools.metalava.apiSurfacesFromConfig 23 import com.android.tools.metalava.apilevels.ApiVersion 24 import com.android.tools.metalava.cli.common.MetalavaSubCommand 25 import com.android.tools.metalava.cli.common.cliError 26 import com.android.tools.metalava.cli.common.executionEnvironment 27 import com.android.tools.metalava.cli.common.existingDir 28 import com.android.tools.metalava.cli.common.map 29 import com.android.tools.metalava.cli.common.progressTracker 30 import com.android.tools.metalava.cli.common.stderr 31 import com.android.tools.metalava.cli.common.stdout 32 import com.android.tools.metalava.cli.signature.SignatureFormatOptions 33 import com.android.tools.metalava.jar.StandaloneJarCodebaseLoader 34 import com.android.tools.metalava.reporter.BasicReporter 35 import com.github.ajalt.clikt.parameters.arguments.argument 36 import com.github.ajalt.clikt.parameters.arguments.validate 37 import com.github.ajalt.clikt.parameters.groups.provideDelegate 38 import com.github.ajalt.clikt.parameters.options.option 39 import com.github.ajalt.clikt.parameters.options.split 40 41 private const val ARG_ANDROID_ROOT_DIR = "<android-root-dir>" 42 43 const val ARG_API_SURFACES = "--api-surfaces" 44 45 class AndroidJarsToSignaturesCommand : 46 MetalavaSubCommand( 47 help = 48 """ 49 Rewrite the signature files in the `prebuilts/sdk` directory in the Android source tree. 50 51 It does this by reading the API defined in the corresponding `android.jar` files. 52 """ 53 .trimIndent(), 54 ) { 55 56 private val androidRootDir by 57 argument( 58 ARG_ANDROID_ROOT_DIR, 59 help = 60 """ 61 The root directory of the Android source tree. The new signature files will 62 be generated in the `prebuilts/sdk/<api>/<surface>/api/android.txt` 63 sub-directories. 64 """ 65 .trimIndent() 66 ) 67 .existingDir() 68 .validate { 69 require(it.resolve("prebuilts/sdk").isDirectory) { 70 cliError("$ARG_ANDROID_ROOT_DIR does not point to an Android source tree") 71 } 72 } 73 74 private val configFileOptions by ConfigFileOptions() 75 76 /** Add options for controlling the format of the generated files. */ 77 private val signatureFormat by SignatureFormatOptions() 78 79 /** Optional set of [ApiVersion]s to convert. */ 80 private val apiVersions by 81 option( 82 help = 83 """ 84 Comma separated list of api versions to convert. If unspecified then all 85 versions will be converted. 86 """ 87 .trimIndent(), 88 metavar = "<api-version-list>", 89 ) 90 .split(",") 91 .map { list -> list?.map { ApiVersion.fromString(it) }?.toSet() } 92 93 private val apiSurfaceNames by 94 option( 95 ARG_API_SURFACES, 96 help = 97 """ 98 Comma separated list of api surfaces to convert. If unspecified then only 99 `public` will be converted. 100 """ 101 .trimIndent(), 102 metavar = "<api-surface-list>", 103 ) 104 .split(",") 105 .map { list -> list?.distinct() ?: listOf("public") } 106 107 override fun run() { 108 // Make sure that none of the code called by this command accesses the global `options` 109 // property. 110 OptionsDelegate.disallowAccess() 111 112 // Create a self-consistent set of ApiSurfaces that either need to be converted or 113 // contribute to a surface that needs to be converted. 114 val apiSurfaces = 115 configFileOptions.config.apiSurfaces?.let { apiSurfacesConfig -> 116 117 // Collect the transitive closure of the configured ApiSurfaceConfigs for the 118 // required 119 // API surfaces. 120 val surfaceConfigs = buildSet { 121 for (name in apiSurfaceNames) { 122 val surfaceConfig = 123 apiSurfacesConfig.getByNameOrError(name) { 124 "$ARG_API_SURFACES (`$it`) does not match an <api-surface> in a --config-file" 125 } 126 addAll(apiSurfacesConfig.contributesTo(surfaceConfig)) 127 } 128 } 129 130 apiSurfacesFromConfig(surfaceConfigs, apiSurfaceNames.last()) 131 } 132 ?: error( 133 "$ARG_CONFIG_FILE is either not specified or does not define any API surfaces" 134 ) 135 136 // Get the selected ApiSurface(s). The lookup is guaranteed to not be null because the 137 // name was checked above. Sort, so any extended ApiSurface comes before an extended 138 // ApiSurface. 139 val selectedApiSurfaces = apiSurfaceNames.map { apiSurfaces.byName[it]!! }.sorted() 140 141 StandaloneJarCodebaseLoader.create( 142 executionEnvironment.disableStderrDumping(), 143 progressTracker, 144 BasicReporter(stderr), 145 ) 146 .use { jarCodebaseLoader -> 147 ConvertJarsToSignatureFiles( 148 stderr, 149 stdout, 150 progressTracker, 151 signatureFormat.fileFormat, 152 apiVersions, 153 apiSurfaces, 154 selectedApiSurfaces, 155 jarCodebaseLoader, 156 androidRootDir, 157 ) 158 .convertJars() 159 } 160 } 161 } 162