xref: /aosp_15_r20/external/bazelbuild-rules_android/src/tools/ak/rjar/rjar.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 rjar generated R.jar.
16package rjar
17
18import (
19	"bufio"
20	"flag"
21	"fmt"
22	"io"
23	"io/ioutil"
24	"log"
25	"os"
26	"os/exec"
27	"path/filepath"
28	"strings"
29	"sync"
30
31	"src/common/golang/ziputils"
32	"src/tools/ak/types"
33)
34
35var (
36	// Cmd defines the command.
37	Cmd = types.Command{
38		Init:  Init,
39		Run:   Run,
40		Desc:  desc,
41		Flags: []string{"rjava", "pkgs", "rjar", "jdk", "jartool", "target_label"},
42	}
43
44	// Variables to hold flag values.
45	rjava       string
46	pkgs        string
47	rjar        string
48	jdk         string
49	jartool     string
50	targetLabel string
51
52	initOnce sync.Once
53
54	javaReserved = map[string]bool{
55		"abstract":     true,
56		"assert":       true,
57		"boolean":      true,
58		"break":        true,
59		"byte":         true,
60		"case":         true,
61		"catch":        true,
62		"char":         true,
63		"class":        true,
64		"const":        true,
65		"continue":     true,
66		"default":      true,
67		"do":           true,
68		"double":       true,
69		"else":         true,
70		"enum":         true,
71		"extends":      true,
72		"false":        true,
73		"final":        true,
74		"finally":      true,
75		"float":        true,
76		"for":          true,
77		"goto":         true,
78		"if":           true,
79		"implements":   true,
80		"import":       true,
81		"instanceof":   true,
82		"int":          true,
83		"interface":    true,
84		"long":         true,
85		"native":       true,
86		"new":          true,
87		"null":         true,
88		"package":      true,
89		"private":      true,
90		"protected":    true,
91		"public":       true,
92		"return":       true,
93		"short":        true,
94		"static":       true,
95		"strictfp":     true,
96		"super":        true,
97		"switch":       true,
98		"synchronized": true,
99		"this":         true,
100		"throw":        true,
101		"throws":       true,
102		"transient":    true,
103		"true":         true,
104		"try":          true,
105		"void":         true,
106		"volatile":     true,
107		"while":        true}
108)
109
110// Init initiailizes rjar action. Must be called before google.Init.
111func Init() {
112	initOnce.Do(func() {
113		flag.StringVar(&rjava, "rjava", "", "Input R.java path")
114		flag.StringVar(&pkgs, "pkgs", "", "Packages file path")
115		flag.StringVar(&rjar, "rjar", "", "Output R.jar path")
116		flag.StringVar(&jdk, "jdk", "", "Jdk path")
117		flag.StringVar(&jartool, "jartool", "", "Jartool path")
118		flag.StringVar(&targetLabel, "target_label", "", "The target label")
119	})
120}
121
122func desc() string {
123	return "rjar creates the R.jar"
124}
125
126// Run is the entry point for rjar. Will exit on error.
127func Run() {
128	if err := doWork(rjava, pkgs, rjar, jdk, jartool, targetLabel); err != nil {
129		log.Fatalf("Error creating R.jar: %v", err)
130	}
131}
132
133func doWork(rjava, pkgs, rjar, jdk, jartool string, targetLabel string) error {
134	f, err := os.Stat(rjava)
135	if os.IsNotExist(err) || (err == nil && f.Size() == 0) {
136		// If we don't have an input r_java or have an empty r_java just write
137		// an empty jar apps might not define resources and in some cases (aar
138		// files) its not possible to know during analysis phase, so this action
139		// gets executed regardless.
140		return ziputils.EmptyZip(rjar)
141	}
142	if err != nil {
143		return fmt.Errorf("os.Stat(%s) failed: %v", rjava, err)
144	}
145
146	srcDir, err := ioutil.TempDir("", "rjar")
147	if err != nil {
148		return err
149	}
150	defer os.RemoveAll(srcDir)
151
152	var parentPkg, subclassTmpl string
153	var srcs []string
154
155	filteredPkgs, err := getPkgs(pkgs)
156	if err != nil {
157		return err
158	}
159	for _, pkg := range filteredPkgs {
160		pkgParts := strings.Split(pkg, ".")
161		if hasInvalid(pkgParts) {
162			continue
163		}
164		pkgDir := filepath.Join(append([]string{srcDir}, pkgParts...)...)
165		err = os.MkdirAll(pkgDir, 0777)
166		if err != nil {
167			return err
168		}
169		outRJava := filepath.Join(pkgDir, "R.java")
170		srcs = append(srcs, outRJava)
171		if parentPkg == "" {
172			parentPkg = pkg
173			var classes []string
174			out, err := os.Create(outRJava)
175			if err != nil {
176				return err
177			}
178			defer out.Close()
179			in, err := os.Open(rjava)
180			if err != nil {
181				return err
182			}
183			defer in.Close()
184			if _, err := fmt.Fprintf(out, "package %s;", pkg); err != nil {
185				return err
186			}
187			if _, err := io.Copy(out, in); err != nil {
188				return err
189			}
190			if _, err := in.Seek(0, 0); err != nil {
191				return err
192			}
193			scanner := bufio.NewScanner(in)
194			for scanner.Scan() {
195				line := scanner.Text()
196				if strings.Contains(line, "public static class ") {
197					classes = append(classes, strings.Split(strings.Split(line, "public static class ")[1], " ")[0])
198				}
199			}
200			subclassPts := []string{"package %s;", fmt.Sprintf("public class R extends %s.R {", pkg)}
201			for _, t := range classes {
202				subclassPts = append(subclassPts, fmt.Sprintf("  public static class %s extends %s.R.%s {}", t, pkg, t))
203			}
204			subclassPts = append(subclassPts, "}")
205			subclassTmpl = strings.Join(subclassPts, "\n")
206		} else {
207			out, err := os.Create(outRJava)
208			if err != nil {
209				return err
210			}
211			defer out.Close()
212			fmt.Fprintf(out, subclassTmpl, pkg)
213		}
214	}
215	if _, err := os.Lstat(rjar); err == nil {
216		if err := os.Remove(rjar); err != nil {
217			return err
218		}
219	}
220	if err = os.MkdirAll(filepath.Dir(rjar), 0777); err != nil {
221		return err
222	}
223	return compileRJar(srcs, rjar, jdk, jartool, targetLabel)
224}
225
226func compileRJar(srcs []string, rjar, jdk, jartool string, targetLabel string) error {
227	control, err := ioutil.TempFile("", "control")
228	if err != nil {
229		return err
230	}
231	defer os.Remove(control.Name())
232
233	args := []string{"--javacopts",
234		"-source", "8",
235		"-target", "8",
236		"-nowarn", "--", "--sources"}
237	args = append(args, srcs...)
238	args = append(args, []string{
239		"--strict_java_deps", "ERROR",
240		"--output", rjar,
241	}...)
242	if len(targetLabel) > 0 {
243		// Deal with "@//"-prefixed labels (in Bazel)
244		if strings.HasPrefix(targetLabel, "@//") {
245			targetLabel = strings.Replace(targetLabel, "@//", "//", 1)
246		}
247
248		args = append(args, []string{
249			"--target_label", targetLabel,
250		}...)
251	}
252	if _, err := fmt.Fprint(control, strings.Join(args, "\n")); err != nil {
253		return err
254	}
255	if err := control.Sync(); err != nil {
256		return err
257	}
258	c, err := exec.Command(jdk, "-jar", jartool, fmt.Sprintf("@%s", control.Name())).CombinedOutput()
259	if err != nil {
260		return fmt.Errorf("%v:\n%s", err, c)
261	}
262	return nil
263}
264
265func getPkgs(pkgs string) ([]string, error) {
266	var filteredPkgs []string
267	seenPkgs := map[string]bool{}
268
269	f, err := os.Open(pkgs)
270	if err != nil {
271		return nil, err
272	}
273	defer f.Close()
274
275	scanner := bufio.NewScanner(f)
276	for scanner.Scan() {
277		pkg := strings.TrimSpace(scanner.Text())
278		if strings.ContainsAny(pkg, "-$/") || pkg == "" {
279			continue
280		}
281		if seenPkgs[pkg] {
282			continue
283		}
284		filteredPkgs = append(filteredPkgs, pkg)
285		seenPkgs[pkg] = true
286	}
287	if err := scanner.Err(); err != nil {
288		return nil, err
289	}
290	return filteredPkgs, nil
291}
292
293func hasInvalid(parts []string) bool {
294	for _, p := range parts {
295		if javaReserved[p] {
296			return true
297		}
298	}
299	return false
300}
301