• 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	"android/soong/remoteexec"
31)
32
33var (
34	pctx = android.NewPackageContext("android/soong/java")
35
36	// Compiling java is not conducive to proper dependency tracking.  The path-matches-class-name
37	// requirement leads to unpredictable generated source file names, and a single .java file
38	// will get compiled into multiple .class files if it contains inner classes.  To work around
39	// this, all java rules write into separate directories and then are combined into a .jar file
40	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
41	// .srcjar files are unzipped into a temporary directory when compiled with javac.
42	// TODO(b/143658984): goma can't handle the --system argument to javac.
43	javac, javacRE = pctx.MultiCommandRemoteStaticRules("javac",
44		blueprint.RuleParams{
45			Command: `rm -rf "$outDir" "$annoDir" "$annoSrcJar.tmp" "$srcJarDir" "$out.tmp" && ` +
46				`mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
47				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
48				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
49				`${config.FindInputDeltaCmd} --template '' --target "$out" --inputs_file "$out.rsp" && ` +
50				`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
51				`${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
52				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
53				`-source $javaVersion -target $javaVersion ` +
54				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
55				`$annoSrcJarTemplate${config.SoongZipCmd} -jar -o $annoSrcJar.tmp -C $annoDir -D $annoDir && ` +
56				`$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` +
57				`if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` +
58				`if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` +
59				`if [ -f "$out.pc_state.new" ]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
60				`rm -rf "$srcJarDir" "$outDir"`,
61			CommandDeps: []string{
62				"${config.FindInputDeltaCmd}",
63				"${config.JavacCmd}",
64				"${config.SoongZipCmd}",
65				"${config.ZipSyncCmd}",
66			},
67			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
68			Restat:           true,
69			Rspfile:          "$out.rsp",
70			RspfileContent:   "$in",
71		}, map[string]*remoteexec.REParams{
72			"$javaTemplate": &remoteexec.REParams{
73				Labels:       map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
74				ExecStrategy: "${config.REJavacExecStrategy}",
75				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
76			},
77			"$zipTemplate": &remoteexec.REParams{
78				Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
79				Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
80				OutputFiles:  []string{"$out.tmp"},
81				ExecStrategy: "${config.REJavacExecStrategy}",
82				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
83			},
84			"$annoSrcJarTemplate": &remoteexec.REParams{
85				Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
86				Inputs:       []string{"${config.SoongZipCmd}", "$annoDir"},
87				OutputFiles:  []string{"$annoSrcJar.tmp"},
88				ExecStrategy: "${config.REJavacExecStrategy}",
89				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
90			},
91		}, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
92			"outDir", "annoDir", "annoSrcJar", "javaVersion"}, nil)
93
94	_ = pctx.VariableFunc("kytheCorpus",
95		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
96	_ = pctx.VariableFunc("kytheCuEncoding",
97		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
98	_ = pctx.VariableFunc("kytheCuJavaSourceMax",
99		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuJavaSourceMax() })
100	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
101	// Run it with several --add-exports to allow the classes in the
102	// com.google.devtools.kythe.extractors.java.standalone package access the packages in the
103	// jdk.compiler compiler module. Long live Java modules.
104	kytheExtract = pctx.AndroidStaticRule("kythe",
105		blueprint.RuleParams{
106			Command: `${config.ZipSyncCmd} -d $srcJarDir ` +
107				`-l $srcJarDir/list -f "*.java" $srcJars && ` +
108				`( [ ! -s $srcJarDir/list -a ! -s $out.rsp ] || ` +
109				`KYTHE_ROOT_DIRECTORY=. KYTHE_OUTPUT_FILE=$out ` +
110				`KYTHE_CORPUS=${kytheCorpus} ` +
111				`KYTHE_VNAMES=${kytheVnames} ` +
112				`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
113				`KYTHE_JAVA_SOURCE_BATCH_SIZE=${kytheCuJavaSourceMax} ` +
114				`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
115				// Avoid JDK9's warning about "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
116				// to field java.nio.Buffer.address"
117				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
118				// Allow the classes in the com.google.devtools.kythe.extractors.java.standalone package
119				// access the packages in the jdk.compiler compiler module
120				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
121				`--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED ` +
122				`--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED ` +
123				`--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED ` +
124				`--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED ` +
125				`--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ` +
126				`--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED ` +
127				`--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED ` +
128				`-jar ${config.JavaKytheExtractorJar} ` +
129				`${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
130				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
131				`-source $javaVersion -target $javaVersion ` +
132				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list)`,
133			CommandDeps: []string{
134				"${config.JavaCmd}",
135				"${config.JavaKytheExtractorJar}",
136				"${kytheVnames}",
137				"${config.ZipSyncCmd}",
138			},
139			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
140			Rspfile:          "$out.rsp",
141			RspfileContent:   "$in",
142		},
143		"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
144		"outDir", "annoDir", "javaVersion")
145
146	extractMatchingApks = pctx.StaticRule(
147		"extractMatchingApks",
148		blueprint.RuleParams{
149			Command: `rm -rf "$out" && ` +
150				`${config.ExtractApksCmd} -o "${out}" -zip "${zip}" -allow-prereleased=${allow-prereleased} ` +
151				`-sdk-version=${sdk-version} -skip-sdk-check=${skip-sdk-check} -abis=${abis} ` +
152				`--screen-densities=${screen-densities} --stem=${stem} ` +
153				`-apkcerts=${apkcerts} -partition=${partition} ` +
154				`${in}`,
155			CommandDeps: []string{"${config.ExtractApksCmd}"},
156		},
157		"abis", "allow-prereleased", "screen-densities", "sdk-version", "skip-sdk-check", "stem", "apkcerts", "partition", "zip")
158
159	turbine, turbineRE = pctx.RemoteStaticRules("turbine",
160		blueprint.RuleParams{
161			Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} $outputFlags ` +
162				`--sources @$out.rsp ` +
163				`--javacopts ${config.CommonJdkFlags} ` +
164				`$javacFlags -source $javaVersion -target $javaVersion -- $turbineFlags && ` +
165				`(for o in $outputs; do if cmp -s $${o}.tmp $${o} ; then rm $${o}.tmp ; else mv $${o}.tmp $${o} ; fi; done )`,
166			CommandDeps: []string{
167				"${config.TurbineJar}",
168				"${config.JavaCmd}",
169			},
170			Rspfile:        "$out.rsp",
171			RspfileContent: "$in_newline",
172			Restat:         true,
173		},
174		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
175			ExecStrategy:    "${config.RETurbineExecStrategy}",
176			Inputs:          []string{"${config.TurbineJar}", "${out}.rsp", "$rbeInputs"},
177			RSPFiles:        []string{"$out.rsp", "$rspFiles"},
178			OutputFiles:     []string{"$rbeOutputs"},
179			ToolchainInputs: []string{"${config.JavaCmd}"},
180			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
181		},
182		[]string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs"}, []string{"rbeInputs", "rspFiles"})
183
184	jar, jarRE = pctx.RemoteStaticRules("jar",
185		blueprint.RuleParams{
186			Command:        `$reTemplate${config.SoongZipCmd} -jar -o $out @$out.rsp`,
187			CommandDeps:    []string{"${config.SoongZipCmd}"},
188			Rspfile:        "$out.rsp",
189			RspfileContent: "$jarArgs",
190		},
191		&remoteexec.REParams{
192			ExecStrategy: "${config.REJarExecStrategy}",
193			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp"},
194			RSPFiles:     []string{"${out}.rsp"},
195			OutputFiles:  []string{"$out"},
196			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
197		}, []string{"jarArgs"}, nil)
198
199	zip, zipRE = pctx.RemoteStaticRules("zip",
200		blueprint.RuleParams{
201			Command:        `${config.SoongZipCmd} -o $out @$out.rsp`,
202			CommandDeps:    []string{"${config.SoongZipCmd}"},
203			Rspfile:        "$out.rsp",
204			RspfileContent: "$jarArgs",
205		},
206		&remoteexec.REParams{
207			ExecStrategy: "${config.REZipExecStrategy}",
208			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"},
209			RSPFiles:     []string{"${out}.rsp"},
210			OutputFiles:  []string{"$out"},
211			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
212		}, []string{"jarArgs"}, []string{"implicits"})
213
214	combineJar = pctx.AndroidStaticRule("combineJar",
215		blueprint.RuleParams{
216			Command:     `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out $in`,
217			CommandDeps: []string{"${config.MergeZipsCmd}"},
218		},
219		"jarArgs")
220	combineJarRsp = pctx.AndroidStaticRule("combineJarRsp",
221		blueprint.RuleParams{
222			Command:        `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out @$out.rsp`,
223			CommandDeps:    []string{"${config.MergeZipsCmd}"},
224			Rspfile:        "$out.rsp",
225			RspfileContent: "$in",
226		},
227		"jarArgs")
228
229	extractR8Rules = pctx.AndroidStaticRule("extractR8Rules",
230		blueprint.RuleParams{
231			Command:     `${config.ExtractR8RulesCmd} --rules-output $out --include-origin-comments $in`,
232			CommandDeps: []string{"${config.ExtractR8RulesCmd}"},
233		})
234
235	jarjar = pctx.AndroidStaticRule("jarjar",
236		blueprint.RuleParams{
237			Command: "" +
238				// Jarjar doesn't exit with an error when the rules file contains a syntax error,
239				// leading to stale or missing files later in the build.  Remove the output file
240				// before running jarjar.
241				"rm -f ${out} && " +
242				"${config.JavaCmd} ${config.JavaVmFlags}" +
243				// b/146418363 Enable Android specific jarjar transformer to drop compat annotations
244				// for newly repackaged classes. Dropping @UnsupportedAppUsage on repackaged classes
245				// avoids adding new hiddenapis after jarjar'ing.
246				" -DremoveAndroidCompatAnnotations=true" +
247				" -jar ${config.JarjarCmd} process $rulesFile $in $out $total_shards $shard_index && " +
248				// Turn a missing output file into a ninja error
249				`[ -e ${out} ] || (echo "Missing output file"; exit 1)`,
250			CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
251		},
252		"rulesFile", "total_shards", "shard_index")
253
254	packageCheck = pctx.AndroidStaticRule("packageCheck",
255		blueprint.RuleParams{
256			Command: "rm -f $out && " +
257				"${config.PackageCheckCmd} $in $packages && " +
258				"touch $out",
259			CommandDeps: []string{"${config.PackageCheckCmd}"},
260		},
261		"packages")
262
263	jetifier = pctx.AndroidStaticRule("jetifier",
264		blueprint.RuleParams{
265			Command:     "${config.JavaCmd}  ${config.JavaVmFlags} -jar ${config.JetifierJar} -l error -o $out -i $in -t epoch",
266			CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"},
267		},
268	)
269
270	ravenizer = pctx.AndroidStaticRule("ravenizer",
271		blueprint.RuleParams{
272			Command:     "rm -f $out && ${ravenizer} --in-jar $in --out-jar $out $ravenizerArgs",
273			CommandDeps: []string{"${ravenizer}"},
274		},
275		"ravenizerArgs")
276
277	apimapper = pctx.AndroidStaticRule("apimapper",
278		blueprint.RuleParams{
279			Command:     "${apimapper} --in-jar $in --out-jar $out",
280			CommandDeps: []string{"${apimapper}"},
281		},
282	)
283
284	zipalign = pctx.AndroidStaticRule("zipalign",
285		blueprint.RuleParams{
286			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
287				"${config.ZipAlign} -f -p 4 $in $out; " +
288				"else " +
289				"cp -f $in $out; " +
290				"fi",
291			CommandDeps: []string{"${config.ZipAlign}"},
292		},
293	)
294
295	convertImplementationJarToHeaderJarRule = pctx.AndroidStaticRule("convertImplementationJarToHeaderJar",
296		blueprint.RuleParams{
297			Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} -x 'META-INF/services/**/*'`,
298			CommandDeps: []string{"${config.Zip2ZipCmd}"},
299		})
300
301	writeCombinedProguardFlagsFileRule = pctx.AndroidStaticRule("writeCombinedProguardFlagsFileRule",
302		blueprint.RuleParams{
303			Command: `rm -f $out && ` +
304				`for f in $in; do ` +
305				` echo  && ` +
306				` echo "# including $$f" && ` +
307				` cat $$f; ` +
308				`done > $out`,
309		})
310
311	gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule",
312		blueprint.RuleParams{
313			Command: `${aconfig} dump-cache --dedup --format=protobuf ` +
314				`--out ${out} ` +
315				`${flags_path} ` +
316				`${filter_args} `,
317			CommandDeps: []string{"${aconfig}"},
318			Description: "aconfig_bool",
319		}, "flags_path", "filter_args")
320
321	generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule",
322		blueprint.RuleParams{
323			Command:     `${aconfig-to-metalava-flags} ${in} > ${out}`,
324			CommandDeps: []string{"${aconfig-to-metalava-flags}"},
325		})
326
327	generateApiXMLRule = pctx.AndroidStaticRule("generateApiXMLRule",
328		blueprint.RuleParams{
329			Command:     `${config.JavaCmd} ${config.JavaVmFlags} -Xmx4g -jar ${config.MetalavaJar} jar-to-jdiff ${in} ${out}`,
330			CommandDeps: []string{"${config.JavaCmd}", "${config.MetalavaJar}"},
331			Description: "Converting API file to XML",
332		})
333)
334
335func init() {
336	pctx.Import("android/soong/android")
337	pctx.Import("android/soong/java/config")
338
339	pctx.HostBinToolVariable("aconfig", "aconfig")
340	pctx.HostBinToolVariable("ravenizer", "ravenizer")
341	pctx.HostBinToolVariable("apimapper", "apimapper")
342	pctx.HostBinToolVariable("aconfig-to-metalava-flags", "aconfig-to-metalava-flags")
343}
344
345type javaBuilderFlags struct {
346	javacFlags string
347
348	// bootClasspath is the list of jars that form the boot classpath (generally the java.* and
349	// android.* classes) for tools that still use it.  javac targeting 1.9 or higher uses
350	// systemModules and java9Classpath instead.
351	bootClasspath classpath
352
353	// classpath is the list of jars that form the classpath for javac and kotlinc rules.  It
354	// contains header jars for all static and non-static dependencies.
355	classpath classpath
356
357	// dexClasspath is the list of jars that form the classpath for d8 and r8 rules.  It contains
358	// header jars for all non-static dependencies.  Static dependencies have already been
359	// combined into the program jar.
360	dexClasspath classpath
361
362	// java9Classpath is the list of jars that will be added to the classpath when targeting
363	// 1.9 or higher.  It generally contains the android.* classes, while the java.* classes
364	// are provided by systemModules.
365	java9Classpath classpath
366
367	processorPath classpath
368	processors    []string
369	systemModules *systemModules
370	aidlFlags     string
371	aidlDeps      android.Paths
372	javaVersion   javaVersion
373
374	errorProneExtraJavacFlags string
375	errorProneProcessorPath   classpath
376
377	kotlincFlags     string
378	kotlincClasspath classpath
379	kotlincDeps      android.Paths
380
381	proto android.ProtoFlags
382}
383
384func DefaultJavaBuilderFlags() javaBuilderFlags {
385	return javaBuilderFlags{
386		javaVersion: JAVA_VERSION_8,
387	}
388}
389
390func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
391	srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath, flags javaBuilderFlags, deps android.Paths) {
392
393	// Compile java sources into .class files
394	desc := "javac"
395	if shardIdx >= 0 {
396		desc += strconv.Itoa(shardIdx)
397	}
398
399	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, annoSrcJar, flags, deps, "javac", desc)
400}
401
402// Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
403// to compile with given set of builder flags, etc.
404func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int,
405	srcFiles, srcJars android.Paths,
406	flags javaBuilderFlags, deps android.Paths) {
407
408	deps = append(deps, srcJars...)
409	classpath := flags.classpath
410
411	var bootClasspath string
412	if flags.javaVersion.usesJavaModules() {
413		var systemModuleDeps android.Paths
414		bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
415		deps = append(deps, systemModuleDeps...)
416		classpath = append(flags.java9Classpath, classpath...)
417	} else {
418		deps = append(deps, flags.bootClasspath...)
419		if len(flags.bootClasspath) == 0 && ctx.Device() {
420			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
421			// ensure java does not fall back to the default bootclasspath.
422			bootClasspath = `-bootclasspath ""`
423		} else {
424			bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
425		}
426	}
427
428	deps = append(deps, classpath...)
429	deps = append(deps, flags.processorPath...)
430
431	processor := "-proc:none"
432	if len(flags.processors) > 0 {
433		processor = "-processor " + strings.Join(flags.processors, ",")
434	}
435
436	intermediatesDir := "xref"
437	if idx >= 0 {
438		intermediatesDir += strconv.Itoa(idx)
439	}
440
441	ctx.Build(pctx,
442		android.BuildParams{
443			Rule:        kytheExtract,
444			Description: "Xref Java extractor",
445			Output:      xrefFile,
446			Inputs:      srcFiles,
447			Implicits:   deps,
448			Args: map[string]string{
449				"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, "anno").String(),
450				"bootClasspath": bootClasspath,
451				"classpath":     classpath.FormJavaClassPath("-classpath"),
452				"javacFlags":    flags.javacFlags,
453				"javaVersion":   flags.javaVersion.String(),
454				"outDir":        android.PathForModuleOut(ctx, "javac", "classes.xref").String(),
455				"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
456				"processor":     processor,
457				"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, "srcjars.xref").String(),
458				"srcJars":       strings.Join(srcJars.Strings(), " "),
459			},
460		})
461}
462
463func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string, srcJars android.Paths) (string, android.Paths, android.Paths, android.Paths) {
464	var implicits android.Paths
465	var rbeInputs android.Paths
466	var rspFiles android.Paths
467
468	classpath := flags.classpath
469
470	srcJarArgs := strings.Join(srcJars.Strings(), " ")
471	implicits = append(implicits, srcJars...)
472	const srcJarArgsLimit = 32 * 1024
473	if len(srcJarArgs) > srcJarArgsLimit {
474		srcJarRspFile := android.PathForModuleOut(ctx, "turbine", "srcjars.rsp")
475		android.WriteFileRule(ctx, srcJarRspFile, strings.Join(srcJars.Strings(), "\n"))
476		srcJarArgs = "@" + srcJarRspFile.String()
477		implicits = append(implicits, srcJarRspFile)
478		rspFiles = append(rspFiles, srcJarRspFile)
479		rbeInputs = append(rbeInputs, srcJarRspFile)
480	} else {
481		rbeInputs = append(rbeInputs, srcJars...)
482	}
483
484	var bootClasspathFlags string
485	if flags.javaVersion.usesJavaModules() {
486		var systemModuleDeps android.Paths
487		bootClasspathFlags, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
488		implicits = append(implicits, systemModuleDeps...)
489		rbeInputs = append(rbeInputs, systemModuleDeps...)
490		classpath = append(flags.java9Classpath, classpath...)
491	} else {
492		implicits = append(implicits, flags.bootClasspath...)
493		rbeInputs = append(rbeInputs, flags.bootClasspath...)
494		if len(flags.bootClasspath) == 0 && ctx.Device() {
495			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
496			// ensure turbine does not fall back to the default bootclasspath.
497			bootClasspathFlags = `--bootclasspath ""`
498		} else {
499			bootClasspathFlags = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
500		}
501	}
502
503	classpathFlags := classpath.FormTurbineClassPath("")
504	implicits = append(implicits, classpath...)
505	const classpathLimit = 32 * 1024
506	if len(classpathFlags) > classpathLimit {
507		classpathRspFile := android.PathForModuleOut(ctx, dir, "classpath.rsp")
508		android.WriteFileRule(ctx, classpathRspFile, strings.Join(classpath.Strings(), "\n"))
509		classpathFlags = "@" + classpathRspFile.String()
510		implicits = append(implicits, classpathRspFile)
511		rspFiles = append(rspFiles, classpathRspFile)
512		rbeInputs = append(rbeInputs, classpathRspFile)
513	} else {
514		rbeInputs = append(rbeInputs, classpath...)
515	}
516
517	turbineFlags := "--source_jars " + srcJarArgs + " " + bootClasspathFlags + " --classpath " + classpathFlags
518
519	return turbineFlags, implicits, rbeInputs, rspFiles
520}
521
522func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
523	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
524
525	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine", srcJars)
526
527	rule := turbine
528	args := map[string]string{
529		"javacFlags":   flags.javacFlags,
530		"javaVersion":  flags.javaVersion.String(),
531		"turbineFlags": turbineFlags,
532		"outputFlags":  "--output " + outputFile.String() + ".tmp",
533		"outputs":      outputFile.String(),
534	}
535	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
536		rule = turbineRE
537		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
538		args["rbeOutputs"] = outputFile.String() + ".tmp"
539		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
540	}
541	ctx.Build(pctx, android.BuildParams{
542		Rule:        rule,
543		Description: "turbine",
544		Output:      outputFile,
545		Inputs:      srcFiles,
546		Implicits:   implicits,
547		Args:        args,
548	})
549}
550
551// TurbineApt produces a rule to run annotation processors using turbine.
552func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath,
553	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
554
555	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine-apt", srcJars)
556
557	implicits = append(implicits, flags.processorPath...)
558	rbeInputs = append(rbeInputs, flags.processorPath...)
559	turbineFlags += " " + flags.processorPath.FormTurbineClassPath("--processorpath ")
560	turbineFlags += " --processors " + strings.Join(flags.processors, " ")
561
562	outputs := android.WritablePaths{outputSrcJar, outputResJar}
563	outputFlags := "--gensrc_output " + outputSrcJar.String() + ".tmp " +
564		"--resource_output " + outputResJar.String() + ".tmp"
565
566	rule := turbine
567	args := map[string]string{
568		"javacFlags":   flags.javacFlags,
569		"javaVersion":  flags.javaVersion.String(),
570		"turbineFlags": turbineFlags,
571		"outputFlags":  outputFlags,
572		"outputs":      strings.Join(outputs.Strings(), " "),
573	}
574	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
575		rule = turbineRE
576		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
577		args["rbeOutputs"] = outputSrcJar.String() + ".tmp," + outputResJar.String() + ".tmp"
578		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
579	}
580	ctx.Build(pctx, android.BuildParams{
581		Rule:            rule,
582		Description:     "turbine apt",
583		Output:          outputs[0],
584		ImplicitOutputs: outputs[1:],
585		Inputs:          srcFiles,
586		Implicits:       implicits,
587		Args:            args,
588	})
589}
590
591// transformJavaToClasses takes source files and converts them to a jar containing .class files.
592// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
593// sources.  flags contains various command line flags to be passed to the compiler.
594//
595// This method may be used for different compilers, including javac and Error Prone.  The rule
596// argument specifies which command line to use and desc sets the description of the rule that will
597// be printed at build time.  The stem argument provides the file name of the output jar, and
598// suffix will be appended to various intermediate files and directories to avoid collisions when
599// this function is called twice in the same module directory.
600func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
601	shardIdx int, srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath,
602	flags javaBuilderFlags, deps android.Paths,
603	intermediatesDir, desc string) {
604
605	deps = append(deps, srcJars...)
606
607	javacClasspath := flags.classpath
608
609	var bootClasspath string
610	if flags.javaVersion.usesJavaModules() {
611		var systemModuleDeps android.Paths
612		bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
613		deps = append(deps, systemModuleDeps...)
614		javacClasspath = append(flags.java9Classpath, javacClasspath...)
615	} else {
616		deps = append(deps, flags.bootClasspath...)
617		if len(flags.bootClasspath) == 0 && ctx.Device() {
618			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
619			// ensure java does not fall back to the default bootclasspath.
620			bootClasspath = `-bootclasspath ""`
621		} else {
622			bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
623		}
624	}
625
626	classpathArg := javacClasspath.FormJavaClassPath("-classpath")
627
628	// Keep the command line under the MAX_ARG_STRLEN limit by putting the classpath argument into an rsp file
629	// if it is too long.
630	const classpathLimit = 64 * 1024
631	if len(classpathArg) > classpathLimit {
632		classpathRspFile := outputFile.ReplaceExtension(ctx, "classpath")
633		android.WriteFileRule(ctx, classpathRspFile, classpathArg)
634		deps = append(deps, classpathRspFile)
635		classpathArg = "@" + classpathRspFile.String()
636	}
637
638	deps = append(deps, javacClasspath...)
639	deps = append(deps, flags.processorPath...)
640
641	processor := "-proc:none"
642	if len(flags.processors) > 0 {
643		processor = "-processor " + strings.Join(flags.processors, ",")
644	}
645
646	srcJarDir := "srcjars"
647	outDir := "classes"
648	annoDir := "anno"
649	if shardIdx >= 0 {
650		shardDir := "shard" + strconv.Itoa(shardIdx)
651		srcJarDir = filepath.Join(shardDir, srcJarDir)
652		outDir = filepath.Join(shardDir, outDir)
653		annoDir = filepath.Join(shardDir, annoDir)
654	}
655	rule := javac
656	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_JAVAC") {
657		rule = javacRE
658	}
659	ctx.Build(pctx, android.BuildParams{
660		Rule:           rule,
661		Description:    desc,
662		Output:         outputFile,
663		ImplicitOutput: annoSrcJar,
664		Inputs:         srcFiles,
665		Implicits:      deps,
666		Args: map[string]string{
667			"javacFlags":    flags.javacFlags,
668			"bootClasspath": bootClasspath,
669			"classpath":     classpathArg,
670			"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
671			"processor":     processor,
672			"srcJars":       strings.Join(srcJars.Strings(), " "),
673			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
674			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
675			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
676			"annoSrcJar":    annoSrcJar.String(),
677			"javaVersion":   flags.javaVersion.String(),
678		},
679	})
680}
681
682func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath,
683	jarArgs []string, deps android.Paths) {
684
685	rule := jar
686	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_JAR") {
687		rule = jarRE
688	}
689	ctx.Build(pctx, android.BuildParams{
690		Rule:        rule,
691		Description: "jar",
692		Output:      outputFile,
693		Implicits:   deps,
694		Args: map[string]string{
695			"jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
696		},
697	})
698}
699
700func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath, desc string,
701	jars android.Paths, manifest android.OptionalPath, stripDirEntries bool, filesToStrip []string,
702	dirsToStrip []string) {
703
704	var deps android.Paths
705
706	var jarArgs []string
707	if manifest.Valid() {
708		jarArgs = append(jarArgs, "-m ", manifest.String())
709		deps = append(deps, manifest.Path())
710	}
711
712	for _, dir := range dirsToStrip {
713		jarArgs = append(jarArgs, "-stripDir ", dir)
714	}
715
716	for _, file := range filesToStrip {
717		jarArgs = append(jarArgs, "-stripFile ", file)
718	}
719
720	// Remove any module-info.class files that may have come from prebuilt jars, they cause problems
721	// for downstream tools like desugar.
722	jarArgs = append(jarArgs, "-stripFile module-info.class")
723	jarArgs = append(jarArgs, "-stripFile META-INF/versions/*/module-info.class")
724
725	if stripDirEntries {
726		jarArgs = append(jarArgs, "-D")
727	}
728
729	rule := combineJar
730	// Keep the command line under the MAX_ARG_STRLEN limit by putting the list of jars into an rsp file
731	// if it is too long.
732	const jarsLengthLimit = 64 * 1024
733	jarsLength := 0
734	for i, jar := range jars {
735		if i != 0 {
736			jarsLength += 1
737		}
738		jarsLength += len(jar.String())
739	}
740	if jarsLength > jarsLengthLimit {
741		rule = combineJarRsp
742	}
743
744	ctx.Build(pctx, android.BuildParams{
745		Rule:        rule,
746		Description: desc,
747		Output:      outputFile,
748		Inputs:      jars,
749		Implicits:   deps,
750		Args: map[string]string{
751			"jarArgs": strings.Join(jarArgs, " "),
752		},
753	})
754}
755
756func TransformJarToR8Rules(ctx android.ModuleContext, outputFile android.WritablePath,
757	jar android.Path) {
758
759	ctx.Build(pctx, android.BuildParams{
760		Rule:   extractR8Rules,
761		Output: outputFile,
762		Input:  jar,
763	})
764}
765
766func convertImplementationJarToHeaderJar(ctx android.ModuleContext, implementationJarFile android.Path,
767	headerJarFile android.WritablePath) {
768	ctx.Build(pctx, android.BuildParams{
769		Rule:   convertImplementationJarToHeaderJarRule,
770		Input:  implementationJarFile,
771		Output: headerJarFile,
772	})
773}
774
775func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
776	classesJar android.Path, rulesFile android.Path) {
777	TransformJarJarWithShards(ctx, outputFile, classesJar, rulesFile, 1)
778}
779
780func TransformJarJarWithShards(ctx android.ModuleContext, outputFile android.WritablePath,
781	classesJar android.Path, rulesFile android.Path, totalShards int) {
782
783	// If the total number of shards is 1, just run jarjar as-is, with `total_shards` = 1
784	// and `shard_index` == 0, which effectively disables sharding
785	if totalShards == 1 {
786		ctx.Build(pctx, android.BuildParams{
787			Rule:        jarjar,
788			Description: "jarjar",
789			Output:      outputFile,
790			Input:       classesJar,
791			Implicit:    rulesFile,
792			Args: map[string]string{
793				"rulesFile":    rulesFile.String(),
794				"total_shards": "1",
795				"shard_index":  "0",
796			},
797		})
798		return
799	}
800
801	// Otherwise, run multiple jarjar instances and use merge_zips to combine the output.
802	tempJars := make([]android.Path, 0)
803	totalStr := strconv.Itoa(totalShards)
804	for i := 0; i < totalShards; i++ {
805		iStr := strconv.Itoa(i)
806		tempOut := outputFile.ReplaceExtension(ctx, "-"+iStr+".jar")
807		ctx.Build(pctx, android.BuildParams{
808			Rule:        jarjar,
809			Description: "jarjar (" + iStr + "/" + totalStr + ")",
810			Output:      tempOut,
811			Input:       classesJar,
812			Implicit:    rulesFile,
813			Args: map[string]string{
814				"rulesFile":    rulesFile.String(),
815				"total_shards": totalStr,
816				"shard_index":  iStr,
817			},
818		})
819		tempJars = append(tempJars, tempOut)
820	}
821
822	ctx.Build(pctx, android.BuildParams{
823		Rule:        combineJar,
824		Description: "merge jarjar shards",
825		Output:      outputFile,
826		Inputs:      tempJars,
827	})
828
829}
830
831func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath,
832	classesJar android.Path, permittedPackages []string) {
833	ctx.Build(pctx, android.BuildParams{
834		Rule:        packageCheck,
835		Description: "packageCheck",
836		Output:      outputFile,
837		Input:       classesJar,
838		Args: map[string]string{
839			"packages": strings.Join(permittedPackages, " "),
840		},
841	})
842}
843
844func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePath,
845	inputFile android.Path) {
846	ctx.Build(pctx, android.BuildParams{
847		Rule:        jetifier,
848		Description: "jetifier",
849		Output:      outputFile,
850		Input:       inputFile,
851	})
852}
853
854func TransformRavenizer(ctx android.ModuleContext, outputFile android.WritablePath,
855	inputFile android.Path, ravenizerArgs string) {
856	ctx.Build(pctx, android.BuildParams{
857		Rule:        ravenizer,
858		Description: "ravenizer",
859		Output:      outputFile,
860		Input:       inputFile,
861		Args: map[string]string{
862			"ravenizerArgs": ravenizerArgs,
863		},
864	})
865}
866
867func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
868	android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
869}
870
871func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path, validations android.Paths) {
872	ctx.Build(pctx, android.BuildParams{
873		Rule:        zipalign,
874		Description: "align",
875		Input:       inputFile,
876		Output:      outputFile,
877		Validations: validations,
878	})
879}
880
881func writeCombinedProguardFlagsFile(ctx android.ModuleContext, outputFile android.WritablePath, files android.Paths) {
882	ctx.Build(pctx, android.BuildParams{
883		Rule:        writeCombinedProguardFlagsFileRule,
884		Description: "write combined proguard flags file",
885		Inputs:      files,
886		Output:      outputFile,
887	})
888}
889
890type classpath android.Paths
891
892func (x *classpath) formJoinedClassPath(optName string, sep string) string {
893	if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
894		optName += " "
895	}
896	if len(*x) > 0 {
897		return optName + strings.Join(x.Strings(), sep)
898	} else {
899		return ""
900	}
901}
902func (x *classpath) FormJavaClassPath(optName string) string {
903	return x.formJoinedClassPath(optName, ":")
904}
905
906func (x *classpath) FormTurbineClassPath(optName string) string {
907	return x.formJoinedClassPath(optName, " ")
908}
909
910// FormRepeatedClassPath returns a list of arguments with the given optName prefixed to each element of the classpath.
911func (x *classpath) FormRepeatedClassPath(optName string) []string {
912	if x == nil || *x == nil {
913		return nil
914	}
915	flags := make([]string, len(*x))
916	for i, v := range *x {
917		flags[i] = optName + v.String()
918	}
919
920	return flags
921}
922
923// Convert a classpath to an android.Paths
924func (x *classpath) Paths() android.Paths {
925	return append(android.Paths(nil), (*x)...)
926}
927
928func (x *classpath) Strings() []string {
929	if x == nil {
930		return nil
931	}
932	ret := make([]string, len(*x))
933	for i, path := range *x {
934		ret[i] = path.String()
935	}
936	return ret
937}
938
939type systemModules struct {
940	dir  android.Path
941	deps android.Paths
942}
943
944// Returns a --system argument in the form javac expects with -source 1.9 and the list of files to
945// depend on.  If forceEmpty is true, returns --system=none if the list is empty to ensure javac
946// does not fall back to the default system modules.
947func (x *systemModules) FormJavaSystemModulesPath(forceEmpty bool) (string, android.Paths) {
948	if x != nil {
949		return "--system=" + x.dir.String(), x.deps
950	} else if forceEmpty {
951		return "--system=none", nil
952	} else {
953		return "", nil
954	}
955}
956
957// Returns a --system argument in the form turbine expects with -source 1.9 and the list of files to
958// depend on.  If forceEmpty is true, returns --bootclasspath "" if the list is empty to ensure turbine
959// does not fall back to the default bootclasspath.
960func (x *systemModules) FormTurbineSystemModulesPath(forceEmpty bool) (string, android.Paths) {
961	if x != nil {
962		return "--system " + x.dir.String(), x.deps
963	} else if forceEmpty {
964		return `--bootclasspath ""`, nil
965	} else {
966		return "--system ${config.JavaHome}", nil
967	}
968}
969