Add java_genrules to use jars as inputs and outputs

Add a java_genrule that has the right multilib flags to be a
dependency of a java rule.  Make java libraries implement
SourceFileProducer so that their classes jar can be used as an
input to a java_genrule.  Allow libs and static_libs dependencies
to be a java_genrule.

Test: TestJarGenrules
Change-Id: Ib1b31ef9c0b7e72eeed2c9ecc4ce8a1088e0b1c9
diff --git a/java/genrule.go b/java/genrule.go
new file mode 100644
index 0000000..80b7030
--- /dev/null
+++ b/java/genrule.go
@@ -0,0 +1,35 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"android/soong/genrule"
+)
+
+func init() {
+	android.RegisterModuleType("java_genrule", genRuleFactory)
+}
+
+// java_genrule is a genrule that can depend on other java_* objects.
+// The cmd may be run multiple times, once for each of the different host/device
+// variations.
+func genRuleFactory() android.Module {
+	module := genrule.NewGenRule()
+
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+
+	return module
+}
diff --git a/java/java.go b/java/java.go
index 4355200..0a47db0 100644
--- a/java/java.go
+++ b/java/java.go
@@ -211,6 +211,12 @@
 	compiledSrcJars  android.Paths
 }
 
+func (j *Module) Srcs() android.Paths {
+	return android.Paths{j.implementationJarFile}
+}
+
+var _ android.SourceFileProducer = (*Module)(nil)
+
 type Dependency interface {
 	HeaderJars() android.Paths
 	ImplementationJars() android.Paths
@@ -446,6 +452,15 @@
 	kotlinStdlib       android.Paths
 }
 
+func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
+	for _, f := range dep.Srcs() {
+		if f.Ext() != ".jar" {
+			ctx.ModuleErrorf("genrule %q must generate files ending with .jar to be used as a libs or static_libs dependency",
+				ctx.OtherModuleName(dep.(blueprint.Module)))
+		}
+	}
+}
+
 func (j *Module) collectDeps(ctx android.ModuleContext) deps {
 	var deps deps
 
@@ -462,8 +477,46 @@
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
-		dep, _ := module.(Dependency)
-		if dep == nil {
+		switch dep := module.(type) {
+		case Dependency:
+			switch tag {
+			case bootClasspathTag:
+				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
+			case libTag:
+				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+			case staticLibTag:
+				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
+					// generated by framework-res.apk
+					deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar)
+				}
+			case kotlinStdlibTag:
+				deps.kotlinStdlib = dep.HeaderJars()
+			default:
+				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
+			}
+
+			deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
+		case android.SourceFileProducer:
+			switch tag {
+			case libTag:
+				checkProducesJars(ctx, dep)
+				deps.classpath = append(deps.classpath, dep.Srcs()...)
+			case staticLibTag:
+				checkProducesJars(ctx, dep)
+				deps.classpath = append(deps.classpath, dep.Srcs()...)
+				deps.staticJars = append(deps.staticJars, dep.Srcs()...)
+				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
+			case android.DefaultsDepTag, android.SourceDepTag:
+				// Nothing to do
+			default:
+				ctx.ModuleErrorf("dependency on genrule %q may only be in srcs, libs, or static_libs", otherName)
+			}
+		default:
 			switch tag {
 			case android.DefaultsDepTag, android.SourceDepTag:
 				// Nothing to do
@@ -479,31 +532,7 @@
 			default:
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
-			return
 		}
-
-		switch tag {
-		case bootClasspathTag:
-			deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
-		case libTag:
-			deps.classpath = append(deps.classpath, dep.HeaderJars()...)
-		case staticLibTag:
-			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
-				// generated by framework-res.apk
-				deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar)
-			}
-		case kotlinStdlibTag:
-			deps.kotlinStdlib = dep.HeaderJars()
-		default:
-			panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
-		}
-
-		deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
 	})
 
 	return deps
diff --git a/java/java_test.go b/java/java_test.go
index dbecc70..58b27c3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -67,6 +67,7 @@
 	ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
 	ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
 	ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory))
+	ctx.RegisterModuleType("java_genrule", android.ModuleFactoryAdaptor(genRuleFactory))
 	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(genrule.FileGroupFactory))
 	ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory))
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
@@ -775,6 +776,60 @@
 	}
 }
 
+func TestJarGenrules(t *testing.T) {
+	ctx := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_genrule {
+			name: "jargen",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["jargen.jar"],
+			srcs: [":foo"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jargen"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jargen"],
+			srcs: ["c.java"],
+		}
+	`)
+
+	foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
+	jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
+	bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
+	baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
+	barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
+
+	if len(jargen.Inputs) != 1 || jargen.Inputs[0].String() != foo.Output.String() {
+		t.Errorf("expected jargen inputs [%q], got %q", foo.Output.String(), jargen.Inputs.Strings())
+	}
+
+	if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
+		t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String())
+	}
+
+	if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) {
+		t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String())
+	}
+
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != jargen.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String())
+	}
+}
+
 func fail(t *testing.T, errs []error) {
 	if len(errs) > 0 {
 		for _, err := range errs {