Initial device java support
First pass at java support for the device. Adds desugar before
dx, and passes javalib.jar to make.
Test: m -j checkbuild
Change-Id: I3138d943bc4867a52c3fce8fbdb597773793988d
diff --git a/java/androidmk.go b/java/androidmk.go
index 680d864..89d7d51 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -25,13 +25,17 @@
func (library *Library) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(library.outputFile),
+ OutputFile: android.OptionalPathForPath(library.classpathFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
if library.properties.Installable != nil && *library.properties.Installable == false {
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
}
+ if library.dexJarFile != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
+ }
+ fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.deviceProperties.Sdk_version)
},
},
}
@@ -53,7 +57,7 @@
func (binary *Binary) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(binary.outputFile),
+ OutputFile: android.OptionalPathForPath(binary.classpathFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
diff --git a/java/builder.go b/java/builder.go
index cf6ab6b..b8332ad 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -83,12 +83,27 @@
},
"jarArgs")
+ desugar = pctx.AndroidStaticRule("desugar",
+ blueprint.RuleParams{
+ Command: `rm -rf $dumpDir && mkdir -p $dumpDir && ` +
+ `${config.JavaCmd} ` +
+ `-Djdk.internal.lambda.dumpProxyClasses=$$(cd $dumpDir && pwd) ` +
+ `$javaFlags ` +
+ `-jar ${config.DesugarJar} $classpathFlags $desugarFlags ` +
+ `-i $in -o $out`,
+ CommandDeps: []string{"${config.DesugarJar}"},
+ },
+ "javaFlags", "classpathFlags", "desugarFlags", "dumpDir")
+
dx = pctx.AndroidStaticRule("dx",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `${config.DxCmd} --dex --output=$outDir $dxFlags $in || ( rm -rf "$outDir"; exit 41 ) && ` +
- `find "$outDir" -name "classes*.dex" | sort > $out`,
- CommandDeps: []string{"${config.DxCmd}"},
+ `${config.DxCmd} --dex --output=$outDir $dxFlags $in && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+ CommandDeps: []string{
+ "${config.DxCmd}",
+ "${config.SoongZipCmd}",
+ },
},
"outDir", "dxFlags")
@@ -107,8 +122,9 @@
type javaBuilderFlags struct {
javacFlags string
dxFlags string
- bootClasspath string
- classpath string
+ bootClasspath classpath
+ classpath classpath
+ desugarFlags string
aidlFlags string
javaVersion string
}
@@ -131,6 +147,8 @@
javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@")
deps = append(deps, srcFileLists...)
+ deps = append(deps, flags.bootClasspath...)
+ deps = append(deps, flags.classpath...)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: javac,
@@ -140,8 +158,8 @@
Implicits: deps,
Args: map[string]string{
"javacFlags": javacFlags,
- "bootClasspath": flags.bootClasspath,
- "classpath": flags.classpath,
+ "bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()),
+ "classpath": flags.classpath.JavaClasspath(),
"outDir": classDir.String(),
"annoDir": annoDir.String(),
"javaVersion": flags.javaVersion,
@@ -166,6 +184,8 @@
javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@")
deps = append(deps, srcFileLists...)
+ deps = append(deps, flags.bootClasspath...)
+ deps = append(deps, flags.classpath...)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: errorprone,
@@ -175,8 +195,8 @@
Implicits: deps,
Args: map[string]string{
"javacFlags": javacFlags,
- "bootClasspath": flags.bootClasspath,
- "classpath": flags.classpath,
+ "bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()),
+ "classpath": flags.classpath.JavaClasspath(),
"outDir": classDir.String(),
"annoDir": annoDir.String(),
"javaVersion": flags.javaVersion,
@@ -246,11 +266,47 @@
return outputFile
}
-func TransformClassesJarToDex(ctx android.ModuleContext, classesJar android.Path,
- flags javaBuilderFlags) jarSpec {
+func TransformDesugar(ctx android.ModuleContext, classesJar android.Path,
+ flags javaBuilderFlags) android.Path {
+
+ outputFile := android.PathForModuleOut(ctx, "classes-desugar.jar")
+ dumpDir := android.PathForModuleOut(ctx, "desugar_dumped_classes")
+
+ javaFlags := ""
+ if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") != "" {
+ javaFlags = "--add-opens java.base/java.lang.invoke=ALL-UNNAMED"
+ }
+
+ var desugarFlags []string
+ desugarFlags = append(desugarFlags, flags.bootClasspath.DesugarBootClasspath()...)
+ desugarFlags = append(desugarFlags, flags.classpath.DesugarClasspath()...)
+
+ var deps android.Paths
+ deps = append(deps, flags.bootClasspath...)
+ deps = append(deps, flags.classpath...)
+
+ ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+ Rule: desugar,
+ Description: "desugar",
+ Output: outputFile,
+ Input: classesJar,
+ Implicits: deps,
+ Args: map[string]string{
+ "dumpDir": dumpDir.String(),
+ "javaFlags": javaFlags,
+ "classpathFlags": strings.Join(desugarFlags, " "),
+ "desugarFlags": flags.desugarFlags,
+ },
+ })
+
+ return outputFile
+}
+
+func TransformClassesJarToDexJar(ctx android.ModuleContext, classesJar android.Path,
+ flags javaBuilderFlags) android.Path {
outDir := android.PathForModuleOut(ctx, "dex")
- outputFile := android.PathForModuleOut(ctx, "dex.filelist")
+ outputFile := android.PathForModuleOut(ctx, "classes.dex.jar")
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: dx,
@@ -263,34 +319,6 @@
},
})
- return jarSpec{outputFile, outDir}
-}
-
-func TransformDexToJavaLib(ctx android.ModuleContext, resources []jarSpec,
- dexJarSpec jarSpec) android.Path {
-
- outputFile := android.PathForModuleOut(ctx, "javalib.jar")
- var deps android.Paths
- var jarArgs []string
-
- for _, j := range resources {
- deps = append(deps, j.fileList)
- jarArgs = append(jarArgs, j.soongJarArgs())
- }
-
- deps = append(deps, dexJarSpec.fileList)
- jarArgs = append(jarArgs, dexJarSpec.soongJarArgs())
-
- ctx.ModuleBuild(pctx, android.ModuleBuildParams{
- Rule: jar,
- Description: "jar",
- Output: outputFile,
- Implicits: deps,
- Args: map[string]string{
- "jarArgs": strings.Join(jarArgs, " "),
- },
- })
-
return outputFile
}
@@ -309,3 +337,83 @@
return outputFile
}
+
+type classpath []android.Path
+
+// Returns a -classpath argument in the form java or javac expects
+func (x *classpath) JavaClasspath() string {
+ if len(*x) > 0 {
+ return "-classpath " + strings.Join(x.Strings(), ":")
+ } else {
+ return ""
+ }
+}
+
+// Returns a -processorpath argument in the form java or javac expects
+func (x *classpath) JavaProcessorpath() string {
+ if len(*x) > 0 {
+ return "-processorpath " + strings.Join(x.Strings(), ":")
+ } else {
+ return ""
+ }
+}
+
+// Returns a -bootclasspath argument in the form java or javac expects. If forceEmpty is true,
+// returns -bootclasspath "" if the bootclasspath is empty to ensure javac does not fall back to the
+// default bootclasspath.
+func (x *classpath) JavaBootClasspath(forceEmpty bool) string {
+ if len(*x) > 0 {
+ return "-bootclasspath " + strings.Join(x.Strings(), ":")
+ } else if forceEmpty {
+ return `-bootclasspath ""`
+ } else {
+ return ""
+ }
+}
+
+func (x *classpath) DesugarBootClasspath() []string {
+ if x == nil || *x == nil {
+ return nil
+ }
+ flags := make([]string, len(*x))
+ for i, v := range *x {
+ flags[i] = "--bootclasspath_entry " + v.String()
+ }
+
+ return flags
+}
+
+func (x *classpath) DesugarClasspath() []string {
+ if x == nil || *x == nil {
+ return nil
+ }
+ flags := make([]string, len(*x))
+ for i, v := range *x {
+ flags[i] = "--classpath_entry " + v.String()
+ }
+
+ return flags
+}
+
+// Append an android.Paths to the end of the classpath list
+func (x *classpath) AddPaths(paths android.Paths) {
+ for _, path := range paths {
+ *x = append(*x, path)
+ }
+}
+
+// Convert a classpath to an android.Paths
+func (x *classpath) Paths() android.Paths {
+ return append(android.Paths(nil), (*x)...)
+}
+
+func (x *classpath) Strings() []string {
+ if x == nil {
+ return nil
+ }
+ ret := make([]string, len(*x))
+ for i, path := range *x {
+ ret[i] = path.String()
+ }
+ return ret
+}
diff --git a/java/config/config.go b/java/config/config.go
index 69d6fc3..3029a5a 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -68,6 +68,7 @@
pctx.StaticVariable("MergeZipsCmd", filepath.Join("${bootstrap.ToolDir}", "merge_zips"))
pctx.HostBinToolVariable("DxCmd", "dx")
pctx.HostJavaToolVariable("JarjarCmd", "jarjar.jar")
+ pctx.HostJavaToolVariable("DesugarJar", "desugar.jar")
pctx.VariableFunc("JavacWrapper", func(config interface{}) (string, error) {
if override := config.(android.Config).Getenv("JAVAC_WRAPPER"); override != "" {
diff --git a/java/java.go b/java/java.go
index d7b5847..41da3fc 100644
--- a/java/java.go
+++ b/java/java.go
@@ -137,6 +137,12 @@
// output file suitable for inserting into the classpath of another compile
classpathFile android.Path
+ // output file containing classes.dex
+ dexJarFile android.Path
+
+ // output files containing resources
+ resourceJarFiles android.Paths
+
// output file suitable for installing or running
outputFile android.Path
@@ -154,6 +160,7 @@
type Dependency interface {
ClasspathFiles() android.Paths
+ ResourceJarFiles() android.Paths
AidlIncludeDirs() android.Paths
}
@@ -168,12 +175,11 @@
}
var (
- staticLibTag = dependencyTag{name: "staticlib"}
- libTag = dependencyTag{name: "javalib"}
- bootClasspathTag = dependencyTag{name: "bootclasspath"}
- frameworkResTag = dependencyTag{name: "framework-res"}
- sdkDependencyTag = dependencyTag{name: "sdk"}
- annotationProcessorTag = dependencyTag{name: "annotation processor"}
+ staticLibTag = dependencyTag{name: "staticlib"}
+ libTag = dependencyTag{name: "javalib"}
+ bootClasspathTag = dependencyTag{name: "bootclasspath"}
+ frameworkResTag = dependencyTag{name: "framework-res"}
+ sdkDependencyTag = dependencyTag{name: "sdk"}
)
func (j *Module) deps(ctx android.BottomUpMutatorContext) {
@@ -202,7 +208,7 @@
}
ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
- ctx.AddDependency(ctx.Module(), annotationProcessorTag, j.properties.Annotation_processors...)
+ ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
android.ExtractSourcesDeps(ctx, j.properties.Srcs)
}
@@ -230,13 +236,13 @@
}
type deps struct {
- classpath android.Paths
- bootClasspath android.Paths
- staticJars android.Paths
- aidlIncludeDirs android.Paths
- srcFileLists android.Paths
- annotationProcessors android.Paths
- aidlPreprocess android.OptionalPath
+ classpath android.Paths
+ bootClasspath android.Paths
+ staticJars android.Paths
+ staticJarResources android.Paths
+ aidlIncludeDirs android.Paths
+ srcFileLists android.Paths
+ aidlPreprocess android.OptionalPath
}
func (j *Module) collectDeps(ctx android.ModuleContext) deps {
@@ -264,8 +270,7 @@
case staticLibTag:
deps.classpath = append(deps.classpath, dep.ClasspathFiles()...)
deps.staticJars = append(deps.staticJars, dep.ClasspathFiles()...)
- case annotationProcessorTag:
- deps.annotationProcessors = append(deps.annotationProcessors, dep.ClasspathFiles()...)
+ deps.staticJarResources = append(deps.staticJarResources, dep.ResourceJarFiles()...)
case frameworkResTag:
if ctx.ModuleName() == "framework" {
// framework.jar has a one-off dependency on the R.java and Manifest.java files
@@ -306,21 +311,17 @@
javacFlags = config.StripJavac9Flags(javacFlags)
}
- if len(deps.annotationProcessors) > 0 {
- javacFlags = append(javacFlags,
- "-processorpath "+strings.Join(deps.annotationProcessors.Strings(), ":"))
- }
-
- for _, c := range j.properties.Annotation_processor_classes {
- javacFlags = append(javacFlags, "-processor "+c)
- }
-
if j.properties.Java_version != nil {
flags.javaVersion = *j.properties.Java_version
} else {
flags.javaVersion = "${config.DefaultJavaVersion}"
}
+ var extraDeps android.Paths
+
+ flags.bootClasspath.AddPaths(deps.bootClasspath)
+ flags.classpath.AddPaths(deps.classpath)
+
if len(javacFlags) > 0 {
ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
flags.javacFlags = "$javacFlags"
@@ -332,21 +333,6 @@
flags.aidlFlags = "$aidlFlags"
}
- var extraDeps android.Paths
-
- if len(deps.bootClasspath) > 0 {
- flags.bootClasspath = "-bootclasspath " + strings.Join(deps.bootClasspath.Strings(), ":")
- extraDeps = append(extraDeps, deps.bootClasspath...)
- } else if ctx.Device() {
- // Explicitly clear the bootclasspath for device builds
- flags.bootClasspath = `-bootclasspath ""`
- }
-
- if len(deps.classpath) > 0 {
- flags.classpath = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
- extraDeps = append(extraDeps, deps.classpath...)
- }
-
srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
srcFiles = j.genSources(ctx, srcFiles, flags)
@@ -392,9 +378,15 @@
return
}
+ j.resourceJarFiles = append(j.resourceJarFiles, resourceJar)
jars = append(jars, resourceJar)
}
+ // Propagate the resources from the transitive closure of static dependencies for copying
+ // into dex jars
+ j.resourceJarFiles = append(j.resourceJarFiles, deps.staticJarResources...)
+
+ // static classpath jars have the resources in them, so the resource jars aren't necessary here
jars = append(jars, deps.staticJars...)
manifest := android.OptionalPathForModuleSrc(ctx, j.properties.Manifest)
@@ -450,14 +442,39 @@
flags.dxFlags = strings.Join(dxFlags, " ")
- // Compile classes.jar into classes.dex
- dexJarSpec := TransformClassesJarToDex(ctx, outputFile, flags)
+ desugarFlags := []string{
+ "--min_sdk_version " + minSdkVersion,
+ "--desugar_try_with_resources_if_needed=false",
+ "--allow_empty_bootclasspath",
+ }
+
+ if inList("--core-library", dxFlags) {
+ desugarFlags = append(desugarFlags, "--core_library")
+ }
+
+ flags.desugarFlags = strings.Join(desugarFlags, " ")
+
+ desugarJar := TransformDesugar(ctx, outputFile, flags)
if ctx.Failed() {
return
}
- // Combine classes.dex + resources into javalib.jar
- outputFile = TransformDexToJavaLib(ctx, resourceJarSpecs, dexJarSpec)
+ // TODO(ccross): For now, use the desugared jar as the classpath file. Eventually this
+ // might cause problems because desugar wants non-desugared jars in its class path.
+ j.classpathFile = desugarJar
+
+ // Compile classes.jar into classes.dex
+ dexJarFile := TransformClassesJarToDexJar(ctx, desugarJar, flags)
+ if ctx.Failed() {
+ return
+ }
+
+ jars := android.Paths{dexJarFile}
+ jars = append(jars, j.resourceJarFiles...)
+
+ outputFile = TransformJarsToJar(ctx, "javalib.jar", jars, android.OptionalPath{}, true)
+
+ j.dexJarFile = outputFile
}
ctx.CheckbuildFile(outputFile)
j.outputFile = outputFile
@@ -469,6 +486,10 @@
return android.Paths{j.classpathFile}
}
+func (j *Module) ResourceJarFiles() android.Paths {
+ return j.resourceJarFiles
+}
+
func (j *Module) AidlIncludeDirs() android.Paths {
return j.exportAidlIncludeDirs
}
@@ -625,6 +646,11 @@
return j.classpathFiles
}
+func (j *Import) ResourceJarFiles() android.Paths {
+ // resources are in the ClasspathFiles
+ return nil
+}
+
func (j *Import) AidlIncludeDirs() android.Paths {
return nil
}
diff --git a/java/java_test.go b/java/java_test.go
index ab5af0a..632ad75 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -119,8 +119,8 @@
t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
}
- bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-compiled.jar")
- baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-compiled.jar")
+ bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-desugar.jar")
+ baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-desugar.jar")
if !strings.Contains(javac.Args["classpath"], bar) {
t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
@@ -182,7 +182,7 @@
check := func(module string, depType depType, deps ...string) {
for i := range deps {
- deps[i] = filepath.Join(buildDir, ".intermediates", deps[i], "classes-compiled.jar")
+ deps[i] = filepath.Join(buildDir, ".intermediates", deps[i], "classes-desugar.jar")
}
dep := strings.Join(deps, ":")
@@ -279,12 +279,12 @@
t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
}
- bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-compiled.jar")
+ bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-desugar.jar")
if !strings.Contains(javac.Args["classpath"], bar) {
t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
}
- baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-compiled.jar")
+ baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-desugar.jar")
if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
}