Support Turbine in Soong.

If sdk jars(android_stubs_current, etc) are compiled using soong java
modules, we have to filter them when running Java build with Turbine.

TODO: provide more unit-tests.

Test: m clean && m -j32; go test java_test

Change-Id: Iad7c241b0e8b0ca760950733f513124b56c84564
diff --git a/java/java.go b/java/java.go
index 6973915..0cd99eb 100644
--- a/java/java.go
+++ b/java/java.go
@@ -160,8 +160,11 @@
 	protoProperties  android.ProtoProperties
 	deviceProperties CompilerDeviceProperties
 
-	// output file suitable for inserting into the classpath of another compile
-	classpathFile android.Path
+	// header jar file suitable for inserting into the bootclasspath/classpath of another compile
+	headerJarFile android.Path
+
+	// full implementation jar file suitable for static dependency of another module compile
+	implementationJarFile android.Path
 
 	// output file containing classes.dex
 	dexJarFile android.Path
@@ -182,7 +185,8 @@
 }
 
 type Dependency interface {
-	ClasspathFiles() android.Paths
+	HeaderJars() android.Paths
+	ImplementationJars() android.Paths
 	AidlIncludeDirs() android.Paths
 }
 
@@ -379,6 +383,7 @@
 	classpath          android.Paths
 	bootClasspath      android.Paths
 	staticJars         android.Paths
+	staticHeaderJars   android.Paths
 	staticJarResources android.Paths
 	aidlIncludeDirs    android.Paths
 	srcJars            android.Paths
@@ -394,6 +399,7 @@
 	if sdkDep.invalidVersion {
 		ctx.AddMissingDependencies([]string{sdkDep.module})
 	} else if sdkDep.useFiles {
+		// sdkDep.jar is actually equivalent to turbine header.jar.
 		deps.classpath = append(deps.classpath, sdkDep.jar)
 		deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, sdkDep.aidl)
 	}
@@ -424,12 +430,13 @@
 
 		switch tag {
 		case bootClasspathTag:
-			deps.bootClasspath = append(deps.bootClasspath, dep.ClasspathFiles()...)
+			deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
 		case libTag:
-			deps.classpath = append(deps.classpath, dep.ClasspathFiles()...)
+			deps.classpath = append(deps.classpath, dep.HeaderJars()...)
 		case staticLibTag:
-			deps.classpath = append(deps.classpath, dep.ClasspathFiles()...)
-			deps.staticJars = append(deps.staticJars, dep.ClasspathFiles()...)
+			deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+			deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
+			deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
 		case frameworkResTag:
 			if ctx.ModuleName() == "framework" {
 				// framework.jar has a one-off dependency on the R.java and Manifest.java files
@@ -437,7 +444,7 @@
 				// TODO(ccross): aapt java files should go in a src jar
 			}
 		case kotlinStdlibTag:
-			deps.kotlinStdlib = dep.ClasspathFiles()
+			deps.kotlinStdlib = dep.HeaderJars()
 		default:
 			panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 		}
@@ -448,20 +455,22 @@
 	return deps
 }
 
-func (j *Module) compile(ctx android.ModuleContext) {
-
-	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Export_aidl_include_dirs)
-
-	deps := j.collectDeps(ctx)
+func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
 
 	var flags javaBuilderFlags
 
+	// javac flags.
 	javacFlags := j.properties.Javacflags
 	if ctx.AConfig().TargetOpenJDK9() {
 		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
-		j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
+	}
+	if len(javacFlags) > 0 {
+		// optimization.
+		ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
+		flags.javacFlags = "$javacFlags"
 	}
 
+	// javaVersion flag.
 	sdk := sdkStringToNumber(ctx, j.deviceProperties.Sdk_version)
 	if j.properties.Java_version != nil {
 		flags.javaVersion = *j.properties.Java_version
@@ -473,35 +482,43 @@
 		flags.javaVersion = "1.9"
 	}
 
+	// classpath
 	flags.bootClasspath.AddPaths(deps.bootClasspath)
 	flags.classpath.AddPaths(deps.classpath)
-
+	// systemModules
 	if deps.systemModules != nil {
 		flags.systemModules = append(flags.systemModules, deps.systemModules)
 	}
 
-	if len(javacFlags) > 0 {
-		ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
-		flags.javacFlags = "$javacFlags"
-	}
-
+	// aidl flags.
 	aidlFlags := j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
 	if len(aidlFlags) > 0 {
+		// optimization.
 		ctx.Variable(pctx, "aidlFlags", strings.Join(aidlFlags, " "))
 		flags.aidlFlags = "$aidlFlags"
 	}
 
-	srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
+	return flags
+}
 
