• 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" "$srcJarDir" "$out" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
46				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
47				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
48				`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
49				`${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
50				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
51				`-source $javaVersion -target $javaVersion ` +
52				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
53				`$zipTemplate${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
54				`rm -rf "$srcJarDir"`,
55			CommandDeps: []string{
56				"${config.JavacCmd}",
57				"${config.SoongZipCmd}",
58				"${config.ZipSyncCmd}",
59			},
60			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
61			Rspfile:          "$out.rsp",
62			RspfileContent:   "$in",
63		}, map[string]*remoteexec.REParams{
64			"$javaTemplate": &remoteexec.REParams{
65				Labels:       map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
66				ExecStrategy: "${config.REJavacExecStrategy}",
67				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
68			},
69			"$zipTemplate": &remoteexec.REParams{
70				Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
71				Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
72				OutputFiles:  []string{"$out"},
73				ExecStrategy: "${config.REJavacExecStrategy}",
74				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
75			},
76		}, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
77			"outDir", "annoDir", "javaVersion"}, nil)
78
79	_ = pctx.VariableFunc("kytheCorpus",
80		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
81	_ = pctx.VariableFunc("kytheCuEncoding",
82		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
83	_ = pctx.VariableFunc("kytheCuJavaSourceMax",
84		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuJavaSourceMax() })
85	_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
86	// Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about
87	// "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
88	// to field java.nio.Buffer.address"
89	kytheExtract = pctx.AndroidStaticRule("kythe",
90		blueprint.RuleParams{
91			Command: `${config.ZipSyncCmd} -d $srcJarDir ` +
92				`-l $srcJarDir/list -f "*.java" $srcJars && ` +
93				`( [ ! -s $srcJarDir/list -a ! -s $out.rsp ] || ` +
94				`KYTHE_ROOT_DIRECTORY=. KYTHE_OUTPUT_FILE=$out ` +
95				`KYTHE_CORPUS=${kytheCorpus} ` +
96				`KYTHE_VNAMES=${kytheVnames} ` +
97				`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
98				`KYTHE_JAVA_SOURCE_BATCH_SIZE=${kytheCuJavaSourceMax} ` +
99				`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
100				`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
101				`-jar ${config.JavaKytheExtractorJar} ` +
102				`${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
103				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
104				`-source $javaVersion -target $javaVersion ` +
105				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list)`,
106			CommandDeps: []string{
107				"${config.JavaCmd}",
108				"${config.JavaKytheExtractorJar}",
109				"${kytheVnames}",
110				"${config.ZipSyncCmd}",
111			},
112			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
113			Rspfile:          "$out.rsp",
114			RspfileContent:   "$in",
115		},
116		"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
117		"outDir", "annoDir", "javaVersion")
118
119	extractMatchingApks = pctx.StaticRule(
120		"extractMatchingApks",
121		blueprint.RuleParams{
122			Command: `rm -rf "$out" && ` +
123				`${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
124				`-sdk-version=${sdk-version} -abis=${abis} ` +
125				`--screen-densities=${screen-densities} --stem=${stem} ` +
126				`-apkcerts=${apkcerts} -partition=${partition} ` +
127				`${in}`,
128			CommandDeps: []string{"${config.ExtractApksCmd}"},
129		},
130		"abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition")
131
132	turbine, turbineRE = pctx.RemoteStaticRules("turbine",
133		blueprint.RuleParams{
134			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
135				`$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` +
136				`--temp_dir "$outDir" --sources @$out.rsp  --source_jars $srcJars ` +
137				`--javacopts ${config.CommonJdkFlags} ` +
138				`$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` +
139				`${config.Ziptime} $out.tmp && ` +
140				`(if cmp -s $out.tmp $out ; then rm $out.tmp ; else mv $out.tmp $out ; fi )`,
141			CommandDeps: []string{
142				"${config.TurbineJar}",
143				"${config.JavaCmd}",
144				"${config.Ziptime}",
145			},
146			Rspfile:        "$out.rsp",
147			RspfileContent: "$in",
148			Restat:         true,
149		},
150		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
151			ExecStrategy:      "${config.RETurbineExecStrategy}",
152			Inputs:            []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
153			RSPFiles:          []string{"${out}.rsp"},
154			OutputFiles:       []string{"$out.tmp"},
155			OutputDirectories: []string{"$outDir"},
156			ToolchainInputs:   []string{"${config.JavaCmd}"},
157			Platform:          map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
158		}, []string{"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion"}, []string{"implicits"})
159
160	jar, jarRE = pctx.RemoteStaticRules("jar",
161		blueprint.RuleParams{
162			Command:        `$reTemplate${config.SoongZipCmd} -jar -o $out @$out.rsp`,
163			CommandDeps:    []string{"${config.SoongZipCmd}"},
164			Rspfile:        "$out.rsp",
165			RspfileContent: "$jarArgs",
166		},
167		&remoteexec.REParams{
168			ExecStrategy: "${config.REJarExecStrategy}",
169			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp"},
170			RSPFiles:     []string{"${out}.rsp"},
171			OutputFiles:  []string{"$out"},
172			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
173		}, []string{"jarArgs"}, nil)
174
175	zip, zipRE = pctx.RemoteStaticRules("zip",
176		blueprint.RuleParams{
177			Command:        `${config.SoongZipCmd} -o $out @$out.rsp`,
178			CommandDeps:    []string{"${config.SoongZipCmd}"},
179			Rspfile:        "$out.rsp",
180			RspfileContent: "$jarArgs",
181		},
182		&remoteexec.REParams{
183			ExecStrategy: "${config.REZipExecStrategy}",
184			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"},
185			RSPFiles:     []string{"${out}.rsp"},
186			OutputFiles:  []string{"$out"},
187			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
188		}, []string{"jarArgs"}, []string{"implicits"})
189
190	combineJar = pctx.AndroidStaticRule("combineJar",
191		blueprint.RuleParams{
192			Command:     `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out $in`,
193			CommandDeps: []string{"${config.MergeZipsCmd}"},
194		},
195		"jarArgs")
196
197	jarjar = pctx.AndroidStaticRule("jarjar",
198		blueprint.RuleParams{
199			Command: "" +
200				// Jarjar doesn't exit with an error when the rules file contains a syntax error,
201				// leading to stale or missing files later in the build.  Remove the output file
202				// before running jarjar.
203				"rm -f ${out} && " +
204				"${config.JavaCmd} ${config.JavaVmFlags}" +
205				// b/146418363 Enable Android specific jarjar transformer to drop compat annotations
206				// for newly repackaged classes. Dropping @UnsupportedAppUsage on repackaged classes
207				// avoids adding new hiddenapis after jarjar'ing.
208				" -DremoveAndroidCompatAnnotations=true" +
209				" -jar ${config.JarjarCmd} process $rulesFile $in $out && " +
210				// Turn a missing output file into a ninja error
211				`[ -e ${out} ] || (echo "Missing output file"; exit 1)`,
212			CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
213		},
214		"rulesFile")
215
216	packageCheck = pctx.AndroidStaticRule("packageCheck",
217		blueprint.RuleParams{
218			Command: "rm -f $out && " +
219				"${config.PackageCheckCmd} $in $packages && " +
220				"touch $out",
221			CommandDeps: []string{"${config.PackageCheckCmd}"},
222		},
223		"packages")
224
225	jetifier = pctx.AndroidStaticRule("jetifier",
226		blueprint.RuleParams{
227			Command:     "${config.JavaCmd}  ${config.JavaVmFlags} -jar ${config.JetifierJar} -l error -o $out -i $in",
228			CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"},
229		},
230	)
231
232	zipalign = pctx.AndroidStaticRule("zipalign",
233		blueprint.RuleParams{
234			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
235				"${config.ZipAlign} -f -p 4 $in $out; " +
236				"else " +
237				"cp -f $in $out; " +
238				"fi",
239			CommandDeps: []string{"${config.ZipAlign}"},
240		},
241	)
242)
243
244func init() {
245	pctx.Import("android/soong/android")
246	pctx.Import("android/soong/java/config")
247}
248
249type javaBuilderFlags struct {
250	javacFlags     string
251	bootClasspath  classpath
252	classpath      classpath
253	java9Classpath classpath
254	processorPath  classpath
255	processors     []string
256	systemModules  *systemModules
257	aidlFlags      string
258	aidlDeps       android.Paths
259	javaVersion    javaVersion
260
261	errorProneExtraJavacFlags string
262	errorProneProcessorPath   classpath
263
264	kotlincFlags     string
265	kotlincClasspath classpath
266
267	proto android.ProtoFlags
268}
269
270func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
271	srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) {
272
273	// Compile java sources into .class files
274	desc := "javac"
275	if shardIdx >= 0 {
276		desc += strconv.Itoa(shardIdx)
277	}
278
279	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
280}
281
282func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
283	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
284
285	flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
286
287	if len(flags.errorProneExtraJavacFlags) > 0 {
288		if len(flags.javacFlags) > 0 {
289			flags.javacFlags += " " + flags.errorProneExtraJavacFlags
290		} else {
291			flags.javacFlags = flags.errorProneExtraJavacFlags
292		}
293	}
294
295	transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
296		"errorprone", "errorprone")
297}
298
299// Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
300// to compile with given set of builder flags, etc.
301func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int,
302	srcFiles, srcJars android.Paths,
303	flags javaBuilderFlags, deps android.Paths) {
304
305	deps = append(deps, srcJars...)
306	classpath := flags.classpath
307
308	var bootClasspath string
309	if flags.javaVersion.usesJavaModules() {
310		var systemModuleDeps android.Paths
311		bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
312		deps = append(deps, systemModuleDeps...)
313		classpath = append(flags.java9Classpath, classpath...)
314	} else {
315		deps = append(deps, flags.bootClasspath...)
316		if len(flags.bootClasspath) == 0 && ctx.Device() {
317			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
318			// ensure java does not fall back to the default bootclasspath.
319			bootClasspath = `-bootclasspath ""`
320		} else {
321			bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
322		}
323	}
324
325	deps = append(deps, classpath...)
326	deps = append(deps, flags.processorPath...)
327
328	processor := "-proc:none"
329	if len(flags.processors) > 0 {
330		processor = "-processor " + strings.Join(flags.processors, ",")
331	}
332
333	intermediatesDir := "xref"
334	if idx >= 0 {
335		intermediatesDir += strconv.Itoa(idx)
336	}
337
338	ctx.Build(pctx,
339		android.BuildParams{
340			Rule:        kytheExtract,
341			Description: "Xref Java extractor",
342			Output:      xrefFile,
343			Inputs:      srcFiles,
344			Implicits:   deps,
345			Args: map[string]string{
346				"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, "anno").String(),
347				"bootClasspath": bootClasspath,
348				"classpath":     classpath.FormJavaClassPath("-classpath"),
349				"javacFlags":    flags.javacFlags,
350				"javaVersion":   flags.javaVersion.String(),
351				"outDir":        android.PathForModuleOut(ctx, "javac", "classes.xref").String(),
352				"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
353				"processor":     processor,
354				"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, "srcjars.xref").String(),
355				"srcJars":       strings.Join(srcJars.Strings(), " "),
356			},
357		})
358}
359
360func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
361	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
362
363	var deps android.Paths
364	deps = append(deps, srcJars...)
365
366	classpath := flags.classpath
367
368	var bootClasspath string
369	if flags.javaVersion.usesJavaModules() {
370		var systemModuleDeps android.Paths
371		bootClasspath, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
372		deps = append(deps, systemModuleDeps...)
373		classpath = append(flags.java9Classpath, classpath...)
374	} else {
375		deps = append(deps, flags.bootClasspath...)
376		if len(flags.bootClasspath) == 0 && ctx.Device() {
377			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
378			// ensure turbine does not fall back to the default bootclasspath.
379			bootClasspath = `--bootclasspath ""`
380		} else {
381			bootClasspath = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
382		}
383	}
384
385	deps = append(deps, classpath...)
386	deps = append(deps, flags.processorPath...)
387
388	rule := turbine
389	args := map[string]string{
390		"javacFlags":    flags.javacFlags,
391		"bootClasspath": bootClasspath,
392		"srcJars":       strings.Join(srcJars.Strings(), " "),
393		"classpath":     classpath.FormTurbineClassPath("--classpath "),
394		"outDir":        android.PathForModuleOut(ctx, "turbine", "classes").String(),
395		"javaVersion":   flags.javaVersion.String(),
396	}
397	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
398		rule = turbineRE
399		args["implicits"] = strings.Join(deps.Strings(), ",")
400	}
401	ctx.Build(pctx, android.BuildParams{
402		Rule:        rule,
403		Description: "turbine",
404		Output:      outputFile,
405		Inputs:      srcFiles,
406		Implicits:   deps,
407		Args:        args,
408	})
409}
410
411// transformJavaToClasses takes source files and converts them to a jar containing .class files.
412// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
413// sources.  flags contains various command line flags to be passed to the compiler.
414//
415// This method may be used for different compilers, including javac and Error Prone.  The rule
416// argument specifies which command line to use and desc sets the description of the rule that will
417// be printed at build time.  The stem argument provides the file name of the output jar, and
418// suffix will be appended to various intermediate files and directories to avoid collisions when
419// this function is called twice in the same module directory.
420func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
421	shardIdx int, srcFiles, srcJars android.Paths,
422	flags javaBuilderFlags, deps android.Paths,
423	intermediatesDir, desc string) {
424
425	deps = append(deps, srcJars...)
426
427	classpath := flags.classpath
428
429	var bootClasspath string
430	if flags.javaVersion.usesJavaModules() {
431		var systemModuleDeps android.Paths
432		bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
433		deps = append(deps, systemModuleDeps...)
434		classpath = append(flags.java9Classpath, classpath...)
435	} else {
436		deps = append(deps, flags.bootClasspath...)
437		if len(flags.bootClasspath) == 0 && ctx.Device() {
438			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
439			// ensure java does not fall back to the default bootclasspath.
440			bootClasspath = `-bootclasspath ""`
441		} else {
442			bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
443		}
444	}
445
446	deps = append(deps, classpath...)
447	deps = append(deps, flags.processorPath...)
448
449	processor := "-proc:none"
450	if len(flags.processors) > 0 {
451		processor = "-processor " + strings.Join(flags.processors, ",")
452	}
453
454	srcJarDir := "srcjars"
455	outDir := "classes"
456	annoDir := "anno"
457	if shardIdx >= 0 {
458		shardDir := "shard" + strconv.Itoa(shardIdx)
459		srcJarDir = filepath.Join(shardDir, srcJarDir)
460		outDir = filepath.Join(shardDir, outDir)
461		annoDir = filepath.Join(shardDir, annoDir)
462	}
463	rule := javac
464	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_JAVAC") {
465		rule = javacRE
466	}
467	ctx.Build(pctx, android.BuildParams{
468		Rule:        rule,
469		Description: desc,
470		Output:      outputFile,
471		Inputs:      srcFiles,
472		Implicits:   deps,
473		Args: map[string]string{
474			"javacFlags":    flags.javacFlags,
475			"bootClasspath": bootClasspath,
476			"classpath":     classpath.FormJavaClassPath("-classpath"),
477			"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
478			"processor":     processor,
479			"srcJars":       strings.Join(srcJars.Strings(), " "),
480			"srcJarDir":     android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
481			"outDir":        android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
482			"annoDir":       android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
483			"javaVersion":   flags.javaVersion.String(),
484		},
485	})
486}
487
488func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath,
489	jarArgs []string, deps android.Paths) {
490
491	rule := jar
492	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_JAR") {
493		rule = jarRE
494	}
495	ctx.Build(pctx, android.BuildParams{
496		Rule:        rule,
497		Description: "jar",
498		Output:      outputFile,
499		Implicits:   deps,
500		Args: map[string]string{
501			"jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
502		},
503	})
504}
505
506func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath, desc string,
507	jars android.Paths, manifest android.OptionalPath, stripDirEntries bool, filesToStrip []string,
508	dirsToStrip []string) {
509
510	var deps android.Paths
511
512	var jarArgs []string
513	if manifest.Valid() {
514		jarArgs = append(jarArgs, "-m ", manifest.String())
515		deps = append(deps, manifest.Path())
516	}
517
518	for _, dir := range dirsToStrip {
519		jarArgs = append(jarArgs, "-stripDir ", dir)
520	}
521
522	for _, file := range filesToStrip {
523		jarArgs = append(jarArgs, "-stripFile ", file)
524	}
525
526	// Remove any module-info.class files that may have come from prebuilt jars, they cause problems
527	// for downstream tools like desugar.
528	jarArgs = append(jarArgs, "-stripFile module-info.class")
529
530	if stripDirEntries {
531		jarArgs = append(jarArgs, "-D")
532	}
533
534	ctx.Build(pctx, android.BuildParams{
535		Rule:        combineJar,
536		Description: desc,
537		Output:      outputFile,
538		Inputs:      jars,
539		Implicits:   deps,
540		Args: map[string]string{
541			"jarArgs": strings.Join(jarArgs, " "),
542		},
543	})
544}
545
546func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
547	classesJar android.Path, rulesFile android.Path) {
548	ctx.Build(pctx, android.BuildParams{
549		Rule:        jarjar,
550		Description: "jarjar",
551		Output:      outputFile,
552		Input:       classesJar,
553		Implicit:    rulesFile,
554		Args: map[string]string{
555			"rulesFile": rulesFile.String(),
556		},
557	})
558}
559
560func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath,
561	classesJar android.Path, permittedPackages []string) {
562	ctx.Build(pctx, android.BuildParams{
563		Rule:        packageCheck,
564		Description: "packageCheck",
565		Output:      outputFile,
566		Input:       classesJar,
567		Args: map[string]string{
568			"packages": strings.Join(permittedPackages, " "),
569		},
570	})
571}
572
573func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePath,
574	inputFile android.Path) {
575	ctx.Build(pctx, android.BuildParams{
576		Rule:        jetifier,
577		Description: "jetifier",
578		Output:      outputFile,
579		Input:       inputFile,
580	})
581}
582
583func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
584	android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
585}
586
587func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) {
588	ctx.Build(pctx, android.BuildParams{
589		Rule:        zipalign,
590		Description: "align",
591		Input:       inputFile,
592		Output:      outputFile,
593	})
594}
595
596type classpath android.Paths
597
598func (x *classpath) formJoinedClassPath(optName string, sep string) string {
599	if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
600		optName += " "
601	}
602	if len(*x) > 0 {
603		return optName + strings.Join(x.Strings(), sep)
604	} else {
605		return ""
606	}
607}
608func (x *classpath) FormJavaClassPath(optName string) string {
609	return x.formJoinedClassPath(optName, ":")
610}
611
612func (x *classpath) FormTurbineClassPath(optName string) string {
613	return x.formJoinedClassPath(optName, " ")
614}
615
616// FormRepeatedClassPath returns a list of arguments with the given optName prefixed to each element of the classpath.
617func (x *classpath) FormRepeatedClassPath(optName string) []string {
618	if x == nil || *x == nil {
619		return nil
620	}
621	flags := make([]string, len(*x))
622	for i, v := range *x {
623		flags[i] = optName + v.String()
624	}
625
626	return flags
627}
628
629// Convert a classpath to an android.Paths
630func (x *classpath) Paths() android.Paths {
631	return append(android.Paths(nil), (*x)...)
632}
633
634func (x *classpath) Strings() []string {
635	if x == nil {
636		return nil
637	}
638	ret := make([]string, len(*x))
639	for i, path := range *x {
640		ret[i] = path.String()
641	}
642	return ret
643}
644
645type systemModules struct {
646	dir  android.Path
647	deps android.Paths
648}
649
650// Returns a --system argument in the form javac expects with -source 1.9 and the list of files to
651// depend on.  If forceEmpty is true, returns --system=none if the list is empty to ensure javac
652// does not fall back to the default system modules.
653func (x *systemModules) FormJavaSystemModulesPath(forceEmpty bool) (string, android.Paths) {
654	if x != nil {
655		return "--system=" + x.dir.String(), x.deps
656	} else if forceEmpty {
657		return "--system=none", nil
658	} else {
659		return "", nil
660	}
661}
662
663// Returns a --system argument in the form turbine expects with -source 1.9 and the list of files to
664// depend on.  If forceEmpty is true, returns --bootclasspath "" if the list is empty to ensure turbine
665// does not fall back to the default bootclasspath.
666func (x *systemModules) FormTurbineSystemModulesPath(forceEmpty bool) (string, android.Paths) {
667	if x != nil {
668		return "--system " + x.dir.String(), x.deps
669	} else if forceEmpty {
670		return `--bootclasspath ""`, nil
671	} else {
672		return "", nil
673	}
674}
675