blob: 768e57a11b613ff5df94ea933d4dcfb88fda34f9 [file] [log] [blame] [edit]
// Copyright 2024 Google Inc. All rights reserved.
//
// 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 java
import (
"android/soong/android"
"android/soong/etc"
"fmt"
"path"
"strings"
"github.com/google/blueprint/proptools"
)
// ---------------------------------------------------------------------------------------------
// Naming scheme of the submodules generated by java_sdk_library and java_sdk_library_import
// ---------------------------------------------------------------------------------------------
const (
sdkXmlFileSuffix = ".xml"
implLibSuffix = ".impl"
)
func implLibraryModuleName(sdkLibName string) string {
return sdkLibName + implLibSuffix
}
// Module name of the runtime implementation library
func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
return implLibraryModuleName(c.module.RootLibraryName())
}
// Module name of the XML file for the lib
func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
return c.module.RootLibraryName() + sdkXmlFileSuffix
}
// Name of the java_library module that compiles the stubs source.
func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
baseName := c.module.RootLibraryName()
return apiScope.stubsLibraryModuleName(baseName)
}
// Name of the java_library module that compiles the exportable stubs source.
func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
baseName := c.module.RootLibraryName()
return apiScope.exportableStubsLibraryModuleName(baseName)
}
// Name of the droidstubs module that generates the stubs source and may also
// generate/check the API.
func (c *commonToSdkLibraryAndImport) droidstubsModuleName(apiScope *apiScope) string {
baseName := c.module.RootLibraryName()
return apiScope.stubsSourceModuleName(baseName)
}
// Name of the java_api_library module that generates the from-text stubs source
// and compiles to a jar file.
func (c *commonToSdkLibraryAndImport) fromTextStubsLibraryModuleName(apiScope *apiScope) string {
baseName := c.module.RootLibraryName()
return apiScope.apiLibraryModuleName(baseName)
}
// Name of the java_library module that compiles the stubs
// generated from source Java files.
func (c *commonToSdkLibraryAndImport) fromSourceStubsLibraryModuleName(apiScope *apiScope) string {
baseName := c.module.RootLibraryName()
return apiScope.sourceStubsLibraryModuleName(baseName)
}
// Name of the java_library module that compiles the exportable stubs
// generated from source Java files.
func (c *commonToSdkLibraryAndImport) exportableFromSourceStubsLibraryModuleName(apiScope *apiScope) string {
baseName := c.module.RootLibraryName()
return apiScope.exportableSourceStubsLibraryModuleName(baseName)
}
// ---------------------------------------------------------------------------------------------
// Build rules of the submodules generated by java_sdk_library.
// java_sdk_library "framework-foo" generates the following submodules:
//
// - "framework-foo.impl" (type: [Library]): the implementation library, which generates the
// compilation outputs that include the implementation details and the private apis
// (i.e. class/methods that are annotated @hide).
//
// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [Droidstubs]): droidstubs module that
// generates the stubs and the api files for the given api scope.
//
// - "framework-foo.stubs.<[apiScope.name]>" (type: [Library]): stub library module that
// provides the compilation output of the stubs to the reverse dependencies. The module
// itself does not perform any compilation actions; the module statically depends on one of
// the from-source stub module or the from-text stub configuration based on the build
// configuration.
//
// - "framework-foo.stubs.<[apiScope.name]>.from-source" (type: [Library]): stub library module
// that compiles the stubs generated by the droidstubs submodule. This module is a static
// dependency of the stub library module when
// [android/soong/android/config.BuildFromTextStub()] is false.
//
// - "framework-foo.stubs.<[apiScope.name]>.from-text" (type: [ApiLibrary]): api library module
// that generates and compiles the stubs from the api files checked in the tree instead of
// the source Java files (e.g. *-current.txt files). This module is a static dependency of
// the stub library module when [android/soong/android/config.BuildFromTextStub()] is true.
//
// - "framework-foo.stubs.exportable.<[apiScope.name]>" (type: [Library]): stub library module
// that provides the "exportable" stubs. "exportable" stubs are the stubs that do not
// include in-development flagged apis. This module is only used for SDK builds to generate
// the SDK artifacts, and not purposed for consumption for other modules.
//
// - "framework-foo.stubs.exportable.<[apiScope.name]>.from-source" (type: [Library]): stub
// library module that compiles the "exportable" stubs generated by the droidstubs
// submodule. This module is always a static dependency of the "exportable" stub library
// module given that from-text stubs cannot be used for SDK builds as it does not contain
// documentations.
//
// - "framework-foo.xml" (type: [sdkLibraryXml]): xml library that generates the permission xml
// file, which allows [SdkLibrary] to be used with <uses-permission> tag in the
// AndroidManifest.xml files.
// ---------------------------------------------------------------------------------------------
// Creates the implementation [Library] with ".impl" suffix.
func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility)
staticLibs := module.properties.Static_libs.Clone()
staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs)
props := struct {
Name *string
Enabled proptools.Configurable[bool]
Visibility []string
Libs []string
Static_libs proptools.Configurable[[]string]
Apex_available []string
Stem *string
}{
Name: proptools.StringPtr(module.implLibraryModuleName()),
Enabled: module.EnabledProperty(),
Visibility: visibility,
Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
Static_libs: staticLibs,
// Pass the apex_available settings down so that the impl library can be statically
// embedded within a library that is added to an APEX. Needed for updatable-media.
Apex_available: module.ApexAvailable(),
Stem: proptools.StringPtr(module.Name()),
}
properties := []interface{}{
&module.properties,
&module.protoProperties,
&module.deviceProperties,
&module.dexProperties,
&module.dexpreoptProperties,
&module.linter.properties,
&module.overridableProperties,
&props,
module.sdkComponentPropertiesForChildLibrary(),
}
mctx.CreateModule(LibraryFactory, properties...)
}
// Creates the [Droidstubs] module with ".stubs.source.<[apiScope.name]>" that creates stubs
// source files from the given full source files and also updates and checks the API
// specification files (i.e. "*-current.txt", "*-removed.txt" files).
func (module *SdkLibrary) createDroidstubs(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
props := struct {
Name *string
Enabled proptools.Configurable[bool]
Visibility []string
Srcs []string
Installable *bool
Sdk_version *string
Api_surface *string
System_modules *string
Libs proptools.Configurable[[]string]
Output_javadoc_comments *bool
Arg_files []string
Args *string
Java_version *string
Annotations_enabled *bool
Merge_annotations_dirs []string
Merge_inclusion_annotations_dirs []string
Generate_stubs *bool
Previous_api *string
Aconfig_declarations []string
Check_api struct {
Current ApiToCheck
Last_released ApiToCheck
Api_lint struct {
Enabled *bool
New_since *string
Baseline_file *string
}
}
Aidl struct {
Include_dirs []string
Local_include_dirs []string
}
Dists []android.Dist
}{}
// The stubs source processing uses the same compile time classpath when extracting the
// API from the implementation library as it does when compiling it. i.e. the same
// * sdk version
// * system_modules
// * libs (static_libs/libs)
props.Name = proptools.StringPtr(name)
props.Enabled = module.EnabledProperty()
props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility)
props.Srcs = append(props.Srcs, module.properties.Srcs...)
props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
props.Sdk_version = module.deviceProperties.Sdk_version
props.Api_surface = &apiScope.name
props.System_modules = module.deviceProperties.System_modules
props.Installable = proptools.BoolPtr(false)
// A droiddoc module has only one Libs property and doesn't distinguish between
// shared libs and static libs. So we need to add both of these libs to Libs property.
props.Libs = proptools.NewConfigurable[[]string](nil, nil)
props.Libs.AppendSimpleValue(module.properties.Libs)
props.Libs.Append(module.properties.Static_libs)
props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
props.Java_version = module.properties.Java_version
props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled
props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations
droidstubsArgs := []string{}
if len(module.sdkLibraryProperties.Api_packages) != 0 {
droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
}
droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
disabledWarnings := []string{"HiddenSuperclass"}
if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
disabledWarnings = append(disabledWarnings,
"BroadcastBehavior",
"DeprecationMismatch",
"MissingPermission",
"SdkConstant",
"Todo",
)
}
droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
// Output Javadoc comments for public scope.
if apiScope == apiScopePublic {
props.Output_javadoc_comments = proptools.BoolPtr(true)
}
// Add in scope specific arguments.
droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
// List of APIs identified from the provided source files are created. They are later
// compared against to the not-yet-released (a.k.a current) list of APIs and to the
// last-released (a.k.a numbered) list of API.
currentApiFileName := apiScope.apiFilePrefix + "current.txt"
removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
apiDir := module.getApiDir()
currentApiFileName = path.Join(apiDir, currentApiFileName)
removedApiFileName = path.Join(apiDir, removedApiFileName)
// check against the not-yet-release API
props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
if module.compareAgainstLatestApi(apiScope) {
// check against the latest released API
latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
props.Previous_api = latestApiFilegroupName
props.Check_api.Last_released.Api_file = latestApiFilegroupName
props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
module.latestRemovedApiFilegroupName(apiScope))
props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
module.latestIncompatibilitiesFilegroupName(apiScope))
if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
// Enable api lint.
props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
props.Check_api.Api_lint.New_since = latestApiFilegroupName
// If it exists then pass a lint-baseline.txt through to droidstubs.
baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
if err != nil {
mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
}
if len(paths) == 1 {
props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
} else if len(paths) != 0 {
mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
}
}
}
if !Bool(module.sdkLibraryProperties.No_dist) {
// Dist the api txt and removed api txt artifacts for sdk builds.
distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
stubsTypeTagPrefix := ""
if mctx.Config().ReleaseHiddenApiExportableStubs() {
stubsTypeTagPrefix = ".exportable"
}
for _, p := range []struct {
tag string
pattern string
}{
// "exportable" api files are copied to the dist directory instead of the
// "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag
// is set. Otherwise, the "everything" api files are copied to the dist directory.
{tag: "%s.api.txt", pattern: "%s.txt"},
{tag: "%s.removed-api.txt", pattern: "%s-removed.txt"},
} {
props.Dists = append(props.Dists, android.Dist{
Targets: []string{"sdk", "win_sdk"},
Dir: distDir,
Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
Tag: proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)),
})
}
}
mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
}
type libraryProperties struct {
Name *string
Enabled proptools.Configurable[bool]
Visibility []string
Srcs []string
Installable *bool
Sdk_version *string
System_modules *string
Patch_module *string
Libs []string
Static_libs []string
Compile_dex *bool
Java_version *string
Openjdk9 struct {
Srcs []string
Javacflags []string
}
Dist struct {
Targets []string
Dest *string
Dir *string
Tag *string
}
Is_stubs_module *bool
Stub_contributing_api *string
}
func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
props := libraryProperties{}
props.Enabled = module.EnabledProperty()
props.Visibility = []string{"//visibility:override", "//visibility:private"}
// sources are generated from the droiddoc
sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
props.Sdk_version = proptools.StringPtr(sdkVersion)
props.System_modules = module.deviceProperties.System_modules
props.Patch_module = module.properties.Patch_module
props.Installable = proptools.BoolPtr(false)
props.Libs = module.sdkLibraryProperties.Stub_only_libs
props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
// The stub-annotations library contains special versions of the annotations
// with CLASS retention policy, so that they're kept.
if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) {
props.Libs = append(props.Libs, "stub-annotations")
}
props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
// interop with older developer tools that don't support 1.9.
props.Java_version = proptools.StringPtr("1.8")
props.Is_stubs_module = proptools.BoolPtr(true)
props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
return props
}
// Creates the from-source stub [Library] with ".stubs.<[apiScope.name]>.from-source" suffix.
func (module *SdkLibrary) createFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
props := module.stubsLibraryProps(mctx, apiScope)
props.Name = proptools.StringPtr(module.fromSourceStubsLibraryModuleName(apiScope))
props.Srcs = []string{":" + module.droidstubsModuleName(apiScope)}
mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
// Creates the "exportable" from-source stub [Library] with
// ".stubs.exportable.<[apiScope.name]>" suffix.
func (module *SdkLibrary) createExportableFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
props := module.stubsLibraryProps(mctx, apiScope)
props.Name = proptools.StringPtr(module.exportableFromSourceStubsLibraryModuleName(apiScope))
props.Srcs = []string{":" + module.droidstubsModuleName(apiScope) + "{.exportable}"}
mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
// Creates the from-text stub [ApiLibrary] with ".stubs.<[apiScope.name]>.from-text" suffix.
func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
props := struct {
Name *string
Enabled proptools.Configurable[bool]
Visibility []string
Api_contributions []string
Libs proptools.Configurable[[]string]
Static_libs []string
System_modules *string
Enable_validation *bool
Stubs_type *string
Sdk_version *string
Previous_api *string
}{}
props.Name = proptools.StringPtr(module.fromTextStubsLibraryModuleName(apiScope))
props.Enabled = module.EnabledProperty()
props.Visibility = []string{"//visibility:override", "//visibility:private"}
apiContributions := []string{}
// Api surfaces are not independent of each other, but have subset relationships,
// and so does the api files. To generate from-text stubs for api surfaces other than public,
// all subset api domains' api_contriubtions must be added as well.
scope := apiScope
for scope != nil {
apiContributions = append(apiContributions, module.droidstubsModuleName(scope)+".api.contribution")
scope = scope.extends
}
if apiScope == apiScopePublic {
additionalApiContribution := module.apiLibraryAdditionalApiContribution()
if additionalApiContribution != "" {
apiContributions = append(apiContributions, additionalApiContribution)
}
}
props.Api_contributions = apiContributions
// Ensure that stub-annotations is added to the classpath before any other libs
props.Libs = proptools.NewConfigurable[[]string](nil, nil)
props.Libs.AppendSimpleValue([]string{"stub-annotations"})
props.Libs.AppendSimpleValue(module.properties.Libs)
props.Libs.Append(module.properties.Static_libs)
props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
props.System_modules = module.deviceProperties.System_modules
props.Enable_validation = proptools.BoolPtr(true)
props.Stubs_type = proptools.StringPtr("everything")
if module.deviceProperties.Sdk_version != nil {
props.Sdk_version = module.deviceProperties.Sdk_version
}
if module.compareAgainstLatestApi(apiScope) {
// check against the latest released API
latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
props.Previous_api = latestApiFilegroupName
}
mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties {
props := libraryProperties{}
props.Enabled = module.EnabledProperty()
props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
props.Sdk_version = proptools.StringPtr(sdkVersion)
props.System_modules = module.deviceProperties.System_modules
// The imports need to be compiled to dex if the java_sdk_library requests it.
compileDex := module.dexProperties.Compile_dex
if module.stubLibrariesCompiledForDex() {
compileDex = proptools.BoolPtr(true)
}
props.Compile_dex = compileDex
props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
if !Bool(module.sdkLibraryProperties.No_dist) && doDist {
props.Dist.Targets = []string{"sdk", "win_sdk"}
props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
props.Dist.Tag = proptools.StringPtr(".jar")
}
props.Is_stubs_module = proptools.BoolPtr(true)
return props
}
// Creates the stub [Library] with ".stubs.<[apiScope.name]>" suffix.
func (module *SdkLibrary) createTopLevelStubsLibrary(
mctx android.DefaultableHookContext, apiScope *apiScope) {
// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
// Add the stub compiling java_library/java_api_library as static lib based on build config
staticLib := module.fromSourceStubsLibraryModuleName(apiScope)
if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
staticLib = module.fromTextStubsLibraryModuleName(apiScope)
}
props.Static_libs = append(props.Static_libs, staticLib)
mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
// Creates the "exportable" stub [Library] with ".stubs.exportable.<[apiScope.name]>" suffix.
func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
mctx android.DefaultableHookContext, apiScope *apiScope) {
// Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true
doDist := mctx.Config().ReleaseHiddenApiExportableStubs()
props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
staticLib := module.exportableFromSourceStubsLibraryModuleName(apiScope)
props.Static_libs = append(props.Static_libs, staticLib)
mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
// Creates the [sdkLibraryXml] with ".xml" suffix.
func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
var moduleMinApiLevelStr = moduleMinApiLevel.String()
if moduleMinApiLevel == android.NoneApiLevel {
moduleMinApiLevelStr = "current"
}
props := struct {
Name *string
Enabled proptools.Configurable[bool]
Lib_name *string
Apex_available []string
On_bootclasspath_since *string
On_bootclasspath_before *string
Min_device_sdk *string
Max_device_sdk *string
Sdk_library_min_api_level *string
Uses_libs_dependencies proptools.Configurable[[]string]
}{
Name: proptools.StringPtr(module.xmlPermissionsModuleName()),
Enabled: module.EnabledProperty(),
Lib_name: proptools.StringPtr(module.BaseModuleName()),
Apex_available: module.ApexProperties.Apex_available,
On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since,
On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before,
Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk,
Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk,
Sdk_library_min_api_level: &moduleMinApiLevelStr,
Uses_libs_dependencies: module.usesLibraryProperties.Uses_libs.Clone(),
}
mctx.CreateModule(sdkLibraryXmlFactory, &props)
}
// ---------------------------------------------------------------------------------------------
// Build rules of the submodules generated by java_sdk_library_import.
// Note that the java_sdk_library_import module does not generate the implementation library.
// Instead, it will create a dependency to the source implemenetation library if one exists.
// java_sdk_library_import "framework-foo" generates the following submodules:
//
// - "framework-foo.stubs.<[apiScope.name]>" (type: [Import]): prebuilt stub library module that
// provides the stub jar file checked in the tree.
//
// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [PrebuiltStubsSources]): prebuilt
// droidstubs module that provides the stub source jar file checked in the tree.
//
// - "framework-foo.stubs.source.<[apiScope.name]>.api.contribution"
// (type [JavaApiContributionImport]): prebuilt java_api_contribution module that provides
// the prebuilt api file for previously released from-text stub generation.
// ---------------------------------------------------------------------------------------------
// Creates the prebuilt stub [Import] with ".stubs.<[apiScope.name]>" suffix.
func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
// Creates a java import for the jar with ".stubs" suffix
props := struct {
Name *string
Source_module_name *string
Created_by_java_sdk_library_name *string
Sdk_version *string
Libs []string
Jars []string
Compile_dex *bool
Is_stubs_module *bool
android.UserSuppliedPrebuiltProperties
}{}
props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
props.Sdk_version = scopeProperties.Sdk_version
// Prepend any of the libs from the legacy public properties to the libs for each of the
// scopes to avoid having to duplicate them in each scope.
props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
props.Jars = scopeProperties.Jars
// The imports are preferred if the java_sdk_library_import is preferred.
props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
// The imports need to be compiled to dex if the java_sdk_library_import requests it.
compileDex := module.properties.Compile_dex
if module.stubLibrariesCompiledForDex() {
compileDex = proptools.BoolPtr(true)
}
props.Compile_dex = compileDex
props.Is_stubs_module = proptools.BoolPtr(true)
mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
props := struct {
Name *string
Source_module_name *string
Created_by_java_sdk_library_name *string
Srcs []string
android.UserSuppliedPrebuiltProperties
}{}
props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope))
props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
props.Srcs = scopeProperties.Stub_srcs
// The stubs source is preferred if the java_sdk_library_import is preferred.
props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
// Creates the prebuilt api contribution [JavaApiContributionImport] with
// ".stubs.source.<[apiScope.name]>.api.contribution" suffix.
func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
api_file := scopeProperties.Current_api
api_surface := &apiScope.name
props := struct {
Name *string
Source_module_name *string
Created_by_java_sdk_library_name *string
Api_surface *string
Api_file *string
Visibility []string
}{}
props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope) + ".api.contribution")
props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
props.Api_surface = api_surface
props.Api_file = api_file
props.Visibility = []string{"//visibility:override", "//visibility:public"}
mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
}
// ---------------------------------------------------------------------------------------------
// End of the build rules of the submodules generated by java_sdk_library_import.
// ---------------------------------------------------------------------------------------------
// Definition of the [sdkLibraryXml] module. The module generates the permissions xml file,
// so that the apps can specify the java_sdk_library using <uses-permission> tag in the
// AndroidManifest.xml file.
type sdkLibraryXml struct {
android.ModuleBase
android.DefaultableModuleBase
android.ApexModuleBase
properties sdkLibraryXmlProperties
outputFilePath android.OutputPath
installDirPath android.InstallPath
hideApexVariantFromMake bool
}
type sdkLibraryXmlProperties struct {
// canonical name of the lib
Lib_name *string
// Signals that this shared library is part of the bootclasspath starting
// on the version indicated in this attribute.
//
// This will make platforms at this level and above to ignore
// <uses-library> tags with this library name because the library is already
// available
On_bootclasspath_since *string
// Signals that this shared library was part of the bootclasspath before
// (but not including) the version indicated in this attribute.
//
// The system will automatically add a <uses-library> tag with this library to
// apps that target any SDK less than the version indicated in this attribute.
On_bootclasspath_before *string
// Indicates that PackageManager should ignore this shared library if the
// platform is below the version indicated in this attribute.
//
// This means that the device won't recognise this library as installed.
Min_device_sdk *string
// Indicates that PackageManager should ignore this shared library if the
// platform is above the version indicated in this attribute.
//
// This means that the device won't recognise this library as installed.
Max_device_sdk *string
// The SdkLibrary's min api level as a string
//
// This value comes from the ApiLevel of the MinSdkVersion property.
Sdk_library_min_api_level *string
// Uses-libs dependencies that the shared library requires to work correctly.
//
// This will add dependency="foo:bar" to the <library> section.
Uses_libs_dependencies proptools.Configurable[[]string]
}
// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
// Not to be used directly by users. java_sdk_library internally uses this.
func sdkLibraryXmlFactory() android.Module {
module := &sdkLibraryXml{}
module.AddProperties(&module.properties)
android.InitApexModule(module)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
func (module *sdkLibraryXml) UniqueApexVariations() bool {
// sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the
// mounted APEX, which contains the name of the APEX.
return true
}
// from android.PrebuiltEtcModule
func (module *sdkLibraryXml) BaseDir() string {
return "etc"
}
// from android.PrebuiltEtcModule
func (module *sdkLibraryXml) SubDir() string {
return "permissions"
}
var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
// from android.ApexModule
func (module *sdkLibraryXml) AvailableFor(what string) bool {
return true
}
func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
// do nothing
}
var _ android.ApexModule = (*sdkLibraryXml)(nil)
// Implements android.ApexModule
func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
return nil
}
// File path to the runtime implementation library
func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string {
implName := proptools.String(module.properties.Lib_name)
if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
// In most cases, this works fine. But when apex_name is set or override_apex is used
// this can be wrong.
return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName)
}
partition := "system"
if module.SocSpecific() {
partition = "vendor"
} else if module.DeviceSpecific() {
partition = "odm"
} else if module.ProductSpecific() {
partition = "product"
} else if module.SystemExtSpecific() {
partition = "system_ext"
}
return "/" + partition + "/framework/" + implName + ".jar"
}
func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
if value == nil {
return ""
}
apiLevel, err := android.ApiLevelFromUser(ctx, *value)
if err != nil {
// attributes in bp files have underscores but in the xml have dashes.
ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
return ""
}
if apiLevel.IsCurrent() {
// passing "current" would always mean a future release, never the current (or the current in
// progress) which means some conditions would never be triggered.
ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
`"current" is not an allowed value for this attribute`)
return ""
}
// "safeValue" is safe because it translates finalized codenames to a string
// with their SDK int.
safeValue := apiLevel.String()
return formattedOptionalAttribute(attrName, &safeValue)
}
// formats an attribute for the xml permissions file if the value is not null
// returns empty string otherwise
func formattedOptionalAttribute(attrName string, value *string) string {
if value == nil {
return ""
}
return fmt.Sprintf(" %s=\"%s\"\n", attrName, *value)
}
func formattedDependenciesAttribute(dependencies []string) string {
if dependencies == nil {
return ""
}
return fmt.Sprintf(" dependency=\"%s\"\n", strings.Join(dependencies, ":"))
}
func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
libName := proptools.String(module.properties.Lib_name)
libNameAttr := formattedOptionalAttribute("name", &libName)
filePath := module.implPath(ctx)
filePathAttr := formattedOptionalAttribute("file", &filePath)
implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies.GetOrDefault(ctx, nil))
// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
var libraryTag string
if module.properties.Min_device_sdk != nil {
libraryTag = " <apex-library\n"
} else {
libraryTag = " <library\n"
}
return strings.Join([]string{
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
"<!-- Copyright (C) 2018 The Android Open Source Project\n",
"\n",
" Licensed under the Apache License, Version 2.0 (the \"License\");\n",
" you may not use this file except in compliance with the License.\n",
" You may obtain a copy of the License at\n",
"\n",
" http://www.apache.org/licenses/LICENSE-2.0\n",
"\n",
" Unless required by applicable law or agreed to in writing, software\n",
" distributed under the License is distributed on an \"AS IS\" BASIS,\n",
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
" See the License for the specific language governing permissions and\n",
" limitations under the License.\n",
"-->\n",
"<permissions>\n",
libraryTag,
libNameAttr,
filePathAttr,
implicitFromAttr,
implicitUntilAttr,
minSdkAttr,
maxSdkAttr,
dependenciesAttr,
" />\n",
"</permissions>\n",
}, "")
}
func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
module.hideApexVariantFromMake = !apexInfo.IsForPlatform()
libName := proptools.String(module.properties.Lib_name)
module.selfValidate(ctx)
xmlContent := module.permissionsContents(ctx)
module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
}
func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
if module.hideApexVariantFromMake {
return []android.AndroidMkEntries{{
Disabled: true,
}}
}
return []android.AndroidMkEntries{{
Class: "ETC",
OutputFile: android.OptionalPathForPath(module.outputFilePath),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_TAGS", "optional")
entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
},
},
}}
}
func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
module.validateAtLeastTAttributes(ctx)
module.validateMinAndMaxDeviceSdk(ctx)
module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
module.validateOnBootclasspathBeforeRequirements(ctx)
}
func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
t := android.ApiLevelOrPanic(ctx, "Tiramisu")
module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
}
func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
if attr != nil {
if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
// we will inform the user of invalid inputs when we try to write the
// permissions xml file so we don't need to do it here
if t.GreaterThan(level) {
ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
}
}
}
}
func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
if minErr == nil && maxErr == nil {
// we will inform the user of invalid inputs when we try to write the
// permissions xml file so we don't need to do it here
if min.GreaterThan(max) {
ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
}
}
}
}
func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
if module.properties.Min_device_sdk != nil {
api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
if err == nil {
if moduleMinApi.GreaterThan(api) {
ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
}
}
}
if module.properties.Max_device_sdk != nil {
api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
if err == nil {
if moduleMinApi.GreaterThan(api) {
ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
}
}
}
}
func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
if module.properties.On_bootclasspath_before != nil {
t := android.ApiLevelOrPanic(ctx, "Tiramisu")
// if we use the attribute, then we need to do this validation
if moduleMinApi.LessThan(t) {
// if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
if module.properties.Min_device_sdk == nil {
ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
}
}
}
}