• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 Google Inc. 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
15package java
16
17// This file generates the final rules for compiling all Java.  All properties related to
18// compiling should have been translated into javaBuilderFlags or another argument to the Transform*
19// functions.
20
21import (
22	"path/filepath"
23	"strconv"
24	"strings"
25
26	"github.com/google/blueprint"
27	"github.com/google/blueprint/proptools"
28
29	"android/soong/android"
30)
31
32var (
33	pctx = android.NewPackageContext("android/soong/java")
34
35	// Compiling java is not conducive to proper dependency tracking.  The path-matches-class-name
36	// requirement leads to unpredictable generated source file names, and a single .java file
37	// will get compiled into multiple .class files if it contains inner classes.  To work around
38	// this, all java rules write into separate directories and then are combined into a .jar file
39	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
40	// .srcjar files are unzipped into a temporary directory when compiled with javac.
41	javac = pctx.AndroidGomaStaticRule("javac",
42		blueprint.RuleParams{
43			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
44				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
45				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
46				`${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
47				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
48				`-source $javaVersion -target $javaVersion ` +
49				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
50				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
51				`rm -rf "$srcJarDir"`,
52			CommandDeps: []string{
53				"${config.JavacCmd}",
54				"${config.SoongZipCmd}",
55				"${config.ZipSyncCmd}",
56			},
57			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
58			Rspfile:          "$out.rsp",
59			RspfileContent:   "$in",
60		},
61		"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
62		"outDir", "annoDir", "javaVersion")
63
64	turbine = pctx.AndroidStaticRule("turbine",
65		blueprint.RuleParams{
66			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
67				`${config.JavaCmd} -jar ${config.TurbineJar} --output $out.tmp ` +
68				`--temp_dir "$outDir" --sources @$out.rsp  --source_jars $srcJars ` +
69				`--javacopts ${config.CommonJdkFlags} ` +
70				`$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` +
71				`${config.Ziptime} $out.tmp && ` +
72				`(if cmp -s $out.tmp $out ; then rm $out.tmp ; else mv $out.tmp $out ; fi )`,
73			CommandDeps: []string{
74				"${config.TurbineJar}",
75				"${config.JavaCmd}",
76				"${config.Ziptime}",
77			},
78			Rspfile:        "$out.rsp",
79			RspfileContent: "$in",
80			Restat:         true,
81		},
82		"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion")
83
84	jar = pctx.AndroidStaticRule("jar",
85		blueprint.RuleParams{
86			Command:        `${config.SoongZipCmd} -jar -o $out @$out.rsp`,
87			CommandDeps:    []string{"${config.SoongZipCmd}"},
88			Rspfile:        "$out.rsp",
89			RspfileContent: "$jarArgs",
90		},
91		"jarArgs")
92
93	zip = pctx.AndroidStaticRule("zip",
94		blueprint.RuleParams{
95			Command:        `${config.SoongZipCmd} -o $out @$out.rsp`,
96			CommandDeps:    []string{"${config.SoongZipCmd}"},
97			Rspfile:        "$out.rsp",
98			RspfileContent: "$jarArgs",
99		},
100		"jarArgs")
101
102	combineJar = pctx.AndroidStaticRule("combineJar",
103		blueprint.RuleParams{
104			Command:     `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out $in`,
105			CommandDeps: []string{"${config.MergeZipsCmd}"},
106		},
107		"jarArgs")
108
109	jarjar = pctx.AndroidStaticRule("jarjar",
110		blueprint.RuleParams{
111			Command:     "${config.JavaCmd} -jar ${config.JarjarCmd} process $rulesFile $in $out",
112			CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
113		},
114		"rulesFile")
115
116	packageCheck = pctx.AndroidStaticRule("packageCheck",
117		blueprint.RuleParams{
118			Command: "rm -f $out && " +
119				"${config.PackageCheckCmd} $in $packages && " +
120				"touch $out",
121			CommandDeps: []string{"${config.PackageCheckCmd}"},
122		},
123		"packages")
124
125	jetifier = pctx.AndroidStaticRule("jetifier",
126		blueprint.RuleParams{
127			Command:     "${config.JavaCmd} -jar ${config.JetifierJar} -l error -o $out -i $in",
128			CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"},
129		},
130	)
131
132	zipalign = pctx.AndroidStaticRule("zipalign",
133		blueprint.RuleParams{
134			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
135				"${config.ZipAlign} -f -p 4 $in $out; " +
136				"else " +
137				"cp -f $in $out; " +
138				"fi",
139			CommandDeps: []string{"${config.ZipAlign}"},
140		},
141	)
142)
143
144func init() {
145	pctx.Import("android/soong/android")
146	pctx.Import("android/soong/java/config")
147}
148
149type javaBuilderFlags struct {
150	javacFlags    string
151	bootClasspath classpath
152	classpath     classpath
153	processorPath classpath
154	processor     string
155	systemModules classpath
156	aidlFlags     string
157	aidlDeps      android.Paths
158	javaVersion   string
159
160	errorProneExtraJavacFlags string
161	errorProneProcessorPath   classpath
162
163	kotlincFlags     string
164	kotlincClasspath classpath
165
166	proto android.ProtoFlags
167}
168
169func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
170	srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) {
171
172	// Compile java sources into .class files
173	desc := "javac"
174	if shardIdx >= 0 {
175		desc += strconv.Itoa(shardIdx)
176	}
177
178	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
179}
180
181func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
182	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
183
184	flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
185
186	if len(flags.errorProneExtraJavacFlags) > 0 {
187		if len(flags.javacFlags) > 0 {
188			flags.javacFlags += " " + flags.errorProneExtraJavacFlags
189		} else {
190			flags.javacFlags = flags.errorProneExtraJavacFlags
191		}
192	}
193
194	transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
195		"errorprone", "errorprone")
196}
197
198func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
199	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
200
201	var deps android.Paths
202	deps = append(deps, srcJars...)
203	deps = append(deps, flags.bootClasspath...)
204	deps = append(deps, flags.classpath...)
205
206	var bootClasspath string
207	if len(flags.bootClasspath) == 0 && ctx.Device() {
208		// explicitly specify -bootclasspath "" if the bootclasspath is empty to
209		// ensure java does not fall back to the default bootclasspath.
210		bootClasspath = `--bootclasspath ""`
211	} else {
212		bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath "), " ")
213	}
214
215	ctx.Build(pctx, android.BuildParams{
216		Rule:        turbine,
217		Description: "turbine",
218		Output:      outputFile,
219		Inputs:      srcFiles,
220		Implicits:   deps,
221		Args: map[string]string{
222			"javacFlags":    flags.javacFlags,
223			"bootClasspath": bootClasspath,
224			"srcJars":       strings.Join(srcJars.Strings(), " "),
225			"classpath":     strings.Join(flags.classpath.FormTurbineClasspath("--classpath "), " "),
226			"outDir":        android.PathForModuleOut(ctx, "turbine", "classes").String(),
227			"javaVersion":   flags.javaVersion,
228		},
229	})
230}
231
232// transformJavaToClasses takes source files and converts them to a jar containing .class files.
233// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
234// sources.  flags contains various command line flags to be passed to the compiler.
235//
236// This method may be used for different compilers, including javac and Error Prone.  The rule
237// argument specifies which command line to use and desc sets the description of the rule that will
238// be printed at build time.  The stem argument provides the file name of the output jar, and
239// suffix will be appended to various intermediate files and directories to avoid collisions when
240// this function is called twice in the same module directory.
241func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
242	shardIdx int, srcFiles, srcJars android.Paths,
243	flags javaBuilderFlags, deps android.Paths,
244	intermediatesDir, desc string) {
245
246	deps = append(deps, srcJars...)
247
248	var bootClasspath string
249	if flags.javaVersion == "1.9" {
250		deps = append(deps, flags.systemModules...)
251		bootClasspath = flags.systemModules.FormJavaSystemModulesPath("--system=", ctx.Device())
252	} else {
253		deps = append(deps, flags.bootClasspath...)
254		if len(flags.bootClasspath) == 0 && ctx.Device() {
255			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
256			// ensure java does not fall back to the default bootclasspath.
257			bootClasspath = `-bootclasspath ""`
258		} else {
259			bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
260		}
261	}
262
263	deps = append(deps, flags.classpath...)
264	deps = append(deps, flags.processorPath...)
265
266	processor := "-proc:none"
267	if flags.processor != "" {
268		processor = "-processor " + flags.processor
269	}
270
271	srcJarDir := "srcjars"
272	outDir := "classes"
273	annoDir := "anno"
274	if shardIdx >= 0 {
275		shardDir := "shard" + strconv.Itoa(shardIdx)
276		srcJarDir = filepath.Join(shardDir, srcJarDir)
277		outDir = filepath.Join(shardDir, outDir)
278		annoDir = filepath.Join(shardDir, annoDir)
279	}
280	ctx.Build(pctx, android.BuildParams{
281		Rule:        javac,
282		Description: desc,
283		Output:      outputFile,
284		Inputs:      srcFiles,
285		Implicits:   deps,
286		Args: map[string]string{
287			"javacFlags":    flags.javacFlags,
288			"bootClasspath": bootClasspath,
289			"classpath":     flags.classpath.FormJavaClassPath("-classpath"),
290			"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
291			"processor":     processor,
292			"srcJars":       strings.Join(srcJars.Strings(), " "),
293			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
294			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
295			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
296			"javaVersion":   flags.javaVersion,
297		},
298	})
299}
300
301func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath,
302	jarArgs []string, deps android.Paths) {
303
304	ctx.Build(pctx, android.BuildParams{
305		Rule:        jar,
306		Description: "jar",
307		Output:      outputFile,
308		Implicits:   deps,
309		Args: map[string]string{
310			"jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
311		},
312	})
313}
314
315func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath, desc string,
316	jars android.Paths, manifest android.OptionalPath, stripDirEntries bool, filesToStrip []string,
317	dirsToStrip []string) {
318
319	var deps android.Paths
320
321	var jarArgs []string
322	if manifest.Valid() {
323		jarArgs = append(jarArgs, "-m ", manifest.String())
324		deps = append(deps, manifest.Path())
325	}
326
327	for _, dir := range dirsToStrip {
328		jarArgs = append(jarArgs, "-stripDir ", dir)
329	}
330
331	for _, file := range filesToStrip {
332		jarArgs = append(jarArgs, "-stripFile ", file)
333	}
334
335	// Remove any module-info.class files that may have come from prebuilt jars, they cause problems
336	// for downstream tools like desugar.
337	jarArgs = append(jarArgs, "-stripFile module-info.class")
338
339	if stripDirEntries {
340		jarArgs = append(jarArgs, "-D")
341	}
342
343	ctx.Build(pctx, android.BuildParams{
344		Rule:        combineJar,
345		Description: desc,
346		Output:      outputFile,
347		Inputs:      jars,
348		Implicits:   deps,
349		Args: map[string]string{
350			"jarArgs": strings.Join(jarArgs, " "),
351		},
352	})
353}
354
355func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
356	classesJar android.Path, rulesFile android.Path) {
357	ctx.Build(pctx, android.BuildParams{
358		Rule:        jarjar,
359		Description: "jarjar",
360		Output:      outputFile,
361		Input:       classesJar,
362		Implicit:    rulesFile,
363		Args: map[string]string{
364			"rulesFile": rulesFile.String(),
365		},
366	})
367}
368
369func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath,
370	classesJar android.Path, permittedPackages []string) {
371	ctx.Build(pctx, android.BuildParams{
372		Rule:        packageCheck,
373		Description: "packageCheck",
374		Output:      outputFile,
375		Input:       classesJar,
376		Args: map[string]string{
377			"packages": strings.Join(permittedPackages, " "),
378		},
379	})
380}
381
382func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePath,
383	inputFile android.Path) {
384	ctx.Build(pctx, android.BuildParams{
385		Rule:        jetifier,
386		Description: "jetifier",
387		Output:      outputFile,
388		Input:       inputFile,
389	})
390}
391
392func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
393	ctx.Build(pctx, android.BuildParams{
394		Rule:        android.WriteFile,
395		Description: "manifest",
396		Output:      outputFile,
397		Args: map[string]string{
398			"content": "Main-Class: " + mainClass + "\n",
399		},
400	})
401}
402
403func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) {
404	ctx.Build(pctx, android.BuildParams{
405		Rule:        zipalign,
406		Description: "align",
407		Input:       inputFile,
408		Output:      outputFile,
409	})
410}
411
412type classpath []android.Path
413
414func (x *classpath) FormJavaClassPath(optName string) string {
415	if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
416		optName += " "
417	}
418	if len(*x) > 0 {
419		return optName + strings.Join(x.Strings(), ":")
420	} else {
421		return ""
422	}
423}
424
425// Returns a --system argument in the form javac expects with -source 1.9.  If forceEmpty is true,
426// returns --system=none if the list is empty to ensure javac does not fall back to the default
427// system modules.
428func (x *classpath) FormJavaSystemModulesPath(optName string, forceEmpty bool) string {
429	if len(*x) > 1 {
430		panic("more than one system module")
431	} else if len(*x) == 1 {
432		return optName + strings.TrimSuffix((*x)[0].String(), "lib/modules")
433	} else if forceEmpty {
434		return optName + "none"
435	} else {
436		return ""
437	}
438}
439
440func (x *classpath) FormTurbineClasspath(optName string) []string {
441	if x == nil || *x == nil {
442		return nil
443	}
444	flags := make([]string, len(*x))
445	for i, v := range *x {
446		flags[i] = optName + v.String()
447	}
448
449	return flags
450}
451
452// Convert a classpath to an android.Paths
453func (x *classpath) Paths() android.Paths {
454	return append(android.Paths(nil), (*x)...)
455}
456
457func (x *classpath) Strings() []string {
458	if x == nil {
459		return nil
460	}
461	ret := make([]string, len(*x))
462	for i, path := range *x {
463		ret[i] = path.String()
464	}
465	return ret
466}
467