diff --git a/android/api_levels.go b/android/api_levels.go index 087206633..e5405ca08 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -24,6 +24,192 @@ func init() { RegisterSingletonType("api_levels", ApiLevelsSingleton) } +// An API level, which may be a finalized (numbered) API, a preview (codenamed) +// API, or the future API level (10000). Can be parsed from a string with +// ApiLevelFromUser or ApiLevelOrPanic. +// +// The different *types* of API levels are handled separately. Currently only +// Java has these, and they're managed with the sdkKind enum of the sdkSpec. A +// future cleanup should be to migrate sdkSpec to using ApiLevel instead of its +// sdkVersion int, and to move sdkSpec into this package. +type ApiLevel struct { + // The string representation of the API level. + value string + + // A number associated with the API level. The exact value depends on + // whether this API level is a preview or final API. + // + // For final API levels, this is the assigned version number. + // + // For preview API levels, this value has no meaning except to index known + // previews to determine ordering. + number int + + // Identifies this API level as either a preview or final API level. + isPreview bool +} + +// Returns the canonical name for this API level. For a finalized API level +// this will be the API number as a string. For a preview API level this +// will be the codename, or "current". +func (this ApiLevel) String() string { + return this.value +} + +// Returns true if this is a non-final API level. +func (this ApiLevel) IsPreview() bool { + return this.isPreview +} + +// Returns true if this is the unfinalized "current" API level. This means +// different things across Java and native. Java APIs do not use explicit +// codenames, so all non-final codenames are grouped into "current". For native +// explicit codenames are typically used, and current is the union of all +// non-final APIs, including those that may not yet be in any codename. +// +// Note that in a build where the platform is final, "current" will not be a +// preview API level but will instead be canonicalized to the final API level. +func (this ApiLevel) IsCurrent() bool { + return this.value == "current" +} + +// Returns -1 if the current API level is less than the argument, 0 if they +// are equal, and 1 if it is greater than the argument. +func (this ApiLevel) CompareTo(other ApiLevel) int { + if this.IsPreview() && !other.IsPreview() { + return 1 + } else if !this.IsPreview() && other.IsPreview() { + return -1 + } + + if this.number < other.number { + return -1 + } else if this.number == other.number { + return 0 + } else { + return 1 + } +} + +func (this ApiLevel) EqualTo(other ApiLevel) bool { + return this.CompareTo(other) == 0 +} + +func (this ApiLevel) GreaterThan(other ApiLevel) bool { + return this.CompareTo(other) > 0 +} + +func (this ApiLevel) GreaterThanOrEqualTo(other ApiLevel) bool { + return this.CompareTo(other) >= 0 +} + +func (this ApiLevel) LessThan(other ApiLevel) bool { + return this.CompareTo(other) < 0 +} + +func (this ApiLevel) LessThanOrEqualTo(other ApiLevel) bool { + return this.CompareTo(other) <= 0 +} + +func uncheckedFinalApiLevel(num int) ApiLevel { + return ApiLevel{ + value: strconv.Itoa(num), + number: num, + isPreview: false, + } +} + +// TODO: Merge with FutureApiLevel +var CurrentApiLevel = ApiLevel{ + value: "current", + number: 10000, + isPreview: true, +} + +var NoneApiLevel = ApiLevel{ + value: "(no version)", + // Not 0 because we don't want this to compare equal with the first preview. + number: -1, + isPreview: true, +} + +// The first version that introduced 64-bit ABIs. +var FirstLp64Version = uncheckedFinalApiLevel(21) + +// The first API level that does not require NDK code to link +// libandroid_support. +var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21) + +// If the `raw` input is the codename of an API level has been finalized, this +// function returns the API level number associated with that API level. If the +// input is *not* a finalized codename, the input is returned unmodified. +// +// For example, at the time of writing, R has been finalized as API level 30, +// but S is in development so it has no number assigned. For the following +// inputs: +// +// * "30" -> "30" +// * "R" -> "30" +// * "S" -> "S" +func ReplaceFinalizedCodenames(ctx EarlyModuleContext, raw string) string { + num, ok := getFinalCodenamesMap(ctx.Config())[raw] + if !ok { + return raw + } + + return strconv.Itoa(num) +} + +// Converts the given string `raw` to an ApiLevel, possibly returning an error. +// +// `raw` must be non-empty. Passing an empty string results in a panic. +// +// "current" will return CurrentApiLevel, which is the ApiLevel associated with +// an arbitrary future release (often referred to as API level 10000). +// +// Finalized codenames will be interpreted as their final API levels, not the +// preview of the associated releases. R is now API 30, not the R preview. +// +// Future codenames return a preview API level that has no associated integer. +// +// Inputs that are not "current", known previews, or convertible to an integer +// will return an error. +func ApiLevelFromUser(ctx EarlyModuleContext, raw string) (ApiLevel, error) { + if raw == "" { + panic("API level string must be non-empty") + } + + if raw == "current" { + return CurrentApiLevel, nil + } + + for _, preview := range ctx.Config().PreviewApiLevels() { + if raw == preview.String() { + return preview, nil + } + } + + canonical := ReplaceFinalizedCodenames(ctx, raw) + asInt, err := strconv.Atoi(canonical) + if err != nil { + return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical) + } + + apiLevel := uncheckedFinalApiLevel(asInt) + return apiLevel, nil +} + +// Converts an API level string `raw` into an ApiLevel in the same method as +// `ApiLevelFromUser`, but the input is assumed to have no errors and any errors +// will panic instead of returning an error. +func ApiLevelOrPanic(ctx EarlyModuleContext, raw string) ApiLevel { + value, err := ApiLevelFromUser(ctx, raw) + if err != nil { + panic(err.Error()) + } + return value +} + func ApiLevelsSingleton() Singleton { return &apiLevelsSingleton{} } @@ -52,6 +238,36 @@ func GetApiLevelsJson(ctx PathContext) WritablePath { return PathForOutput(ctx, "api_levels.json") } +var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap") + +func getFinalCodenamesMap(config Config) map[string]int { + return config.Once(finalCodenamesMapKey, func() interface{} { + apiLevelsMap := map[string]int{ + "G": 9, + "I": 14, + "J": 16, + "J-MR1": 17, + "J-MR2": 18, + "K": 19, + "L": 21, + "L-MR1": 22, + "M": 23, + "N": 24, + "N-MR1": 25, + "O": 26, + "O-MR1": 27, + "P": 28, + "Q": 29, + } + + if Bool(config.productVariables.Platform_sdk_final) { + apiLevelsMap["current"] = config.PlatformSdkVersionInt() + } + + return apiLevelsMap + }).(map[string]int) +} + var apiLevelsMapKey = NewOnceKey("ApiLevelsMap") func getApiLevelsMap(config Config) map[string]int { diff --git a/android/config.go b/android/config.go index dd622e5b2..1c06e8c55 100644 --- a/android/config.go +++ b/android/config.go @@ -642,8 +642,34 @@ func (c *config) PlatformBaseOS() string { return String(c.productVariables.Platform_base_os) } -func (c *config) MinSupportedSdkVersion() int { - return 16 +func (c *config) MinSupportedSdkVersion() ApiLevel { + return uncheckedFinalApiLevel(16) +} + +func (c *config) FinalApiLevels() []ApiLevel { + var levels []ApiLevel + for i := 1; i <= c.PlatformSdkVersionInt(); i++ { + levels = append(levels, uncheckedFinalApiLevel(i)) + } + return levels +} + +func (c *config) PreviewApiLevels() []ApiLevel { + var levels []ApiLevel + for i, codename := range c.PlatformVersionActiveCodenames() { + levels = append(levels, ApiLevel{ + value: codename, + number: i, + isPreview: true, + }) + } + return levels +} + +func (c *config) AllSupportedApiLevels() []ApiLevel { + var levels []ApiLevel + levels = append(levels, c.FinalApiLevels()...) + return append(levels, c.PreviewApiLevels()...) } func (c *config) DefaultAppTargetSdkInt() int { diff --git a/android/makevars.go b/android/makevars.go index 003a9df32..374986e84 100644 --- a/android/makevars.go +++ b/android/makevars.go @@ -18,7 +18,6 @@ import ( "bytes" "fmt" "sort" - "strconv" "strings" "github.com/google/blueprint" @@ -31,7 +30,7 @@ func init() { } func androidMakeVarsProvider(ctx MakeVarsContext) { - ctx.Strict("MIN_SUPPORTED_SDK_VERSION", strconv.Itoa(ctx.Config().MinSupportedSdkVersion())) + ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String()) } /////////////////////////////////////////////////////////////////////////////// diff --git a/cc/Android.bp b/cc/Android.bp index 831911e45..ff2cdf374 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -13,6 +13,7 @@ bootstrap_go_package { ], srcs: [ "androidmk.go", + "api_level.go", "builder.go", "cc.go", "ccdeps.go", diff --git a/cc/androidmk.go b/cc/androidmk.go index 380b4e92f..5bdbac649 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -451,7 +451,7 @@ func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries * } func (c *stubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { - entries.SubName = ndkLibrarySuffix + "." + c.properties.ApiLevel + entries.SubName = ndkLibrarySuffix + "." + c.apiLevel.String() entries.Class = "SHARED_LIBRARIES" entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) { diff --git a/cc/api_level.go b/cc/api_level.go new file mode 100644 index 000000000..c93d6eda3 --- /dev/null +++ b/cc/api_level.go @@ -0,0 +1,71 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cc + +import ( + "fmt" + + "android/soong/android" +) + +func minApiForArch(ctx android.BaseModuleContext, + arch android.ArchType) android.ApiLevel { + + switch arch { + case android.Arm, android.X86: + return ctx.Config().MinSupportedSdkVersion() + case android.Arm64, android.X86_64: + return android.FirstLp64Version + default: + panic(fmt.Errorf("Unknown arch %q", arch)) + } +} + +func nativeApiLevelFromUser(ctx android.BaseModuleContext, + raw string) (android.ApiLevel, error) { + + min := minApiForArch(ctx, ctx.Arch().ArchType) + if raw == "minimum" { + return min, nil + } + + value, err := android.ApiLevelFromUser(ctx, raw) + if err != nil { + return android.NoneApiLevel, err + } + + if value.LessThan(min) { + return min, nil + } + + return value, nil +} + +func nativeApiLevelFromUserWithDefault(ctx android.BaseModuleContext, + raw string, defaultValue string) (android.ApiLevel, error) { + if raw == "" { + raw = defaultValue + } + return nativeApiLevelFromUser(ctx, raw) +} + +func nativeApiLevelOrPanic(ctx android.BaseModuleContext, + raw string) android.ApiLevel { + value, err := nativeApiLevelFromUser(ctx, raw) + if err != nil { + panic(err.Error()) + } + return value +} diff --git a/cc/cc.go b/cc/cc.go index 70229be5a..b5a02611d 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -629,7 +629,7 @@ func (c *Module) Toc() android.OptionalPath { func (c *Module) ApiLevel() string { if c.linker != nil { if stub, ok := c.linker.(*stubDecorator); ok { - return stub.properties.ApiLevel + return stub.apiLevel.String() } } panic(fmt.Errorf("ApiLevel() called on non-stub library module: %q", c.BaseModuleName())) @@ -1682,11 +1682,13 @@ func (c *Module) begin(ctx BaseModuleContext) { feature.begin(ctx) } if ctx.useSdk() && c.IsSdkVariant() { - version, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch()) + version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion()) if err != nil { ctx.PropertyErrorf("sdk_version", err.Error()) + c.Properties.Sdk_version = nil + } else { + c.Properties.Sdk_version = StringPtr(version.String()) } - c.Properties.Sdk_version = StringPtr(version) } } @@ -3119,13 +3121,6 @@ func (c *Module) IsSdkVariant() bool { return c.Properties.IsSdkVariant || c.AlwaysSdk() } -func getCurrentNdkPrebuiltVersion(ctx DepsContext) string { - if ctx.Config().PlatformSdkVersionInt() > config.NdkMaxPrebuiltVersionInt { - return strconv.Itoa(config.NdkMaxPrebuiltVersionInt) - } - return ctx.Config().PlatformSdkVersion() -} - func kytheExtractAllFactory() android.Singleton { return &kytheExtractAllSingleton{} } diff --git a/cc/config/global.go b/cc/config/global.go index 32f163d88..b9f033288 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -126,8 +126,6 @@ var ( ExperimentalCStdVersion = "gnu11" ExperimentalCppStdVersion = "gnu++2a" - NdkMaxPrebuiltVersionInt = 27 - // prebuilts/clang default settings. ClangDefaultBase = "prebuilts/clang/host" ClangDefaultVersion = "clang-r383902b" diff --git a/cc/ndk_library.go b/cc/ndk_library.go index fe3efc01e..4c6d98c52 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -16,7 +16,6 @@ package cc import ( "fmt" - "strconv" "strings" "sync" @@ -52,6 +51,10 @@ var ( ndkKnownLibsLock sync.Mutex ) +// The First_version and Unversioned_until properties of this struct should not +// be used directly, but rather through the ApiLevel returning methods +// firstVersion() and unversionedUntil(). + // Creates a stub shared library based on the provided version file. // // Example: @@ -77,9 +80,7 @@ type libraryProperties struct { // https://github.com/android-ndk/ndk/issues/265. Unversioned_until *string - // Private property for use by the mutator that splits per-API level. Can be - // one of or or "current" passed to - // "ndkstubgen.py" as it is + // Use via apiLevel on the stubDecorator. ApiLevel string `blueprint:"mutated"` // True if this API is not yet ready to be shipped in the NDK. It will be @@ -96,125 +97,33 @@ type stubDecorator struct { versionScriptPath android.ModuleGenPath parsedCoverageXmlPath android.ModuleOutPath installPath android.Path + + apiLevel android.ApiLevel + firstVersion android.ApiLevel + unversionedUntil android.ApiLevel } -// OMG GO -func intMax(a int, b int) int { - if a > b { - return a - } else { - return b - } -} - -func normalizeNdkApiLevel(ctx android.BaseModuleContext, apiLevel string, - arch android.Arch) (string, error) { - - if apiLevel == "" { - panic("empty apiLevel not allowed") - } - - if apiLevel == "current" { - return apiLevel, nil - } - - minVersion := ctx.Config().MinSupportedSdkVersion() - firstArchVersions := map[android.ArchType]int{ - android.Arm: minVersion, - android.Arm64: 21, - android.X86: minVersion, - android.X86_64: 21, - } - - firstArchVersion, ok := firstArchVersions[arch.ArchType] - if !ok { - panic(fmt.Errorf("Arch %q not found in firstArchVersions", arch.ArchType)) - } - - if apiLevel == "minimum" { - return strconv.Itoa(firstArchVersion), nil - } - - // If the NDK drops support for a platform version, we don't want to have to - // fix up every module that was using it as its SDK version. Clip to the - // supported version here instead. - version, err := strconv.Atoi(apiLevel) - if err != nil { - // Non-integer API levels are codenames. - return apiLevel, nil - } - version = intMax(version, minVersion) - - return strconv.Itoa(intMax(version, firstArchVersion)), nil -} - -func getFirstGeneratedVersion(firstSupportedVersion string, platformVersion int) (int, error) { - if firstSupportedVersion == "current" { - return platformVersion + 1, nil - } - - return strconv.Atoi(firstSupportedVersion) -} - -func shouldUseVersionScript(ctx android.BaseModuleContext, stub *stubDecorator) (bool, error) { - // unversioned_until is normally empty, in which case we should use the version script. - if String(stub.properties.Unversioned_until) == "" { - return true, nil - } - - if String(stub.properties.Unversioned_until) == "current" { - if stub.properties.ApiLevel == "current" { - return true, nil - } else { - return false, nil - } - } - - if stub.properties.ApiLevel == "current" { - return true, nil - } - - unversionedUntil, err := android.ApiStrToNum(ctx, String(stub.properties.Unversioned_until)) - if err != nil { - return true, err - } - - version, err := android.ApiStrToNum(ctx, stub.properties.ApiLevel) - if err != nil { - return true, err - } - - return version >= unversionedUntil, nil +func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool { + return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil) } func generatePerApiVariants(ctx android.BottomUpMutatorContext, m *Module, - propName string, propValue string, perSplit func(*Module, string)) { - platformVersion := ctx.Config().PlatformSdkVersionInt() + from android.ApiLevel, perSplit func(*Module, android.ApiLevel)) { - firstSupportedVersion, err := normalizeNdkApiLevel(ctx, propValue, - ctx.Arch()) - if err != nil { - ctx.PropertyErrorf(propName, err.Error()) + var versions []android.ApiLevel + versionStrs := []string{} + for _, version := range ctx.Config().AllSupportedApiLevels() { + if version.GreaterThanOrEqualTo(from) { + versions = append(versions, version) + versionStrs = append(versionStrs, version.String()) + } } - - firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion, - platformVersion) - if err != nil { - // In theory this is impossible because we've already run this through - // normalizeNdkApiLevel above. - ctx.PropertyErrorf(propName, err.Error()) - } - - var versionStrs []string - for version := firstGenVersion; version <= platformVersion; version++ { - versionStrs = append(versionStrs, strconv.Itoa(version)) - } - versionStrs = append(versionStrs, ctx.Config().PlatformVersionActiveCodenames()...) - versionStrs = append(versionStrs, "current") + versions = append(versions, android.CurrentApiLevel) + versionStrs = append(versionStrs, android.CurrentApiLevel.String()) modules := ctx.CreateVariations(versionStrs...) for i, module := range modules { - perSplit(module.(*Module), versionStrs[i]) + perSplit(module.(*Module), versions[i]) } } @@ -228,25 +137,56 @@ func NdkApiMutator(ctx android.BottomUpMutatorContext) { ctx.Module().Disable() return } - generatePerApiVariants(ctx, m, "first_version", - String(compiler.properties.First_version), - func(m *Module, version string) { + firstVersion, err := nativeApiLevelFromUser(ctx, + String(compiler.properties.First_version)) + if err != nil { + ctx.PropertyErrorf("first_version", err.Error()) + return + } + generatePerApiVariants(ctx, m, firstVersion, + func(m *Module, version android.ApiLevel) { m.compiler.(*stubDecorator).properties.ApiLevel = - version + version.String() }) } else if m.SplitPerApiLevel() && m.IsSdkVariant() { if ctx.Os() != android.Android { return } - generatePerApiVariants(ctx, m, "min_sdk_version", - m.MinSdkVersion(), func(m *Module, version string) { - m.Properties.Sdk_version = &version + from, err := nativeApiLevelFromUser(ctx, m.MinSdkVersion()) + if err != nil { + ctx.PropertyErrorf("min_sdk_version", err.Error()) + return + } + generatePerApiVariants(ctx, m, from, + func(m *Module, version android.ApiLevel) { + m.Properties.Sdk_version = StringPtr(version.String()) }) } } } } +func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool { + this.apiLevel = nativeApiLevelOrPanic(ctx, this.properties.ApiLevel) + + var err error + this.firstVersion, err = nativeApiLevelFromUser(ctx, + String(this.properties.First_version)) + if err != nil { + ctx.PropertyErrorf("first_version", err.Error()) + return false + } + + this.unversionedUntil, err = nativeApiLevelFromUserWithDefault(ctx, + String(this.properties.Unversioned_until), "minimum") + if err != nil { + ctx.PropertyErrorf("unversioned_until", err.Error()) + return false + } + + return true +} + func (c *stubDecorator) compilerInit(ctx BaseModuleContext) { c.baseCompiler.compilerInit(ctx) @@ -340,11 +280,16 @@ func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) O ctx.PropertyErrorf("symbol_file", "must end with .map.txt") } + if !c.initializeProperties(ctx) { + // Emits its own errors, so we don't need to. + return Objects{} + } + symbolFile := String(c.properties.Symbol_file) objs, versionScript := compileStubLibrary(ctx, flags, symbolFile, - c.properties.ApiLevel, "") + c.apiLevel.String(), "") c.versionScriptPath = versionScript - if c.properties.ApiLevel == "current" && ctx.PrimaryArch() { + if c.apiLevel.IsCurrent() && ctx.PrimaryArch() { c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile) } return objs @@ -366,12 +311,7 @@ func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags { func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path { - useVersionScript, err := shouldUseVersionScript(ctx, stub) - if err != nil { - ctx.ModuleErrorf(err.Error()) - } - - if useVersionScript { + if shouldUseVersionScript(ctx, stub) { linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String() flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag) flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath) @@ -386,8 +326,6 @@ func (stub *stubDecorator) nativeCoverage() bool { func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { arch := ctx.Target().Arch.ArchType.Name - apiLevel := stub.properties.ApiLevel - // arm64 isn't actually a multilib toolchain, so unlike the other LP64 // architectures it's just installed to lib. libDir := "lib" @@ -396,7 +334,7 @@ func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { } installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf( - "platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir)) + "platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir)) stub.installPath = ctx.InstallFile(installDir, path.Base(), path) } diff --git a/cc/stl.go b/cc/stl.go index e18fe9547..406fa3a3e 100644 --- a/cc/stl.go +++ b/cc/stl.go @@ -17,7 +17,6 @@ package cc import ( "android/soong/android" "fmt" - "strconv" ) func getNdkStlFamily(m LinkableInterface) string { @@ -136,23 +135,8 @@ func (stl *stl) begin(ctx BaseModuleContext) { } func needsLibAndroidSupport(ctx BaseModuleContext) bool { - versionStr, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch()) - if err != nil { - ctx.PropertyErrorf("sdk_version", err.Error()) - } - - if versionStr == "current" { - return false - } - - version, err := strconv.Atoi(versionStr) - if err != nil { - panic(fmt.Sprintf( - "invalid API level returned from normalizeNdkApiLevel: %q", - versionStr)) - } - - return version < 21 + version := nativeApiLevelOrPanic(ctx, ctx.sdkVersion()) + return version.LessThan(android.FirstNonLibAndroidSupportVersion) } func staticUnwinder(ctx android.BaseModuleContext) string {