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
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.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
319func 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
329type 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
368func DefaultJavaBuilderFlags() javaBuilderFlags {
369	return javaBuilderFlags{
370		javaVersion: JAVA_VERSION_8,
371	}
372}
373
374func 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.
388func 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
447func 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
505func 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.
535func 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.
583func 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
665func 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
683func 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
739func 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
748func 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
762func 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
775func 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
785func 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
798func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
799	android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
800}
801
802func 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
812func 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
821type classpath android.Paths
822
823func (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}
833func (x *classpath) FormJavaClassPath(optName string) string {
834	return x.formJoinedClassPath(optName, ":")
835}
836
837func (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.
842func (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
855func (x *classpath) Paths() android.Paths {
856	return append(android.Paths(nil), (*x)...)
857}
858
859func (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
870type 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.
878func (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.
891func (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