From fa00c061873cc89d036d43103956a85cc4b1548c Mon Sep 17 00:00:00 2001 From: Jaewoong Jung Date: Thu, 14 May 2020 14:15:24 -0700 Subject: [PATCH] Add apex_set module. apex_set takes an .apks file that contains a set of prebuilt apexes with different configurations. It uses extract_apks to select and install the best matching one for the current target. Bug: 153456259 Test: apex_test.go Test: com.android.media.apks Change-Id: I1da8bbcf1611b7c580a0cb225856cbd7029cc0a7 --- apex/apex.go | 1 + apex/apex_test.go | 44 +++++++ apex/builder.go | 1 + apex/prebuilt.go | 130 ++++++++++++++++++++ cmd/extract_apks/main.go | 137 ++++++++++++++++----- cmd/extract_apks/main_test.go | 223 +++++++++++++++++++++++++++++++--- java/app.go | 8 +- 7 files changed, 488 insertions(+), 56 deletions(-) diff --git a/apex/apex.go b/apex/apex.go index 9525ff22c..0b68ae4eb 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -722,6 +722,7 @@ func init() { android.RegisterModuleType("apex_defaults", defaultsFactory) android.RegisterModuleType("prebuilt_apex", PrebuiltFactory) android.RegisterModuleType("override_apex", overrideApexFactory) + android.RegisterModuleType("apex_set", apexSetFactory) android.PreDepsMutators(RegisterPreDepsMutators) android.PostDepsMutators(RegisterPostDepsMutators) diff --git a/apex/apex_test.go b/apex/apex_test.go index 021c3e22a..5461debf4 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -175,6 +175,7 @@ func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*andr "testkey2.pem": nil, "myapex-arm64.apex": nil, "myapex-arm.apex": nil, + "myapex.apks": nil, "frameworks/base/api/current.txt": nil, "framework/aidl/a.aidl": nil, "build/make/core/proguard.flags": nil, @@ -218,6 +219,7 @@ func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*andr ctx.RegisterModuleType("apex_defaults", defaultsFactory) ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory) ctx.RegisterModuleType("override_apex", overrideApexFactory) + ctx.RegisterModuleType("apex_set", apexSetFactory) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) @@ -4789,6 +4791,48 @@ func TestTestFor(t *testing.T) { ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so") } +// TODO(jungjw): Move this to proptools +func intPtr(i int) *int { + return &i +} + +func TestApexSet(t *testing.T) { + ctx, config := testApex(t, ` + apex_set { + name: "myapex", + set: "myapex.apks", + filename: "foo_v2.apex", + overrides: ["foo"], + } + `, func(fs map[string][]byte, config android.Config) { + config.TestProductVariables.Platform_sdk_version = intPtr(30) + config.TestProductVariables.DeviceArch = proptools.StringPtr("arm") + config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm64") + }) + + m := ctx.ModuleForTests("myapex", "android_common") + + // Check extract_apks tool parameters. + extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex") + actual := extractedApex.Args["abis"] + expected := "ARMEABI_V7A,ARM64_V8A" + if actual != expected { + t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual) + } + actual = extractedApex.Args["sdk-version"] + expected = "30" + if actual != expected { + t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual) + } + + a := m.Module().(*ApexSet) + expectedOverrides := []string{"foo"} + actualOverrides := android.AndroidMkEntriesForTest(t, config, "", a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"] + if !reflect.DeepEqual(actualOverrides, expectedOverrides) { + t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES - expected %q vs actual %q", expectedOverrides, actualOverrides) + } +} + func TestMain(m *testing.M) { run := func() int { setUp() diff --git a/apex/builder.go b/apex/builder.go index 47ae501f9..3d0e9b254 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -61,6 +61,7 @@ func init() { pctx.HostBinToolVariable("zipalign", "zipalign") pctx.HostBinToolVariable("jsonmodify", "jsonmodify") pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest") + pctx.HostBinToolVariable("extract_apks", "extract_apks") } var ( diff --git a/apex/prebuilt.go b/apex/prebuilt.go index d089c285d..03266c55e 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -16,13 +16,29 @@ package apex import ( "fmt" + "strconv" "strings" "android/soong/android" + "android/soong/java" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) +var ( + extractMatchingApex = pctx.StaticRule( + "extractMatchingApex", + blueprint.RuleParams{ + Command: `rm -rf "$out" && ` + + `${extract_apks} -o "${out}" -allow-prereleased=${allow-prereleased} ` + + `-sdk-version=${sdk-version} -abis=${abis} -screen-densities=all -extract-single ` + + `${in}`, + CommandDeps: []string{"${extract_apks}"}, + }, + "abis", "allow-prereleased", "sdk-version") +) + type Prebuilt struct { android.ModuleBase prebuilt android.Prebuilt @@ -208,3 +224,117 @@ func (p *Prebuilt) AndroidMkEntries() []android.AndroidMkEntries { }, }} } + +type ApexSet struct { + android.ModuleBase + prebuilt android.Prebuilt + + properties ApexSetProperties + + installDir android.InstallPath + installFilename string + outputApex android.WritablePath + + // list of commands to create symlinks for backward compatibility. + // these commands will be attached as LOCAL_POST_INSTALL_CMD + compatSymlinks []string +} + +type ApexSetProperties struct { + // the .apks file path that contains prebuilt apex files to be extracted. + Set *string + + // whether the extracted apex file installable. + Installable *bool + + // optional name for the installed apex. If unspecified, name of the + // module is used as the file name + Filename *string + + // names of modules to be overridden. Listed modules can only be other binaries + // (in Make or Soong). + // This does not completely prevent installation of the overridden binaries, but if both + // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed + // from PRODUCT_PACKAGES. + Overrides []string + + // apexes in this set use prerelease SDK version + Prerelease *bool +} + +func (a *ApexSet) installable() bool { + return a.properties.Installable == nil || proptools.Bool(a.properties.Installable) +} + +func (a *ApexSet) InstallFilename() string { + return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix) +} + +func (a *ApexSet) Prebuilt() *android.Prebuilt { + return &a.prebuilt +} + +func (a *ApexSet) Name() string { + return a.prebuilt.Name(a.ModuleBase.Name()) +} + +// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex. +func apexSetFactory() android.Module { + module := &ApexSet{} + module.AddProperties(&module.properties) + android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set") + android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) + return module +} + +func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { + a.installFilename = a.InstallFilename() + if !strings.HasSuffix(a.installFilename, imageApexSuffix) { + ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix) + } + + apexSet := a.prebuilt.SingleSourcePath(ctx) + a.outputApex = android.PathForModuleOut(ctx, a.installFilename) + ctx.Build(pctx, + android.BuildParams{ + Rule: extractMatchingApex, + Description: "Extract an apex from an apex set", + Inputs: android.Paths{apexSet}, + Output: a.outputApex, + Args: map[string]string{ + "abis": strings.Join(java.SupportedAbis(ctx), ","), + "allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)), + "sdk-version": ctx.Config().PlatformSdkVersion(), + }, + }) + a.installDir = android.PathForModuleInstall(ctx, "apex") + if a.installable() { + ctx.InstallFile(a.installDir, a.installFilename, a.outputApex) + } + + // in case that apex_set replaces source apex (using prefer: prop) + a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx) + // or that apex_set overrides other apexes (using overrides: prop) + for _, overridden := range a.properties.Overrides { + a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...) + } +} + +func (a *ApexSet) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{android.AndroidMkEntries{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(a.outputApex), + Include: "$(BUILD_PREBUILT)", + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String()) + entries.SetString("LOCAL_MODULE_STEM", a.installFilename) + entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable()) + entries.AddStrings("LOCAL_OVERRIDES_MODULES", a.properties.Overrides...) + if len(a.compatSymlinks) > 0 { + entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(a.compatSymlinks, " && ")) + } + }, + }, + }} +} diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go index 4a146daf6..a638db2a8 100644 --- a/cmd/extract_apks/main.go +++ b/cmd/extract_apks/main.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "log" + "math" "os" "regexp" "strings" @@ -32,9 +33,10 @@ import ( ) type TargetConfig struct { - sdkVersion int32 - screenDpi map[android_bundle_proto.ScreenDensity_DensityAlias]bool - abis map[android_bundle_proto.Abi_AbiAlias]bool + sdkVersion int32 + screenDpi map[android_bundle_proto.ScreenDensity_DensityAlias]bool + // Map holding : info. + abis map[android_bundle_proto.Abi_AbiAlias]int allowPrereleased bool stem string } @@ -88,6 +90,7 @@ func (apkSet *ApkSet) close() { } // Matchers for selection criteria + type abiTargetingMatcher struct { *android_bundle_proto.AbiTargeting } @@ -99,12 +102,28 @@ func (m abiTargetingMatcher) matches(config TargetConfig) bool { if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok { return true } + // Find the one that appears first in the abis flags. + abiIdx := math.MaxInt32 for _, v := range m.GetValue() { - if _, ok := config.abis[v.Alias]; ok { - return true + if i, ok := config.abis[v.Alias]; ok { + if i < abiIdx { + abiIdx = i + } } } - return false + if abiIdx == math.MaxInt32 { + return false + } + // See if any alternatives appear before the above one. + for _, a := range m.GetAlternatives() { + if i, ok := config.abis[a.Alias]; ok { + if i < abiIdx { + // There is a better alternative. Skip this one. + return false + } + } + } + return true } type apkDescriptionMatcher struct { @@ -161,16 +180,55 @@ func (m moduleTargetingMatcher) matches(config TargetConfig) bool { userCountriesTargetingMatcher{m.UserCountriesTargeting}.matches(config)) } +// A higher number means a higher priority. +// This order must be kept identical to bundletool's. +var multiAbiPriorities = map[android_bundle_proto.Abi_AbiAlias]int{ + android_bundle_proto.Abi_ARMEABI: 1, + android_bundle_proto.Abi_ARMEABI_V7A: 2, + android_bundle_proto.Abi_ARM64_V8A: 3, + android_bundle_proto.Abi_X86: 4, + android_bundle_proto.Abi_X86_64: 5, + android_bundle_proto.Abi_MIPS: 6, + android_bundle_proto.Abi_MIPS64: 7, +} + type multiAbiTargetingMatcher struct { *android_bundle_proto.MultiAbiTargeting } -func (t multiAbiTargetingMatcher) matches(_ TargetConfig) bool { +func (t multiAbiTargetingMatcher) matches(config TargetConfig) bool { if t.MultiAbiTargeting == nil { return true } - log.Fatal("multiABI based selection is not implemented") - return false + if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok { + return true + } + // Find the one with the highest priority. + highestPriority := 0 + for _, v := range t.GetValue() { + for _, a := range v.GetAbi() { + if _, ok := config.abis[a.Alias]; ok { + if highestPriority < multiAbiPriorities[a.Alias] { + highestPriority = multiAbiPriorities[a.Alias] + } + } + } + } + if highestPriority == 0 { + return false + } + // See if there are any matching alternatives with a higher priority. + for _, v := range t.GetAlternatives() { + for _, a := range v.GetAbi() { + if _, ok := config.abis[a.Alias]; ok { + if highestPriority < multiAbiPriorities[a.Alias] { + // There's a better one. Skip this one. + return false + } + } + } + } + return true } type screenDensityTargetingMatcher struct { @@ -349,13 +407,28 @@ func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig, return nil } +func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error { + if len(selected.entries) != 1 { + return fmt.Errorf("Too many matching entries for extract-single:\n%v", selected.entries) + } + apk, ok := apkSet.entries[selected.entries[0]] + if !ok { + return fmt.Errorf("Couldn't find apk path %s", selected.entries[0]) + } + inputReader, _ := apk.Open() + _, err := io.Copy(outFile, inputReader) + return err +} + // Arguments parsing var ( - outputZip = flag.String("o", "", "output zip containing extracted entries") + outputFile = flag.String("o", "", "output file containing extracted entries") targetConfig = TargetConfig{ screenDpi: map[android_bundle_proto.ScreenDensity_DensityAlias]bool{}, - abis: map[android_bundle_proto.Abi_AbiAlias]bool{}, + abis: map[android_bundle_proto.Abi_AbiAlias]int{}, } + extractSingle = flag.Bool("extract-single", false, + "extract a single target and output it uncompressed. only available for standalone apks and apexes.") ) // Parse abi values @@ -368,19 +441,12 @@ func (a abiFlagValue) String() string { } func (a abiFlagValue) Set(abiList string) error { - if abiList == "none" { - return nil - } - if abiList == "all" { - targetConfig.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE] = true - return nil - } - for _, abi := range strings.Split(abiList, ",") { + for i, abi := range strings.Split(abiList, ",") { v, ok := android_bundle_proto.Abi_AbiAlias_value[abi] if !ok { return fmt.Errorf("bad ABI value: %q", abi) } - targetConfig.abis[android_bundle_proto.Abi_AbiAlias(v)] = true + targetConfig.abis[android_bundle_proto.Abi_AbiAlias(v)] = i } return nil } @@ -414,20 +480,21 @@ func (s screenDensityFlagValue) Set(densityList string) error { func processArgs() { flag.Usage = func() { - fmt.Fprintln(os.Stderr, `usage: extract_apks -o -sdk-version value -abis value -screen-densities value `) + fmt.Fprintln(os.Stderr, `usage: extract_apks -o -sdk-version value -abis value `+ + `-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `) flag.PrintDefaults() os.Exit(2) } version := flag.Uint("sdk-version", 0, "SDK version") flag.Var(abiFlagValue{&targetConfig}, "abis", - "'all' or comma-separated ABIs list of ARMEABI ARMEABI_V7A ARM64_V8A X86 X86_64 MIPS MIPS64") + "comma-separated ABIs list of ARMEABI ARMEABI_V7A ARM64_V8A X86 X86_64 MIPS MIPS64") flag.Var(screenDensityFlagValue{&targetConfig}, "screen-densities", "'all' or comma-separated list of screen density names (NODPI LDPI MDPI TVDPI HDPI XHDPI XXHDPI XXXHDPI)") flag.BoolVar(&targetConfig.allowPrereleased, "allow-prereleased", false, "allow prereleased") - flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name") + flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file") flag.Parse() - if (*outputZip == "") || len(flag.Args()) != 1 || *version == 0 || targetConfig.stem == "" { + if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 || (targetConfig.stem == "" && !*extractSingle) { flag.Usage() } targetConfig.sdkVersion = int32(*version) @@ -450,18 +517,24 @@ func main() { log.Fatalf("there are no entries for the target configuration: %#v", targetConfig) } - outFile, err := os.Create(*outputZip) + outFile, err := os.Create(*outputFile) if err != nil { log.Fatal(err) } defer outFile.Close() - writer := zip.NewWriter(outFile) - defer func() { - if err := writer.Close(); err != nil { - log.Fatal(err) - } - }() - if err = apkSet.writeApks(sel, targetConfig, writer); err != nil { + + if *extractSingle { + err = apkSet.extractAndCopySingle(sel, outFile) + } else { + writer := zip.NewWriter(outFile) + defer func() { + if err := writer.Close(); err != nil { + log.Fatal(err) + } + }() + err = apkSet.writeApks(sel, targetConfig, writer) + } + if err != nil { log.Fatal(err) } } diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go index 1d7726b36..bc4d377c8 100644 --- a/cmd/extract_apks/main_test.go +++ b/cmd/extract_apks/main_test.go @@ -35,20 +35,20 @@ type TestDesc struct { configs []TestConfigDesc } -var ( - testCases = []TestDesc{ +func TestSelectApks_ApkSet(t *testing.T) { + testCases := []TestDesc{ { protoText: ` variant { targeting { - sdk_version_targeting { + sdk_version_targeting { value { min { value: 29 } } } } apk_set { - module_metadata { + module_metadata { name: "base" targeting {} delivery_type: INSTALL_TIME } apk_description { targeting { - screen_density_targeting { + screen_density_targeting { value { density_alias: LDPI } } sdk_version_targeting { value { min { value: 21 } } } } @@ -71,7 +71,10 @@ variant { apk_description { targeting { abi_targeting { - value { alias: ARMEABI_V7A } } + value { alias: ARMEABI_V7A } + alternatives { alias: ARM64_V8A } + alternatives { alias: X86 } + alternatives { alias: X86_64 } } sdk_version_targeting { value { min { value: 21 } } } } path: "splits/base-armeabi_v7a.apk" @@ -79,7 +82,10 @@ variant { apk_description { targeting { abi_targeting { - value { alias: ARM64_V8A } } + value { alias: ARM64_V8A } + alternatives { alias: ARMEABI_V7A } + alternatives { alias: X86 } + alternatives { alias: X86_64 } } sdk_version_targeting { value { min { value: 21 } } } } path: "splits/base-arm64_v8a.apk" @@ -87,7 +93,10 @@ variant { apk_description { targeting { abi_targeting { - value { alias: X86 } } + value { alias: X86 } + alternatives { alias: ARMEABI_V7A } + alternatives { alias: ARM64_V8A } + alternatives { alias: X86_64 } } sdk_version_targeting { value { min { value: 21 } } } } path: "splits/base-x86.apk" @@ -95,7 +104,10 @@ variant { apk_description { targeting { abi_targeting { - value { alias: X86_64 } } + value { alias: X86_64 } + alternatives { alias: ARMEABI_V7A } + alternatives { alias: ARM64_V8A } + alternatives { alias: X86 } } sdk_version_targeting { value { min { value: 21 } } } } path: "splits/base-x86_64.apk" @@ -113,9 +125,9 @@ bundletool { screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ bp.ScreenDensity_DENSITY_UNSPECIFIED: true, }, - abis: map[bp.Abi_AbiAlias]bool{ - bp.Abi_ARMEABI_V7A: true, - bp.Abi_ARM64_V8A: true, + abis: map[bp.Abi_AbiAlias]int{ + bp.Abi_ARMEABI_V7A: 0, + bp.Abi_ARM64_V8A: 1, }, }, expected: SelectionResult{ @@ -125,7 +137,6 @@ bundletool { "splits/base-mdpi.apk", "splits/base-master.apk", "splits/base-armeabi_v7a.apk", - "splits/base-arm64_v8a.apk", }, }, }, @@ -136,7 +147,7 @@ bundletool { screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ bp.ScreenDensity_LDPI: true, }, - abis: map[bp.Abi_AbiAlias]bool{}, + abis: map[bp.Abi_AbiAlias]int{}, }, expected: SelectionResult{ "base", @@ -153,23 +164,44 @@ bundletool { screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ bp.ScreenDensity_LDPI: true, }, - abis: map[bp.Abi_AbiAlias]bool{}, + abis: map[bp.Abi_AbiAlias]int{}, }, expected: SelectionResult{ "", nil, }, }, + { + name: "four", + targetConfig: TargetConfig{ + sdkVersion: 29, + screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ + bp.ScreenDensity_MDPI: true, + }, + abis: map[bp.Abi_AbiAlias]int{ + bp.Abi_ARM64_V8A: 0, + bp.Abi_ARMEABI_V7A: 1, + }, + }, + expected: SelectionResult{ + "base", + []string{ + "splits/base-mdpi.apk", + "splits/base-master.apk", + "splits/base-arm64_v8a.apk", + }, + }, + }, }, }, { protoText: ` variant { targeting { - sdk_version_targeting { + sdk_version_targeting { value { min { value: 10000 } } } } apk_set { - module_metadata { + module_metadata { name: "base" targeting {} delivery_type: INSTALL_TIME } apk_description { targeting { @@ -183,7 +215,7 @@ variant { targetConfig: TargetConfig{ sdkVersion: 30, screenDpi: map[bp.ScreenDensity_DensityAlias]bool{}, - abis: map[bp.Abi_AbiAlias]bool{}, + abis: map[bp.Abi_AbiAlias]int{}, allowPrereleased: true, }, expected: SelectionResult{ @@ -194,9 +226,160 @@ variant { }, }, } -) + for _, testCase := range testCases { + var toc bp.BuildApksResult + if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil { + t.Fatal(err) + } + for _, config := range testCase.configs { + actual := selectApks(&toc, config.targetConfig) + if !reflect.DeepEqual(config.expected, actual) { + t.Errorf("%s: expected %v, got %v", config.name, config.expected, actual) + } + } + } +} -func TestSelectApks(t *testing.T) { +func TestSelectApks_ApexSet(t *testing.T) { + testCases := []TestDesc{ + { + protoText: ` +variant { + targeting { + sdk_version_targeting { + value { min { value: 29 } } } } + apk_set { + module_metadata { + name: "base" targeting {} delivery_type: INSTALL_TIME } + apk_description { + targeting { + multi_abi_targeting { + value { abi { alias: ARMEABI_V7A } } + alternatives { abi { alias: ARM64_V8A } } + alternatives { abi { alias: X86 } } + alternatives { abi { alias: X86_64 } } } + sdk_version_targeting { + value { min { value: 21 } } } } + path: "standalones/standalone-armeabi_v7a.apex" + apex_apk_metadata { } } + apk_description { + targeting { + multi_abi_targeting { + value { abi { alias: ARM64_V8A } } + alternatives { abi { alias: ARMEABI_V7A } } + alternatives { abi { alias: X86 } } + alternatives { abi { alias: X86_64 } } } + sdk_version_targeting { + value { min { value: 21 } } } } + path: "standalones/standalone-arm64_v8a.apex" + apex_apk_metadata { } } + apk_description { + targeting { + multi_abi_targeting { + value { abi { alias: X86 } } + alternatives { abi { alias: ARMEABI_V7A } } + alternatives { abi { alias: ARM64_V8A } } + alternatives { abi { alias: X86_64 } } } + sdk_version_targeting { + value { min { value: 21 } } } } + path: "standalones/standalone-x86.apex" + apex_apk_metadata { } } + apk_description { + targeting { + multi_abi_targeting { + value { abi { alias: X86_64 } } + alternatives { abi { alias: ARMEABI_V7A } } + alternatives { abi { alias: ARM64_V8A } } + alternatives { abi { alias: X86 } } } + sdk_version_targeting { + value { min { value: 21 } } } } + path: "standalones/standalone-x86_64.apex" + apex_apk_metadata { } } } +} +bundletool { + version: "0.10.3" } + +`, + configs: []TestConfigDesc{ + { + name: "order matches priorities", + targetConfig: TargetConfig{ + sdkVersion: 29, + screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ + bp.ScreenDensity_DENSITY_UNSPECIFIED: true, + }, + abis: map[bp.Abi_AbiAlias]int{ + bp.Abi_ARM64_V8A: 0, + bp.Abi_ARMEABI_V7A: 1, + }, + }, + expected: SelectionResult{ + "base", + []string{ + "standalones/standalone-arm64_v8a.apex", + }, + }, + }, + { + name: "order doesn't match priorities", + targetConfig: TargetConfig{ + sdkVersion: 29, + screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ + bp.ScreenDensity_DENSITY_UNSPECIFIED: true, + }, + abis: map[bp.Abi_AbiAlias]int{ + bp.Abi_ARMEABI_V7A: 0, + bp.Abi_ARM64_V8A: 1, + }, + }, + expected: SelectionResult{ + "base", + []string{ + "standalones/standalone-arm64_v8a.apex", + }, + }, + }, + { + name: "single choice", + targetConfig: TargetConfig{ + sdkVersion: 29, + screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ + bp.ScreenDensity_DENSITY_UNSPECIFIED: true, + }, + abis: map[bp.Abi_AbiAlias]int{ + bp.Abi_ARMEABI_V7A: 0, + }, + }, + expected: SelectionResult{ + "base", + []string{ + "standalones/standalone-armeabi_v7a.apex", + }, + }, + }, + { + name: "cross platform", + targetConfig: TargetConfig{ + sdkVersion: 29, + screenDpi: map[bp.ScreenDensity_DensityAlias]bool{ + bp.ScreenDensity_DENSITY_UNSPECIFIED: true, + }, + abis: map[bp.Abi_AbiAlias]int{ + bp.Abi_ARM64_V8A: 0, + bp.Abi_MIPS64: 1, + bp.Abi_X86: 2, + }, + }, + expected: SelectionResult{ + "base", + []string{ + "standalones/standalone-x86.apex", + }, + }, + }, + }, + }, + } for _, testCase := range testCases { var toc bp.BuildApksResult if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil { diff --git a/java/app.go b/java/app.go index f5f70d1a6..9d8b7d3cd 100755 --- a/java/app.go +++ b/java/app.go @@ -96,16 +96,16 @@ func (as *AndroidAppSet) Privileged() bool { return Bool(as.properties.Privileged) } -var targetCpuAbi = map[string]string{ +var TargetCpuAbi = map[string]string{ "arm": "ARMEABI_V7A", "arm64": "ARM64_V8A", "x86": "X86", "x86_64": "X86_64", } -func supportedAbis(ctx android.ModuleContext) []string { +func SupportedAbis(ctx android.ModuleContext) []string { abiName := func(archVar string, deviceArch string) string { - if abi, found := targetCpuAbi[deviceArch]; found { + if abi, found := TargetCpuAbi[deviceArch]; found { return abi } ctx.ModuleErrorf("Invalid %s: %s", archVar, deviceArch) @@ -138,7 +138,7 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) Output: as.packedOutput, Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)}, Args: map[string]string{ - "abis": strings.Join(supportedAbis(ctx), ","), + "abis": strings.Join(SupportedAbis(ctx), ","), "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)), "screen-densities": screenDensities, "sdk-version": ctx.Config().PlatformSdkVersion(),