Merge changes from topics "presubmit-am-0a046d9514b34cc1a3b2b3584e857f6c", "presubmit-am-22410ea0b46e4a3d961d51518c65514d", "presubmit-am-2646dd78e751450296e76a7e6fac60eb", "presubmit-am-2762d254a366481180c66eefcb7b8c53", "presubmit-am-36ef44194069468da39e59065e3b9d39", "presubmit-am-6e59a6b21a5047bd940a9bff59c79228", "presubmit-am-855c4e732f2645568065c3c870ecd0da", "presubmit-am-dae9703bfd65425b8b44605c42e9d5b9" into sc-mainline-prod
* changes: Allow java_sdk_library in an APEX to have higher min_sdk_version. Perform CheckMinSdkVersion for java_sdk_library. Add MinSdkVersion(ctx) method to ModuleWithMinSdkVersionCheck interface. Add ModuleWithMinSdkVersionCheck type. Use textproto format for classpaths.proto generation. Propagate min and max sdk versions to classpaths.proto configs. Introduce max_sdk_version device property. Test SdkSpecForm.
This commit is contained in:
committed by
Android (Google) Code Review
commit
aa7b36e681
@@ -110,6 +110,7 @@ bootstrap_go_package {
|
||||
"paths_test.go",
|
||||
"prebuilt_test.go",
|
||||
"rule_builder_test.go",
|
||||
"sdk_version_test.go",
|
||||
"singleton_module_test.go",
|
||||
"soong_config_modules_test.go",
|
||||
"util_test.go",
|
||||
|
@@ -903,16 +903,18 @@ var minSdkVersionAllowlist = func(apiMap map[string]int) map[string]ApiLevel {
|
||||
//
|
||||
// Return true if the `to` module should be visited, false otherwise.
|
||||
type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
|
||||
type WalkPayloadDepsFunc func(ctx ModuleContext, do PayloadDepsCallback)
|
||||
|
||||
// UpdatableModule represents updatable APEX/APK
|
||||
type UpdatableModule interface {
|
||||
// ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
|
||||
type ModuleWithMinSdkVersionCheck interface {
|
||||
Module
|
||||
WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback)
|
||||
MinSdkVersion(ctx EarlyModuleContext) SdkSpec
|
||||
CheckMinSdkVersion(ctx ModuleContext)
|
||||
}
|
||||
|
||||
// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version
|
||||
// accordingly
|
||||
func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
|
||||
func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayloadDepsFunc) {
|
||||
// do not enforce min_sdk_version for host
|
||||
if ctx.Host() {
|
||||
return
|
||||
@@ -928,7 +930,7 @@ func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiL
|
||||
return
|
||||
}
|
||||
|
||||
m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
|
||||
walk(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
|
||||
if externalDep {
|
||||
// external deps are outside the payload boundary, which is "stable"
|
||||
// interface. We don't have to check min_sdk_version for external
|
||||
@@ -938,6 +940,14 @@ func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiL
|
||||
if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
|
||||
return false
|
||||
}
|
||||
if m, ok := to.(ModuleWithMinSdkVersionCheck); ok {
|
||||
// This dependency performs its own min_sdk_version check, just make sure it sets min_sdk_version
|
||||
// to trigger the check.
|
||||
if !m.MinSdkVersion(ctx).Specified() {
|
||||
ctx.OtherModuleErrorf(m, "must set min_sdk_version")
|
||||
}
|
||||
return false
|
||||
}
|
||||
if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
|
||||
toName := ctx.OtherModuleName(to)
|
||||
if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
|
||||
|
@@ -188,8 +188,8 @@ var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21)
|
||||
// * "30" -> "30"
|
||||
// * "R" -> "30"
|
||||
// * "S" -> "S"
|
||||
func ReplaceFinalizedCodenames(ctx PathContext, raw string) string {
|
||||
num, ok := getFinalCodenamesMap(ctx.Config())[raw]
|
||||
func ReplaceFinalizedCodenames(config Config, raw string) string {
|
||||
num, ok := getFinalCodenamesMap(config)[raw]
|
||||
if !ok {
|
||||
return raw
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func ReplaceFinalizedCodenames(ctx PathContext, raw string) string {
|
||||
return strconv.Itoa(num)
|
||||
}
|
||||
|
||||
// Converts the given string `raw` to an ApiLevel, possibly returning an error.
|
||||
// ApiLevelFromUser converts the given string `raw` to an ApiLevel, possibly returning an error.
|
||||
//
|
||||
// `raw` must be non-empty. Passing an empty string results in a panic.
|
||||
//
|
||||
@@ -212,6 +212,12 @@ func ReplaceFinalizedCodenames(ctx PathContext, raw string) string {
|
||||
// Inputs that are not "current", known previews, or convertible to an integer
|
||||
// will return an error.
|
||||
func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) {
|
||||
return ApiLevelFromUserWithConfig(ctx.Config(), raw)
|
||||
}
|
||||
|
||||
// ApiLevelFromUserWithConfig implements ApiLevelFromUser, see comments for
|
||||
// ApiLevelFromUser for more details.
|
||||
func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) {
|
||||
if raw == "" {
|
||||
panic("API level string must be non-empty")
|
||||
}
|
||||
@@ -220,13 +226,13 @@ func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) {
|
||||
return FutureApiLevel, nil
|
||||
}
|
||||
|
||||
for _, preview := range ctx.Config().PreviewApiLevels() {
|
||||
for _, preview := range config.PreviewApiLevels() {
|
||||
if raw == preview.String() {
|
||||
return preview, nil
|
||||
}
|
||||
}
|
||||
|
||||
canonical := ReplaceFinalizedCodenames(ctx, raw)
|
||||
canonical := ReplaceFinalizedCodenames(config, raw)
|
||||
asInt, err := strconv.Atoi(canonical)
|
||||
if err != nil {
|
||||
return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical)
|
||||
|
@@ -698,6 +698,16 @@ func (c *config) PreviewApiLevels() []ApiLevel {
|
||||
return levels
|
||||
}
|
||||
|
||||
func (c *config) LatestPreviewApiLevel() ApiLevel {
|
||||
level := NoneApiLevel
|
||||
for _, l := range c.PreviewApiLevels() {
|
||||
if l.GreaterThan(level) {
|
||||
level = l
|
||||
}
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
func (c *config) AllSupportedApiLevels() []ApiLevel {
|
||||
var levels []ApiLevel
|
||||
levels = append(levels, c.FinalApiLevels()...)
|
||||
|
@@ -117,7 +117,7 @@ func (s SdkSpec) Stable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PrebuiltSdkAvailableForUnbundledBuilt tells whether this SdkSpec can have a prebuilt SDK
|
||||
// PrebuiltSdkAvailableForUnbundledBuild 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
|
||||
@@ -212,6 +212,10 @@ var (
|
||||
)
|
||||
|
||||
func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
|
||||
return SdkSpecFromWithConfig(ctx.Config(), str)
|
||||
}
|
||||
|
||||
func SdkSpecFromWithConfig(config Config, str string) SdkSpec {
|
||||
switch str {
|
||||
// special cases first
|
||||
case "":
|
||||
@@ -252,7 +256,7 @@ func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
|
||||
return SdkSpec{SdkInvalid, NoneApiLevel, str}
|
||||
}
|
||||
|
||||
apiLevel, err := ApiLevelFromUser(ctx, versionString)
|
||||
apiLevel, err := ApiLevelFromUserWithConfig(config, versionString)
|
||||
if err != nil {
|
||||
return SdkSpec{SdkInvalid, apiLevel, str}
|
||||
}
|
||||
|
89
android/sdk_version_test.go
Normal file
89
android/sdk_version_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2015 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 android
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSdkSpecFrom(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: "private_current",
|
||||
},
|
||||
{
|
||||
input: "none",
|
||||
expected: "none_(no version)",
|
||||
},
|
||||
{
|
||||
input: "core_platform",
|
||||
expected: "core_platform_current",
|
||||
},
|
||||
{
|
||||
input: "_",
|
||||
expected: "invalid_(no version)",
|
||||
},
|
||||
{
|
||||
input: "_31",
|
||||
expected: "invalid_(no version)",
|
||||
},
|
||||
{
|
||||
input: "system_R",
|
||||
expected: "system_30",
|
||||
},
|
||||
{
|
||||
input: "test_31",
|
||||
expected: "test_31",
|
||||
},
|
||||
{
|
||||
input: "module_current",
|
||||
expected: "module-lib_current",
|
||||
},
|
||||
{
|
||||
input: "31",
|
||||
expected: "public_31",
|
||||
},
|
||||
{
|
||||
input: "S",
|
||||
expected: "public_31",
|
||||
},
|
||||
{
|
||||
input: "current",
|
||||
expected: "public_current",
|
||||
},
|
||||
{
|
||||
input: "Tiramisu",
|
||||
expected: "public_Tiramisu",
|
||||
},
|
||||
}
|
||||
|
||||
config := NullConfig("")
|
||||
|
||||
config.productVariables = productVariables{
|
||||
Platform_sdk_version: intPtr(31),
|
||||
Platform_sdk_codename: stringPtr("Tiramisu"),
|
||||
Platform_version_active_codenames: []string{"Tiramisu"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if got := SdkSpecFromWithConfig(config, tc.input).String(); tc.expected != got {
|
||||
t.Errorf("Expected %v, got %v", tc.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
18
apex/apex.go
18
apex/apex.go
@@ -1632,7 +1632,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
// 1) do some validity checks such as apex_available, min_sdk_version, etc.
|
||||
a.checkApexAvailability(ctx)
|
||||
a.checkUpdatable(ctx)
|
||||
a.checkMinSdkVersion(ctx)
|
||||
a.CheckMinSdkVersion(ctx)
|
||||
a.checkStaticLinkingToStubLibraries(ctx)
|
||||
if len(a.properties.Tests) > 0 && !a.testApex {
|
||||
ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
|
||||
@@ -2246,18 +2246,28 @@ func overrideApexFactory() android.Module {
|
||||
//
|
||||
// TODO(jiyong): move these checks to a separate go file.
|
||||
|
||||
var _ android.ModuleWithMinSdkVersionCheck = (*apexBundle)(nil)
|
||||
|
||||
// Entures that min_sdk_version of the included modules are equal or less than the min_sdk_version
|
||||
// of this apexBundle.
|
||||
func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) {
|
||||
func (a *apexBundle) CheckMinSdkVersion(ctx android.ModuleContext) {
|
||||
if a.testApex || a.vndkApex {
|
||||
return
|
||||
}
|
||||
// apexBundle::minSdkVersion reports its own errors.
|
||||
minSdkVersion := a.minSdkVersion(ctx)
|
||||
android.CheckMinSdkVersion(a, ctx, minSdkVersion)
|
||||
android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
|
||||
}
|
||||
|
||||
func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
|
||||
func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
|
||||
return android.SdkSpec{
|
||||
Kind: android.SdkNone,
|
||||
ApiLevel: a.minSdkVersion(ctx),
|
||||
Raw: String(a.properties.Min_sdk_version),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
|
||||
ver := proptools.String(a.properties.Min_sdk_version)
|
||||
if ver == "" {
|
||||
return android.NoneApiLevel
|
||||
|
@@ -7742,6 +7742,184 @@ func TestApexJavaCoverage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
|
||||
preparer := android.GroupFixturePreparers(
|
||||
PrepareForTestWithApexBuildComponents,
|
||||
prepareForTestWithMyapex,
|
||||
java.PrepareForTestWithJavaSdkLibraryFiles,
|
||||
java.PrepareForTestWithJavaDefaultModules,
|
||||
android.PrepareForTestWithAndroidBuildComponents,
|
||||
dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"),
|
||||
dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"),
|
||||
)
|
||||
|
||||
// Test java_sdk_library in bootclasspath_fragment may define higher min_sdk_version than the apex
|
||||
t.Run("bootclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
|
||||
preparer.RunTestWithBp(t, `
|
||||
apex {
|
||||
name: "myapex",
|
||||
key: "myapex.key",
|
||||
bootclasspath_fragments: ["mybootclasspathfragment"],
|
||||
min_sdk_version: "30",
|
||||
updatable: false,
|
||||
}
|
||||
|
||||
apex_key {
|
||||
name: "myapex.key",
|
||||
public_key: "testkey.avbpubkey",
|
||||
private_key: "testkey.pem",
|
||||
}
|
||||
|
||||
bootclasspath_fragment {
|
||||
name: "mybootclasspathfragment",
|
||||
contents: ["mybootclasspathlib"],
|
||||
apex_available: ["myapex"],
|
||||
}
|
||||
|
||||
java_sdk_library {
|
||||
name: "mybootclasspathlib",
|
||||
srcs: ["mybootclasspathlib.java"],
|
||||
apex_available: ["myapex"],
|
||||
compile_dex: true,
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
min_sdk_version: "31",
|
||||
static_libs: ["util"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "util",
|
||||
srcs: ["a.java"],
|
||||
apex_available: ["myapex"],
|
||||
min_sdk_version: "31",
|
||||
static_libs: ["another_util"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "another_util",
|
||||
srcs: ["a.java"],
|
||||
min_sdk_version: "31",
|
||||
apex_available: ["myapex"],
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
// Test java_sdk_library in systemserverclasspath_fragment may define higher min_sdk_version than the apex
|
||||
t.Run("systemserverclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
|
||||
preparer.RunTestWithBp(t, `
|
||||
apex {
|
||||
name: "myapex",
|
||||
key: "myapex.key",
|
||||
systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
|
||||
min_sdk_version: "30",
|
||||
updatable: false,
|
||||
}
|
||||
|
||||
apex_key {
|
||||
name: "myapex.key",
|
||||
public_key: "testkey.avbpubkey",
|
||||
private_key: "testkey.pem",
|
||||
}
|
||||
|
||||
systemserverclasspath_fragment {
|
||||
name: "mysystemserverclasspathfragment",
|
||||
contents: ["mysystemserverclasspathlib"],
|
||||
apex_available: ["myapex"],
|
||||
}
|
||||
|
||||
java_sdk_library {
|
||||
name: "mysystemserverclasspathlib",
|
||||
srcs: ["mysystemserverclasspathlib.java"],
|
||||
apex_available: ["myapex"],
|
||||
compile_dex: true,
|
||||
min_sdk_version: "32",
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
static_libs: ["util"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "util",
|
||||
srcs: ["a.java"],
|
||||
apex_available: ["myapex"],
|
||||
min_sdk_version: "31",
|
||||
static_libs: ["another_util"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "another_util",
|
||||
srcs: ["a.java"],
|
||||
min_sdk_version: "31",
|
||||
apex_available: ["myapex"],
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("bootclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
|
||||
preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mybootclasspathlib".*must set min_sdk_version`)).
|
||||
RunTestWithBp(t, `
|
||||
apex {
|
||||
name: "myapex",
|
||||
key: "myapex.key",
|
||||
bootclasspath_fragments: ["mybootclasspathfragment"],
|
||||
min_sdk_version: "30",
|
||||
updatable: false,
|
||||
}
|
||||
|
||||
apex_key {
|
||||
name: "myapex.key",
|
||||
public_key: "testkey.avbpubkey",
|
||||
private_key: "testkey.pem",
|
||||
}
|
||||
|
||||
bootclasspath_fragment {
|
||||
name: "mybootclasspathfragment",
|
||||
contents: ["mybootclasspathlib"],
|
||||
apex_available: ["myapex"],
|
||||
}
|
||||
|
||||
java_sdk_library {
|
||||
name: "mybootclasspathlib",
|
||||
srcs: ["mybootclasspathlib.java"],
|
||||
apex_available: ["myapex"],
|
||||
compile_dex: true,
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("systemserverclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
|
||||
preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mysystemserverclasspathlib".*must set min_sdk_version`)).
|
||||
RunTestWithBp(t, `
|
||||
apex {
|
||||
name: "myapex",
|
||||
key: "myapex.key",
|
||||
systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
|
||||
min_sdk_version: "30",
|
||||
updatable: false,
|
||||
}
|
||||
|
||||
apex_key {
|
||||
name: "myapex.key",
|
||||
public_key: "testkey.avbpubkey",
|
||||
private_key: "testkey.pem",
|
||||
}
|
||||
|
||||
systemserverclasspath_fragment {
|
||||
name: "mysystemserverclasspathfragment",
|
||||
contents: ["mysystemserverclasspathlib"],
|
||||
apex_available: ["myapex"],
|
||||
}
|
||||
|
||||
java_sdk_library {
|
||||
name: "mysystemserverclasspathlib",
|
||||
srcs: ["mysystemserverclasspathlib.java"],
|
||||
apex_available: ["myapex"],
|
||||
compile_dex: true,
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
}
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
@@ -122,6 +122,14 @@ func FixtureSetBootJars(bootJars ...string) android.FixturePreparer {
|
||||
func FixtureSetApexBootJars(bootJars ...string) android.FixturePreparer {
|
||||
return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
|
||||
dexpreoptConfig.ApexBootJars = android.CreateTestConfiguredJarList(bootJars)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// FixtureSetApexSystemServerJars sets the ApexSystemServerJars property in the global config.
|
||||
func FixtureSetApexSystemServerJars(jars ...string) android.FixturePreparer {
|
||||
return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
|
||||
dexpreoptConfig.ApexSystemServerJars = android.CreateTestConfiguredJarList(jars)
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -288,7 +288,7 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
|
||||
|
||||
if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil {
|
||||
a.checkJniLibsSdkVersion(ctx, minSdkVersion)
|
||||
android.CheckMinSdkVersion(a, ctx, minSdkVersion)
|
||||
android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
|
||||
} else {
|
||||
ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
|
||||
}
|
||||
|
15
java/base.go
15
java/base.go
@@ -185,6 +185,10 @@ type DeviceProperties struct {
|
||||
// Defaults to sdk_version if not set.
|
||||
Min_sdk_version *string
|
||||
|
||||
// if not blank, set the maximum version of the sdk that the compiled artifacts will run against.
|
||||
// Defaults to empty string "". See sdk_version for possible values.
|
||||
Max_sdk_version *string
|
||||
|
||||
// if not blank, set the targetSdkVersion in the AndroidManifest.xml.
|
||||
// Defaults to sdk_version if not set.
|
||||
Target_sdk_version *string
|
||||
@@ -367,6 +371,7 @@ type Module struct {
|
||||
|
||||
sdkVersion android.SdkSpec
|
||||
minSdkVersion android.SdkSpec
|
||||
maxSdkVersion android.SdkSpec
|
||||
}
|
||||
|
||||
func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
|
||||
@@ -524,6 +529,13 @@ func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
|
||||
return j.SdkVersion(ctx)
|
||||
}
|
||||
|
||||
func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
|
||||
maxSdkVersion := proptools.StringDefault(j.deviceProperties.Max_sdk_version, "")
|
||||
// SdkSpecFrom returns SdkSpecPrivate for this, which may be confusing.
|
||||
// TODO(b/208456999): ideally MaxSdkVersion should be an ApiLevel and not SdkSpec.
|
||||
return android.SdkSpecFrom(ctx, maxSdkVersion)
|
||||
}
|
||||
|
||||
func (j *Module) MinSdkVersionString() string {
|
||||
return j.minSdkVersion.Raw
|
||||
}
|
||||
@@ -1488,8 +1500,7 @@ func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu
|
||||
}
|
||||
|
||||
// Implements android.ApexModule
|
||||
func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
|
||||
sdkVersion android.ApiLevel) error {
|
||||
func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
|
||||
sdkSpec := j.MinSdkVersion(ctx)
|
||||
if !sdkSpec.Specified() {
|
||||
return fmt.Errorf("min_sdk_version is not specified")
|
||||
|
@@ -84,11 +84,10 @@ func initClasspathFragment(c classpathFragment, classpathType classpathType) {
|
||||
|
||||
// Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto
|
||||
type classpathJar struct {
|
||||
path string
|
||||
classpath classpathType
|
||||
// TODO(satayev): propagate min/max sdk versions for the jars
|
||||
minSdkVersion int32
|
||||
maxSdkVersion int32
|
||||
path string
|
||||
classpath classpathType
|
||||
minSdkVersion string
|
||||
maxSdkVersion string
|
||||
}
|
||||
|
||||
// gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the
|
||||
@@ -120,10 +119,32 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars
|
||||
jars := make([]classpathJar, 0, len(paths)*len(classpaths))
|
||||
for i := 0; i < len(paths); i++ {
|
||||
for _, classpathType := range classpaths {
|
||||
jars = append(jars, classpathJar{
|
||||
jar := classpathJar{
|
||||
classpath: classpathType,
|
||||
path: paths[i],
|
||||
}
|
||||
ctx.VisitDirectDepsIf(func(m android.Module) bool {
|
||||
return m.Name() == configuredJars.Jar(i)
|
||||
}, func(m android.Module) {
|
||||
if s, ok := m.(*SdkLibrary); ok {
|
||||
// TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
|
||||
if s.minSdkVersion.Specified() {
|
||||
if s.minSdkVersion.ApiLevel.IsCurrent() {
|
||||
jar.minSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
|
||||
} else {
|
||||
jar.minSdkVersion = s.minSdkVersion.ApiLevel.String()
|
||||
}
|
||||
}
|
||||
if s.maxSdkVersion.Specified() {
|
||||
if s.maxSdkVersion.ApiLevel.IsCurrent() {
|
||||
jar.maxSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
|
||||
} else {
|
||||
jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
jars = append(jars, jar)
|
||||
}
|
||||
}
|
||||
return jars
|
||||
@@ -136,15 +157,15 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M
|
||||
c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
|
||||
c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
|
||||
|
||||
generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
|
||||
writeClasspathsJson(ctx, generatedJson, jars)
|
||||
generatedTextproto := android.PathForModuleOut(ctx, outputFilename+".textproto")
|
||||
writeClasspathsTextproto(ctx, generatedTextproto, jars)
|
||||
|
||||
rule := android.NewRuleBuilder(pctx, ctx)
|
||||
rule.Command().
|
||||
BuiltTool("conv_classpaths_proto").
|
||||
Flag("encode").
|
||||
Flag("--format=json").
|
||||
FlagWithInput("--input=", generatedJson).
|
||||
Flag("--format=textproto").
|
||||
FlagWithInput("--input=", generatedTextproto).
|
||||
FlagWithOutput("--output=", c.outputFilepath)
|
||||
|
||||
rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
|
||||
@@ -159,24 +180,18 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M
|
||||
ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
|
||||
}
|
||||
|
||||
func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
|
||||
func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
|
||||
var content strings.Builder
|
||||
fmt.Fprintf(&content, "{\n")
|
||||
fmt.Fprintf(&content, "\"jars\": [\n")
|
||||
for idx, jar := range jars {
|
||||
fmt.Fprintf(&content, "{\n")
|
||||
|
||||
fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path)
|
||||
fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath)
|
||||
|
||||
if idx < len(jars)-1 {
|
||||
fmt.Fprintf(&content, "},\n")
|
||||
} else {
|
||||
fmt.Fprintf(&content, "}\n")
|
||||
}
|
||||
for _, jar := range jars {
|
||||
fmt.Fprintf(&content, "jars {\n")
|
||||
fmt.Fprintf(&content, "path: \"%s\"\n", jar.path)
|
||||
fmt.Fprintf(&content, "classpath: %s\n", jar.classpath)
|
||||
fmt.Fprintf(&content, "min_sdk_version: \"%s\"\n", jar.minSdkVersion)
|
||||
fmt.Fprintf(&content, "max_sdk_version: \"%s\"\n", jar.maxSdkVersion)
|
||||
fmt.Fprintf(&content, "}\n")
|
||||
}
|
||||
fmt.Fprintf(&content, "]\n")
|
||||
fmt.Fprintf(&content, "}\n")
|
||||
|
||||
android.WriteFileRule(ctx, output, content.String())
|
||||
}
|
||||
|
||||
|
@@ -487,6 +487,7 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo
|
||||
func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
j.sdkVersion = j.SdkVersion(ctx)
|
||||
j.minSdkVersion = j.MinSdkVersion(ctx)
|
||||
j.maxSdkVersion = j.MaxSdkVersion(ctx)
|
||||
|
||||
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
|
||||
if !apexInfo.IsForPlatform() {
|
||||
|
@@ -68,7 +68,6 @@ type platformBootclasspathProperties struct {
|
||||
func platformBootclasspathFactory() android.SingletonModule {
|
||||
m := &platformBootclasspathModule{}
|
||||
m.AddProperties(&m.properties)
|
||||
// TODO(satayev): split apex jars into separate configs.
|
||||
initClasspathFragment(m, BOOTCLASSPATH)
|
||||
android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
|
||||
return m
|
||||
|
@@ -1122,6 +1122,22 @@ func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext)
|
||||
return generatedScopes
|
||||
}
|
||||
|
||||
var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil)
|
||||
|
||||
func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) {
|
||||
android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx).ApiLevel, func(c android.ModuleContext, do android.PayloadDepsCallback) {
|
||||
ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
|
||||
isExternal := !module.depIsInSameApex(ctx, child)
|
||||
if am, ok := child.(android.ApexModule); ok {
|
||||
if !do(ctx, parent, am, isExternal) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return !isExternal
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type sdkLibraryComponentTag struct {
|
||||
blueprint.BaseDependencyTag
|
||||
name string
|
||||
@@ -1207,6 +1223,10 @@ func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
|
||||
}
|
||||
|
||||
func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
if proptools.String(module.deviceProperties.Min_sdk_version) != "" {
|
||||
module.CheckMinSdkVersion(ctx)
|
||||
}
|
||||
|
||||
module.generateCommonBuildActions(ctx)
|
||||
|
||||
// Only build an implementation library if required.
|
||||
@@ -2466,12 +2486,12 @@ func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleConte
|
||||
|
||||
func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
|
||||
if module.hideApexVariantFromMake {
|
||||
return []android.AndroidMkEntries{android.AndroidMkEntries{
|
||||
return []android.AndroidMkEntries{{
|
||||
Disabled: true,
|
||||
}}
|
||||
}
|
||||
|
||||
return []android.AndroidMkEntries{android.AndroidMkEntries{
|
||||
return []android.AndroidMkEntries{{
|
||||
Class: "ETC",
|
||||
OutputFile: android.OptionalPathForPath(module.outputFilePath),
|
||||
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
|
||||
|
@@ -958,3 +958,87 @@ func TestJavaSdkLibraryDist(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) {
|
||||
preparer := android.GroupFixturePreparers(
|
||||
PrepareForTestWithJavaBuildComponents,
|
||||
PrepareForTestWithJavaDefaultModules,
|
||||
PrepareForTestWithJavaSdkLibraryFiles,
|
||||
)
|
||||
|
||||
preparer.RunTestWithBp(t, `
|
||||
java_sdk_library {
|
||||
name: "sdklib",
|
||||
srcs: ["a.java"],
|
||||
static_libs: ["util"],
|
||||
min_sdk_version: "30",
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "util",
|
||||
srcs: ["a.java"],
|
||||
min_sdk_version: "30",
|
||||
}
|
||||
`)
|
||||
|
||||
preparer.
|
||||
RunTestWithBp(t, `
|
||||
java_sdk_library {
|
||||
name: "sdklib",
|
||||
srcs: ["a.java"],
|
||||
libs: ["util"],
|
||||
impl_only_libs: ["util"],
|
||||
stub_only_libs: ["util"],
|
||||
stub_only_static_libs: ["util"],
|
||||
min_sdk_version: "30",
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "util",
|
||||
srcs: ["a.java"],
|
||||
}
|
||||
`)
|
||||
|
||||
preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "util".*should support min_sdk_version\(30\)`)).
|
||||
RunTestWithBp(t, `
|
||||
java_sdk_library {
|
||||
name: "sdklib",
|
||||
srcs: ["a.java"],
|
||||
static_libs: ["util"],
|
||||
min_sdk_version: "30",
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "util",
|
||||
srcs: ["a.java"],
|
||||
min_sdk_version: "31",
|
||||
}
|
||||
`)
|
||||
|
||||
preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "another_util".*should support min_sdk_version\(30\)`)).
|
||||
RunTestWithBp(t, `
|
||||
java_sdk_library {
|
||||
name: "sdklib",
|
||||
srcs: ["a.java"],
|
||||
static_libs: ["util"],
|
||||
min_sdk_version: "30",
|
||||
unsafe_ignore_missing_latest_api: true,
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "util",
|
||||
srcs: ["a.java"],
|
||||
static_libs: ["another_util"],
|
||||
min_sdk_version: "30",
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "another_util",
|
||||
srcs: ["a.java"],
|
||||
min_sdk_version: "31",
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
Reference in New Issue
Block a user