Add java_genrule_combiner module

This module is intended to combine an implemenation generated by
java_genrule with headers from the rule's inputs.

Bug: b/285975842
Test: manual, TH
Change-Id: I9d83634ad7fa56fe5e0a6363d2891d34ac58257e
diff --git a/java/genrule_combiner_test.go b/java/genrule_combiner_test.go
new file mode 100644
index 0000000..a458952
--- /dev/null
+++ b/java/genrule_combiner_test.go
@@ -0,0 +1,237 @@
+// Copyright 2018 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 (
+	"reflect"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestJarGenruleCombinerSingle(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := prepareForJavaTest.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_genrule {
+			name: "gen",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen.jar"],
+			srcs: [":foo"],
+		}
+
+		java_genrule_combiner {
+			name: "jarcomb",
+			static_libs: ["gen"],
+			headers: ["foo"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+	`).TestContext
+
+	fooMod := ctx.ModuleForTests("foo", "android_common")
+	fooCombined := fooMod.Output("turbine-combined/foo.jar")
+	fooOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), fooMod.Module(), android.OutputFilesProvider)
+	fooHeaderJars := fooOutputFiles.TaggedOutputFiles[".hjar"]
+
+	genMod := ctx.ModuleForTests("gen", "android_common")
+	gen := genMod.Output("gen.jar")
+
+	jarcombMod := ctx.ModuleForTests("jarcomb", "android_common")
+	jarcombInfo, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), JavaInfoProvider)
+	jarcombOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), android.OutputFilesProvider)
+
+	// Confirm that jarcomb simply forwards the jarcomb implementation and the foo headers.
+	if len(jarcombOutputFiles.DefaultOutputFiles) != 1 ||
+		android.PathRelativeToTop(jarcombOutputFiles.DefaultOutputFiles[0]) != android.PathRelativeToTop(gen.Output) {
+		t.Errorf("jarcomb Implementation %v is not [%q]",
+			android.PathsRelativeToTop(jarcombOutputFiles.DefaultOutputFiles), android.PathRelativeToTop(gen.Output))
+	}
+	jarcombHeaderJars := jarcombOutputFiles.TaggedOutputFiles[".hjar"]
+	if !reflect.DeepEqual(jarcombHeaderJars, fooHeaderJars) {
+		t.Errorf("jarcomb Header jar %v is not %q",
+			jarcombHeaderJars, fooHeaderJars)
+	}
+
+	// Confirm that JavaInfoProvider agrees.
+	if len(jarcombInfo.ImplementationJars) != 1 ||
+		android.PathRelativeToTop(jarcombInfo.ImplementationJars[0]) != android.PathRelativeToTop(gen.Output) {
+		t.Errorf("jarcomb ImplementationJars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombInfo.ImplementationJars), android.PathRelativeToTop(gen.Output))
+	}
+	if len(jarcombInfo.HeaderJars) != 1 ||
+		android.PathRelativeToTop(jarcombInfo.HeaderJars[0]) != android.PathRelativeToTop(fooCombined.Output) {
+		t.Errorf("jarcomb HeaderJars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombInfo.HeaderJars), android.PathRelativeToTop(fooCombined.Output))
+	}
+
+	barMod := ctx.ModuleForTests("bar", "android_common")
+	bar := barMod.Output("javac/bar.jar")
+	barCombined := barMod.Output("combined/bar.jar")
+
+	// Confirm that bar uses the Implementation from gen and headerJars from foo.
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != gen.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), gen.Output.String())
+	}
+
+	bazMod := ctx.ModuleForTests("baz", "android_common")
+	baz := bazMod.Output("javac/baz.jar")
+
+	string_in_list := func(s string, l []string) bool {
+		for _, v := range l {
+			if s == v {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Confirm that baz uses the headerJars from foo.
+	bazImplicitsRel := android.PathsRelativeToTop(baz.Implicits)
+	for _, v := range android.PathsRelativeToTop(fooHeaderJars) {
+		if !string_in_list(v, bazImplicitsRel) {
+			t.Errorf("baz Implicits %v does not contain %q", bazImplicitsRel, v)
+		}
+	}
+}
+
+func TestJarGenruleCombinerMulti(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := prepareForJavaTest.RunTestWithBp(t, `
+		java_library {
+			name: "foo1",
+			srcs: ["foo1_a.java"],
+		}
+
+		java_library {
+			name: "foo2",
+			srcs: ["foo2_a.java"],
+		}
+
+		java_genrule {
+			name: "gen1",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen1.jar"],
+			srcs: [":foo1"],
+		}
+
+		java_genrule {
+			name: "gen2",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen2.jar"],
+			srcs: [":foo2"],
+		}
+
+		// Combine multiple java_genrule modules.
+		java_genrule_combiner {
+			name: "jarcomb",
+			static_libs: ["gen1", "gen2"],
+			headers: ["foo1", "foo2"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+	`).TestContext
+
+	gen1Mod := ctx.ModuleForTests("gen1", "android_common")
+	gen1 := gen1Mod.Output("gen1.jar")
+	gen2Mod := ctx.ModuleForTests("gen2", "android_common")
+	gen2 := gen2Mod.Output("gen2.jar")
+
+	jarcombMod := ctx.ModuleForTests("jarcomb", "android_common")
+	jarcomb := jarcombMod.Output("combined/jarcomb.jar")
+	jarcombTurbine := jarcombMod.Output("turbine-combined/jarcomb.jar")
+	_ = jarcombTurbine
+	jarcombInfo, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), JavaInfoProvider)
+	_ = jarcombInfo
+	jarcombOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), android.OutputFilesProvider)
+	jarcombHeaderJars := jarcombOutputFiles.TaggedOutputFiles[".hjar"]
+
+	if len(jarcomb.Inputs) != 2 ||
+		jarcomb.Inputs[0].String() != gen1.Output.String() ||
+		jarcomb.Inputs[1].String() != gen2.Output.String() {
+		t.Errorf("jarcomb inputs %v are not [%q, %q]",
+			jarcomb.Inputs.Strings(), gen1.Output.String(), gen2.Output.String())
+	}
+
+	if len(jarcombHeaderJars) != 1 ||
+		android.PathRelativeToTop(jarcombHeaderJars[0]) != android.PathRelativeToTop(jarcombTurbine.Output) {
+		t.Errorf("jarcomb Header jars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombHeaderJars), android.PathRelativeToTop(jarcombTurbine.Output))
+	}
+
+	barMod := ctx.ModuleForTests("bar", "android_common")
+	bar := barMod.Output("javac/bar.jar")
+	barCombined := barMod.Output("combined/bar.jar")
+
+	// Confirm that bar uses the Implementation and Headers from jarcomb.
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != jarcomb.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), jarcomb.Output.String())
+	}
+
+	bazMod := ctx.ModuleForTests("baz", "android_common")
+	baz := bazMod.Output("javac/baz.jar")
+
+	string_in_list := func(s string, l []string) bool {
+		for _, v := range l {
+			if s == v {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Confirm that baz uses the headerJars from foo.
+	bazImplicitsRel := android.PathsRelativeToTop(baz.Implicits)
+	for _, v := range android.PathsRelativeToTop(jarcombHeaderJars) {
+		if !string_in_list(v, bazImplicitsRel) {
+			t.Errorf("baz Implicits %v does not contain %q", bazImplicitsRel, v)
+		}
+	}
+}