From 97d0bae750d56d0e8daa687e190348e8efad594a Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Wed, 16 Feb 2022 16:15:10 +0000 Subject: [PATCH 1/3] Add base sdk extension version to the config So that it can be used by prebuilt_apis in follow-up CLs. Bug: 220086085 Test: m nothing && inspect soong.variables Change-Id: If987b8aef9802c52a751ea5351cab2a3df6f2e00 --- android/config.go | 33 +++++++++++++++++++++------------ android/variable.go | 1 + 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/android/config.go b/android/config.go index f10732bd7..08eea6385 100644 --- a/android/config.go +++ b/android/config.go @@ -350,18 +350,19 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string config := &config{ productVariables: productVariables{ - DeviceName: stringPtr("test_device"), - Platform_sdk_version: intPtr(30), - Platform_sdk_codename: stringPtr("S"), - Platform_version_active_codenames: []string{"S", "Tiramisu"}, - DeviceSystemSdkVersions: []string{"14", "15"}, - Platform_systemsdk_versions: []string{"29", "30"}, - AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}, - AAPTPreferredConfig: stringPtr("xhdpi"), - AAPTCharacteristics: stringPtr("nosdcard"), - AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"}, - UncompressPrivAppDex: boolPtr(true), - ShippingApiLevel: stringPtr("30"), + DeviceName: stringPtr("test_device"), + Platform_sdk_version: intPtr(30), + Platform_sdk_codename: stringPtr("S"), + Platform_base_sdk_extension_version: intPtr(1), + Platform_version_active_codenames: []string{"S", "Tiramisu"}, + DeviceSystemSdkVersions: []string{"14", "15"}, + Platform_systemsdk_versions: []string{"29", "30"}, + AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}, + AAPTPreferredConfig: stringPtr("xhdpi"), + AAPTCharacteristics: stringPtr("nosdcard"), + AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"}, + UncompressPrivAppDex: boolPtr(true), + ShippingApiLevel: stringPtr("30"), }, outDir: buildDir, @@ -742,6 +743,14 @@ func (c *config) PlatformSdkCodename() string { return String(c.productVariables.Platform_sdk_codename) } +func (c *config) PlatformSdkExtensionVersion() int { + return *c.productVariables.Platform_sdk_extension_version +} + +func (c *config) PlatformBaseSdkExtensionVersion() int { + return *c.productVariables.Platform_base_sdk_extension_version +} + func (c *config) PlatformSecurityPatch() string { return String(c.productVariables.Platform_security_patch) } diff --git a/android/variable.go b/android/variable.go index ff77fefe6..dbe7b04de 100644 --- a/android/variable.go +++ b/android/variable.go @@ -191,6 +191,7 @@ type productVariables struct { Platform_sdk_version_or_codename *string `json:",omitempty"` Platform_sdk_final *bool `json:",omitempty"` Platform_sdk_extension_version *int `json:",omitempty"` + Platform_base_sdk_extension_version *int `json:",omitempty"` Platform_version_active_codenames []string `json:",omitempty"` Platform_vndk_version *string `json:",omitempty"` Platform_systemsdk_versions []string `json:",omitempty"` From 377318b33fca4bde6cea3b5d5059c3aceab73890 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Tue, 15 Feb 2022 12:55:11 +0000 Subject: [PATCH 2/3] Refactor prebuilt_apis.go Improve code re-use and improve the names and data types of various vars and functions. This makes it easier to extend it for the new use-case of also supporting sdk extension versions on top of the current api level versions. This change is intended to be a noop. Bug: 220086085 Test: m nothing (existing soong tests) Change-Id: Icf4b320285f791e3b335f71f3c8b1ac52129dbc9 --- java/prebuilt_apis.go | 159 +++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index c67e2bd59..f7841b53d 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -16,6 +16,7 @@ package java import ( "fmt" + "path" "strconv" "strings" @@ -60,36 +61,45 @@ func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContex // no need to implement } -func parseJarPath(path string) (module string, apiver string, scope string) { - elements := strings.Split(path, "/") +// parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g. +// //.jar +// //api/.txt +// extensions///.jar +// extensions///api/.txt +func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) { + elements := strings.Split(p, "/") - apiver = elements[0] - scope = elements[1] - - module = strings.TrimSuffix(elements[2], ".jar") - return -} - -func parseApiFilePath(ctx android.LoadHookContext, path string) (module string, apiver string, scope string) { - elements := strings.Split(path, "/") - apiver = elements[0] - - scope = elements[1] - if scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" { - ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path) + scopeIdx := len(elements) - 2 + if elements[scopeIdx] == "api" { + scopeIdx-- + } + scope = elements[scopeIdx] + if scope != "core" && scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" { + ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, p) return } + version = elements[scopeIdx-1] - // elements[2] is string literal "api". skipping. - module = strings.TrimSuffix(elements[3], ".txt") + module = strings.TrimSuffix(path.Base(p), path.Ext(p)) return } -func prebuiltApiModuleName(mctx android.LoadHookContext, module string, scope string, apiver string) string { - return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module +// 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) { + module, v, scope := parsePrebuiltPath(ctx, p) + version, err := strconv.Atoi(v) + if err != nil { + ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v) + return + } + return } -func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdkVersion string, compileDex bool) { +func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string { + return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module) +} + +func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) { props := struct { Name *string Jars []string @@ -97,7 +107,7 @@ func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdk Installable *bool Compile_dex *bool }{} - props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver)) + props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)) props.Jars = append(props.Jars, path) props.Sdk_version = proptools.StringPtr(sdkVersion) props.Installable = proptools.BoolPtr(false) @@ -132,111 +142,103 @@ func createEmptyFile(mctx android.LoadHookContext, name string) { mctx.CreateModule(genrule.GenRuleFactory, &props) } -func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string { +// globApiDirs collects all the files in all api_dirs and all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'. +// // for all api-dir and scope. +func globApiDirs(mctx android.LoadHookContext, p *prebuiltApis, api_dir_glob string) []string { var files []string for _, apiver := range p.properties.Api_dirs { - files = append(files, getPrebuiltFilesInSubdir(mctx, apiver, name)...) + files = append(files, globScopeDir(mctx, apiver, api_dir_glob)...) } return files } -func getPrebuiltFilesInSubdir(mctx android.LoadHookContext, subdir string, name string) []string { +// 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 { var files []string dir := mctx.ModuleDir() + "/" + subdir for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} { - glob := fmt.Sprintf("%s/%s/%s", dir, scope, name) + glob := fmt.Sprintf("%s/%s/%s", dir, scope, subdir_glob) vfiles, err := mctx.GlobWithDeps(glob, nil) if err != nil { - mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, dir+"/"+scope, err) + mctx.ModuleErrorf("failed to glob %s files under %q: %s", subdir_glob, dir+"/"+scope, err) } files = append(files, vfiles...) } + for i, f := range files { + files[i] = strings.TrimPrefix(f, mctx.ModuleDir()+"/") + } return files } func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) { - mydir := mctx.ModuleDir() + "/" // //.jar - files := getPrebuiltFiles(mctx, p, "*.jar") + files := globApiDirs(mctx, p, "*.jar") sdkVersion := proptools.StringDefault(p.properties.Imports_sdk_version, "current") compileDex := proptools.BoolDefault(p.properties.Imports_compile_dex, false) for _, f := range files { // create a Import module for each jar file - localPath := strings.TrimPrefix(f, mydir) - module, apiver, scope := parseJarPath(localPath) - createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex) + module, version, scope := parsePrebuiltPath(mctx, f) + createImport(mctx, module, scope, version, f, sdkVersion, compileDex) if module == "core-for-system-modules" { - createSystemModules(mctx, apiver, scope) + createSystemModules(mctx, version, scope) } } } -func createSystemModules(mctx android.LoadHookContext, apiver string, scope string) { +func createSystemModules(mctx android.LoadHookContext, version, scope string) { props := struct { Name *string Libs []string }{} - props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, apiver)) - props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, apiver)) + props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version)) + props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version)) mctx.CreateModule(systemModulesImportFactory, &props) } func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { - mydir := mctx.ModuleDir() + "/" // //api/.txt - files := getPrebuiltFiles(mctx, p, "api/*.txt") - - if len(files) == 0 { - mctx.ModuleErrorf("no api file found under %q", mydir) - } - - // construct a map to find out the latest api file path - // for each (, ) pair. - type latestApiInfo struct { - module string - scope string - version int - path string + apiLevelFiles := globApiDirs(mctx, p, "api/*.txt") + if len(apiLevelFiles) == 0 { + mctx.ModuleErrorf("no api file found under %q", mctx.ModuleDir()) } // Create modules for all (, ) triplets, - // and a "latest" module variant for each (, ) pair apiModuleName := func(module, scope, version string) string { return module + ".api." + scope + "." + version } - m := make(map[string]latestApiInfo) - for _, f := range files { - localPath := strings.TrimPrefix(f, mydir) - module, apiver, scope := parseApiFilePath(mctx, localPath) - createApiModule(mctx, apiModuleName(module, scope, apiver), localPath) + for _, f := range apiLevelFiles { + module, version, scope := parseFinalizedPrebuiltPath(mctx, f) + createApiModule(mctx, apiModuleName(module, scope, strconv.Itoa(version)), f) + } - version, err := strconv.Atoi(apiver) - if err != nil { - mctx.ModuleErrorf("Found finalized API files in non-numeric dir %v", apiver) - return + // Figure out the latest version of each module/scope + type latestApiInfo struct { + module, scope, path string + version int + } + + latest := make(map[string]latestApiInfo) + for _, f := range apiLevelFiles { + module, version, scope := parseFinalizedPrebuiltPath(mctx, f) + if strings.HasSuffix(module, "incompatibilities") { + continue } - // Track latest version of each module/scope, except for incompatibilities - if !strings.HasSuffix(module, "incompatibilities") { - key := module + "." + scope - info, ok := m[key] - if !ok { - m[key] = latestApiInfo{module, scope, version, localPath} - } else if version > info.version { - info.version = version - info.path = localPath - m[key] = info - } + key := module + "." + scope + info, exists := latest[key] + if !exists || version > info.version { + latest[key] = latestApiInfo{module, scope, f, version} } } // Sort the keys in order to make build.ninja stable - for _, k := range android.SortedStringKeys(m) { - info := m[k] + for _, k := range android.SortedStringKeys(latest) { + info := latest[k] name := apiModuleName(info.module, info.scope, "latest") createApiModule(mctx, name, info.path) } @@ -244,21 +246,20 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // Create incompatibilities tracking files for all modules, if we have a "next" api. incompatibilities := make(map[string]bool) if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" { - files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt") + files := globScopeDir(mctx, nextApiDir, "api/*incompatibilities.txt") for _, f := range files { - localPath := strings.TrimPrefix(f, mydir) - filename, _, scope := parseApiFilePath(mctx, localPath) + filename, _, scope := parsePrebuiltPath(mctx, f) referencedModule := strings.TrimSuffix(filename, "-incompatibilities") - createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath) + createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f) incompatibilities[referencedModule+"."+scope] = true } } // Create empty incompatibilities files for remaining modules - for _, k := range android.SortedStringKeys(m) { + for _, k := range android.SortedStringKeys(latest) { if _, ok := incompatibilities[k]; !ok { - createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest")) + createEmptyFile(mctx, apiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest")) } } } From 3a3f169b5634ef6a7c9ad1040e7cf92dd60a37da Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Tue, 15 Feb 2022 12:55:11 +0000 Subject: [PATCH 3/3] Add support for sdk extensions in prebuilt_apis This makes it possible to pass an extensions_dir containing finalized module APIs to prebuilt_apis. The extension versions are compared to the api level versions to figure out what the "latest" finalized API is for each module. This is done using the base_sdk_extension_version, such that any extension higher than than base_sdk_extension_version is assumed to be finalized after any of the existing api level versions. Bug: 220086085 Test: prebuilt_apis_test.go Test: existing module in prebuilts/sdk Change-Id: Ib792f84202d436f594ba5e8716c6a187f9cd60dc --- java/prebuilt_apis.go | 45 +++++++++++++++++++++++++++------- java/prebuilt_apis_test.go | 36 +++++++++++++++++++++++++++ java/testing.go | 27 ++++++++++++++++++-- scripts/build-ndk-prebuilts.sh | 2 ++ 4 files changed, 99 insertions(+), 11 deletions(-) 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",