platform_build_soong/java/sdk.go
Paul Duffin d3c1513df9 Add tempPathForRestat to improve consistency
Previously, there were two approaches used to construct the temporary
path needed by the commitChangeForRestat method and neither was
suitable for general use. One was:
    android.PathForOutput(ctx, outputPath.Rel()+".tmp")
The other was:
    combinedAidl.ReplaceExtension(ctx, "aidl.tmp")

The first approach would not work if the supplied path had been created
by PathForModuleOut() or similar as it would drop the module directory.
That could lead to the same path being used for multiple rules.

The second approach would not work for files with a different
extension.

The tempPathForRestat combines the two approaches so it can be used
generally with any WritablePath.

Bug: 179354495
Test: verified that the ninja rules that used these files were not
      changed by these changes.
Change-Id: I4439dea0a823512c281eeb1366522fb49dceb4e3
2021-04-21 23:26:57 +01:00

400 lines
12 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"
"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")
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
}
func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpec) javaVersion {
sdk, err := s.EffectiveVersion(ctx)
if err != nil {
ctx.PropertyErrorf("sdk_version", "%s", err)
}
if sdk.FinalOrFutureInt() <= 23 {
return JAVA_VERSION_7
} else if sdk.FinalOrFutureInt() <= 29 {
return JAVA_VERSION_8
} else {
return JAVA_VERSION_9
}
}
func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
sdkVersion := sdkContext.SdkVersion(ctx)
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.ApiLevel.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.
publicDir := filepath.Join("prebuilts", "sdk", sdkVersion.ApiLevel.String(), "public")
aidl := filepath.Join(publicDir, "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.ApiLevel.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 defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() {
systemModules = "sdk_public_" + sdkVersion.ApiLevel.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 android.SdkPrivate:
return sdkDep{
useModule: true,
systemModules: corePlatformSystemModules(ctx),
bootclasspath: corePlatformBootclasspathLibraries(ctx),
classpath: config.FrameworkLibraries,
frameworkResModule: "framework-res",
}
case android.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 android.SdkCorePlatform:
return sdkDep{
useModule: true,
systemModules: corePlatformSystemModules(ctx),
bootclasspath: corePlatformBootclasspathLibraries(ctx),
noFrameworksLibs: true,
}
case android.SdkPublic:
return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
case android.SdkSystem:
return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
case android.SdkTest:
return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
case android.SdkCore:
return sdkDep{
useModule: true,
bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
systemModules: "core-current-stubs-system-modules",
noFrameworksLibs: true,
}
case android.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 android.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 := tempPathForRestat(ctx, combinedAidl)
rule := createFrameworkAidl(stubsModules, tempPath, ctx)
commitChangeForRestat(rule, tempPath, combinedAidl)
rule.Build("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 := tempPathForRestat(ctx, combinedAidl)
rule := createFrameworkAidl(stubsModules, tempPath, ctx)
commitChangeForRestat(rule, tempPath, combinedAidl)
rule.Build("framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
}
func createFrameworkAidl(stubsModules []string, path android.WritablePath, 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 ctx.ModuleHasProvider(module, JavaInfoProvider) {
j := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
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(pctx, ctx)
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("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(pctx, ctx)
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().FrameworksBaseDirExists(ctx) && !ctx.Config().AlwaysUsePrebuiltSdks() {
cmd.Text("cat")
apiTxtFileModules := []string{
"frameworks-base-api-current.txt",
"frameworks-base-api-system-current.txt",
"frameworks-base-api-module-lib-current.txt",
}
count := 0
ctx.VisitAllModules(func(module android.Module) {
name := ctx.ModuleName(module)
if android.InList(name, apiTxtFileModules) {
cmd.Inputs(android.OutputFilesForModule(ctx, module, ""))
count++
}
})
if count != len(apiTxtFileModules) {
ctx.Errorf("Could not find all the expected API modules %v, found %d\n", apiTxtFileModules, count)
return
}
cmd.Input(android.PathForSource(ctx, "frameworks/base/services/api/current.txt")).
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("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())
}