1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package api 16 17import ( 18 "sort" 19 20 "github.com/google/blueprint/proptools" 21 22 "android/soong/android" 23 "android/soong/genrule" 24 "android/soong/java" 25) 26 27const art = "art.module.public.api" 28const conscrypt = "conscrypt.module.public.api" 29const i18n = "i18n.module.public.api" 30 31var core_libraries_modules = []string{art, conscrypt, i18n} 32 33// The intention behind this soong plugin is to generate a number of "merged" 34// API-related modules that would otherwise require a large amount of very 35// similar Android.bp boilerplate to define. For example, the merged current.txt 36// API definitions (created by merging the non-updatable current.txt with all 37// the module current.txts). This simplifies the addition of new android 38// modules, by reducing the number of genrules etc a new module must be added to. 39 40// The properties of the combined_apis module type. 41type CombinedApisProperties struct { 42 // Module libraries in the bootclasspath 43 Bootclasspath []string 44 // Module libraries on the bootclasspath if include_nonpublic_framework_api is true. 45 Conditional_bootclasspath []string 46 // Module libraries in system server 47 System_server_classpath []string 48} 49 50type CombinedApis struct { 51 android.ModuleBase 52 53 properties CombinedApisProperties 54} 55 56func init() { 57 registerBuildComponents(android.InitRegistrationContext) 58} 59 60func registerBuildComponents(ctx android.RegistrationContext) { 61 ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory) 62} 63 64var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents) 65 66func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) { 67} 68 69type genruleProps struct { 70 Name *string 71 Cmd *string 72 Dists []android.Dist 73 Out []string 74 Srcs []string 75 Tools []string 76 Visibility []string 77} 78 79type libraryProps struct { 80 Name *string 81 Sdk_version *string 82 Static_libs []string 83 Visibility []string 84} 85 86type fgProps struct { 87 Name *string 88 Srcs []string 89 Visibility []string 90} 91 92// Struct to pass parameters for the various merged [current|removed].txt file modules we create. 93type MergedTxtDefinition struct { 94 // "current.txt" or "removed.txt" 95 TxtFilename string 96 // Filename in the new dist dir. "android.txt" or "android-removed.txt" 97 DistFilename string 98 // The module for the non-updatable / non-module part of the api. 99 BaseTxt string 100 // The list of modules that are relevant for this merged txt. 101 Modules []string 102 // The output tag for each module to use.e.g. {.public.api.txt} for current.txt 103 ModuleTag string 104 // public, system, module-lib or system-server 105 Scope string 106} 107 108func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { 109 metalavaCmd := "$(location metalava)" 110 // Silence reflection warnings. See b/168689341 111 metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " 112 metalavaCmd += " --quiet --no-banner --format=v2 " 113 114 filename := txt.TxtFilename 115 if txt.Scope != "public" { 116 filename = txt.Scope + "-" + filename 117 } 118 props := genruleProps{} 119 props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) 120 props.Tools = []string{"metalava"} 121 props.Out = []string{filename} 122 props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)") 123 props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...) 124 props.Dists = []android.Dist{ 125 { 126 Targets: []string{"droidcore"}, 127 Dir: proptools.StringPtr("api"), 128 Dest: proptools.StringPtr(filename), 129 }, 130 { 131 Targets: []string{"api_txt", "sdk"}, 132 Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"), 133 Dest: proptools.StringPtr(txt.DistFilename), 134 }, 135 } 136 props.Visibility = []string{"//visibility:public"} 137 ctx.CreateModule(genrule.GenRuleFactory, &props) 138} 139 140func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) { 141 props := genruleProps{} 142 props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar") 143 props.Tools = []string{"merge_zips"} 144 props.Out = []string{"current.srcjar"} 145 props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)") 146 props.Srcs = append([]string{":api-stubs-docs-non-updatable"}, createSrcs(modules, "{.public.stubs.source}")...) 147 props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind 148 ctx.CreateModule(genrule.GenRuleFactory, &props) 149} 150 151// This produces the same annotations.zip as framework-doc-stubs, but by using 152// outputs from individual modules instead of all the source code. 153func createMergedAnnotations(ctx android.LoadHookContext, modules []string) { 154 props := genruleProps{} 155 props.Name = proptools.StringPtr("sdk-annotations.zip") 156 props.Tools = []string{"merge_annotation_zips", "soong_zip"} 157 props.Out = []string{"annotations.zip"} 158 props.Cmd = proptools.StringPtr("$(location merge_annotation_zips) $(genDir)/out $(in) && " + 159 "$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out") 160 props.Srcs = append([]string{":android-non-updatable-doc-stubs{.annotations.zip}"}, createSrcs(modules, "{.public.annotations.zip}")...) 161 ctx.CreateModule(genrule.GenRuleFactory, &props) 162} 163 164func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) { 165 // For the filtered api versions, we prune all APIs except art module's APIs. because 166 // 1) ART apis are available by default to all modules, while other module-to-module deps are 167 // explicit and probably receive more scrutiny anyway 168 // 2) The number of ART/libcore APIs is large, so not linting them would create a large gap 169 // 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have 170 // per-module lint databases that excludes just that module's APIs. Alas, that's more 171 // difficult to achieve. 172 modules = remove(modules, art) 173 174 props := genruleProps{} 175 props.Name = proptools.StringPtr("api-versions-xml-public-filtered") 176 props.Tools = []string{"api_versions_trimmer"} 177 props.Out = []string{"api-versions-public-filtered.xml"} 178 props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)") 179 // Note: order matters: first parameter is the full api-versions.xml 180 // after that the stubs files in any order 181 // stubs files are all modules that export API surfaces EXCEPT ART 182 props.Srcs = append([]string{":framework-doc-stubs{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...) 183 props.Dists = []android.Dist{{Targets: []string{"sdk"}}} 184 ctx.CreateModule(genrule.GenRuleFactory, &props) 185} 186 187func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) { 188 props := libraryProps{} 189 props.Name = proptools.StringPtr("all-modules-public-stubs") 190 props.Static_libs = transformArray(modules, "", ".stubs") 191 props.Sdk_version = proptools.StringPtr("module_current") 192 props.Visibility = []string{"//frameworks/base"} 193 ctx.CreateModule(java.LibraryFactory, &props) 194} 195 196func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) { 197 props := libraryProps{} 198 props.Name = proptools.StringPtr("all-modules-system-stubs") 199 props.Static_libs = transformArray(modules, "", ".stubs.system") 200 props.Sdk_version = proptools.StringPtr("module_current") 201 props.Visibility = []string{"//frameworks/base"} 202 ctx.CreateModule(java.LibraryFactory, &props) 203} 204 205func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) { 206 // This module is for the "framework-all" module, which should not include the core libraries. 207 modules = removeAll(modules, core_libraries_modules) 208 props := libraryProps{} 209 props.Name = proptools.StringPtr("all-framework-module-impl") 210 props.Static_libs = transformArray(modules, "", ".impl") 211 props.Sdk_version = proptools.StringPtr("module_current") 212 props.Visibility = []string{"//frameworks/base"} 213 ctx.CreateModule(java.LibraryFactory, &props) 214} 215 216func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) { 217 // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes. 218 modules = removeAll(modules, core_libraries_modules) 219 props := libraryProps{} 220 props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api") 221 props.Static_libs = transformArray(modules, "", ".stubs.module_lib") 222 props.Sdk_version = proptools.StringPtr("module_current") 223 props.Visibility = []string{"//frameworks/base"} 224 ctx.CreateModule(java.LibraryFactory, &props) 225} 226 227func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) { 228 props := fgProps{} 229 props.Name = proptools.StringPtr("all-modules-public-stubs-source") 230 props.Srcs = createSrcs(modules, "{.public.stubs.source}") 231 props.Visibility = []string{"//frameworks/base"} 232 ctx.CreateModule(android.FileGroupFactory, &props) 233} 234 235func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) { 236 var textFiles []MergedTxtDefinition 237 238 tagSuffix := []string{".api.txt}", ".removed-api.txt}"} 239 distFilename := []string{"android.txt", "android-removed.txt"} 240 for i, f := range []string{"current.txt", "removed.txt"} { 241 textFiles = append(textFiles, MergedTxtDefinition{ 242 TxtFilename: f, 243 DistFilename: distFilename[i], 244 BaseTxt: ":non-updatable-" + f, 245 Modules: bootclasspath, 246 ModuleTag: "{.public" + tagSuffix[i], 247 Scope: "public", 248 }) 249 textFiles = append(textFiles, MergedTxtDefinition{ 250 TxtFilename: f, 251 DistFilename: distFilename[i], 252 BaseTxt: ":non-updatable-system-" + f, 253 Modules: bootclasspath, 254 ModuleTag: "{.system" + tagSuffix[i], 255 Scope: "system", 256 }) 257 textFiles = append(textFiles, MergedTxtDefinition{ 258 TxtFilename: f, 259 DistFilename: distFilename[i], 260 BaseTxt: ":non-updatable-module-lib-" + f, 261 Modules: bootclasspath, 262 ModuleTag: "{.module-lib" + tagSuffix[i], 263 Scope: "module-lib", 264 }) 265 textFiles = append(textFiles, MergedTxtDefinition{ 266 TxtFilename: f, 267 DistFilename: distFilename[i], 268 BaseTxt: ":non-updatable-system-server-" + f, 269 Modules: system_server_classpath, 270 ModuleTag: "{.system-server" + tagSuffix[i], 271 Scope: "system-server", 272 }) 273 } 274 for _, txt := range textFiles { 275 createMergedTxt(ctx, txt) 276 } 277} 278 279func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { 280 bootclasspath := a.properties.Bootclasspath 281 if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") { 282 bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...) 283 sort.Strings(bootclasspath) 284 } 285 createMergedTxts(ctx, bootclasspath, a.properties.System_server_classpath) 286 287 createMergedStubsSrcjar(ctx, bootclasspath) 288 289 createMergedPublicStubs(ctx, bootclasspath) 290 createMergedSystemStubs(ctx, bootclasspath) 291 createMergedFrameworkModuleLibStubs(ctx, bootclasspath) 292 createMergedFrameworkImpl(ctx, bootclasspath) 293 294 createMergedAnnotations(ctx, bootclasspath) 295 296 createFilteredApiVersions(ctx, bootclasspath) 297 298 createPublicStubsSourceFilegroup(ctx, bootclasspath) 299} 300 301func combinedApisModuleFactory() android.Module { 302 module := &CombinedApis{} 303 module.AddProperties(&module.properties) 304 android.InitAndroidModule(module) 305 android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) 306 return module 307} 308 309// Various utility methods below. 310 311// Creates an array of ":<m><tag>" for each m in <modules>. 312func createSrcs(modules []string, tag string) []string { 313 return transformArray(modules, ":", tag) 314} 315 316// Creates an array of "<prefix><m><suffix>", for each m in <modules>. 317func transformArray(modules []string, prefix, suffix string) []string { 318 a := make([]string, 0, len(modules)) 319 for _, module := range modules { 320 a = append(a, prefix+module+suffix) 321 } 322 return a 323} 324 325func removeAll(s []string, vs []string) []string { 326 for _, v := range vs { 327 s = remove(s, v) 328 } 329 return s 330} 331 332func remove(s []string, v string) []string { 333 s2 := make([]string, 0, len(s)) 334 for _, sv := range s { 335 if sv != v { 336 s2 = append(s2, sv) 337 } 338 } 339 return s2 340} 341