blob: c5195453de199bcfdd7d0bc9a94da097faf958ee [file] [log] [blame]
// Copyright 2018 The Bazel Authors. 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 manifest provides a thin wrapper around aapt2 to compile an AndroidManifest.xml
package manifest
import (
"archive/zip"
"bytes"
"flag"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"sync"
"src/common/golang/flags"
"src/tools/ak/manifestutils"
"src/tools/ak/types"
)
const errMsg string = `
+-----------------------------------------------------------
| Error while compiling AndroidManifest.xml
| If your build succeeds with Blaze/Bazel build, this is most
| likely due to the stricter aapt2 used by mobile-install
` +
`
+-----------------------------------------------------------
ERROR: %s
`
var (
// Cmd defines the command to run
Cmd = types.Command{
Init: Init,
Run: Run,
Desc: desc,
Flags: []string{
"aapt2",
"manifest",
"out",
"sdk_jar",
"res",
"attr",
},
}
// Flag variables
aapt2, manifest, out, sdkJar, res string
attr flags.StringList
initOnce sync.Once
)
// Init initializes manifest flags
func Init() {
initOnce.Do(func() {
flag.StringVar(&aapt2, "aapt2", "", "Path to aapt2")
flag.StringVar(&manifest, "manifest", "", "Path to manifest")
flag.StringVar(&out, "out", "", "Path to output")
flag.StringVar(&sdkJar, "sdk_jar", "", "Path to sdk jar")
flag.StringVar(&res, "res", "", "Path to res")
flag.Var(&attr, "attr", "(optional) attr(s) to set. {element}:{attr}:{value}.")
})
}
func desc() string {
return "Compile an AndroidManifest.xml"
}
// Run is the main entry point
func Run() {
if aapt2 == "" || manifest == "" || out == "" || sdkJar == "" || res == "" {
log.Fatal("Missing required flags. Must specify --aapt2 --manifest --out --sdk_jar --res")
}
aaptOut, err := ioutil.TempFile("", "manifest_apk")
if err != nil {
log.Fatalf("Creating temp file failed: %v", err)
}
defer os.Remove(aaptOut.Name())
manifestPath := manifest
if len(attr) > 0 {
patchedManifest, err := ioutil.TempFile("", "AndroidManifest_patched.xml")
if err != nil {
log.Fatalf("Creating temp file failed: %v", err)
}
defer os.Remove(patchedManifest.Name())
manifestPath = patchManifest(manifest, patchedManifest, attr)
}
stdoutStderr, err := exec.Command(aapt2, "link", "-o", aaptOut.Name(), "--manifest", manifestPath, "-I", sdkJar, "-I", res).CombinedOutput()
if err != nil {
log.Fatalf(errMsg, stdoutStderr)
}
reader, err := zip.OpenReader(aaptOut.Name())
if err != nil {
log.Fatalf("Opening zip %q failed: %v", aaptOut.Name(), err)
}
defer reader.Close()
for _, file := range reader.File {
if file.Name == "AndroidManifest.xml" {
err = os.MkdirAll(filepath.Dir(out), os.ModePerm)
if err != nil {
log.Fatalf("Creating output directory for %q failed: %v", out, err)
}
fileReader, err := file.Open()
if err != nil {
log.Fatalf("Opening file %q inside zip %q failed: %v", file.Name, aaptOut.Name(), err)
}
defer fileReader.Close()
outFile, err := os.Create(out)
if err != nil {
log.Fatalf("Creating output %q failed: %v", out, err)
}
if _, err := io.Copy(outFile, fileReader); err != nil {
log.Fatalf("Writing to output %q failed: %v", out, err)
}
if err = outFile.Close(); err != nil {
log.Fatal(err)
}
break
}
}
}
func patchManifest(manifest string, patchedManifest *os.File, attrs []string) string {
b, err := ioutil.ReadFile(manifest)
if err != nil {
log.Fatalf("Failed to read manifest: %v", err)
}
err = manifestutils.WriteManifest(patchedManifest, bytes.NewReader(b), manifestutils.CreatePatchElements(attrs))
if err != nil {
log.Fatalf("Failed to update manifest: %v", err)
}
return patchedManifest.Name()
}