blob: 07fd65addb911f55601218823f067d775a506625 [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 patch sets/replaces the application class/package in a given AndroidManifest.xml.
package patch
import (
"bytes"
"encoding/xml"
"flag"
"io/ioutil"
"log"
"os"
"strings"
"sync"
"src/common/golang/flags"
"src/tools/ak/manifestutils"
"src/tools/ak/types"
)
var stubManifest = `<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<application/>
</manifest>`
var (
// Cmd defines the command to run patch
Cmd = types.Command{
Init: Init,
Run: Run,
Desc: desc,
Flags: []string{"in", "out", "attr", "app", "oldapp", "pkg"},
}
// Variables that hold flag values
split flags.StringList
attr flags.StringList
in string
out string
app string
oldApp string
pkg string
initOnce sync.Once
)
// Init initializes patch.
func Init() {
initOnce.Do(func() {
flag.StringVar(&in, "in", "", "Path to the input xml file.")
flag.StringVar(&out, "out", "", "Path to the output xml file.")
flag.Var(&attr, "attr", "(optional) attr(s) to set. {element}:{attr}:{value}.")
flag.Var(&split, "split", "(optional) splits(s) to write. {name}:{file}.")
flag.StringVar(&oldApp, "oldapp", "", "(optional) Path to output the old application class name.")
flag.StringVar(&pkg, "pkg", "", "(optional) Path to output the package name.")
})
}
func desc() string {
return "Setapp sets/replaces the application class in a given AndroidManifest.xml."
}
// Run is the entry point for patch.
func Run() {
if in == "" || (out == "" && split == nil) {
log.Fatal("fields and -in and -out|-splits and must be defined.")
}
elems := manifestutils.CreatePatchElements(attr)
elems[manifestutils.ElemManifest] = make(map[string]xml.Attr)
b, err := ioutil.ReadFile(in)
if err != nil {
log.Fatalf("ioutil.ReadFile(%q) failed: %v", in, err)
}
var manifest manifestutils.Manifest
xml.Unmarshal(b, &manifest)
// Optional parse package name and/or application class name before replacing
if pkg != "" || oldApp != "" {
if pkg != "" {
err = ioutil.WriteFile(pkg, []byte(manifest.Package), 0644)
if err != nil {
log.Fatalf("ioutil.WriteFile(%q) failed: %v", pkg, err)
}
}
if oldApp != "" {
appName := manifest.Application.Name
if appName == "" {
appName = "android.app.Application"
}
err := ioutil.WriteFile(oldApp, []byte(appName), 0644)
if err != nil {
log.Fatalf("ioutil.WriteFile(%q) failed: %v", oldApp, err)
}
}
}
// parse manifest attrs if any for stub manifest
if manifest.Package != "" {
elems[manifestutils.ElemManifest][manifestutils.AttrPackage] = xml.Attr{
Name: xml.Name{Local: manifestutils.AttrPackage}, Value: manifest.Package}
}
if manifest.SharedUserID != "" {
elems[manifestutils.ElemManifest][manifestutils.AttrSharedUserID] = xml.Attr{
Name: xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrSharedUserID},
Value: manifest.SharedUserID}
}
if manifest.SharedUserLabel != "" {
elems[manifestutils.ElemManifest][manifestutils.AttrSharedUserLabel] = xml.Attr{
Name: xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrSharedUserLabel},
Value: manifest.SharedUserLabel}
}
if manifest.VersionCode != "" {
elems[manifestutils.ElemManifest][manifestutils.AttrVersionCode] = xml.Attr{
Name: xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrVersionCode},
Value: manifest.VersionCode}
}
if manifest.VersionName != "" {
elems[manifestutils.ElemManifest][manifestutils.AttrVersionName] = xml.Attr{
Name: xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrVersionName},
Value: manifest.VersionName}
}
if out != "" {
o, err := os.Create(out)
if err != nil {
log.Fatalf("Error creating output file %q: %v", out, err)
}
defer o.Close()
if err := manifestutils.WriteManifest(o, bytes.NewReader(b), elems); err != nil {
log.Fatalf("Error setting fields: %v", err)
}
}
// Patch the splits
b = []byte(stubManifest)
for _, s := range split {
pts := strings.Split(s, ":")
if len(pts) != 2 {
log.Fatalf("Failed to parse split %s", s)
}
elems[manifestutils.ElemManifest][manifestutils.AttrSplit] = xml.Attr{
Name: xml.Name{Local: manifestutils.AttrSplit}, Value: pts[0]}
o, err := os.Create(pts[1])
if err != nil {
log.Fatalf("Error creating output file %q: %v", pts[1], err)
}
if err := manifestutils.WriteManifest(o, bytes.NewReader(b), elems); err != nil {
log.Fatalf("Error setting fields: %v", err)
}
}
}