Create an extra variation in `test_per_src` mutator collecting all outputs.

Have `cc.testPerSrcMutator` create an additional variation named "",
having no sources (and generating no output file), but depending on
all other `test_per_src` variations and collecting their output files
in a new field named `cc.Module.testPerSrcOutputFiles`. This is useful
in the case where a module depends on all the `test_per_src`
variations of a test module.

Test: m
Bug: 129534335
Change-Id: I905decc0b9417f47cee9113466677d3bb61ad7b6
diff --git a/cc/cc.go b/cc/cc.go
index a0ab255..70da92d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -369,6 +369,7 @@
 	vndkExtDepTag         = dependencyTag{name: "vndk extends", library: true}
 	runtimeDepTag         = dependencyTag{name: "runtime lib"}
 	coverageDepTag        = dependencyTag{name: "coverage"}
+	testPerSrcDepTag      = dependencyTag{name: "test_per_src"}
 )
 
 // Module contains the properties and members used by all C/C++ module types, and implements
@@ -404,6 +405,9 @@
 
 	outputFile android.OptionalPath
 
+	// Test output files, in the case of a test module using `test_per_src`.
+	testPerSrcOutputFiles []android.Path
+
 	cachedToolchain config.Toolchain
 
 	subAndroidMkOnce map[subAndroidMkProvider]bool
@@ -426,6 +430,10 @@
 	return c.outputFile
 }
 
+func (c *Module) TestPerSrcOutputFiles() []android.Path {
+	return c.testPerSrcOutputFiles
+}
+
 func (c *Module) UnstrippedOutputFile() android.Path {
 	if c.linker != nil {
 		return c.linker.unstrippedOutputFilePath()
@@ -940,6 +948,29 @@
 }
 
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+	// Handle the case of a test module split by `test_per_src` mutator.
+	if test, ok := c.linker.(testPerSrc); ok {
+		// The `test_per_src` mutator adds an extra variant named "", depending on all the
+		// other `test_per_src` variants of the test module. Collect the output files of
+		// these dependencies and record them in the `testPerSrcOutputFiles` for later use
+		// (see e.g. `apexBundle.GenerateAndroidBuildActions`).
+		if test.isAllTestsVariation() {
+			var testPerSrcOutputFiles []android.Path
+			for _, dep := range actx.GetDirectDepsWithTag(testPerSrcDepTag) {
+				if ccDep, ok := dep.(*Module); ok {
+					depOutputFile := ccDep.OutputFile().Path()
+					testPerSrcOutputFiles =
+						append(testPerSrcOutputFiles, depOutputFile)
+				}
+			}
+			c.testPerSrcOutputFiles = testPerSrcOutputFiles
+			// Set outputFile to an empty path, as this module does not produce an
+			// output file per se.
+			c.outputFile = android.OptionalPath{}
+			return
+		}
+	}
+
 	c.makeLinkType = c.getMakeLinkType(actx)
 
 	ctx := &moduleContext{
diff --git a/cc/test.go b/cc/test.go
index 0a00aa1..95d0bfa 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -121,7 +121,9 @@
 type testPerSrc interface {
 	testPerSrc() bool
 	srcs() []string
+	isAllTestsVariation() bool
 	setSrc(string, string)
+	unsetSrc()
 }
 
 func (test *testBinary) testPerSrc() bool {
@@ -132,28 +134,55 @@
 	return test.baseCompiler.Properties.Srcs
 }
 
+func (test *testBinary) isAllTestsVariation() bool {
+	stem := test.binaryDecorator.Properties.Stem
+	return stem != nil && *stem == ""
+}
+
 func (test *testBinary) setSrc(name, src string) {
 	test.baseCompiler.Properties.Srcs = []string{src}
 	test.binaryDecorator.Properties.Stem = StringPtr(name)
 }
 
+func (test *testBinary) unsetSrc() {
+	test.baseCompiler.Properties.Srcs = nil
+	test.binaryDecorator.Properties.Stem = StringPtr("")
+}
+
 var _ testPerSrc = (*testBinary)(nil)
 
 func testPerSrcMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok {
 		if test, ok := m.linker.(testPerSrc); ok {
-			if test.testPerSrc() && len(test.srcs()) > 0 {
+			numTests := len(test.srcs())
+			if test.testPerSrc() && numTests > 0 {
 				if duplicate, found := checkDuplicate(test.srcs()); found {
 					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
 					return
 				}
-				testNames := make([]string, len(test.srcs()))
+				testNames := make([]string, numTests)
 				for i, src := range test.srcs() {
 					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
 				}
+				// In addition to creating one variation per test source file,
+				// create an additional "all tests" variation named "", and have it
+				// depends on all other test_per_src variations. This is useful to
+				// create subsequent dependencies of a given module on all
+				// test_per_src variations created above: by depending on
+				// variation "", that module will transitively depend on all the
+				// other test_per_src variations without the need to know their
+				// name or even their number.
+				testNames = append(testNames, "")
 				tests := mctx.CreateLocalVariations(testNames...)
+				all_tests := tests[numTests]
+				all_tests.(*Module).linker.(testPerSrc).unsetSrc()
+				// Prevent the "all tests" variation from being installable nor
+				// exporting to Make, as it won't create any output file.
+				all_tests.(*Module).Properties.PreventInstall = true
+				all_tests.(*Module).Properties.HideFromMake = true
 				for i, src := range test.srcs() {
 					tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
+					mctx.AddInterVariantDependency(testPerSrcDepTag, all_tests, tests[i])
 				}
 			}
 		}