aboutsummaryrefslogtreecommitdiff
path: root/java/builder.go
diff options
context:
space:
mode:
Diffstat (limited to 'java/builder.go')
-rw-r--r--java/builder.go294
1 files changed, 282 insertions, 12 deletions
diff --git a/java/builder.go b/java/builder.go
index dff0032d8..3251dfd9c 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -20,6 +20,7 @@ package java
import (
"path/filepath"
+ "slices"
"strconv"
"strings"
@@ -33,13 +34,101 @@ import (
var (
pctx = android.NewPackageContext("android/soong/java")
+ // Unzips java src files from supplied jars into a directory provided.
+ extractSrcJars = pctx.AndroidStaticRule("javac-extract-srcJars",
+ blueprint.RuleParams{
+ Command: `rm -rf "$extractDir" && mkdir -p "$extractDir" && ${config.ZipSyncCmd} -d "$extractDir" -l "$out" -f "*.java" $jars`,
+ CommandDeps: []string{
+ "${config.ZipSyncCmd}",
+ },
+ }, "extractDir", "jars",
+ )
+
+ // Removes all outputs of inc-javac rule
+ javacIncClean = pctx.AndroidStaticRule("javac-inc-partialcompileclean",
+ blueprint.RuleParams{
+ Command: `rm -rf "${srcJarDir}" "${outDir}" "${annoDir}" "${annoSrcJar}" "${builtOut}"`,
+ }, "srcJarDir", "outDir", "annoDir", "annoSrcJar", "builtOut",
+ )
+
+ // Incremental javac rule
+ // The idea of this rule is to make javac incremental, i.e. use the input and
+ // output of previous javac execution to reduce the src lists.
+ // This rule is very similar to the normal javac rule with a few differences:
+ // * Does not unzip srcJars in the rule
+ // * Does not remove the temp directories containing classes/generated sources
+ // * Saves the states of a bunch of input params, when javac executes successfully.
+ // * Runs additional tools on the output to prepare for next iteration
+ javacInc, javacIncRE = pctx.MultiCommandRemoteStaticRules("javac-inc",
+ blueprint.RuleParams{
+ Command: `rm -rf "$annoSrcJar.tmp" "$out.tmp" && ` +
+ `mkdir -p "$annoDir" && ` +
+ `if [ -s $out.rsp ] && [ -s $srcJarList ] ; then ` +
+ `echo >> $out.rsp; fi && ` +
+ `cat $srcJarList >> $out.rsp && ` +
+ `if [ -s $genAnnoSrcJarList ] ; then ` +
+ `echo >> $out.rsp && cat $genAnnoSrcJarList >> $out.rsp; fi && ` +
+ `${config.IncrementalJavacInputCmd} ` +
+ `--srcs $out.rsp --classDir $outDir --deps $javacDeps --javacTarget $out --srcDepsProto $out.proto --localHeaderJars $localHeaderJars && ` +
+ `mkdir -p "$outDir" && ` +
+ `(if [ -s $out.inc.rsp ] ; then ` +
+ `${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
+ `${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
+ `$processorpath $processor $javacFlags $bootClasspath $classpath ` +
+ `-source $javaVersion -target $javaVersion ` +
+ `-d $outDir -s $annoDir @$out.inc.rsp ; fi ) && ` +
+ `cat $out.rem.rsp | xargs rm -f && ` +
+ `$annoSrcJarTemplate${config.SoongZipCmd} -jar -o $annoSrcJar.tmp -C $annoDir -D $annoDir && ` +
+ `$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` +
+ `if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` +
+ `if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` +
+ `if [ -f "$out.input.pc_state.new" ]; then mv "$out.input.pc_state.new" "$out.input.pc_state" && ` +
+ `rm -rf $out.input.pc_state.new; fi && ` +
+ `if [ -f "$out.deps.pc_state.new" ]; then mv "$out.deps.pc_state.new" "$out.deps.pc_state" && ` +
+ `rm -rf $out.deps.pc_state.new; fi && ` +
+ `if [ -f "$out.headers.pc_state.new" ]; then mv "$out.headers.pc_state.new" "$out.headers.pc_state" && ` +
+ `rm -rf $out.headers.pc_state.new; fi && ` +
+ `if [ -f $out.rsp ] && [ -f $out ]; then ` +
+ `${config.DependencyMapperJavacCmd} --src-path $out.rsp --jar-path $out --dependency-map-path $out.proto; fi`,
+ CommandDeps: []string{
+ "${config.DependencyMapperJavacCmd}",
+ "${config.IncrementalJavacInputCmd}",
+ "${config.JavacCmd}",
+ "${config.ZipSyncCmd}",
+ },
+ CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
+ Restat: true,
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ }, map[string]*remoteexec.REParams{
+ "$javaTemplate": &remoteexec.REParams{
+ Labels: map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
+ ExecStrategy: "${config.REJavacExecStrategy}",
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ },
+ "$zipTemplate": &remoteexec.REParams{
+ Labels: map[string]string{"type": "tool", "name": "soong_zip"},
+ Inputs: []string{"${config.SoongZipCmd}", "$outDir"},
+ OutputFiles: []string{"$out.tmp"},
+ ExecStrategy: "${config.REJavacExecStrategy}",
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ },
+ "$annoSrcJarTemplate": &remoteexec.REParams{
+ Labels: map[string]string{"type": "tool", "name": "soong_zip"},
+ Inputs: []string{"${config.SoongZipCmd}", "$annoDir"},
+ OutputFiles: []string{"$annoSrcJar.tmp"},
+ ExecStrategy: "${config.REJavacExecStrategy}",
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ },
+ }, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJarList", "genAnnoSrcJarList",
+ "outDir", "annoDir", "annoSrcJar", "javaVersion", "javacDeps", "localHeaderJars"}, nil)
+
// Compiling java is not conducive to proper dependency tracking. The path-matches-class-name
// requirement leads to unpredictable generated source file names, and a single .java file
// will get compiled into multiple .class files if it contains inner classes. To work around
// this, all java rules write into separate directories and then are combined into a .jar file
// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
// .srcjar files are unzipped into a temporary directory when compiled with javac.
- // TODO(b/143658984): goma can't handle the --system argument to javac.
javac, javacRE = pctx.MultiCommandRemoteStaticRules("javac",
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$annoDir" "$annoSrcJar.tmp" "$srcJarDir" "$out.tmp" && ` +
@@ -312,10 +401,12 @@ var (
blueprint.RuleParams{
Command: `${aconfig} dump-cache --dedup --format=protobuf ` +
`--out ${out} ` +
- `${flags_path} ` +
+ `@$out.rsp ` +
`${filter_args} `,
- CommandDeps: []string{"${aconfig}"},
- Description: "aconfig_bool",
+ CommandDeps: []string{"${aconfig}"},
+ Description: "aconfig_bool",
+ Rspfile: "$out.rsp",
+ RspfileContent: "${flags_path}",
}, "flags_path", "filter_args")
generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule",
@@ -374,9 +465,12 @@ type javaBuilderFlags struct {
errorProneExtraJavacFlags string
errorProneProcessorPath classpath
- kotlincFlags string
- kotlincClasspath classpath
- kotlincDeps android.Paths
+ kotlincFlags string
+ kotlincPluginFlags string
+ composePluginFlag string
+ composeEmbeddablePluginFlag string
+ kotlincClasspath classpath
+ kotlincDeps android.Paths
proto android.ProtoFlags
}
@@ -387,6 +481,14 @@ func DefaultJavaBuilderFlags() javaBuilderFlags {
}
}
+func TransformJavaToClassesInc(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars, headerJars android.Paths, annoSrcJar android.WritablePath, flags javaBuilderFlags, deps android.Paths, genAnnoSrcJar android.Path) {
+
+ // Compile java sources into .class files
+ desc := "javac-inc"
+ transformJavaToClassesInc(ctx, outputFile, srcFiles, srcJars, headerJars, annoSrcJar, flags, deps, "javac", desc, genAnnoSrcJar)
+}
+
func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath, flags javaBuilderFlags, deps android.Paths) {
@@ -395,8 +497,18 @@ func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab
if shardIdx >= 0 {
desc += strconv.Itoa(shardIdx)
}
+ transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, annoSrcJar, false, flags, deps, "javac", desc)
+}
+func GenerateJavaAnnotations(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
+ srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath, flags javaBuilderFlags, deps android.Paths) {
+
+ // Generate src files from Java Annotations.
+ desc := "javac-apt"
+ if shardIdx >= 0 {
+ desc += strconv.Itoa(shardIdx)
+ }
- transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, annoSrcJar, flags, deps, "javac", desc)
+ transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, annoSrcJar, true, flags, deps, "javac-apt", desc)
}
// Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
@@ -588,6 +700,153 @@ func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.Wr
})
}
+// Similar to transformJavaToClasses, with additional tweaks to make java
+// compilation work incrementally (i.e. work with a smaller subset of src java files
+// rather than the full set)
+func transformJavaToClassesInc(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars, shardingHeaderJars android.Paths, annoSrcJar android.WritablePath,
+ flags javaBuilderFlags, deps android.Paths, intermediatesDir, desc string, genAnnoSrcJar android.Path) {
+
+ javacClasspath := flags.classpath
+
+ var bootClasspath string
+ if flags.javaVersion.usesJavaModules() {
+ var systemModuleDeps android.Paths
+ bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
+ deps = append(deps, systemModuleDeps...)
+ javacClasspath = append(flags.java9Classpath, javacClasspath...)
+ } else {
+ deps = append(deps, flags.bootClasspath...)
+ if len(flags.bootClasspath) == 0 && ctx.Device() {
+ // explicitly specify -bootclasspath "" if the bootclasspath is empty to
+ // ensure java does not fall back to the default bootclasspath.
+ bootClasspath = `-bootclasspath ""`
+ } else {
+ bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
+ }
+ }
+
+ deps = append(deps, flags.processorPath...)
+ deps = append(deps, javacClasspath...)
+
+ // The file containing dependencies of the current module
+ // Any change in them may warrant changes in the incremental compilation
+ // source set.
+ javacDeps := outputFile.ReplaceExtension(ctx, "jar.deps.rsp")
+ android.WriteFileRule(ctx, javacDeps, strings.Join(deps.Strings(), "\n"))
+ deps = append(deps, javacDeps)
+
+ // Add localHeader Jars in classpath, they are required for incremental compilation.
+ // These are conspicuously not added to javacDeps file, as a change in them
+ // does not warrant full re-compilation.
+ javacClasspath = append(classpath(slices.Clone(shardingHeaderJars)), javacClasspath...)
+ deps = append(deps, shardingHeaderJars...)
+ shardingHeaderRsp := outputFile.ReplaceExtension(ctx, "jar.headers.rsp")
+ android.WriteFileRule(ctx, shardingHeaderRsp, strings.Join(shardingHeaderJars.Strings(), "\n"))
+ deps = append(deps, shardingHeaderRsp)
+
+ // Doing this now, sow that they are not added to javacDeps file
+ deps = append(deps, srcJars...)
+
+ classpathArg := javacClasspath.FormJavaClassPath("-classpath")
+
+ // Keep the command line under the MAX_ARG_STRLEN limit by putting the classpath argument into an rsp file
+ // if it is too long.
+ const classpathLimit = 64 * 1024
+ if len(classpathArg) > classpathLimit {
+ classpathRspFile := outputFile.ReplaceExtension(ctx, "classpath")
+ android.WriteFileRule(ctx, classpathRspFile, classpathArg)
+ deps = append(deps, classpathRspFile)
+ classpathArg = "@" + classpathRspFile.String()
+ }
+
+ processor := "-proc:none"
+ if len(flags.processors) > 0 {
+ processor = "-processor " + strings.Join(flags.processors, ",")
+ }
+
+ srcJarDir := "srcjars"
+ outDir := "classes"
+ annoDir := "anno"
+ srcJarList := android.PathForModuleOut(ctx, intermediatesDir, srcJarDir, "list")
+ deps = append(deps, srcJarList)
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: extractSrcJars,
+ Description: "javacExtractSrcJars",
+ Inputs: srcJars,
+ Output: srcJarList,
+ Args: map[string]string{
+ "extractDir": android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
+ "jars": strings.Join(srcJars.Strings(), " "),
+ },
+ })
+
+ genAnnoSrcJarList := android.PathForModuleOut(ctx, intermediatesDir, annoDir, "list")
+ deps = append(deps, genAnnoSrcJarList)
+ var jars string
+ if genAnnoSrcJar != nil {
+ jars = genAnnoSrcJar.String()
+ } else {
+ jars = ""
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: extractSrcJars,
+ Description: "javacExtractAnnoSrcJar",
+ Input: genAnnoSrcJar,
+ Output: genAnnoSrcJarList,
+ Args: map[string]string{
+ "extractDir": android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
+ "jars": jars,
+ },
+ })
+
+ rule := javacInc
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rule,
+ Description: desc,
+ Output: outputFile,
+ ImplicitOutput: annoSrcJar,
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: map[string]string{
+ "javacFlags": flags.javacFlags,
+ "bootClasspath": bootClasspath,
+ "classpath": classpathArg,
+ "processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
+ "processor": processor,
+ "srcJarList": srcJarList.String(),
+ "genAnnoSrcJarList": genAnnoSrcJarList.String(),
+ "outDir": android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
+ "annoDir": android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
+ "annoSrcJar": annoSrcJar.String(),
+ "javaVersion": flags.javaVersion.String(),
+ "javacDeps": javacDeps.String(),
+ "localHeaderJars": shardingHeaderRsp.String(),
+ },
+ })
+
+ // The Phony Clean rule allows javac to move from incremental to full, without
+ // re-analysis by removing all the outputs of javac, triggering all rules
+ // that generate them.
+ cleanPhonyPath := android.PathForModuleOut(ctx, "dex", outputFile.String()+"-partialcompileclean").OutputPath
+ // Generate the rule for partial compile clean.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: javacIncClean,
+ Description: "javacIncClean",
+ Output: cleanPhonyPath,
+ Args: map[string]string{
+ "srcJarDir": android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
+ "outDir": android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
+ "annoDir": android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(),
+ "annoSrcJar": annoSrcJar.String(),
+ "builtOut": outputFile.String(),
+ },
+ PhonyOutput: true,
+ })
+ ctx.Phony("partialcompileclean", cleanPhonyPath)
+}
+
// transformJavaToClasses takes source files and converts them to a jar containing .class files.
// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
// sources. flags contains various command line flags to be passed to the compiler.
@@ -597,8 +856,11 @@ func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.Wr
// be printed at build time. The stem argument provides the file name of the output jar, and
// suffix will be appended to various intermediate files and directories to avoid collisions when
// this function is called twice in the same module directory.
+//
+// This method can also be used to only process Annotations, without completely
+// compiling java sources.
func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
- shardIdx int, srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath,
+ shardIdx int, srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath, onlyGenerateAnnotations bool,
flags javaBuilderFlags, deps android.Paths,
intermediatesDir, desc string) {
@@ -640,7 +902,11 @@ func transformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab
processor := "-proc:none"
if len(flags.processors) > 0 {
- processor = "-processor " + strings.Join(flags.processors, ",")
+ if onlyGenerateAnnotations {
+ processor = "-proc:only -processor " + strings.Join(flags.processors, ",")
+ } else {
+ processor = "-processor " + strings.Join(flags.processors, ",")
+ }
}
srcJarDir := "srcjars"
@@ -852,14 +1118,14 @@ func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePat
}
func TransformRavenizer(ctx android.ModuleContext, outputFile android.WritablePath,
- inputFile android.Path, ravenizerArgs string) {
+ inputFile android.Path, ravenizerArgs []string) {
ctx.Build(pctx, android.BuildParams{
Rule: ravenizer,
Description: "ravenizer",
Output: outputFile,
Input: inputFile,
Args: map[string]string{
- "ravenizerArgs": ravenizerArgs,
+ "ravenizerArgs": strings.Join(ravenizerArgs, " "),
},
})
}
@@ -920,6 +1186,10 @@ func (x *classpath) FormRepeatedClassPath(optName string) []string {
return flags
}
+func (x *classpath) FirstUniquePaths() classpath {
+ return classpath(android.FirstUniquePaths(x.Paths()))
+}
+
// Convert a classpath to an android.Paths
func (x *classpath) Paths() android.Paths {
return append(android.Paths(nil), (*x)...)