Merge "Update pom2mk to allow duplicate module names if rewritten"
diff --git a/Android.bp b/Android.bp
index 3e09add..643e8df 100644
--- a/Android.bp
+++ b/Android.bp
@@ -218,6 +218,7 @@
         "java/app.go",
         "java/builder.go",
         "java/gen.go",
+        "java/genrule.go",
         "java/jacoco.go",
         "java/java.go",
         "java/proto.go",
diff --git a/android/arch.go b/android/arch.go
index 7f9abc6..af3919c 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1072,19 +1072,30 @@
 	return ret
 }
 
+func preferTargets(targets []Target, filters ...string) []Target {
+	for _, filter := range filters {
+		buildTargets := filterMultilibTargets(targets, filter)
+		if len(buildTargets) > 0 {
+			return buildTargets
+		}
+	}
+	return nil
+}
+
 // Use the module multilib setting to select one or more targets from a target list
 func decodeMultilib(multilib string, targets []Target, prefer32 bool) ([]Target, error) {
 	buildTargets := []Target{}
-	if multilib == "first" {
-		if prefer32 {
-			multilib = "prefer32"
-		} else {
-			multilib = "prefer64"
-		}
-	}
+
 	switch multilib {
 	case "common":
-		buildTargets = append(buildTargets, getCommonTargets(targets)...)
+		buildTargets = getCommonTargets(targets)
+	case "common_first":
+		buildTargets = getCommonTargets(targets)
+		if prefer32 {
+			buildTargets = append(buildTargets, preferTargets(targets, "lib32", "lib64")...)
+		} else {
+			buildTargets = append(buildTargets, preferTargets(targets, "lib64", "lib32")...)
+		}
 	case "both":
 		if prefer32 {
 			buildTargets = append(buildTargets, filterMultilibTargets(targets, "lib32")...)
@@ -1097,16 +1108,14 @@
 		buildTargets = filterMultilibTargets(targets, "lib32")
 	case "64":
 		buildTargets = filterMultilibTargets(targets, "lib64")
+	case "first":
+		if prefer32 {
+			buildTargets = preferTargets(targets, "lib32", "lib64")
+		} else {
+			buildTargets = preferTargets(targets, "lib64", "lib32")
+		}
 	case "prefer32":
-		buildTargets = filterMultilibTargets(targets, "lib32")
-		if len(buildTargets) == 0 {
-			buildTargets = filterMultilibTargets(targets, "lib64")
-		}
-	case "prefer64":
-		buildTargets = filterMultilibTargets(targets, "lib64")
-		if len(buildTargets) == 0 {
-			buildTargets = filterMultilibTargets(targets, "lib32")
-		}
+		buildTargets = preferTargets(targets, "lib32", "lib64")
 	default:
 		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", or "prefer32" found %q`,
 			multilib)
diff --git a/android/module.go b/android/module.go
index e6766a3..865764f 100644
--- a/android/module.go
+++ b/android/module.go
@@ -248,10 +248,11 @@
 type Multilib string
 
 const (
-	MultilibBoth    Multilib = "both"
-	MultilibFirst   Multilib = "first"
-	MultilibCommon  Multilib = "common"
-	MultilibDefault Multilib = ""
+	MultilibBoth        Multilib = "both"
+	MultilibFirst       Multilib = "first"
+	MultilibCommon      Multilib = "common"
+	MultilibCommonFirst Multilib = "common_first"
+	MultilibDefault     Multilib = ""
 )
 
 type HostOrDeviceSupported int
diff --git a/android/paths.go b/android/paths.go
index e0cbd21..80863c9 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -92,7 +92,7 @@
 
 	// Rel returns the portion of the path relative to the directory it was created from.  For
 	// example, Rel on a PathsForModuleSrc would return the path relative to the module source
-	// directory.
+	// directory, and OutputPath.Join("foo").Rel() would return "foo".
 	Rel() string
 }
 
@@ -456,6 +456,12 @@
 	return p.path
 }
 
+func (p basePath) withRel(rel string) basePath {
+	p.path = filepath.Join(p.path, rel)
+	p.rel = rel
+	return p
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
@@ -463,6 +469,11 @@
 
 var _ Path = SourcePath{}
 
+func (p SourcePath) withRel(rel string) SourcePath {
+	p.basePath = p.basePath.withRel(rel)
+	return p
+}
+
 // safePathForSource is for paths that we expect are safe -- only for use by go
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, path string) SourcePath {
@@ -589,7 +600,7 @@
 // provided paths... may not use '..' to escape from the current path.
 func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
 	path := validatePath(ctx, paths...)
-	return PathForSource(ctx, p.path, path)
+	return p.withRel(path)
 }
 
 // OverlayPath returns the overlay for `path' if it exists. This assumes that the
