Files
build_soong/java/sdk.go
Dan Albert 0b176c8038 Replace FutureApiLevel with an ApiLevel.
Keeping the int constant around for now as FutureApiLevelInt because
it's still useful in places that haven't adopted ApiLevel yet for
testing if their non-ApiLevel API level is current or not.

Test: treehugger
Bug: http://b/154667674
Change-Id: I47a7012703f41fdeb56f91edf9c83afa93042deb
2020-09-22 15:04:48 -07:00

679 lines
21 KiB
Go

// Copyright 2019 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 (
"fmt"
"path/filepath"
"sort"
"strconv"
"strings"
"android/soong/android"
"android/soong/java/config"
"github.com/google/blueprint/pathtools"
)
func init() {
android.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
android.RegisterSingletonType("sdk", sdkSingletonFactory)
android.RegisterMakeVarsProvider(pctx, sdkMakeVars)
}
var sdkVersionsKey = android.NewOnceKey("sdkVersionsKey")
var sdkFrameworkAidlPathKey = android.NewOnceKey("sdkFrameworkAidlPathKey")
var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey")
var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
type sdkContext interface {
// sdkVersion returns sdkSpec that corresponds to the sdk_version property of the current module
sdkVersion() sdkSpec
// systemModules returns the system_modules property of the current module, or an empty string if it is not set.
systemModules() string
// minSdkVersion returns sdkSpec that corresponds to the min_sdk_version property of the current module,
// or from sdk_version if it is not set.
minSdkVersion() sdkSpec
// targetSdkVersion returns the sdkSpec that corresponds to the target_sdk_version property of the current module,
// or from sdk_version if it is not set.
targetSdkVersion() sdkSpec
}
func UseApiFingerprint(ctx android.BaseModuleContext) bool {
if ctx.Config().UnbundledBuild() &&
!ctx.Config().AlwaysUsePrebuiltSdks() &&
ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
return true
}
return false
}
// sdkKind represents a particular category of an SDK spec like public, system, test, etc.
type sdkKind int
const (
sdkInvalid sdkKind = iota
sdkNone
sdkCore
sdkCorePlatform
sdkPublic
sdkSystem
sdkTest
sdkModule
sdkSystemServer
sdkPrivate
)
// String returns the string representation of this sdkKind
func (k sdkKind) String() string {
switch k {
case sdkPrivate:
return "private"
case sdkNone:
return "none"
case sdkPublic:
return "public"
case sdkSystem:
return "system"
case sdkTest:
return "test"
case sdkCore:
return "core"
case sdkCorePlatform:
return "core_platform"
case sdkModule:
return "module-lib"
case sdkSystemServer:
return "system-server"
default:
return "invalid"
}
}
// sdkVersion represents a specific version number of an SDK spec of a particular kind
type sdkVersion int
const (
// special version number for a not-yet-frozen SDK
sdkVersionCurrent sdkVersion = sdkVersion(android.FutureApiLevelInt)
// special version number to be used for SDK specs where version number doesn't
// make sense, e.g. "none", "", etc.
sdkVersionNone sdkVersion = sdkVersion(0)
)
// isCurrent checks if the sdkVersion refers to the not-yet-published version of an sdkKind
func (v sdkVersion) isCurrent() bool {
return v == sdkVersionCurrent
}
// isNumbered checks if the sdkVersion refers to the published (a.k.a numbered) version of an sdkKind
func (v sdkVersion) isNumbered() bool {
return !v.isCurrent() && v != sdkVersionNone
}
// String returns the string representation of this sdkVersion.
func (v sdkVersion) String() string {
if v.isCurrent() {
return "current"
} else if v.isNumbered() {
return strconv.Itoa(int(v))
}
return "(no version)"
}
func (v sdkVersion) ApiLevel(ctx android.EarlyModuleContext) android.ApiLevel {
return android.ApiLevelOrPanic(ctx, v.String())
}
// asNumberString directly converts the numeric value of this sdk version as a string.
// When isNumbered() is true, this method is the same as String(). However, for sdkVersionCurrent
// and sdkVersionNone, this returns 10000 and 0 while String() returns "current" and "(no version"),
// respectively.
func (v sdkVersion) asNumberString() string {
return strconv.Itoa(int(v))
}
// sdkSpec represents the kind and the version of an SDK for a module to build against
type sdkSpec struct {
kind sdkKind
version sdkVersion
raw string
}
func (s sdkSpec) String() string {
return fmt.Sprintf("%s_%s", s.kind, s.version)
}
// valid checks if this sdkSpec is well-formed. Note however that true doesn't mean that the
// specified SDK actually exists.
func (s sdkSpec) valid() bool {
return s.kind != sdkInvalid
}
// specified checks if this sdkSpec is well-formed and is not "".
func (s sdkSpec) specified() bool {
return s.valid() && s.kind != sdkPrivate
}
// whether the API surface is managed and versioned, i.e. has .txt file that
// get frozen on SDK freeze and changes get reviewed by API council.
func (s sdkSpec) stable() bool {
if !s.specified() {
return false
}
switch s.kind {
case sdkNone:
// there is nothing to manage and version in this case; de facto stable API.
return true
case sdkCore, sdkPublic, sdkSystem, sdkModule, sdkSystemServer:
return true
case sdkCorePlatform, sdkTest, sdkPrivate:
return false
default:
panic(fmt.Errorf("unknown sdkKind=%v", s.kind))
}
return false
}
// prebuiltSdkAvailableForUnbundledBuilt tells whether this sdkSpec can have a prebuilt SDK
// that can be used for unbundled builds.
func (s sdkSpec) prebuiltSdkAvailableForUnbundledBuild() bool {
// "", "none", and "core_platform" are not available for unbundled build
// as we don't/can't have prebuilt stub for the versions
return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
}
func (s sdkSpec) forVendorPartition(ctx android.EarlyModuleContext) sdkSpec {
// If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
// use it instead of "current" for the vendor partition.
currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
if currentSdkVersion == "current" {
return s
}
if s.kind == sdkPublic || s.kind == sdkSystem {
if s.version.isCurrent() {
if i, err := strconv.Atoi(currentSdkVersion); err == nil {
version := sdkVersion(i)
return sdkSpec{s.kind, version, s.raw}
}
panic(fmt.Errorf("BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES must be either \"current\" or a number, but was %q", currentSdkVersion))
}
}
return s
}
// usePrebuilt determines whether prebuilt SDK should be used for this sdkSpec with the given context.
func (s sdkSpec) usePrebuilt(ctx android.EarlyModuleContext) bool {
if s.version.isCurrent() {
// "current" can be built from source and be from prebuilt SDK
return ctx.Config().AlwaysUsePrebuiltSdks()
} else if s.version.isNumbered() {
// validation check
if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest {
panic(fmt.Errorf("prebuilt SDK is not not available for sdkKind=%q", s.kind))
return false
}
// numbered SDKs are always from prebuilt
return true
}
// "", "none", "core_platform" fall here
return false
}
// effectiveVersion converts an sdkSpec into the concrete sdkVersion that the module
// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
// it returns android.FutureApiLevel(10000).
func (s sdkSpec) effectiveVersion(ctx android.EarlyModuleContext) (sdkVersion, error) {
if !s.valid() {
return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
}
if ctx.DeviceSpecific() || ctx.SocSpecific() {
s = s.forVendorPartition(ctx)
}
if s.version.isNumbered() {
return s.version, nil
}
return sdkVersion(ctx.Config().DefaultAppTargetSdkInt()), nil
}
// effectiveVersionString converts an sdkSpec into the concrete version string that the module
// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
// it returns the codename (P, Q, R, etc.)
func (s sdkSpec) effectiveVersionString(ctx android.EarlyModuleContext) (string, error) {
ver, err := s.effectiveVersion(ctx)
if err == nil && int(ver) == ctx.Config().DefaultAppTargetSdkInt() {
return ctx.Config().DefaultAppTargetSdk(), nil
}
return ver.String(), err
}
func (s sdkSpec) defaultJavaLanguageVersion(ctx android.EarlyModuleContext) javaVersion {
sdk, err := s.effectiveVersion(ctx)
if err != nil {
ctx.PropertyErrorf("sdk_version", "%s", err)
}
if sdk <= 23 {
return JAVA_VERSION_7
} else if sdk <= 29 {
return JAVA_VERSION_8
} else {
return JAVA_VERSION_9
}
}
func sdkSpecFrom(str string) sdkSpec {
switch str {
// special cases first
case "":
return sdkSpec{sdkPrivate, sdkVersionNone, str}
case "none":
return sdkSpec{sdkNone, sdkVersionNone, str}
case "core_platform":
return sdkSpec{sdkCorePlatform, sdkVersionNone, str}
default:
// the syntax is [kind_]version
sep := strings.LastIndex(str, "_")
var kindString string
if sep == 0 {
return sdkSpec{sdkInvalid, sdkVersionNone, str}
} else if sep == -1 {
kindString = ""
} else {
kindString = str[0:sep]
}
versionString := str[sep+1 : len(str)]
var kind sdkKind
switch kindString {
case "":
kind = sdkPublic
case "core":
kind = sdkCore
case "system":
kind = sdkSystem
case "test":
kind = sdkTest
case "module":
kind = sdkModule
case "system_server":
kind = sdkSystemServer
default:
return sdkSpec{sdkInvalid, sdkVersionNone, str}
}
var version sdkVersion
if versionString == "current" {
version = sdkVersionCurrent
} else if i, err := strconv.Atoi(versionString); err == nil {
version = sdkVersion(i)
} else {
return sdkSpec{sdkInvalid, sdkVersionNone, str}
}
return sdkSpec{kind, version, str}
}
}
func (s sdkSpec) validateSystemSdk(ctx android.EarlyModuleContext) bool {
// Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
// Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
// sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
if s.kind != sdkSystem || !s.version.isNumbered() {
return true
}
allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
if len(systemSdkVersions) > 0 {
allowedVersions = systemSdkVersions
}
}
if len(allowedVersions) > 0 && !android.InList(s.version.String(), allowedVersions) {
ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
s.raw, allowedVersions)
return false
}
return true
}
func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
sdkVersion := sdkContext.sdkVersion()
if !sdkVersion.valid() {
ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.raw)
return sdkDep{}
}
if ctx.DeviceSpecific() || ctx.SocSpecific() {
sdkVersion = sdkVersion.forVendorPartition(ctx)
}
if !sdkVersion.validateSystemSdk(ctx) {
return sdkDep{}
}
if sdkVersion.usePrebuilt(ctx) {
dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), sdkVersion.kind.String())
jar := filepath.Join(dir, "android.jar")
// There's no aidl for other SDKs yet.
// TODO(77525052): Add aidl files for other SDKs too.
public_dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), "public")
aidl := filepath.Join(public_dir, "framework.aidl")
jarPath := android.ExistentPathForSource(ctx, jar)
aidlPath := android.ExistentPathForSource(ctx, aidl)
lambdaStubsPath := android.PathForSource(ctx, config.SdkLambdaStubsPath)
if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
return sdkDep{
invalidVersion: true,
bootclasspath: []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.kind, sdkVersion.version.String())},
}
}
if !jarPath.Valid() {
ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, jar)
return sdkDep{}
}
if !aidlPath.Valid() {
ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, aidl)
return sdkDep{}
}
var systemModules string
if sdkVersion.defaultJavaLanguageVersion(ctx).usesJavaModules() {
systemModules = "sdk_public_" + sdkVersion.version.String() + "_system_modules"
}
return sdkDep{
useFiles: true,
jars: android.Paths{jarPath.Path(), lambdaStubsPath},
aidl: android.OptionalPathForPath(aidlPath.Path()),
systemModules: systemModules,
}
}
toModule := func(modules []string, res string, aidl android.Path) sdkDep {
return sdkDep{
useModule: true,
bootclasspath: append(modules, config.DefaultLambdaStubsLibrary),
systemModules: "core-current-stubs-system-modules",
java9Classpath: modules,
frameworkResModule: res,
aidl: android.OptionalPathForPath(aidl),
}
}
switch sdkVersion.kind {
case sdkPrivate:
return sdkDep{
useModule: true,
systemModules: corePlatformSystemModules(ctx),
bootclasspath: corePlatformBootclasspathLibraries(ctx),
classpath: config.FrameworkLibraries,
frameworkResModule: "framework-res",
}
case sdkNone:
systemModules := sdkContext.systemModules()
if systemModules == "" {
ctx.PropertyErrorf("sdk_version",
`system_modules is required to be set to a non-empty value when sdk_version is "none", did you mean sdk_version: "core_platform"?`)
} else if systemModules == "none" {
return sdkDep{
noStandardLibs: true,
}
}
return sdkDep{
useModule: true,
noStandardLibs: true,
systemModules: systemModules,
bootclasspath: []string{systemModules},
}
case sdkCorePlatform:
return sdkDep{
useModule: true,
systemModules: corePlatformSystemModules(ctx),
bootclasspath: corePlatformBootclasspathLibraries(ctx),
noFrameworksLibs: true,
}
case sdkPublic:
return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
case sdkSystem:
return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
case sdkTest:
return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
case sdkCore:
return sdkDep{
useModule: true,
bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
systemModules: "core-current-stubs-system-modules",
noFrameworksLibs: true,
}
case sdkModule:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
return toModule([]string{"android_module_lib_stubs_current"}, "framework-res", nonUpdatableFrameworkAidlPath(ctx))
case sdkSystemServer:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
return toModule([]string{"android_system_server_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
default:
panic(fmt.Errorf("invalid sdk %q", sdkVersion.raw))
}
}
func sdkPreSingletonFactory() android.Singleton {
return sdkPreSingleton{}
}
type sdkPreSingleton struct{}
func (sdkPreSingleton) GenerateBuildActions(ctx android.SingletonContext) {
sdkJars, err := ctx.GlobWithDeps("prebuilts/sdk/*/public/android.jar", nil)
if err != nil {
ctx.Errorf("failed to glob prebuilts/sdk/*/public/android.jar: %s", err.Error())
}
var sdkVersions []int
for _, sdkJar := range sdkJars {
dir := filepath.Base(filepath.Dir(filepath.Dir(sdkJar)))
v, err := strconv.Atoi(dir)
if scerr, ok := err.(*strconv.NumError); ok && scerr.Err == strconv.ErrSyntax {
continue
} else if err != nil {
ctx.Errorf("invalid sdk jar %q, %s, %v", sdkJar, err.Error())
}
sdkVersions = append(sdkVersions, v)
}
sort.Ints(sdkVersions)
ctx.Config().Once(sdkVersionsKey, func() interface{} { return sdkVersions })
}
func LatestSdkVersionInt(ctx android.EarlyModuleContext) int {
sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
latestSdkVersion := 0
if len(sdkVersions) > 0 {
latestSdkVersion = sdkVersions[len(sdkVersions)-1]
}
return latestSdkVersion
}
func sdkSingletonFactory() android.Singleton {
return sdkSingleton{}
}
type sdkSingleton struct{}
func (sdkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
if ctx.Config().AlwaysUsePrebuiltSdks() {
return
}
createSdkFrameworkAidl(ctx)
createNonUpdatableFrameworkAidl(ctx)
createAPIFingerprint(ctx)
}
// Create framework.aidl by extracting anything that implements android.os.Parcelable from the SDK stubs modules.
func createSdkFrameworkAidl(ctx android.SingletonContext) {
stubsModules := []string{
"android_stubs_current",
"android_test_stubs_current",
"android_system_stubs_current",
}
combinedAidl := sdkFrameworkAidlPath(ctx)
tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
rule := createFrameworkAidl(stubsModules, tempPath, ctx)
commitChangeForRestat(rule, tempPath, combinedAidl)
rule.Build(pctx, ctx, "framework_aidl", "generate framework.aidl")
}
// Creates a version of framework.aidl for the non-updatable part of the platform.
func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) {
stubsModules := []string{"android_module_lib_stubs_current"}
combinedAidl := nonUpdatableFrameworkAidlPath(ctx)
tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
rule := createFrameworkAidl(stubsModules, tempPath, ctx)
commitChangeForRestat(rule, tempPath, combinedAidl)
rule.Build(pctx, ctx, "framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
}
func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx android.SingletonContext) *android.RuleBuilder {
stubsJars := make([]android.Paths, len(stubsModules))
ctx.VisitAllModules(func(module android.Module) {
// Collect dex jar paths for the modules listed above.
if j, ok := module.(Dependency); ok {
name := ctx.ModuleName(module)
if i := android.IndexList(name, stubsModules); i != -1 {
stubsJars[i] = j.HeaderJars()
}
}
})
var missingDeps []string
for i := range stubsJars {
if stubsJars[i] == nil {
if ctx.Config().AllowMissingDependencies() {
missingDeps = append(missingDeps, stubsModules[i])
} else {
ctx.Errorf("failed to find dex jar path for module %q", stubsModules[i])
}
}
}
rule := android.NewRuleBuilder()
rule.MissingDeps(missingDeps)
var aidls android.Paths
for _, jars := range stubsJars {
for _, jar := range jars {
aidl := android.PathForOutput(ctx, "aidl", pathtools.ReplaceExtension(jar.Base(), "aidl"))
rule.Command().
Text("rm -f").Output(aidl)
rule.Command().
BuiltTool(ctx, "sdkparcelables").
Input(jar).
Output(aidl)
aidls = append(aidls, aidl)
}
}
rule.Command().
Text("rm -f").Output(path)
rule.Command().
Text("cat").
Inputs(aidls).
Text("| sort -u >").
Output(path)
return rule
}
func sdkFrameworkAidlPath(ctx android.PathContext) android.OutputPath {
return ctx.Config().Once(sdkFrameworkAidlPathKey, func() interface{} {
return android.PathForOutput(ctx, "framework.aidl")
}).(android.OutputPath)
}
func nonUpdatableFrameworkAidlPath(ctx android.PathContext) android.OutputPath {
return ctx.Config().Once(nonUpdatableFrameworkAidlPathKey, func() interface{} {
return android.PathForOutput(ctx, "framework_non_updatable.aidl")
}).(android.OutputPath)
}
// Create api_fingerprint.txt
func createAPIFingerprint(ctx android.SingletonContext) {
out := ApiFingerprintPath(ctx)
rule := android.NewRuleBuilder()
rule.Command().
Text("rm -f").Output(out)
cmd := rule.Command()
if ctx.Config().PlatformSdkCodename() == "REL" {
cmd.Text("echo REL >").Output(out)
} else if !ctx.Config().AlwaysUsePrebuiltSdks() {
in, err := ctx.GlobWithDeps("frameworks/base/api/*current.txt", nil)
if err != nil {
ctx.Errorf("error globbing API files: %s", err)
}
cmd.Text("cat").
Inputs(android.PathsForSource(ctx, in)).
Text("| md5sum | cut -d' ' -f1 >").
Output(out)
} else {
// Unbundled build
// TODO: use a prebuilt api_fingerprint.txt from prebuilts/sdk/current.txt once we have one
cmd.Text("echo").
Flag(ctx.Config().PlatformPreviewSdkVersion()).
Text(">").
Output(out)
}
rule.Build(pctx, ctx, "api_fingerprint", "generate api_fingerprint.txt")
}
func ApiFingerprintPath(ctx android.PathContext) android.OutputPath {
return ctx.Config().Once(apiFingerprintPathKey, func() interface{} {
return android.PathForOutput(ctx, "api_fingerprint.txt")
}).(android.OutputPath)
}
func sdkMakeVars(ctx android.MakeVarsContext) {
if ctx.Config().AlwaysUsePrebuiltSdks() {
return
}
ctx.Strict("FRAMEWORK_AIDL", sdkFrameworkAidlPath(ctx).String())
ctx.Strict("API_FINGERPRINT", ApiFingerprintPath(ctx).String())
}