+func (j *Module) compile(ctx android.ModuleContext) {
+
+	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Export_aidl_include_dirs)
+
+	deps := j.collectDeps(ctx)
+	flags := j.collectBuilderFlags(ctx, deps)
+
+	if ctx.AConfig().TargetOpenJDK9() {
+		j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
+	}
+	srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
 	if hasSrcExt(srcFiles.Strings(), ".proto") {
 		flags = protoFlags(ctx, &j.protoProperties, flags)
 	}
 
 	var srcJars classpath
 	srcFiles, srcJars = j.genSources(ctx, srcFiles, flags)
-
 	srcJars = append(srcJars, deps.srcJars...)
-
 	srcJars = append(srcJars, j.ExtraSrcJars...)
 
 	var jars android.Paths
@@ -534,7 +551,27 @@
 		jars = append(jars, deps.kotlinStdlib...)
 	}
 
-	if javaSrcFiles := srcFiles.FilterByExt(".java"); len(javaSrcFiles) > 0 {
+	javaSrcFiles := srcFiles.FilterByExt(".java")
+	var uniqueSrcFiles android.Paths
+	set := make(map[string]bool)
+	for _, v := range javaSrcFiles {
+		if _, found := set[v.String()]; !found {
+			set[v.String()] = true
+			uniqueSrcFiles = append(uniqueSrcFiles, v)
+		}
+	}
+
+	if ctx.Device() && !ctx.AConfig().IsEnvFalse("TURBINE_ENABLED") {
+		// If sdk jar is java module, then directly return classesJar as header.jar
+		if j.Name() != "android_stubs_current" && j.Name() != "android_system_stubs_current" &&
+			j.Name() != "android_test_stubs_current" {
+			j.headerJarFile = j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName)
+			if ctx.Failed() {
+				return
+			}
+		}
+	}
+	if len(uniqueSrcFiles) > 0 {
 		var extraJarDeps android.Paths
 		if ctx.AConfig().IsEnvTrue("RUN_ERROR_PRONE") {
 			// If error-prone is enabled, add an additional rule to compile the java files into
@@ -591,7 +628,7 @@
 	manifest := android.OptionalPathForModuleSrc(ctx, j.properties.Manifest)
 
 	// Combine the classes built from sources, any manifests, and any static libraries into
-	// classes.jar.  If there is only one input jar this step will be skipped.
+	// classes.jar. If there is only one input jar this step will be skipped.
 	var outputFile android.Path
 
 	if len(jars) == 1 && !manifest.Valid() {
@@ -599,7 +636,7 @@
 		outputFile = jars[0]
 	} else {
 		combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
-		TransformJarsToJar(ctx, combinedJar, jars, manifest, false)
+		TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest, false, nil)
 		outputFile = combinedJar
 	}
 
@@ -613,86 +650,143 @@
 			return
 		}
 	}
-
-	j.classpathFile = outputFile
+	j.implementationJarFile = outputFile
+	if j.headerJarFile == nil {
+		j.headerJarFile = j.implementationJarFile
+	}
 
 	if ctx.Device() && j.installable() {
-		dxFlags := j.deviceProperties.Dxflags
-		if false /* emma enabled */ {
-			// If you instrument class files that have local variable debug information in
-			// them emma does not correctly maintain the local variable table.
-			// This will cause an error when you try to convert the class files for Android.
-			// The workaround here is to build different dex file here based on emma switch
-			// then later copy into classes.dex. When emma is on, dx is run with --no-locals
-			// option to remove local variable information
-			dxFlags = append(dxFlags, "--no-locals")
-		}
-
-		if ctx.AConfig().Getenv("NO_OPTIMIZE_DX") != "" {
-			dxFlags = append(dxFlags, "--no-optimize")
-		}
-
-		if ctx.AConfig().Getenv("GENERATE_DEX_DEBUG") != "" {
-			dxFlags = append(dxFlags,
-				"--debug",
-				"--verbose",
-				"--dump-to="+android.PathForModuleOut(ctx, "classes.lst").String(),
-				"--dump-width=1000")
-		}
-
-		var minSdkVersion string
-		switch j.deviceProperties.Sdk_version {
-		case "", "current", "test_current", "system_current":
-			minSdkVersion = strconv.Itoa(ctx.AConfig().DefaultAppTargetSdkInt())
-		default:
-			minSdkVersion = j.deviceProperties.Sdk_version
-		}
-
-		dxFlags = append(dxFlags, "--min-sdk-version="+minSdkVersion)
-
-		flags.dxFlags = strings.Join(dxFlags, " ")
-
-		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 := android.PathForModuleOut(ctx, "desugar", jarName)
-		TransformDesugar(ctx, desugarJar, outputFile, flags)
-		outputFile = desugarJar
+		outputFile = j.compileDex(ctx, flags, outputFile, jarName)
 		if ctx.Failed() {
 			return
 		}
-
-		// Compile classes.jar into classes.dex and then a dex jar
-		dexJar := android.PathForModuleOut(ctx, "dex", jarName)
-		TransformClassesJarToDexJar(ctx, dexJar, desugarJar, flags)
-		outputFile = dexJar
-		if ctx.Failed() {
-			return
-		}
-
-		j.dexJarFile = outputFile
 	}
 	ctx.CheckbuildFile(outputFile)
 	j.outputFile = outputFile
 }
 
