aboutsummaryrefslogtreecommitdiff
path: root/src/tools/ak/rjar/rjar.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/ak/rjar/rjar.go')
-rw-r--r--src/tools/ak/rjar/rjar.go295
1 files changed, 295 insertions, 0 deletions
diff --git a/src/tools/ak/rjar/rjar.go b/src/tools/ak/rjar/rjar.go
new file mode 100644
index 0000000..c95dea8
--- /dev/null
+++ b/src/tools/ak/rjar/rjar.go
@@ -0,0 +1,295 @@
+// 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 rjar generated R.jar.
+package rjar
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "src/common/golang/ziputils"
+ "src/tools/ak/types"
+)
+
+var (
+ // Cmd defines the command.
+ Cmd = types.Command{
+ Init: Init,
+ Run: Run,
+ Desc: desc,
+ Flags: []string{"rjava", "pkgs", "rjar", "jdk", "jartool", "target_label"},
+ }
+
+ // Variables to hold flag values.
+ rjava string
+ pkgs string
+ rjar string
+ jdk string
+ jartool string
+ targetLabel string
+
+ initOnce sync.Once
+
+ javaReserved = map[string]bool{
+ "abstract": true,
+ "assert": true,
+ "boolean": true,
+ "break": true,
+ "byte": true,
+ "case": true,
+ "catch": true,
+ "char": true,
+ "class": true,
+ "const": true,
+ "continue": true,
+ "default": true,
+ "do": true,
+ "double": true,
+ "else": true,
+ "enum": true,
+ "extends": true,
+ "false": true,
+ "final": true,
+ "finally": true,
+ "float": true,
+ "for": true,
+ "goto": true,
+ "if": true,
+ "implements": true,
+ "import": true,
+ "instanceof": true,
+ "int": true,
+ "interface": true,
+ "long": true,
+ "native": true,
+ "new": true,
+ "null": true,
+ "package": true,
+ "private": true,
+ "protected": true,
+ "public": true,
+ "return": true,
+ "short": true,
+ "static": true,
+ "strictfp": true,
+ "super": true,
+ "switch": true,
+ "synchronized": true,
+ "this": true,
+ "throw": true,
+ "throws": true,
+ "transient": true,
+ "true": true,
+ "try": true,
+ "void": true,
+ "volatile": true,
+ "while": true}
+)
+
+// Init initiailizes rjar action. Must be called before google.Init.
+func Init() {
+ initOnce.Do(func() {
+ flag.StringVar(&rjava, "rjava", "", "Input R.java path")
+ flag.StringVar(&pkgs, "pkgs", "", "Packages file path")
+ flag.StringVar(&rjar, "rjar", "", "Output R.jar path")
+ flag.StringVar(&jdk, "jdk", "", "Jdk path")
+ flag.StringVar(&jartool, "jartool", "", "Jartool path")
+ flag.StringVar(&targetLabel, "target_label", "", "The target label")
+ })
+}
+
+func desc() string {
+ return "rjar creates the R.jar"
+}
+
+// Run is the entry point for rjar. Will exit on error.
+func Run() {
+ if err := doWork(rjava, pkgs, rjar, jdk, jartool, targetLabel); err != nil {
+ log.Fatalf("Error creating R.jar: %v", err)
+ }
+}
+
+func doWork(rjava, pkgs, rjar, jdk, jartool string, targetLabel string) error {
+ f, err := os.Stat(rjava)
+ if os.IsNotExist(err) || (err == nil && f.Size() == 0) {
+ // If we don't have an input r_java or have an empty r_java just write
+ // an empty jar apps might not define resources and in some cases (aar
+ // files) its not possible to know during analysis phase, so this action
+ // gets executed regardless.
+ return ziputils.EmptyZip(rjar)
+ }
+ if err != nil {
+ return fmt.Errorf("os.Stat(%s) failed: %v", rjava, err)
+ }
+
+ srcDir, err := ioutil.TempDir("", "rjar")
+ if err != nil {
+ return err
+ }
+ defer os.RemoveAll(srcDir)
+
+ var parentPkg, subclassTmpl string
+ var srcs []string
+
+ filteredPkgs, err := getPkgs(pkgs)
+ if err != nil {
+ return err
+ }
+ for _, pkg := range filteredPkgs {
+ pkgParts := strings.Split(pkg, ".")
+ if hasInvalid(pkgParts) {
+ continue
+ }
+ pkgDir := filepath.Join(append([]string{srcDir}, pkgParts...)...)
+ err = os.MkdirAll(pkgDir, 0777)
+ if err != nil {
+ return err
+ }
+ outRJava := filepath.Join(pkgDir, "R.java")
+ srcs = append(srcs, outRJava)
+ if parentPkg == "" {
+ parentPkg = pkg
+ var classes []string
+ out, err := os.Create(outRJava)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+ in, err := os.Open(rjava)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+ if _, err := fmt.Fprintf(out, "package %s;", pkg); err != nil {
+ return err
+ }
+ if _, err := io.Copy(out, in); err != nil {
+ return err
+ }
+ if _, err := in.Seek(0, 0); err != nil {
+ return err
+ }
+ scanner := bufio.NewScanner(in)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.Contains(line, "public static class ") {
+ classes = append(classes, strings.Split(strings.Split(line, "public static class ")[1], " ")[0])
+ }
+ }
+ subclassPts := []string{"package %s;", fmt.Sprintf("public class R extends %s.R {", pkg)}
+ for _, t := range classes {
+ subclassPts = append(subclassPts, fmt.Sprintf(" public static class %s extends %s.R.%s {}", t, pkg, t))
+ }
+ subclassPts = append(subclassPts, "}")
+ subclassTmpl = strings.Join(subclassPts, "\n")
+ } else {
+ out, err := os.Create(outRJava)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+ fmt.Fprintf(out, subclassTmpl, pkg)
+ }
+ }
+ if _, err := os.Lstat(rjar); err == nil {
+ if err := os.Remove(rjar); err != nil {
+ return err
+ }
+ }
+ if err = os.MkdirAll(filepath.Dir(rjar), 0777); err != nil {
+ return err
+ }
+ return compileRJar(srcs, rjar, jdk, jartool, targetLabel)
+}
+
+func compileRJar(srcs []string, rjar, jdk, jartool string, targetLabel string) error {
+ control, err := ioutil.TempFile("", "control")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(control.Name())
+
+ args := []string{"--javacopts",
+ "-source", "8",
+ "-target", "8",
+ "-nowarn", "--", "--sources"}
+ args = append(args, srcs...)
+ args = append(args, []string{
+ "--strict_java_deps", "ERROR",
+ "--output", rjar,
+ }...)
+ if len(targetLabel) > 0 {
+ args = append(args, []string{
+ "--target_label", targetLabel,
+ }...)
+ }
+ if _, err := fmt.Fprint(control, strings.Join(args, "\n")); err != nil {
+ return err
+ }
+ if err := control.Sync(); err != nil {
+ return err
+ }
+ c, err := exec.Command(jdk, "-jar", jartool, fmt.Sprintf("@%s", control.Name())).CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("%v:\n%s", err, c)
+ }
+ return nil
+}
+
+func getPkgs(pkgs string) ([]string, error) {
+ var filteredPkgs []string
+ seenPkgs := map[string]bool{}
+
+ f, err := os.Open(pkgs)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ pkg := strings.TrimSpace(scanner.Text())
+ if strings.ContainsAny(pkg, "-$/") || pkg == "" {
+ continue
+ }
+ if seenPkgs[pkg] {
+ continue
+ }
+ filteredPkgs = append(filteredPkgs, pkg)
+ seenPkgs[pkg] = true
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+ return filteredPkgs, nil
+}
+
+func hasInvalid(parts []string) bool {
+ for _, p := range parts {
+ if javaReserved[p] {
+ return true
+ }
+ }
+ return false
+}