| // 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) | |
| } | |
| } | |
| } |