Files
Jihoon Kang 839adbdf70 Generate merged txt file module with the checked in api files
This change introduces generating the genrule modules that generate the
merged txt files from the checked in api signature files of the
bootclasspath java_sdk_library modules.

Test: m nothing && inspect the ninja graph
Bug: 434908055
Flag: EXEMPT build refactor
Change-Id: I34199a02339307fa4e46668787bfbcebe6eea641
2025-09-03 21:24:55 +00:00

589 lines
22 KiB
Go

// Copyright (C) 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api
import (
"fmt"
"slices"
"github.com/google/blueprint/proptools"
"android/soong/android"
"android/soong/java"
)
const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
const virtualization = "framework-virtualization"
const location = "framework-location"
const platformCrashrecovery = "framework-platformcrashrecovery"
const ondeviceintelligence = "framework-ondeviceintelligence-platform"
const platformTelephony = "framework-platformtelephony"
var core_libraries_modules = []string{art, conscrypt, i18n}
// List of modules that are not yet updatable, and hence they can still compile
// against hidden APIs. These modules are filtered out when building the
// updatable-framework-module-impl (because updatable-framework-module-impl is
// built against module_current SDK). Instead they are directly statically
// linked into the all-framework-module-lib, which is building against hidden
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
var non_updatable_modules = []string{virtualization, location, platformCrashrecovery, ondeviceintelligence, platformTelephony}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
// similar Android.bp boilerplate to define. For example, the merged current.txt
// API definitions (created by merging the non-updatable current.txt with all
// the module current.txts). This simplifies the addition of new android
// modules, by reducing the number of genrules etc a new module must be added to.
// The properties of the combined_apis module type.
type CombinedApisProperties struct {
// Module libraries in the bootclasspath
Bootclasspath proptools.Configurable[[]string]
// Module libraries on the bootclasspath if include_nonpublic_framework_api is true.
Conditional_bootclasspath []string
// Module libraries in system server
System_server_classpath proptools.Configurable[[]string]
}
type CombinedApis struct {
android.ModuleBase
properties CombinedApisProperties
}
func init() {
registerBuildComponents(android.InitRegistrationContext)
}
func registerBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
}
var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
func (a *CombinedApis) apiFingerprintStubDeps(ctx android.BottomUpMutatorContext) []string {
bootClasspath := a.properties.Bootclasspath.GetOrDefault(ctx, nil)
systemServerClasspath := a.properties.System_server_classpath.GetOrDefault(ctx, nil)
var ret []string
ret = append(
ret,
transformArray(bootClasspath, "", ".stubs")...,
)
ret = append(
ret,
transformArray(bootClasspath, "", ".stubs.system")...,
)
ret = append(
ret,
transformArray(bootClasspath, "", ".stubs.module_lib")...,
)
ret = append(
ret,
transformArray(systemServerClasspath, "", ".stubs.system_server")...,
)
return ret
}
func (a *CombinedApis) DepsMutator(ctx android.BottomUpMutatorContext) {
ctx.AddDependency(ctx.Module(), nil, a.apiFingerprintStubDeps(ctx)...)
}
func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
javaInfo, ok := android.OtherModuleProvider(ctx, child, java.JavaInfoProvider)
if ok && javaInfo.AndroidLibraryDependencyInfo != nil && child.Name() != "framework-res" {
// Stubs of BCP and SSCP libraries should not have any dependencies on apps
// This check ensures that we do not run into circular dependencies when UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true
ctx.ModuleErrorf(
"Module %s is not a valid dependency of the stub library %s\n."+
"If this dependency has been added via `libs` of java_sdk_library, please move it to `impl_only_libs`\n",
child.Name(), parent.Name())
return false // error detected
}
return true
})
}
type genruleProps struct {
Name *string
Cmd *string
Dists []android.Dist
Out []string
Srcs proptools.Configurable[[]string]
Tools []string
Visibility []string
}
type libraryProps struct {
Name *string
Sdk_version *string
Static_libs proptools.Configurable[[]string]
Visibility []string
Defaults []string
Is_stubs_module *bool
}
type fgProps struct {
Name *string
Srcs proptools.Configurable[[]string]
Device_common_srcs proptools.Configurable[[]string]
Visibility []string
}
type defaultsProps struct {
Name *string
Api_surface *string
Api_contributions []string
Defaults_visibility []string
Previous_api *string
}
// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
type MergedTxtDefinition struct {
// "current.txt" or "removed.txt"
TxtFilename string
// Filename in the new dist dir. "android.txt" or "android-removed.txt"
DistFilename string
// The module for the non-updatable / non-module part of the api.
BaseTxt string
// The list of modules that are relevant for this merged txt.
Modules proptools.Configurable[[]string]
// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
ModuleTag string
// public, system, module-lib or system-server
Scope string
}
func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition, stubsTypeSuffix string, doDist bool) {
metalavaCmd := "$(location metalava)"
// Silence reflection warnings. See b/168689341
metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
metalavaCmd += " --quiet merge-signatures --format=v2 "
filename := txt.TxtFilename
if txt.Scope != "public" {
filename = txt.Scope + "-" + filename
}
moduleName := ctx.ModuleName() + stubsTypeSuffix + filename
props := genruleProps{}
props.Name = proptools.StringPtr(moduleName)
props.Tools = []string{"metalava"}
props.Out = []string{filename}
props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
props.Srcs = proptools.NewSimpleConfigurable([]string{txt.BaseTxt})
props.Srcs.Append(createSrcs(txt.Modules, txt.ModuleTag))
if doDist {
props.Dists = []android.Dist{
{
Targets: []string{"droidcore"},
Dir: proptools.StringPtr("api"),
Dest: proptools.StringPtr(filename),
},
{
Targets: []string{"api_txt", "sdk"},
Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
Dest: proptools.StringPtr(txt.DistFilename),
},
}
}
props.Visibility = []string{"//visibility:public"}
ctx.CreateModule(java.GenRuleFactory, &props)
}
func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules proptools.Configurable[[]string]) {
for _, i := range []struct {
name string
tag string
modules proptools.Configurable[[]string]
}{
{
name: "all-modules-public-annotations",
tag: "{.public.annotations.zip}",
modules: modules,
}, {
name: "all-modules-system-annotations",
tag: "{.system.annotations.zip}",
modules: modules,
}, {
name: "all-modules-module-lib-annotations",
tag: "{.module-lib.annotations.zip}",
modules: modules,
}, {
name: "all-modules-system-server-annotations",
tag: "{.system-server.annotations.zip}",
modules: system_server_modules,
},
} {
props := fgProps{}
props.Name = proptools.StringPtr(i.name)
props.Device_common_srcs = createSrcs(i.modules, i.tag)
ctx.CreateModule(android.FileGroupFactory, &props)
}
}
func createMergedPublicStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
modules = modules.Clone()
transformConfigurableArray(modules, "", ".stubs")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs")
props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
modules = modules.Clone()
transformConfigurableArray(modules, "", ".stubs.exportable")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs-exportable")
props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
func createMergedSystemStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
// First create the all-updatable-modules-system-stubs
{
updatable_modules := modules.Clone()
removeAll(updatable_modules, non_updatable_modules)
transformConfigurableArray(updatable_modules, "", ".stubs.system")
props := libraryProps{}
props.Name = proptools.StringPtr("all-updatable-modules-system-stubs")
props.Static_libs = updatable_modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
// into all-modules-system-stubs.
{
static_libs := transformArray(non_updatable_modules, "", ".stubs.system")
static_libs = append(static_libs, "all-updatable-modules-system-stubs")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-system-stubs")
props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
}
func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
// First create the all-updatable-modules-system-stubs
{
updatable_modules := modules.Clone()
removeAll(updatable_modules, non_updatable_modules)
transformConfigurableArray(updatable_modules, "", ".stubs.exportable.system")
props := libraryProps{}
props.Name = proptools.StringPtr("all-updatable-modules-system-stubs-exportable")
props.Static_libs = updatable_modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
// into all-modules-system-stubs.
{
static_libs := transformArray(non_updatable_modules, "", ".stubs.exportable.system")
static_libs = append(static_libs, "all-updatable-modules-system-stubs-exportable")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-system-stubs-exportable")
props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
}
func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs")
props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.test"))
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
func createMergedTestExportableStubsForNonUpdatableModules(ctx android.LoadHookContext) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs-exportable")
props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.exportable.test"))
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
func createMergedFrameworkImpl(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
modules = modules.Clone()
// This module is for the "framework-all" module, which should not include the core libraries.
removeAll(modules, core_libraries_modules)
// Remove the modules that belong to non-updatable APEXes since those are allowed to compile
// against unstable APIs.
removeAll(modules, non_updatable_modules)
// First create updatable-framework-module-impl, which contains all updatable modules.
// This module compiles against module_lib SDK.
{
transformConfigurableArray(modules, "", ".impl")
props := libraryProps{}
props.Name = proptools.StringPtr("updatable-framework-module-impl")
props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
}
// Now create all-framework-module-impl, which contains updatable-framework-module-impl
// and all non-updatable modules. This module compiles against hidden APIs.
{
static_libs := transformArray(non_updatable_modules, "", ".impl")
static_libs = append(static_libs, "updatable-framework-module-impl")
props := libraryProps{}
props.Name = proptools.StringPtr("all-framework-module-impl")
props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("core_platform")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
}
}
func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
modules = modules.Clone()
// The user of this module compiles against the "core" SDK and against non-updatable modules,
// so remove to avoid dupes.
removeAll(modules, core_libraries_modules)
removeAll(modules, non_updatable_modules)
transformConfigurableArray(modules, "", ".stubs.exportable.module_lib")
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api-exportable")
props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
modules = modules.Clone()
// The user of this module compiles against the "core" SDK and against non-updatable modules,
// so remove to avoid dupes.
removeAll(modules, core_libraries_modules)
removeAll(modules, non_updatable_modules)
transformConfigurableArray(modules, "", ".stubs.module_lib")
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContext, bootclasspath, system_server_classpath proptools.Configurable[[]string]) {
// The user of this module compiles against the "core" SDK and against non-updatable bootclasspathModules,
// so remove to avoid dupes.
bootclasspathModules := bootclasspath.Clone()
removeAll(bootclasspathModules, core_libraries_modules)
removeAll(bootclasspathModules, non_updatable_modules)
transformConfigurableArray(bootclasspathModules, "", ".stubs.exportable.module_lib")
system_server_classpath = system_server_classpath.Clone()
transformConfigurableArray(system_server_classpath, "", ".stubs.exportable.system_server")
// Include all the module-lib APIs from the bootclasspath libraries.
// Then add all the system-server APIs from the service-* libraries.
bootclasspathModules.Append(system_server_classpath)
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-system_server_api-exportable")
props.Static_libs = bootclasspathModules
props.Sdk_version = proptools.StringPtr("system_server_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
props := fgProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs-source-exportable")
transformConfigurableArray(modules, "", ".stubs.source")
props.Device_common_srcs = createSrcs(modules, "{.exportable}")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(android.FileGroupFactory, &props)
}
func createMergedTxts(
ctx android.LoadHookContext,
bootclasspath proptools.Configurable[[]string],
system_server_classpath proptools.Configurable[[]string],
baseTxtModulePrefix string,
stubsTypeSuffix string,
doDist bool,
checkedIn bool,
) {
if checkedIn && doDist {
ctx.ModuleErrorf("Checked in api txt files cannot be disted.")
}
checkedInPrefix := ""
if checkedIn {
checkedInPrefix = "checked-in-"
}
var textFiles []MergedTxtDefinition
tagSuffix := []string{
fmt.Sprintf(".%sapi.txt}", checkedInPrefix),
fmt.Sprintf(".%sremoved-api.txt}", checkedInPrefix),
}
distFilename := []string{"android.txt", "android-removed.txt"}
for i, f := range []string{"current.txt", "removed.txt"} {
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: checkedInPrefix + f,
DistFilename: distFilename[i],
BaseTxt: ":" + baseTxtModulePrefix + f,
Modules: bootclasspath,
ModuleTag: "{.public" + tagSuffix[i],
Scope: "public",
})
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: checkedInPrefix + f,
DistFilename: distFilename[i],
BaseTxt: ":" + baseTxtModulePrefix + "system-" + f,
Modules: bootclasspath,
ModuleTag: "{.system" + tagSuffix[i],
Scope: "system",
})
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: checkedInPrefix + f,
DistFilename: distFilename[i],
BaseTxt: ":" + baseTxtModulePrefix + "module-lib-" + f,
Modules: bootclasspath,
ModuleTag: "{.module-lib" + tagSuffix[i],
Scope: "module-lib",
})
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: checkedInPrefix + f,
DistFilename: distFilename[i],
BaseTxt: ":" + baseTxtModulePrefix + "system-server-" + f,
Modules: system_server_classpath,
ModuleTag: "{.system-server" + tagSuffix[i],
Scope: "system-server",
})
}
for _, txt := range textFiles {
createMergedTxt(ctx, txt, stubsTypeSuffix, doDist)
}
}
func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
bootclasspath := a.properties.Bootclasspath.Clone()
system_server_classpath := a.properties.System_server_classpath.Clone()
if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
bootclasspath.AppendSimpleValue(a.properties.Conditional_bootclasspath)
}
createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false, false)
createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false, true)
createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true, false)
createMergedPublicStubs(ctx, bootclasspath)
createMergedSystemStubs(ctx, bootclasspath)
createMergedTestStubsForNonUpdatableModules(ctx)
createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
createMergedFrameworkImpl(ctx, bootclasspath)
createMergedPublicExportableStubs(ctx, bootclasspath)
createMergedSystemExportableStubs(ctx, bootclasspath)
createMergedTestExportableStubsForNonUpdatableModules(ctx)
createMergedFrameworkModuleLibExportableStubs(ctx, bootclasspath)
createMergedFrameworkSystemServerExportableStubs(ctx, bootclasspath, system_server_classpath)
createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
createPublicStubsSourceFilegroup(ctx, bootclasspath)
}
func combinedApisModuleFactory() android.Module {
module := &CombinedApis{}
module.AddProperties(&module.properties)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
return module
}
// Various utility methods below.
// Creates an array of ":<m><tag>" for each m in <modules>.
func createSrcs(modules proptools.Configurable[[]string], tag string) proptools.Configurable[[]string] {
result := modules.Clone()
transformConfigurableArray(result, ":", tag)
return result
}
// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
func transformArray(modules []string, prefix, suffix string) []string {
a := make([]string, 0, len(modules))
for _, module := range modules {
a = append(a, prefix+module+suffix)
}
return a
}
// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
func transformConfigurableArray(modules proptools.Configurable[[]string], prefix, suffix string) {
modules.AddPostProcessor(func(s []string) []string {
return transformArray(s, prefix, suffix)
})
}
func removeAll(s proptools.Configurable[[]string], vs []string) {
s.AddPostProcessor(func(s []string) []string {
a := make([]string, 0, len(s))
for _, module := range s {
if !slices.Contains(vs, module) {
a = append(a, module)
}
}
return a
})
}
func remove(s []string, v string) []string {
s2 := make([]string, 0, len(s))
for _, sv := range s {
if sv != v {
s2 = append(s2, sv)
}
}
return s2
}