Merge "Support for incremetal platform prebuilt APIs" into main

This commit is contained in:
Todd Lee 2023-08-28 18:42:58 +00:00 committed by Gerrit Code Review
commit 5b6d1d87d6
4 changed files with 108 additions and 7 deletions

View file

@ -19,6 +19,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strconv" "strconv"
"strings"
) )
func init() { func init() {
@ -237,6 +238,14 @@ func uncheckedFinalApiLevel(num int) ApiLevel {
} }
} }
func uncheckedFinalIncrementalApiLevel(num int, increment int) ApiLevel {
return ApiLevel{
value: strconv.Itoa(num) + "." + strconv.Itoa(increment),
number: num,
isPreview: false,
}
}
var NoneApiLevel = ApiLevel{ var NoneApiLevel = ApiLevel{
value: "(no version)", value: "(no version)",
// Not 0 because we don't want this to compare equal with the first preview. // Not 0 because we don't want this to compare equal with the first preview.
@ -371,6 +380,22 @@ func ApiLevelForTest(raw string) ApiLevel {
return FutureApiLevel return FutureApiLevel
} }
if strings.Contains(raw, ".") {
// Check prebuilt incremental API format MM.m for major (API level) and minor (incremental) revisions
parts := strings.Split(raw, ".")
if len(parts) != 2 {
panic(fmt.Errorf("Found unexpected version '%s' for incremental API - expect MM.m format for incremental API with both major (MM) an minor (m) revision.", raw))
}
sdk, sdk_err := strconv.Atoi(parts[0])
qpr, qpr_err := strconv.Atoi(parts[1])
if sdk_err != nil || qpr_err != nil {
panic(fmt.Errorf("Unable to read version number for incremental api '%s'", raw))
}
apiLevel := uncheckedFinalIncrementalApiLevel(sdk, qpr)
return apiLevel
}
asInt, err := strconv.Atoi(raw) asInt, err := strconv.Atoi(raw)
if err != nil { if err != nil {
panic(fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw)) panic(fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw))

View file

@ -55,6 +55,11 @@ type prebuiltApisProperties struct {
// If set to true, compile dex for java_import modules. Defaults to false. // If set to true, compile dex for java_import modules. Defaults to false.
Imports_compile_dex *bool Imports_compile_dex *bool
// If set to true, allow incremental platform API of the form MM.m where MM is the major release
// version corresponding to the API level/SDK_INT and m is an incremental release version
// (e.g. API changes associated with QPR). Defaults to false.
Allow_incremental_platform_api *bool
} }
type prebuiltApis struct { type prebuiltApis struct {
@ -69,6 +74,8 @@ func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContex
// parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g. // parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g.
// <version>/<scope>/<module>.jar // <version>/<scope>/<module>.jar
// <version>/<scope>/api/<module>.txt // <version>/<scope>/api/<module>.txt
// *Note when using incremental platform API, <version> may be of the form MM.m where MM is the
// API level and m is an incremental release, otherwise <version> is a single integer corresponding to the API level only.
// extensions/<version>/<scope>/<module>.jar // extensions/<version>/<scope>/<module>.jar
// extensions/<version>/<scope>/api/<module>.txt // extensions/<version>/<scope>/api/<module>.txt
func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) { func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) {
@ -90,8 +97,25 @@ func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, ve
} }
// parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version). // parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version).
func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module string, version int, scope string) { func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string, allowIncremental bool) (module string, version int, release int, scope string) {
module, v, scope := parsePrebuiltPath(ctx, p) module, v, scope := parsePrebuiltPath(ctx, p)
if allowIncremental {
parts := strings.Split(v, ".")
if len(parts) != 2 {
ctx.ModuleErrorf("Found unexpected version '%v' for incremental prebuilts - expect MM.m format for incremental API with both major (MM) an minor (m) revision.", v)
return
}
sdk, sdk_err := strconv.Atoi(parts[0])
qpr, qpr_err := strconv.Atoi(parts[1])
if sdk_err != nil || qpr_err != nil {
ctx.ModuleErrorf("Unable to read version number for incremental prebuilt api '%v'", v)
return
}
version = sdk
release = qpr
return
}
release = 0
version, err := strconv.Atoi(v) version, err := strconv.Atoi(v)
if err != nil { if err != nil {
ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v) ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v)
@ -268,29 +292,35 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
} }
// Create modules for all (<module>, <scope, <version>) triplets, // Create modules for all (<module>, <scope, <version>) triplets,
allowIncremental := proptools.BoolDefault(p.properties.Allow_incremental_platform_api, false)
for _, f := range apiLevelFiles { for _, f := range apiLevelFiles {
module, version, scope := parseFinalizedPrebuiltPath(mctx, f) module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f) if allowIncremental {
incrementalVersion := strconv.Itoa(version) + "." + strconv.Itoa(release)
createApiModule(mctx, PrebuiltApiModuleName(module, scope, incrementalVersion), f)
} else {
createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f)
}
} }
// Figure out the latest version of each module/scope // Figure out the latest version of each module/scope
type latestApiInfo struct { type latestApiInfo struct {
module, scope, path string module, scope, path string
version int version, release int
isExtensionApiFile bool isExtensionApiFile bool
} }
getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo { getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
m := make(map[string]latestApiInfo) m := make(map[string]latestApiInfo)
for _, f := range files { for _, f := range files {
module, version, scope := parseFinalizedPrebuiltPath(mctx, f) module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
if strings.HasSuffix(module, "incompatibilities") { if strings.HasSuffix(module, "incompatibilities") {
continue continue
} }
key := module + "." + scope key := module + "." + scope
info, exists := m[key] info, exists := m[key]
if !exists || version > info.version { if !exists || version > info.version || (version == info.version && release > info.release) {
m[key] = latestApiInfo{module, scope, f, version, isExtensionApiFile} m[key] = latestApiInfo{module, scope, f, version, release, isExtensionApiFile}
} }
} }
return m return m

View file

@ -99,3 +99,26 @@ func TestPrebuiltApis_WithExtensions(t *testing.T) {
android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input) android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input) android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
} }
func TestPrebuiltApis_WithIncrementalApi(t *testing.T) {
runTestWithIncrementalApi := func() (foo_input, bar_input, baz_input string) {
result := android.GroupFixturePreparers(
prepareForJavaTest,
FixtureWithPrebuiltIncrementalApis(map[string][]string{
"33.0": {"foo"},
"33.1": {"foo", "bar", "baz"},
"33.2": {"foo", "bar"},
"current": {"foo", "bar"},
}),
).RunTest(t)
foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
baz_input = result.ModuleForTests("baz.api.public.latest", "").Rule("generator").Implicits[0].String()
return
}
// 33.1 is the latest for baz, 33.2 is the latest for both foo & bar
foo_input, bar_input, baz_input := runTestWithIncrementalApi()
android.AssertStringEquals(t, "Expected latest foo = api level 33.2", "prebuilts/sdk/33.2/public/api/foo.txt", foo_input)
android.AssertStringEquals(t, "Expected latest bar = api level 33.2", "prebuilts/sdk/33.2/public/api/bar.txt", bar_input)
android.AssertStringEquals(t, "Expected latest baz = api level 33.1", "prebuilts/sdk/33.1/public/api/baz.txt", baz_input)
}

View file

@ -225,6 +225,29 @@ func FixtureWithPrebuiltApisAndExtensions(apiLevel2Modules map[string][]string,
) )
} }
func FixtureWithPrebuiltIncrementalApis(apiLevel2Modules map[string][]string) android.FixturePreparer {
mockFS := android.MockFS{}
path := "prebuilts/sdk/Android.bp"
bp := fmt.Sprintf(`
prebuilt_apis {
name: "sdk",
api_dirs: ["%s"],
allow_incremental_platform_api: true,
imports_sdk_version: "none",
imports_compile_dex: true,
}
`, strings.Join(android.SortedKeys(apiLevel2Modules), `", "`))
for release, modules := range apiLevel2Modules {
mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
}
return android.GroupFixturePreparers(
android.FixtureAddTextFile(path, bp),
android.FixtureMergeMockFs(mockFS),
)
}
func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte { func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte {
libs := append([]string{"android"}, modules...) libs := append([]string{"android"}, modules...)