xref: /aosp_15_r20/external/bazelbuild-rules_android/src/tools/ak/manifest/manifest.go (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
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