@@ -631,8 +642,7 @@
 }
 
 func (p OutputPath) withRel(rel string) OutputPath {
-	p.basePath.path = filepath.Join(p.basePath.path, rel)
-	p.basePath.rel = rel
+	p.basePath = p.basePath.withRel(rel)
 	return p
 }
 
@@ -660,7 +670,7 @@
 // provided paths... may not use '..' to escape from the current path.
 func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
 	path := validatePath(ctx, paths...)
-	return PathForOutput(ctx, p.path, path)
+	return p.withRel(path)
 }
 
 // PathForIntermediates returns an OutputPath representing the top-level
diff --git a/android/testing.go b/android/testing.go
index 1c0fac1..ae012b0 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -136,6 +136,7 @@
 }
 
 func (m TestingModule) Output(file string) BuildParams {
+	var searchedOutputs []string
 	for _, p := range m.module.BuildParamsForTests() {
 		outputs := append(WritablePaths(nil), p.Outputs...)
 		if p.Output != nil {
@@ -145,7 +146,9 @@
 			if f.String() == file || f.Rel() == file {
 				return p
 			}
+			searchedOutputs = append(searchedOutputs, f.Rel())
 		}
 	}
-	panic(fmt.Errorf("couldn't find output %q", file))
+	panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
+		file, searchedOutputs))
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index fb71e6a..a24b60c 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -131,7 +131,6 @@
 		"frameworks/native/libs/vr/libbufferhubqueue/",
 		"frameworks/native/libs/vr/libdvr/tests/",
 		"frameworks/native/services/surfaceflinger/tests/",
-		"frameworks/native/services/vr/",
 		"vendor/",
 	}
 
@@ -139,20 +138,15 @@
 	WarningAllowedOldProjects = []string{
 		"cts/hostsidetests/security/securityPatch/",
 		"cts/tests/tests/permission/jni/",
-		"development/tutorials/ReverseDebug/",
 		"frameworks/av/drm/mediacas/plugins/",
 		"frameworks/av/services/mediaextractor/",
-		"frameworks/base/core/tests/webkit/apk_with_native_libs/jni/",
 		"frameworks/base/tests/backup/",
 		"frameworks/native/cmds/cmd/",
 		"frameworks/webview/chromium/",
 		"hardware/libhardware/modules/",
-		"hardware/libhardware/tests/",
 		"hardware/qcom/",
 		"sdk/emulator/mksdcard/",
 		"system/vold/tests/",
-		"test/vts-testcase/kernel/api/qtaguid/",
-		"test/vts-testcase/security/poc/target/",
 		"tools/adt/idea/android/ultimate/get_modification_time/jni/",
 	}
 )
diff --git a/java/androidmk.go b/java/androidmk.go
index 2e67639..f52d5e9 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -95,28 +95,35 @@
 }
 
 func (binary *Binary) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
-		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(binary.implementationJarFile),
-		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			android.WriteAndroidMkData(w, data)
 
-			fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
-			fmt.Fprintln(w, "include $(CLEAR_VARS)")
-			fmt.Fprintln(w, "LOCAL_MODULE := "+name)
-			fmt.Fprintln(w, "LOCAL_MODULE_CLASS := EXECUTABLES")
-			if strings.Contains(prefix, "HOST_") {
-				fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
-			}
-			fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
-			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", binary.wrapperFile.String())
-			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+	if !binary.isWrapperVariant {
+		return android.AndroidMkData{
+			Class:      "JAVA_LIBRARIES",
+			OutputFile: android.OptionalPathForPath(binary.implementationJarFile),
+			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+				android.WriteAndroidMkData(w, data)
 
-			// Ensure that the wrapper script timestamp is always updated when the jar is updated
-			fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
-			fmt.Fprintln(w, "jar_installed_module :=")
-		},
+				fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
+			},
+		}
+	} else {
+		return android.AndroidMkData{
+			Class:      "EXECUTABLES",
+			OutputFile: android.OptionalPathForPath(binary.wrapperFile),
+			Extra: []android.AndroidMkExtraFunc{
+				func(w io.Writer, outputFile android.Path) {
+					fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
+				},
+			},
+			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+				android.WriteAndroidMkData(w, data)
+
+				// Ensure that the wrapper script timestamp is always updated when the jar is updated
+				fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
+				fmt.Fprintln(w, "jar_installed_module :=")
+			},
+		}
 	}
 }
 
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..0e54e3c 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
@@ -985,6 +1014,8 @@
 
 	binaryProperties binaryProperties
 
