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