Apex: support codenames for min_sdk_version

Apex can use codenames like "Q", "R" for its min_sdk_version property.
Also, cc_library can use codenames for its stubs.versions.

Bug: 152655956
Test: vendor/google/build/build_mainline_modules.sh
Change-Id: I077ad7b2ac5d90b4c8708921e43846206f05ba70
This commit is contained in:
Jooyung Han 2020-04-02 01:41:41 +09:00
parent 66e365565f
commit 29e91d2121
6 changed files with 135 additions and 19 deletions

View file

@ -16,6 +16,7 @@ package android
import (
"encoding/json"
"fmt"
"strconv"
)
@ -84,14 +85,19 @@ func getApiLevelsMap(config Config) map[string]int {
// Converts an API level string into its numeric form.
// * Codenames are decoded.
// * Numeric API levels are simply converted.
// * "minimum" and "current" are not currently handled since the former is
// NDK specific and the latter has inconsistent meaning.
// * "current" is mapped to FutureApiLevel(10000)
// * "minimum" is NDK specific and not handled with this. (refer normalizeNdkApiLevel in cc.go)
func ApiStrToNum(ctx BaseModuleContext, apiLevel string) (int, error) {
num, ok := getApiLevelsMap(ctx.Config())[apiLevel]
if ok {
if apiLevel == "current" {
return FutureApiLevel, nil
}
if num, ok := getApiLevelsMap(ctx.Config())[apiLevel]; ok {
return num, nil
}
return strconv.Atoi(apiLevel)
if num, err := strconv.Atoi(apiLevel); err == nil {
return num, nil
}
return 0, fmt.Errorf("SDK version should be one of \"current\", <number> or <codename>: %q", apiLevel)
}
func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {

View file

@ -19,7 +19,6 @@ import (
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
@ -1995,14 +1994,11 @@ func (a *apexBundle) walkPayloadDeps(ctx android.ModuleContext,
func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) int {
ver := proptools.StringDefault(a.properties.Min_sdk_version, "current")
if ver != "current" {
minSdkVersion, err := strconv.Atoi(ver)
if err != nil {
ctx.PropertyErrorf("min_sdk_version", "should be \"current\" or <number>, but %q", ver)
}
return minSdkVersion
intVer, err := android.ApiStrToNum(ctx, ver)
if err != nil {
ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
}
return android.FutureApiLevel
return intVer
}
// Ensures that the dependencies are marked as available for this APEX

View file

@ -1106,6 +1106,60 @@ func TestApexUseStubsAccordingToMinSdkVersionInUnbundledBuild(t *testing.T) {
expectNoLink("liba", "shared_otherapex", "libz", "shared")
}
func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) {
ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libx"],
min_sdk_version: "R",
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
cc_library {
name: "libx",
shared_libs: ["libz"],
system_shared_libs: [],
stl: "none",
apex_available: [ "myapex" ],
}
cc_library {
name: "libz",
system_shared_libs: [],
stl: "none",
stubs: {
versions: ["29", "R"],
},
}
`, func(fs map[string][]byte, config android.Config) {
config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
})
expectLink := func(from, from_variant, to, to_variant string) {
ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
}
expectNoLink := func(from, from_variant, to, to_variant string) {
ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
}
// 9000 is quite a magic number.
// Finalized SDK codenames are mapped as P(28), Q(29), ...
// And, codenames which are not finalized yet(active_codenames + future_codenames) are numbered from 9000, 9001, ...
// to distinguish them from finalized and future_api(10000)
// In this test, "R" is assumed not finalized yet( listed in Platform_version_active_codenames) and translated into 9000
// (refer android/api_levels.go)
expectLink("libx", "shared_myapex", "libz", "shared_9000")
expectNoLink("libx", "shared_myapex", "libz", "shared_29")
expectNoLink("libx", "shared_myapex", "libz", "shared")
}
func TestApexMinSdkVersionDefaultsToLatest(t *testing.T) {
ctx, _ := testApex(t, `
apex {
@ -1296,11 +1350,11 @@ func TestInvalidMinSdkVersion(t *testing.T) {
}
`)
testApexError(t, `"myapex" .*: min_sdk_version: should be "current" or <number>`, `
testApexError(t, `"myapex" .*: min_sdk_version: SDK version should be .*`, `
apex {
name: "myapex",
key: "myapex.key",
min_sdk_version: "R",
min_sdk_version: "abc",
}
apex_key {

View file

@ -1455,18 +1455,22 @@ func LatestStubsVersionFor(config android.Config, name string) string {
return ""
}
func checkVersions(ctx android.BaseModuleContext, versions []string) {
func normalizeVersions(ctx android.BaseModuleContext, versions []string) {
numVersions := make([]int, len(versions))
for i, v := range versions {
numVer, err := strconv.Atoi(v)
numVer, err := android.ApiStrToNum(ctx, v)
if err != nil {
ctx.PropertyErrorf("versions", "%q is not a number", v)
ctx.PropertyErrorf("versions", "%s", err.Error())
return
}
numVersions[i] = numVer
}
if !sort.IsSorted(sort.IntSlice(numVersions)) {
ctx.PropertyErrorf("versions", "not sorted: %v", versions)
}
for i, v := range numVersions {
versions[i] = strconv.Itoa(v)
}
}
func createVersionVariations(mctx android.BottomUpMutatorContext, versions []string) {
@ -1488,7 +1492,7 @@ func VersionMutator(mctx android.BottomUpMutatorContext) {
if library, ok := mctx.Module().(LinkableInterface); ok && !library.InRecovery() {
if library.CcLibrary() && library.BuildSharedVariant() && len(library.StubsVersions()) > 0 {
versions := library.StubsVersions()
checkVersions(mctx, versions)
normalizeVersions(mctx, versions)
if mctx.Failed() {
return
}

View file

@ -17,6 +17,8 @@ package cc
import (
"reflect"
"testing"
"android/soong/android"
)
func TestLibraryReuse(t *testing.T) {
@ -186,3 +188,55 @@ func TestLibraryReuse(t *testing.T) {
}
})
}
func TestStubsVersions(t *testing.T) {
bp := `
cc_library {
name: "libfoo",
srcs: ["foo.c"],
stubs: {
versions: ["29", "R", "10000"],
},
}
`
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
ctx := testCcWithConfig(t, config)
variants := ctx.ModuleVariantsForTests("libfoo")
for _, expectedVer := range []string{"29", "9000", "10000"} {
expectedVariant := "android_arm_armv7-a-neon_shared_" + expectedVer
if !inList(expectedVariant, variants) {
t.Errorf("missing expected variant: %q", expectedVariant)
}
}
}
func TestStubsVersions_NotSorted(t *testing.T) {
bp := `
cc_library {
name: "libfoo",
srcs: ["foo.c"],
stubs: {
versions: ["29", "10000", "R"],
},
}
`
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
testCcErrorWithConfig(t, `"libfoo" .*: versions: not sorted`, config)
}
func TestStubsVersions_ParseError(t *testing.T) {
bp := `
cc_library {
name: "libfoo",
srcs: ["foo.c"],
stubs: {
versions: ["29", "10000", "X"],
},
}
`
testCcError(t, `"libfoo" .*: versions: SDK version should be`, bp)
}

View file

@ -96,6 +96,8 @@ type libraryProperties struct {
Unversioned_until *string
// Private property for use by the mutator that splits per-API level.
// can be one of <number:sdk_version> or <codename> or "current"
// passed to "gen_stub_libs.py" as it is
ApiLevel string `blueprint:"mutated"`
// True if this API is not yet ready to be shipped in the NDK. It will be