+func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles android.Paths, srcJars classpath,
+	deps deps, flags javaBuilderFlags, jarName string) android.Path {
+
+	var jars android.Paths
+	if len(srcFiles) > 0 {
+		// Compile java sources into turbine.jar.
+		turbineJar := android.PathForModuleOut(ctx, "turbine", jarName)
+		TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags)
+		if ctx.Failed() {
+			return nil
+		}
+		jars = append(jars, turbineJar)
+	}
+
+	// Combine any static header libraries into classes-header.jar. If there is only
+	// one input jar this step will be skipped.
+	var headerJar android.Path
+	jars = append(jars, deps.staticHeaderJars...)
+
+	if len(jars) == 0 {
+		panic("The turbine.jar is empty without any sources and static libs.")
+	} else {
+		// we cannot skip the combine step for now if there is only one jar
+		// since we have to strip META-INF/TRANSITIVE dir from turbine.jar
+		combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{}, false, []string{"META-INF"})
+		headerJar = combinedJar
+	}
+
+	if j.properties.Jarjar_rules != nil {
+		jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
+		// Transform classes.jar into classes-jarjar.jar
+		jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName)
+		TransformJarJar(ctx, jarjarFile, headerJar, jarjar_rules)
+		headerJar = jarjarFile
+		if ctx.Failed() {
+			return nil
+		}
+	}
+
+	return headerJar
+}
+
+func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
+	classesJar android.Path, jarName string) android.Path {
+
+	dxFlags := j.deviceProperties.Dxflags
+	if false /* emma enabled */ {
+		// If you instrument class files that have local variable debug information in
+		// them emma does not correctly maintain the local variable table.
+		// This will cause an error when you try to convert the class files for Android.
+		// The workaround here is to build different dex file here based on emma switch
+		// then later copy into classes.dex. When emma is on, dx is run with --no-locals
+		// option to remove local variable information
+		dxFlags = append(dxFlags, "--no-locals")
+	}
+
+	if ctx.AConfig().Getenv("NO_OPTIMIZE_DX") != "" {
+		dxFlags = append(dxFlags, "--no-optimize")
+	}
+
+	if ctx.AConfig().Getenv("GENERATE_DEX_DEBUG") != "" {
+		dxFlags = append(dxFlags,
+			"--debug",
+			"--verbose",
+			"--dump-to="+android.PathForModuleOut(ctx, "classes.lst").String(),
+			"--dump-width=1000")
+	}
+
+	var minSdkVersion string
+	switch j.deviceProperties.Sdk_version {
+	case "", "current", "test_current", "system_current":
+		minSdkVersion = strconv.Itoa(ctx.AConfig().DefaultAppTargetSdkInt())
+	default:
+		minSdkVersion = j.deviceProperties.Sdk_version
+	}
+
+	dxFlags = append(dxFlags, "--min-sdk-version="+minSdkVersion)
+
+	flags.dxFlags = strings.Join(dxFlags, " ")
+
+	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 := android.PathForModuleOut(ctx, "desugar", jarName)
+	TransformDesugar(ctx, desugarJar, classesJar, flags)
+	if ctx.Failed() {
+		return nil
+	}
+
+	// Compile classes.jar into classes.dex and then javalib.jar
+	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
+	TransformClassesJarToDexJar(ctx, javalibJar, desugarJar, flags)
+	if ctx.Failed() {
+		return nil
+	}
+
+	j.dexJarFile = javalibJar
+	return javalibJar
+}
+
 func (j *Module) installable() bool {
 	return j.properties.Installable == nil || *j.properties.Installable
 }
 
 var _ Dependency = (*Library)(nil)
 
-func (j *Module) ClasspathFiles() android.Paths {
-	return android.Paths{j.classpathFile}
+func (j *Module) HeaderJars() android.Paths {
+	return android.Paths{j.headerJarFile}
+}
+
+func (j *Module) ImplementationJars() android.Paths {
+	return android.Paths{j.implementationJarFile}
 }
 
 func (j *Module) AidlIncludeDirs() android.Paths {
@@ -850,13 +944,17 @@
 	j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
 
 	outputFile := android.PathForModuleOut(ctx, "classes.jar")
-	TransformJarsToJar(ctx, outputFile, j.classpathFiles, android.OptionalPath{}, false)
+	TransformJarsToJar(ctx, outputFile, "for prebuilts", j.classpathFiles, android.OptionalPath{}, false, nil)
 	j.combinedClasspathFile = outputFile
 }
 
 var _ Dependency = (*Import)(nil)
 
-func (j *Import) ClasspathFiles() android.Paths {
+func (j *Import) HeaderJars() android.Paths {
+	return j.classpathFiles
+}
+
+func (j *Import) ImplementationJars() android.Paths {
 	return j.classpathFiles
 }