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)
 	}