Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b6d1d87 authored by Todd Lee's avatar Todd Lee Committed by Gerrit Code Review
Browse files

Merge "Support for incremetal platform prebuilt APIs" into main

parents 791f6036 2ec7e1c5
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
)

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{
	value: "(no version)",
	// 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
	}

	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)
	if err != nil {
		panic(fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw))
+37 −7
Original line number Diff line number Diff line
@@ -55,6 +55,11 @@ type prebuiltApisProperties struct {

	// If set to true, compile dex for java_import modules. Defaults to false.
	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 {
@@ -69,6 +74,8 @@ func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContex
// parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g.
// <version>/<scope>/<module>.jar
// <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>/api/<module>.txt
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).
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)
	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)
	if err != nil {
		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,
	allowIncremental := proptools.BoolDefault(p.properties.Allow_incremental_platform_api, false)
	for _, f := range apiLevelFiles {
		module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
		module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
		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
	type latestApiInfo struct {
		module, scope, path string
		version             int
		version, release    int
		isExtensionApiFile  bool
	}

	getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
		m := make(map[string]latestApiInfo)
		for _, f := range files {
			module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
			module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
			if strings.HasSuffix(module, "incompatibilities") {
				continue
			}
			key := module + "." + scope
			info, exists := m[key]
			if !exists || version > info.version {
				m[key] = latestApiInfo{module, scope, f, version, isExtensionApiFile}
			if !exists || version > info.version || (version == info.version && release > info.release) {
				m[key] = latestApiInfo{module, scope, f, version, release, isExtensionApiFile}
			}
		}
		return m
+23 −0
Original line number Diff line number Diff line
@@ -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 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)
}
+23 −0
Original line number Diff line number Diff line
@@ -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 {
	libs := append([]string{"android"}, modules...)