+	isWrapperVariant bool
+
 	wrapperFile android.SourcePath
 	binaryFile  android.OutputPath
 }
@@ -994,21 +1025,32 @@
 }
 
 func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	j.Library.GenerateAndroidBuildActions(ctx)
-
-	// Depend on the installed jar (j.installFile) so that the wrapper doesn't get executed by
-	// another build rule before the jar has been installed.
-	if String(j.binaryProperties.Wrapper) != "" {
-		j.wrapperFile = android.PathForModuleSrc(ctx, String(j.binaryProperties.Wrapper)).SourcePath
+	if ctx.Arch().ArchType == android.Common {
+		// Compile the jar
+		j.Library.GenerateAndroidBuildActions(ctx)
 	} else {
-		j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
+		// Handle the binary wrapper
+		j.isWrapperVariant = true
+
+		if String(j.binaryProperties.Wrapper) != "" {
+			j.wrapperFile = android.PathForModuleSrc(ctx, String(j.binaryProperties.Wrapper)).SourcePath
+		} else {
+			j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
+		}
+
+		// Depend on the installed jar so that the wrapper doesn't get executed by
+		// another build rule before the jar has been installed.
+		jarFile := ctx.PrimaryModule().(*Binary).installFile
+
+		j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
+			ctx.ModuleName(), j.wrapperFile, jarFile)
 	}
-	j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
-		ctx.ModuleName(), j.wrapperFile, j.installFile)
 }
 
 func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
-	j.deps(ctx)
+	if ctx.Arch().ArchType == android.Common {
+		j.deps(ctx)
+	}
 }
 
 func BinaryFactory() android.Module {
@@ -1020,7 +1062,8 @@
 		&module.Module.protoProperties,
 		&module.binaryProperties)
 
-	InitJavaModule(module, android.HostAndDeviceSupported)
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -1033,7 +1076,8 @@
 		&module.Module.protoProperties,
 		&module.binaryProperties)
 
-	InitJavaModule(module, android.HostSupported)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst)
+	android.InitDefaultableModule(module)
 	return module
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index dbecc70..78fbd41 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -62,11 +62,13 @@
 
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
+	ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
 	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory(true)))
 	ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
 	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)
@@ -146,6 +148,8 @@
 		// For framework-res, which is an implicit dependency for framework
 		"AndroidManifest.xml":                   nil,
 		"build/target/product/security/testkey": nil,
+
+		"build/soong/scripts/jar-wrapper.sh": nil,
 	}
 
 	for k, v := range fs {
@@ -158,6 +162,7 @@
 }
 
 func run(t *testing.T, ctx *android.TestContext, config android.Config) {
+	t.Helper()
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	fail(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -165,6 +170,7 @@
 }
 
 func testJava(t *testing.T, bp string) *android.TestContext {
+	t.Helper()
 	config := testConfig(nil)
 	ctx := testContext(config, bp, nil)
 	run(t, ctx, config)
@@ -249,6 +255,35 @@
 	}
 }
 
+func TestBinary(t *testing.T) {
+	ctx := testJava(t, `
+		java_library_host {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_binary_host {
+			name: "bar",
+			srcs: ["b.java"],
+			static_libs: ["foo"],
+		}
+	`)
+
+	buildOS := android.BuildOs.String()
+
+	bar := ctx.ModuleForTests("bar", buildOS+"_common")
+	barJar := bar.Output("bar.jar").Output.String()
+	barWrapper := ctx.ModuleForTests("bar", buildOS+"_x86_64")
+	barWrapperDeps := barWrapper.Output("bar").Implicits.Strings()
+
+	// Test that the install binary wrapper depends on the installed jar file
+	if len(barWrapperDeps) != 1 || barWrapperDeps[0] != barJar {
+		t.Errorf("expected binary wrapper implicits [%q], got %v",
+			barJar, barWrapperDeps)
+	}
+
+}
+
 var classpathTestcases = []struct {
 	name          string
 	moduleType    string
@@ -775,7 +810,62 @@
 	}
 }
 
+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) {
+	t.Helper()
 	if len(errs) > 0 {
 		for _, err := range errs {
 			t.Error(err)