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/bazel" 24 "android/soong/genrule" 25 "android/soong/java" 26) 27 28const art = "art.module.public.api" 29const conscrypt = "conscrypt.module.public.api" 30const i18n = "i18n.module.public.api" 31const virtualization = "framework-virtualization" 32 33var core_libraries_modules = []string{art, conscrypt, i18n} 34 35// List of modules that are not yet updatable, and hence they can still compile 36// against hidden APIs. These modules are filtered out when building the 37// updatable-framework-module-impl (because updatable-framework-module-impl is 38// built against module_current SDK). Instead they are directly statically 39// linked into the all-framework-module-lib, which is building against hidden 40// APIs. 41// In addition, the modules in this list are allowed to contribute to test APIs 42// stubs. 43var non_updatable_modules = []string{virtualization} 44 45// The intention behind this soong plugin is to generate a number of "merged" 46// API-related modules that would otherwise require a large amount of very 47// similar Android.bp boilerplate to define. For example, the merged current.txt 48// API definitions (created by merging the non-updatable current.txt with all 49// the module current.txts). This simplifies the addition of new android 50// modules, by reducing the number of genrules etc a new module must be added to. 51 52// The properties of the combined_apis module type. 53type CombinedApisProperties struct { 54 // Module libraries in the bootclasspath 55 Bootclasspath []string 56 // Module libraries on the bootclasspath if include_nonpublic_framework_api is true. 57 Conditional_bootclasspath []string 58 // Module libraries in system server 59 System_server_classpath []string 60} 61 62type CombinedApis struct { 63 android.ModuleBase 64 android.BazelModuleBase 65 66 properties CombinedApisProperties 67} 68 69func init() { 70 registerBuildComponents(android.InitRegistrationContext) 71} 72 73func registerBuildComponents(ctx android.RegistrationContext) { 74 ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory) 75} 76 77var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents) 78 79func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) { 80} 81 82type genruleProps struct { 83 Name *string 84 Cmd *string 85 Dists []android.Dist 86 Out []string 87 Srcs []string 88 Tools []string 89 Visibility []string 90} 91 92type libraryProps struct { 93 Name *string 94 Sdk_version *string 95 Static_libs []string 96 Visibility []string 97} 98 99type fgProps struct { 100 Name *string 101 Srcs []string 102 Visibility []string 103} 104 105type Bazel_module struct { 106 Bp2build_available *bool 107} 108type bazelProperties struct { 109 *Bazel_module 110} 111 112var bp2buildNotAvailable = bazelProperties{ 113 &Bazel_module{ 114 Bp2build_available: proptools.BoolPtr(false), 115 }, 116} 117 118// Struct to pass parameters for the various merged [current|removed].txt file modules we create. 119type MergedTxtDefinition struct { 120 // "current.txt" or "removed.txt" 121 TxtFilename string 122 // Filename in the new dist dir. "android.txt" or "android-removed.txt" 123 DistFilename string 124 // The module for the non-updatable / non-module part of the api. 125 BaseTxt string 126 // The list of modules that are relevant for this merged txt. 127 Modules []string 128 // The output tag for each module to use.e.g. {.public.api.txt} for current.txt 129 ModuleTag string 130 // public, system, module-lib or system-server 131 Scope string 132} 133 134func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { 135 metalavaCmd := "$(location metalava)" 136 // Silence reflection warnings. See b/168689341 137 metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " 138 metalavaCmd += " --quiet --no-banner --format=v2 " 139 140 filename := txt.TxtFilename 141 if txt.Scope != "public" { 142 filename = txt.Scope + "-" + filename 143 } 144 props := genruleProps{} 145 props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) 146 props.Tools = []string{"metalava"} 147 props.Out = []string{filename} 148 props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)") 149 props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...) 150 props.Dists = []android.Dist{ 151 { 152 Targets: []string{"droidcore"}, 153 Dir: proptools.StringPtr("api"), 154 Dest: proptools.StringPtr(filename), 155 }, 156 { 157 Targets: []string{"api_txt", "sdk"}, 158 Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"), 159 Dest: proptools.StringPtr(txt.DistFilename), 160 }, 161 } 162 props.Visibility = []string{"//visibility:public"} 163 ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable) 164} 165 166func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) { 167 for _, i := range []struct{ 168 name string 169 tag string 170 modules []string 171 }{ 172 { 173 name: "all-modules-public-annotations", 174 tag: "{.public.annotations.zip}", 175 modules: modules, 176 }, { 177 name: "all-modules-system-annotations", 178 tag: "{.system.annotations.zip}", 179 modules: modules, 180 }, { 181 name: "all-modules-module-lib-annotations", 182 tag: "{.module-lib.annotations.zip}", 183 modules: modules, 184 }, { 185 name: "all-modules-system-server-annotations", 186 tag: "{.system-server.annotations.zip}", 187 modules: system_server_modules, 188 }, 189 } { 190 props := fgProps{} 191 props.Name = proptools.StringPtr(i.name) 192 props.Srcs = createSrcs(i.modules, i.tag) 193 ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable) 194 } 195} 196 197func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) { 198 props := libraryProps{} 199 props.Name = proptools.StringPtr("all-modules-public-stubs") 200 props.Static_libs = transformArray(modules, "", ".stubs") 201 props.Sdk_version = proptools.StringPtr("module_current") 202 props.Visibility = []string{"//frameworks/base"} 203 ctx.CreateModule(java.LibraryFactory, &props) 204} 205 206func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) { 207 // First create the all-updatable-modules-system-stubs 208 { 209 updatable_modules := removeAll(modules, non_updatable_modules) 210 props := libraryProps{} 211 props.Name = proptools.StringPtr("all-updatable-modules-system-stubs") 212 props.Static_libs = transformArray(updatable_modules, "", ".stubs.system") 213 props.Sdk_version = proptools.StringPtr("module_current") 214 props.Visibility = []string{"//frameworks/base"} 215 ctx.CreateModule(java.LibraryFactory, &props) 216 } 217 // Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules 218 // into all-modules-system-stubs. 219 { 220 props := libraryProps{} 221 props.Name = proptools.StringPtr("all-modules-system-stubs") 222 props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.system") 223 props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs") 224 props.Sdk_version = proptools.StringPtr("module_current") 225 props.Visibility = []string{"//frameworks/base"} 226 ctx.CreateModule(java.LibraryFactory, &props) 227 } 228} 229 230func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) { 231 props := libraryProps{} 232 props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs") 233 props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.test") 234 props.Sdk_version = proptools.StringPtr("module_current") 235 props.Visibility = []string{"//frameworks/base"} 236 ctx.CreateModule(java.LibraryFactory, &props) 237} 238 239func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) { 240 // This module is for the "framework-all" module, which should not include the core libraries. 241 modules = removeAll(modules, core_libraries_modules) 242 // Remove the modules that belong to non-updatable APEXes since those are allowed to compile 243 // against unstable APIs. 244 modules = removeAll(modules, non_updatable_modules) 245 // First create updatable-framework-module-impl, which contains all updatable modules. 246 // This module compiles against module_lib SDK. 247 { 248 props := libraryProps{} 249 props.Name = proptools.StringPtr("updatable-framework-module-impl") 250 props.Static_libs = transformArray(modules, "", ".impl") 251 props.Sdk_version = proptools.StringPtr("module_current") 252 props.Visibility = []string{"//frameworks/base"} 253 ctx.CreateModule(java.LibraryFactory, &props) 254 } 255 256 // Now create all-framework-module-impl, which contains updatable-framework-module-impl 257 // and all non-updatable modules. This module compiles against hidden APIs. 258 { 259 props := libraryProps{} 260 props.Name = proptools.StringPtr("all-framework-module-impl") 261 props.Static_libs = transformArray(non_updatable_modules, "", ".impl") 262 props.Static_libs = append(props.Static_libs, "updatable-framework-module-impl") 263 props.Sdk_version = proptools.StringPtr("core_platform") 264 props.Visibility = []string{"//frameworks/base"} 265 ctx.CreateModule(java.LibraryFactory, &props) 266 } 267} 268 269func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) { 270 // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes. 271 modules = removeAll(modules, core_libraries_modules) 272 props := libraryProps{} 273 props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api") 274 props.Static_libs = transformArray(modules, "", ".stubs.module_lib") 275 props.Sdk_version = proptools.StringPtr("module_current") 276 props.Visibility = []string{"//frameworks/base"} 277 ctx.CreateModule(java.LibraryFactory, &props) 278} 279 280func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) { 281 props := fgProps{} 282 props.Name = proptools.StringPtr("all-modules-public-stubs-source") 283 props.Srcs = createSrcs(modules, "{.public.stubs.source}") 284 props.Visibility = []string{"//frameworks/base"} 285 ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable) 286} 287 288func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) { 289 var textFiles []MergedTxtDefinition 290 291 tagSuffix := []string{".api.txt}", ".removed-api.txt}"} 292 distFilename := []string{"android.txt", "android-removed.txt"} 293 for i, f := range []string{"current.txt", "removed.txt"} { 294 textFiles = append(textFiles, MergedTxtDefinition{ 295 TxtFilename: f, 296 DistFilename: distFilename[i], 297 BaseTxt: ":non-updatable-" + f, 298 Modules: bootclasspath, 299 ModuleTag: "{.public" + tagSuffix[i], 300 Scope: "public", 301 }) 302 textFiles = append(textFiles, MergedTxtDefinition{ 303 TxtFilename: f, 304 DistFilename: distFilename[i], 305 BaseTxt: ":non-updatable-system-" + f, 306 Modules: bootclasspath, 307 ModuleTag: "{.system" + tagSuffix[i], 308 Scope: "system", 309 }) 310 textFiles = append(textFiles, MergedTxtDefinition{ 311 TxtFilename: f, 312 DistFilename: distFilename[i], 313 BaseTxt: ":non-updatable-module-lib-" + f, 314 Modules: bootclasspath, 315 ModuleTag: "{.module-lib" + tagSuffix[i], 316 Scope: "module-lib", 317 }) 318 textFiles = append(textFiles, MergedTxtDefinition{ 319 TxtFilename: f, 320 DistFilename: distFilename[i], 321 BaseTxt: ":non-updatable-system-server-" + f, 322 Modules: system_server_classpath, 323 ModuleTag: "{.system-server" + tagSuffix[i], 324 Scope: "system-server", 325 }) 326 } 327 for _, txt := range textFiles { 328 createMergedTxt(ctx, txt) 329 } 330} 331 332func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { 333 bootclasspath := a.properties.Bootclasspath 334 system_server_classpath := a.properties.System_server_classpath 335 if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") { 336 bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...) 337 sort.Strings(bootclasspath) 338 } 339 createMergedTxts(ctx, bootclasspath, system_server_classpath) 340 341 createMergedPublicStubs(ctx, bootclasspath) 342 createMergedSystemStubs(ctx, bootclasspath) 343 createMergedTestStubsForNonUpdatableModules(ctx) 344 createMergedFrameworkModuleLibStubs(ctx, bootclasspath) 345 createMergedFrameworkImpl(ctx, bootclasspath) 346 347 createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath) 348 349 createPublicStubsSourceFilegroup(ctx, bootclasspath) 350} 351 352func combinedApisModuleFactory() android.Module { 353 module := &CombinedApis{} 354 module.AddProperties(&module.properties) 355 android.InitAndroidModule(module) 356 android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) 357 android.InitBazelModule(module) 358 return module 359} 360 361type bazelCombinedApisAttributes struct { 362 Scope bazel.StringAttribute 363 Base bazel.LabelAttribute 364 Deps bazel.LabelListAttribute 365} 366 367// combined_apis bp2build converter 368func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) { 369 basePrefix := "non-updatable" 370 scopeToSuffix := map[string]string{ 371 "public": "-current.txt", 372 "system": "-system-current.txt", 373 "module-lib": "-module-lib-current.txt", 374 "system-server": "-system-server-current.txt", 375 } 376 377 for scopeName, suffix := range scopeToSuffix{ 378 name := a.Name() + suffix 379 380 var scope bazel.StringAttribute 381 scope.SetValue(scopeName) 382 383 var base bazel.LabelAttribute 384 base.SetValue(android.BazelLabelForModuleDepSingle(ctx, basePrefix+suffix)) 385 386 var deps bazel.LabelListAttribute 387 classpath := a.properties.Bootclasspath 388 if scopeName == "system-server" { 389 classpath = a.properties.System_server_classpath 390 } 391 deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, classpath)) 392 393 attrs := bazelCombinedApisAttributes{ 394 Scope: scope, 395 Base: base, 396 Deps: deps, 397 } 398 props := bazel.BazelTargetModuleProperties{ 399 Rule_class: "merged_txts", 400 Bzl_load_location: "//build/bazel/rules/java:merged_txts.bzl", 401 } 402 ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, &attrs) 403 } 404} 405 406// Various utility methods below. 407 408// Creates an array of ":<m><tag>" for each m in <modules>. 409func createSrcs(modules []string, tag string) []string { 410 return transformArray(modules, ":", tag) 411} 412 413// Creates an array of "<prefix><m><suffix>", for each m in <modules>. 414func transformArray(modules []string, prefix, suffix string) []string { 415 a := make([]string, 0, len(modules)) 416 for _, module := range modules { 417 a = append(a, prefix+module+suffix) 418 } 419 return a 420} 421 422func removeAll(s []string, vs []string) []string { 423 for _, v := range vs { 424 s = remove(s, v) 425 } 426 return s 427} 428 429func remove(s []string, v string) []string { 430 s2 := make([]string, 0, len(s)) 431 for _, sv := range s { 432 if sv != v { 433 s2 = append(s2, sv) 434 } 435 } 436 return s2 437} 438