diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index f7841b53d..44650a6d0 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -38,6 +38,11 @@ type prebuiltApisProperties struct { // list of api version directories Api_dirs []string + // Directory containing finalized api txt files for extension versions. + // Extension versions higher than the base sdk extension version will + // be assumed to be finalized later than all Api_dirs. + Extensions_dir *string + // The next API directory can optionally point to a directory where // files incompatibility-tracking files are stored for the current // "in progress" API. Each module present in one of the api_dirs will have @@ -152,6 +157,13 @@ func globApiDirs(mctx android.LoadHookContext, p *prebuiltApis, api_dir_glob str return files } +// globExtensionDirs collects all the files under the extension dir (for all versions and scopes) that match the given glob +// /// for all version and scope. +func globExtensionDirs(mctx android.LoadHookContext, p *prebuiltApis, extension_dir_glob string) []string { + // // + return globScopeDir(mctx, *p.properties.Extensions_dir+"/*", extension_dir_glob) +} + // globScopeDir collects all the files in the given subdir across all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'. // // for all scope. func globScopeDir(mctx android.LoadHookContext, subdir string, subdir_glob string) []string { @@ -222,17 +234,32 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { version int } - latest := make(map[string]latestApiInfo) - for _, f := range apiLevelFiles { - module, version, scope := parseFinalizedPrebuiltPath(mctx, f) - if strings.HasSuffix(module, "incompatibilities") { - continue + getLatest := func(files []string) map[string]latestApiInfo { + m := make(map[string]latestApiInfo) + for _, f := range files { + module, version, scope := parseFinalizedPrebuiltPath(mctx, f) + 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} + } } + return m + } - key := module + "." + scope - info, exists := latest[key] - if !exists || version > info.version { - latest[key] = latestApiInfo{module, scope, f, version} + latest := getLatest(apiLevelFiles) + if p.properties.Extensions_dir != nil { + extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt") + for k, v := range getLatest(extensionApiFiles) { + if v.version > mctx.Config().PlatformBaseSdkExtensionVersion() { + if _, exists := latest[k]; !exists { + mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version) + } + latest[k] = v + } } } diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go index 79f42250b..75422ad45 100644 --- a/java/prebuilt_apis_test.go +++ b/java/prebuilt_apis_test.go @@ -20,9 +20,14 @@ import ( "testing" "android/soong/android" + "github.com/google/blueprint" ) +func intPtr(v int) *int { + return &v +} + func TestPrebuiltApis_SystemModulesCreation(t *testing.T) { result := android.GroupFixturePreparers( prepareForJavaTest, @@ -54,3 +59,34 @@ func TestPrebuiltApis_SystemModulesCreation(t *testing.T) { sort.Strings(expected) android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules) } + +func TestPrebuiltApis_WithExtensions(t *testing.T) { + runTestWithBaseExtensionLevel := func(v int) (foo_input string, bar_input string) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_base_sdk_extension_version = intPtr(v) + }), + FixtureWithPrebuiltApisAndExtensions(map[string][]string{ + "31": {"foo"}, + "32": {"foo", "bar"}, + "current": {"foo", "bar"}, + }, map[string][]string{ + "1": {"foo"}, + "2": {"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() + return + } + // Here, the base extension level is 1, so extension level 2 is the latest + foo_input, bar_input := runTestWithBaseExtensionLevel(1) + android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input) + android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input) + + // Here, the base extension level is 2, so 2 is not later than 32. + foo_input, bar_input = runTestWithBaseExtensionLevel(2) + android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/foo.txt", foo_input) + android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/bar.txt", bar_input) +} diff --git a/java/testing.go b/java/testing.go index 6c49bc866..82aa29b61 100644 --- a/java/testing.go +++ b/java/testing.go @@ -146,6 +146,10 @@ var PrepareForTestWithPrebuiltsOfCurrentApi = FixtureWithPrebuiltApis(map[string // This defines a file in the mock file system in a predefined location (prebuilts/sdk/Android.bp) // and so only one instance of this can be used in each fixture. func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.FixturePreparer { + return FixtureWithPrebuiltApisAndExtensions(release2Modules, nil) +} + +func FixtureWithPrebuiltApisAndExtensions(apiLevel2Modules map[string][]string, extensionLevel2Modules map[string][]string) android.FixturePreparer { mockFS := android.MockFS{} path := "prebuilts/sdk/Android.bp" @@ -153,14 +157,20 @@ func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.Fixtur prebuilt_apis { name: "sdk", api_dirs: ["%s"], + extensions_dir: "extensions", imports_sdk_version: "none", imports_compile_dex: true, } - `, strings.Join(android.SortedStringKeys(release2Modules), `", "`)) + `, strings.Join(android.SortedStringKeys(apiLevel2Modules), `", "`)) - for release, modules := range release2Modules { + for release, modules := range apiLevel2Modules { mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules)) } + if extensionLevel2Modules != nil { + for release, modules := range extensionLevel2Modules { + mockFS.Merge(prebuiltExtensionApiFiles([]string{release}, modules)) + } + } return android.GroupFixturePreparers( android.FixtureAddTextFile(path, bp), android.FixtureMergeMockFs(mockFS), @@ -198,6 +208,19 @@ func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[strin return fs } +func prebuiltExtensionApiFiles(extensionLevels []string, modules []string) map[string][]byte { + fs := make(map[string][]byte) + for _, level := range extensionLevels { + for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer} { + for _, lib := range modules { + fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil + fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil + } + } + } + return fs +} + // FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and // Config.productVariables structs. As a side effect that enables dexpreopt. func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer { diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh index 4783037b2..15745873c 100755 --- a/scripts/build-ndk-prebuilts.sh +++ b/scripts/build-ndk-prebuilts.sh @@ -23,6 +23,7 @@ TOP=$(pwd) source build/envsetup.sh PLATFORM_SDK_VERSION=$(get_build_var PLATFORM_SDK_VERSION) +PLATFORM_BASE_SDK_EXTENSION_VERSION=$(get_build_var PLATFORM_BASE_SDK_EXTENSION_VERSION) PLATFORM_VERSION_ALL_CODENAMES=$(get_build_var PLATFORM_VERSION_ALL_CODENAMES) # PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to @@ -46,6 +47,7 @@ mkdir -p ${SOONG_OUT} cat > ${SOONG_OUT}/soong.variables << EOF { "Platform_sdk_version": ${PLATFORM_SDK_VERSION}, + "Platform_base_sdk_extension_version": ${PLATFORM_BASE_SDK_EXTENSION_VERSION}, "Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES}, "DeviceName": "generic_arm64",