cmd/gogio: [android] add support for AAB

That patch makes possible to generate Android App Bundle (`.aab`) instead
of APK. In order to generate AAB use `-o outputfile.aab`.

Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
This commit is contained in:
Inkeliz
2021-03-24 14:54:10 +00:00
committed by Elias Naur
parent 238dd1aa86
commit b7dc407dbe
2 changed files with 128 additions and 30 deletions
+127 -29
View File
@@ -153,10 +153,27 @@ func buildAndroid(tmpDir string, bi *buildInfo) error {
case "archive":
return archiveAndroid(tmpDir, bi, perms)
case "exe":
if err := exeAndroid(tmpDir, tools, bi, extraJars, perms); err != nil {
file := *destPath
if file == "" {
file = fmt.Sprintf("%s.apk", bi.name)
}
isBundle := false
switch filepath.Ext(file) {
case ".apk":
case ".aab":
isBundle = true
default:
return fmt.Errorf("the specified output %q does not end in '.apk' or '.aab'", file)
}
if err := exeAndroid(tmpDir, tools, bi, extraJars, perms, isBundle); err != nil {
return err
}
return signAPK(tmpDir, tools, bi)
if isBundle {
return signAAB(tmpDir, file, tools, bi)
}
return signAPK(tmpDir, file, tools, bi)
default:
panic("unreachable")
}
@@ -316,7 +333,7 @@ func archiveAndroid(tmpDir string, bi *buildInfo, perms []string) (err error) {
return aarw.Close()
}
func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, perms []string) (err error) {
func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, perms []string, isBundle bool) (err error) {
classes := filepath.Join(tmpDir, "classes")
var classFiles []string
err = filepath.Walk(classes, func(path string, f os.FileInfo, err error) error {
@@ -441,20 +458,25 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
}
linkAPK := filepath.Join(tmpDir, "link.apk")
link := exec.Command(
aapt2,
args := []string{
"link",
"--manifest", manifest,
"-I", tools.androidjar,
"-o", linkAPK,
resZip,
)
if _, err := runCmd(link); err != nil {
}
if isBundle {
args = append(args, "--proto-format")
}
args = append(args, resZip)
if _, err := runCmd(exec.Command(aapt2, args...)); err != nil {
return err
}
// The Go standard library archive/zip doesn't support appending to zip
// files. Copy files from `link.apk` (generated by aapt2) along with classes.dex and
// the Go libraries to a new `app.ap_` file.
// the Go libraries to a new `app.zip` file.
// Load link.apk as zip.
linkAPKZip, err := zip.OpenReader(linkAPK)
@@ -464,7 +486,7 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
defer linkAPKZip.Close()
// Create new "APK".
unsignedAPK := filepath.Join(tmpDir, "app.ap_")
unsignedAPK := filepath.Join(tmpDir, "app.zip")
unsignedAPKFile, err := os.Create(unsignedAPK)
if err != nil {
return err
@@ -484,6 +506,14 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
Method: f.FileHeader.Method,
}
if isBundle {
// AAB have pre-defined folders.
switch header.Name {
case "AndroidManifest.xml":
header.Name = "manifest/AndroidManifest.xml"
}
}
w, err := unsignedAPKZip.CreateHeader(&header)
if err != nil {
return err
@@ -525,28 +555,19 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
}
// Append classes.dex.
if err := appendToZip("classes.dex", filepath.Join(dexDir, "classes.dex")); err != nil {
classesFolder := "classes.dex"
if isBundle {
classesFolder = "dex/classes.dex"
}
if err := appendToZip(classesFolder, filepath.Join(dexDir, "classes.dex")); err != nil {
return err
}
return unsignedAPKZip.Close()
}
func signAPK(tmpDir string, tools *androidTools, bi *buildInfo) error {
apkFile := *destPath
if apkFile == "" {
apkFile = fmt.Sprintf("%s.apk", bi.name)
}
if filepath.Ext(apkFile) != ".apk" {
return fmt.Errorf("the specified output %q does not end in '.apk'", apkFile)
}
_, err := runCmd(exec.Command(
filepath.Join(tools.buildtools, "zipalign"),
"-f",
"4", // 32-bit alignment.
filepath.Join(tmpDir, "app.ap_"),
apkFile,
))
if err != nil {
func signAPK(tmpDir string, apkFile string, tools *androidTools, bi *buildInfo) error {
if err := zipalign(tools, filepath.Join(tmpDir, "app.zip"), apkFile); err != nil {
return err
}
@@ -556,17 +577,94 @@ func signAPK(tmpDir string, tools *androidTools, bi *buildInfo) error {
}
}
_, err = runCmd(exec.Command(
_, err := runCmd(exec.Command(
filepath.Join(tools.buildtools, "apksigner"),
"sign",
"--ks-pass", "pass:"+bi.password,
"--ks", bi.key,
apkFile,
))
return err
}
func signAAB(tmpDir string, aabFile string, tools *androidTools, bi *buildInfo) error {
allBundleTools, err := filepath.Glob(filepath.Join(tools.buildtools, "bundletool*.jar"))
if err != nil {
return err
}
return nil
bundletool := ""
for _, v := range allBundleTools {
bundletool = v
break
}
if bundletool == "" {
return fmt.Errorf("bundletool was not found at %s. Download it from https://github.com/google/bundletool/releases and move to the respective folder", tools.buildtools)
}
_, err = runCmd(exec.Command(
"java",
"-jar", bundletool,
"build-bundle",
"--modules="+filepath.Join(tmpDir, "app.zip"),
"--output="+filepath.Join(tmpDir, "app.aab"),
))
if err != nil {
return err
}
if err := zipalign(tools, filepath.Join(tmpDir, "app.aab"), aabFile); err != nil {
return err
}
if bi.key == "" {
if err := defaultAndroidKeystore(tmpDir, bi); err != nil {
return err
}
}
keytoolList, err := runCmd(exec.Command(
"keytool",
"-keystore", bi.key,
"-list",
"-keypass", bi.password,
"-v",
))
if err != nil {
return err
}
var alias string
for _, t := range strings.Split(keytoolList, "\n") {
if i, _ := fmt.Sscanf(t, "Alias name: %s", &alias); i > 0 {
break
}
}
_, err = runCmd(exec.Command(
filepath.Join("jarsigner"),
"-sigalg", "SHA256withRSA",
"-digestalg", "SHA-256",
"-keystore", bi.key,
"-storepass", bi.password,
aabFile,
strings.TrimSpace(alias),
))
return err
}
func zipalign(tools *androidTools, input, output string) error {
_, err := runCmd(exec.Command(
filepath.Join(tools.buildtools, "zipalign"),
"-f",
"4", // 32-bit alignment.
input,
output,
))
return err
}
func defaultAndroidKeystore(tmpDir string, bi *buildInfo) error {
+1 -1
View File
@@ -60,7 +60,7 @@ its deletion.
The -x flag will print all the external commands executed by the gogio tool.
The -signkey flag specifies the path of the keystore, used for signing Android apk files.
The -signkey flag specifies the path of the keystore, used for signing Android apk/aab files.
The -signpass flag specifies the password of the keystore, ignored if -signkey is not provided.
`