• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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