1// Copyright 2018 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package manifest provides a thin wrapper around aapt2 to compile an AndroidManifest.xml 16package manifest 17 18import ( 19 "archive/zip" 20 "bytes" 21 "flag" 22 "io" 23 "io/ioutil" 24 "log" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "sync" 29 30 "src/common/golang/flags" 31 "src/tools/ak/manifestutils" 32 "src/tools/ak/types" 33) 34 35const errMsg string = ` 36+----------------------------------------------------------- 37| Error while compiling AndroidManifest.xml 38| If your build succeeds with Blaze/Bazel build, this is most 39| likely due to the stricter aapt2 used by mobile-install 40` + 41 ` 42+----------------------------------------------------------- 43ERROR: %s 44` 45 46var ( 47 // Cmd defines the command to run 48 Cmd = types.Command{ 49 Init: Init, 50 Run: Run, 51 Desc: desc, 52 Flags: []string{ 53 "aapt2", 54 "manifest", 55 "out", 56 "sdk_jar", 57 "res", 58 "attr", 59 }, 60 } 61 62 // Flag variables 63 aapt2, manifest, out, sdkJar, res string 64 attr flags.StringList 65 forceDebuggable bool 66 67 initOnce sync.Once 68) 69 70// Init initializes manifest flags 71func Init() { 72 initOnce.Do(func() { 73 flag.StringVar(&aapt2, "aapt2", "", "Path to aapt2") 74 flag.StringVar(&manifest, "manifest", "", "Path to manifest") 75 flag.StringVar(&out, "out", "", "Path to output") 76 flag.StringVar(&sdkJar, "sdk_jar", "", "Path to sdk jar") 77 flag.StringVar(&res, "res", "", "Path to res") 78 flag.BoolVar(&forceDebuggable, "force_debuggable", false, "Whether to force set android:debuggable=true.") 79 flag.Var(&attr, "attr", "(optional) attr(s) to set. {element}:{attr}:{value}.") 80 }) 81} 82 83func desc() string { 84 return "Compile an AndroidManifest.xml" 85} 86 87// Run is the main entry point 88func Run() { 89 if aapt2 == "" || manifest == "" || out == "" || sdkJar == "" || res == "" { 90 log.Fatal("Missing required flags. Must specify --aapt2 --manifest --out --sdk_jar --res") 91 } 92 93 aaptOut, err := ioutil.TempFile("", "manifest_apk") 94 if err != nil { 95 log.Fatalf("Creating temp file failed: %v", err) 96 } 97 defer os.Remove(aaptOut.Name()) 98 99 manifestPath := manifest 100 if len(attr) > 0 { 101 patchedManifest, err := ioutil.TempFile("", "AndroidManifest_patched.xml") 102 if err != nil { 103 log.Fatalf("Creating temp file failed: %v", err) 104 } 105 defer os.Remove(patchedManifest.Name()) 106 manifestPath = patchManifest(manifest, patchedManifest, attr) 107 } 108 args := []string{"link", "-o", aaptOut.Name(), "--manifest", manifestPath, "-I", sdkJar, "-I", res} 109 if forceDebuggable { 110 args = append(args, "--debug-mode") 111 } 112 stdoutStderr, err := exec.Command(aapt2, args...).CombinedOutput() 113 if err != nil { 114 log.Fatalf(errMsg, stdoutStderr) 115 } 116 117 reader, err := zip.OpenReader(aaptOut.Name()) 118 if err != nil { 119 log.Fatalf("Opening zip %q failed: %v", aaptOut.Name(), err) 120 } 121 defer reader.Close() 122 123 for _, file := range reader.File { 124 if file.Name == "AndroidManifest.xml" { 125 err = os.MkdirAll(filepath.Dir(out), os.ModePerm) 126 if err != nil { 127 log.Fatalf("Creating output directory for %q failed: %v", out, err) 128 } 129 130 fileReader, err := file.Open() 131 if err != nil { 132 log.Fatalf("Opening file %q inside zip %q failed: %v", file.Name, aaptOut.Name(), err) 133 } 134 defer fileReader.Close() 135 136 outFile, err := os.Create(out) 137 if err != nil { 138 log.Fatalf("Creating output %q failed: %v", out, err) 139 } 140 141 if _, err := io.Copy(outFile, fileReader); err != nil { 142 log.Fatalf("Writing to output %q failed: %v", out, err) 143 } 144 145 if err = outFile.Close(); err != nil { 146 log.Fatal(err) 147 } 148 break 149 } 150 } 151} 152 153func patchManifest(manifest string, patchedManifest *os.File, attrs []string) string { 154 b, err := ioutil.ReadFile(manifest) 155 if err != nil { 156 log.Fatalf("Failed to read manifest: %v", err) 157 } 158 err = manifestutils.WriteManifest(patchedManifest, bytes.NewReader(b), manifestutils.CreatePatchElements(attrs)) 159 if err != nil { 160 log.Fatalf("Failed to update manifest: %v", err) 161 } 162 return patchedManifest.Name() 163} 164