cc Bp2build support for genrules that generate .proto file

If `srcs` contains a gensrcs/genrule module, the current bp2build module
will put it in the catch-all `srcs` attribute. This is reserved for .cpp
sources, so if the genrule produces a .proto/.aidl/... file, this will
fail.

This handles genrules that produce .proto files. To implement this, this
creates an additional partition that detects if the other module is a
genrule/gensrc that produces .proto files. If true, it will append it to
the proto partition.

This CL does not handle
- genrule that produce .c/.aidl/.yacc/.... files. They will continue to
  be partitioned into the catch-all partition
- java modules

Test: unit tests
Test: TH
Bug: 293205700
Change-Id: Ib720fdf3a053ff5dd256e6faa632e3fa7776966d
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 9e485d6..9d90a5b 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -48,6 +48,8 @@
 
 	genrulePartition = "genrule"
 
+	protoFromGenPartition = "proto_gen"
+
 	hdrPartition = "hdr"
 
 	stubsSuffix = "_stub_libs_current"
@@ -126,6 +128,41 @@
 	}
 }
 
+// Returns an unchanged label and a bool indicating whether the dep is a genrule that produces .proto files
+func protoFromGenPartitionMapper(pathCtx android.BazelConversionContext) bazel.LabelMapper {
+	return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+		mod, exists := ctx.ModuleFromName(label.OriginalModuleName)
+		if !exists {
+			return label.Label, false
+		}
+		gen, isGen := mod.(*genrule.Module)
+		if !isGen {
+			return label.Label, false
+		}
+		if containsProto(ctx, gen.RawOutputFiles(pathCtx)) {
+			// Return unmodified label + true
+			// True will ensure that this module gets dropped from `srcs` of the generated cc_* target. `srcs` is reserved for .cpp files
+			return label.Label, true
+		}
+		return label.Label, false
+	}
+}
+
+// Returns true if srcs contains only .proto files
+// Raises an exception if there is a combination of .proto and non .proto srcs
+func containsProto(ctx bazel.OtherModuleContext, srcs []string) bool {
+	onlyProtos := false
+	for _, src := range srcs {
+		if strings.HasSuffix(src, ".proto") {
+			onlyProtos = true
+		} else if onlyProtos {
+			// This is not a proto file, and we have encountered a .proto file previously
+			ctx.ModuleErrorf("TOOD: Add bp2build support combination of .proto and non .proto files in outs of genrule")
+		}
+	}
+	return onlyProtos
+}
+
 // groupSrcsByExtension partitions `srcs` into groups based on file extension.
 func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
 	// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
@@ -159,10 +196,11 @@
 		// 		contains .l or .ll files we will need to find a way to add a
 		// 		LabelMapper for these that identifies these filegroups and
 		//		converts them appropriately
-		lSrcPartition:       bazel.LabelPartition{Extensions: []string{".l"}},
-		llSrcPartition:      bazel.LabelPartition{Extensions: []string{".ll"}},
-		rScriptSrcPartition: bazel.LabelPartition{Extensions: []string{".fs", ".rscript"}},
-		xsdSrcPartition:     bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(xsdConfigCppTarget)},
+		lSrcPartition:         bazel.LabelPartition{Extensions: []string{".l"}},
+		llSrcPartition:        bazel.LabelPartition{Extensions: []string{".ll"}},
+		rScriptSrcPartition:   bazel.LabelPartition{Extensions: []string{".fs", ".rscript"}},
+		xsdSrcPartition:       bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(xsdConfigCppTarget)},
+		protoFromGenPartition: bazel.LabelPartition{LabelMapper: protoFromGenPartitionMapper(ctx)},
 		// C++ is the "catch-all" group, and comprises generated sources because we don't
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition:     bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
@@ -594,6 +632,7 @@
 	partitionedHdrs := partitionHeaders(ctx, exportHdrs)
 
 	ca.protoSrcs = partitionedSrcs[protoSrcPartition]
+	ca.protoSrcs.Append(partitionedSrcs[protoFromGenPartition])
 	ca.aidlSrcs = partitionedSrcs[aidlSrcPartition]
 
 	for p, lla := range partitionedSrcs {