Merge "Output apkcerts file for android_app_set." into rvc-dev am: d8f1b81e75

Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/12041704

Change-Id: I39f867293f232116148b0a0ced65754cf70465a5
This commit is contained in:
Jaewoong Jung
2020-07-01 00:23:47 +00:00
committed by Automerger Merge Worker
6 changed files with 75 additions and 24 deletions

View File

@@ -24,6 +24,7 @@ import (
"math" "math"
"os" "os"
"regexp" "regexp"
"sort"
"strings" "strings"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@@ -355,7 +356,7 @@ type Zip2ZipWriter interface {
// Writes out selected entries, renaming them as needed // Writes out selected entries, renaming them as needed
func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig, func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
writer Zip2ZipWriter) error { writer Zip2ZipWriter, partition string) ([]string, error) {
// Renaming rules: // Renaming rules:
// splits/MODULE-master.apk to STEM.apk // splits/MODULE-master.apk to STEM.apk
// else // else
@@ -389,10 +390,11 @@ func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
} }
entryOrigin := make(map[string]string) // output entry to input entry entryOrigin := make(map[string]string) // output entry to input entry
var apkcerts []string
for _, apk := range selected.entries { for _, apk := range selected.entries {
apkFile, ok := apkSet.entries[apk] apkFile, ok := apkSet.entries[apk]
if !ok { if !ok {
return fmt.Errorf("TOC refers to an entry %s which does not exist", apk) return nil, fmt.Errorf("TOC refers to an entry %s which does not exist", apk)
} }
inName := apkFile.Name inName := apkFile.Name
outName, ok := renamer(inName) outName, ok := renamer(inName)
@@ -405,10 +407,15 @@ func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
} }
entryOrigin[outName] = inName entryOrigin[outName] = inName
if err := writer.CopyFrom(apkFile, outName); err != nil { if err := writer.CopyFrom(apkFile, outName); err != nil {
return err return nil, err
}
if partition != "" {
apkcerts = append(apkcerts, fmt.Sprintf(
`name="%s" certificate="PRESIGNED" private_key="" partition="%s"`, outName, partition))
} }
} }
return nil sort.Strings(apkcerts)
return apkcerts, nil
} }
func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error { func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error {
@@ -433,6 +440,9 @@ var (
} }
extractSingle = flag.Bool("extract-single", false, extractSingle = flag.Bool("extract-single", false,
"extract a single target and output it uncompressed. only available for standalone apks and apexes.") "extract a single target and output it uncompressed. only available for standalone apks and apexes.")
apkcertsOutput = flag.String("apkcerts", "",
"optional apkcerts.txt output file containing signing info of all outputted apks")
partition = flag.String("partition", "", "partition string. required when -apkcerts is used.")
) )
// Parse abi values // Parse abi values
@@ -485,7 +495,8 @@ func (s screenDensityFlagValue) Set(densityList string) error {
func processArgs() { func processArgs() {
flag.Usage = func() { flag.Usage = func() {
fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+ 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>`) `-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `+
`[-apkcerts <apkcerts output file> -partition <partition>] <APK set>`)
flag.PrintDefaults() flag.PrintDefaults()
os.Exit(2) os.Exit(2)
} }
@@ -498,7 +509,8 @@ func processArgs() {
"allow prereleased") "allow prereleased")
flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file") flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
flag.Parse() flag.Parse()
if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 || (targetConfig.stem == "" && !*extractSingle) { if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 ||
(targetConfig.stem == "" && !*extractSingle) || (*apkcertsOutput != "" && *partition == "") {
flag.Usage() flag.Usage()
} }
targetConfig.sdkVersion = int32(*version) targetConfig.sdkVersion = int32(*version)
@@ -536,7 +548,20 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
}() }()
err = apkSet.writeApks(sel, targetConfig, writer) apkcerts, err := apkSet.writeApks(sel, targetConfig, writer, *partition)
if err == nil && *apkcertsOutput != "" {
apkcertsFile, err := os.Create(*apkcertsOutput)
if err != nil {
log.Fatal(err)
}
defer apkcertsFile.Close()
for _, a := range apkcerts {
_, err = apkcertsFile.WriteString(a + "\n")
if err != nil {
log.Fatal(err)
}
}
}
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@@ -16,10 +16,11 @@ package main
import ( import (
"fmt" "fmt"
"github.com/golang/protobuf/proto"
"reflect" "reflect"
"testing" "testing"
"github.com/golang/protobuf/proto"
bp "android/soong/cmd/extract_apks/bundle_proto" bp "android/soong/cmd/extract_apks/bundle_proto"
"android/soong/third_party/zip" "android/soong/third_party/zip"
) )
@@ -430,48 +431,63 @@ func (w testZip2ZipWriter) CopyFrom(file *zip.File, out string) error {
return nil return nil
} }
type testCaseWriteZip struct { type testCaseWriteApks struct {
name string name string
moduleName string moduleName string
stem string stem string
partition string
// what we write from what // what we write from what
expected map[string]string expectedZipEntries map[string]string
expectedApkcerts []string
} }
func TestWriteZip(t *testing.T) { func TestWriteApks(t *testing.T) {
testCases := []testCaseWriteZip{ testCases := []testCaseWriteApks{
{ {
name: "splits", name: "splits",
moduleName: "mybase", moduleName: "mybase",
stem: "Foo", stem: "Foo",
expected: map[string]string{ partition: "system",
expectedZipEntries: map[string]string{
"Foo.apk": "splits/mybase-master.apk", "Foo.apk": "splits/mybase-master.apk",
"Foo-xhdpi.apk": "splits/mybase-xhdpi.apk", "Foo-xhdpi.apk": "splits/mybase-xhdpi.apk",
}, },
expectedApkcerts: []string{
`name="Foo-xhdpi.apk" certificate="PRESIGNED" private_key="" partition="system"`,
`name="Foo.apk" certificate="PRESIGNED" private_key="" partition="system"`,
},
}, },
{ {
name: "universal", name: "universal",
moduleName: "base", moduleName: "base",
stem: "Bar", stem: "Bar",
expected: map[string]string{ partition: "product",
expectedZipEntries: map[string]string{
"Bar.apk": "universal.apk", "Bar.apk": "universal.apk",
}, },
expectedApkcerts: []string{
`name="Bar.apk" certificate="PRESIGNED" private_key="" partition="product"`,
},
}, },
} }
for _, testCase := range testCases { for _, testCase := range testCases {
apkSet := ApkSet{entries: make(map[string]*zip.File)} apkSet := ApkSet{entries: make(map[string]*zip.File)}
sel := SelectionResult{moduleName: testCase.moduleName} sel := SelectionResult{moduleName: testCase.moduleName}
for _, in := range testCase.expected { for _, in := range testCase.expectedZipEntries {
apkSet.entries[in] = &zip.File{FileHeader: zip.FileHeader{Name: in}} apkSet.entries[in] = &zip.File{FileHeader: zip.FileHeader{Name: in}}
sel.entries = append(sel.entries, in) sel.entries = append(sel.entries, in)
} }
writer := testZip2ZipWriter{make(map[string]string)} writer := testZip2ZipWriter{make(map[string]string)}
config := TargetConfig{stem: testCase.stem} config := TargetConfig{stem: testCase.stem}
if err := apkSet.writeApks(sel, config, writer); err != nil { apkcerts, err := apkSet.writeApks(sel, config, writer, testCase.partition)
if err != nil {
t.Error(err) t.Error(err)
} }
if !reflect.DeepEqual(testCase.expected, writer.entries) { if !reflect.DeepEqual(testCase.expectedZipEntries, writer.entries) {
t.Errorf("expected %v, got %v", testCase.expected, writer.entries) t.Errorf("expected zip entries %v, got %v", testCase.expectedZipEntries, writer.entries)
}
if !reflect.DeepEqual(testCase.expectedApkcerts, apkcerts) {
t.Errorf("expected apkcerts %v, got %v", testCase.expectedApkcerts, apkcerts)
} }
} }
} }

View File

@@ -719,6 +719,7 @@ func (apkSet *AndroidAppSet) AndroidMkEntries() []android.AndroidMkEntries {
func(entries *android.AndroidMkEntries) { func(entries *android.AndroidMkEntries) {
entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged()) entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
entries.SetString("LOCAL_APK_SET_MASTER_FILE", apkSet.masterFile) entries.SetString("LOCAL_APK_SET_MASTER_FILE", apkSet.masterFile)
entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...) entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...)
}, },
}, },

View File

@@ -79,6 +79,7 @@ type AndroidAppSet struct {
properties AndroidAppSetProperties properties AndroidAppSetProperties
packedOutput android.WritablePath packedOutput android.WritablePath
masterFile string masterFile string
apkcertsFile android.ModuleOutPath
} }
func (as *AndroidAppSet) Name() string { func (as *AndroidAppSet) Name() string {
@@ -130,6 +131,7 @@ func SupportedAbis(ctx android.ModuleContext) []string {
func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip") as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
// We are assuming here that the master file in the APK // We are assuming here that the master file in the APK
// set has `.apk` suffix. If it doesn't the build will fail. // set has `.apk` suffix. If it doesn't the build will fail.
// APK sets containing APEX files are handled elsewhere. // APK sets containing APEX files are handled elsewhere.
@@ -142,16 +144,19 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext)
// TODO(asmundak): do we support device features // TODO(asmundak): do we support device features
ctx.Build(pctx, ctx.Build(pctx,
android.BuildParams{ android.BuildParams{
Rule: extractMatchingApks, Rule: extractMatchingApks,
Description: "Extract APKs from APK set", Description: "Extract APKs from APK set",
Output: as.packedOutput, Output: as.packedOutput,
Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)}, ImplicitOutput: as.apkcertsFile,
Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
Args: map[string]string{ Args: map[string]string{
"abis": strings.Join(SupportedAbis(ctx), ","), "abis": strings.Join(SupportedAbis(ctx), ","),
"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)), "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
"screen-densities": screenDensities, "screen-densities": screenDensities,
"sdk-version": ctx.Config().PlatformSdkVersion(), "sdk-version": ctx.Config().PlatformSdkVersion(),
"stem": as.BaseModuleName(), "stem": as.BaseModuleName(),
"apkcerts": as.apkcertsFile.String(),
"partition": as.PartitionTag(ctx.DeviceConfig()),
}, },
}) })
} }

View File

@@ -147,7 +147,7 @@ func TestAndroidAppSet(t *testing.T) {
name: "foo", name: "foo",
set: "prebuilts/apks/app.apks", set: "prebuilts/apks/app.apks",
prerelease: true, prerelease: true,
}`) }`)
module := ctx.ModuleForTests("foo", "android_common") module := ctx.ModuleForTests("foo", "android_common")
const packedSplitApks = "foo.zip" const packedSplitApks = "foo.zip"
params := module.Output(packedSplitApks) params := module.Output(packedSplitApks)
@@ -157,6 +157,9 @@ func TestAndroidAppSet(t *testing.T) {
if s := params.Args["allow-prereleased"]; s != "true" { if s := params.Args["allow-prereleased"]; s != "true" {
t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s) t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
} }
if s := params.Args["partition"]; s != "system" {
t.Errorf("wrong partition value: '%s', expected 'system'", s)
}
mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0] mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
actualMaster := mkEntries.EntryMap["LOCAL_APK_SET_MASTER_FILE"] actualMaster := mkEntries.EntryMap["LOCAL_APK_SET_MASTER_FILE"]
expectedMaster := []string{"foo.apk"} expectedMaster := []string{"foo.apk"}

View File

@@ -120,10 +120,11 @@ var (
`${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` + `${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
`-sdk-version=${sdk-version} -abis=${abis} ` + `-sdk-version=${sdk-version} -abis=${abis} ` +
`--screen-densities=${screen-densities} --stem=${stem} ` + `--screen-densities=${screen-densities} --stem=${stem} ` +
`-apkcerts=${apkcerts} -partition=${partition} ` +
`${in}`, `${in}`,
CommandDeps: []string{"${config.ExtractApksCmd}"}, CommandDeps: []string{"${config.ExtractApksCmd}"},
}, },
"abis", "allow-prereleased", "screen-densities", "sdk-version", "stem") "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition")
turbine, turbineRE = remoteexec.StaticRules(pctx, "turbine", turbine, turbineRE = remoteexec.StaticRules(pctx, "turbine",
blueprint.RuleParams{ blueprint.RuleParams{