Loading apex/apex.go +1 −0 Original line number Diff line number Diff line Loading @@ -714,6 +714,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) Loading apex/apex_test.go +44 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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() Loading apex/builder.go +1 −0 Original line number Diff line number Diff line Loading @@ -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 ( Loading apex/prebuilt.go +130 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, " && ")) } }, }, }} } cmd/extract_apks/main.go +105 −32 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import ( "fmt" "io" "log" "math" "os" "regexp" "strings" Loading @@ -34,7 +35,8 @@ import ( type TargetConfig struct { sdkVersion int32 screenDpi map[android_bundle_proto.ScreenDensity_DensityAlias]bool abis map[android_bundle_proto.Abi_AbiAlias]bool // Map holding <ABI alias>:<its sequence number in the flag> info. abis map[android_bundle_proto.Abi_AbiAlias]int allowPrereleased bool stem string } Loading Loading @@ -88,6 +90,7 @@ func (apkSet *ApkSet) close() { } // Matchers for selection criteria type abiTargetingMatcher struct { *android_bundle_proto.AbiTargeting } Loading @@ -99,13 +102,29 @@ 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 } } } 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 { *android_bundle_proto.ApkDescription Loading Loading @@ -161,17 +180,56 @@ 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") 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 { *android_bundle_proto.ScreenDensityTargeting Loading Loading @@ -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 Loading @@ -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 } Loading Loading @@ -414,20 +480,21 @@ func (s screenDensityFlagValue) Set(densityList string) error { func processArgs() { flag.Usage = func() { fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-zip> -sdk-version value -abis value -screen-densities value <APK set>`) fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+ `-screen-densities value {-stem value | -extract-single} [-allow-prereleased] <APK set>`) 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) Loading @@ -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() if *extractSingle { err = apkSet.extractAndCopySingle(sel, outFile) } else { writer := zip.NewWriter(outFile) defer func() { if err := writer.Close(); err != nil { log.Fatal(err) } }() if err = apkSet.writeApks(sel, targetConfig, writer); err != nil { err = apkSet.writeApks(sel, targetConfig, writer) } if err != nil { log.Fatal(err) } } Loading
apex/apex.go +1 −0 Original line number Diff line number Diff line Loading @@ -714,6 +714,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) Loading
apex/apex_test.go +44 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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() Loading
apex/builder.go +1 −0 Original line number Diff line number Diff line Loading @@ -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 ( Loading
apex/prebuilt.go +130 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, " && ")) } }, }, }} }
cmd/extract_apks/main.go +105 −32 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import ( "fmt" "io" "log" "math" "os" "regexp" "strings" Loading @@ -34,7 +35,8 @@ import ( type TargetConfig struct { sdkVersion int32 screenDpi map[android_bundle_proto.ScreenDensity_DensityAlias]bool abis map[android_bundle_proto.Abi_AbiAlias]bool // Map holding <ABI alias>:<its sequence number in the flag> info. abis map[android_bundle_proto.Abi_AbiAlias]int allowPrereleased bool stem string } Loading Loading @@ -88,6 +90,7 @@ func (apkSet *ApkSet) close() { } // Matchers for selection criteria type abiTargetingMatcher struct { *android_bundle_proto.AbiTargeting } Loading @@ -99,13 +102,29 @@ 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 } } } 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 { *android_bundle_proto.ApkDescription Loading Loading @@ -161,17 +180,56 @@ 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") 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 { *android_bundle_proto.ScreenDensityTargeting Loading Loading @@ -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 Loading @@ -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 } Loading Loading @@ -414,20 +480,21 @@ func (s screenDensityFlagValue) Set(densityList string) error { func processArgs() { flag.Usage = func() { fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-zip> -sdk-version value -abis value -screen-densities value <APK set>`) fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+ `-screen-densities value {-stem value | -extract-single} [-allow-prereleased] <APK set>`) 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) Loading @@ -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() if *extractSingle { err = apkSet.extractAndCopySingle(sel, outFile) } else { writer := zip.NewWriter(outFile) defer func() { if err := writer.Close(); err != nil { log.Fatal(err) } }() if err = apkSet.writeApks(sel, targetConfig, writer); err != nil { err = apkSet.writeApks(sel, targetConfig, writer) } if err != nil { log.Fatal(err) } }