bp2build: split as, c, and cpp srcs for cc_library

This CL adds support for cc_library to correctly split c, as and cpp
srcs in shared/static nested props, as well as splitting
the *filegroup* deps in those props, where each filegroup is expanded
into its own c, cpp and as srcs filegroups. This ensures that the
correct sources go into cc_library_static's underlying cc_libraries for
c, cpp and as sources respectively.

See the bp2build conversion test for a better visualization.

Bug: 183064430

Test: TH
Change-Id: I29add5140672d042adff65527d8b65f4a5f0a05b
diff --git a/android/filegroup.go b/android/filegroup.go
index fc6850e..e207412 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -63,7 +63,10 @@
 		Srcs: srcs,
 	}
 
-	props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "filegroup",
+		Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
+	}
 
 	ctx.CreateBazelTargetModule(BazelFileGroupFactory, fg.Name(), props, attrs)
 }
diff --git a/bazel/properties.go b/bazel/properties.go
index b2d68da..951081c 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -137,6 +137,39 @@
 	return strings
 }
 
+// Map a function over all labels in a LabelList.
+func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
+	var includes []Label
+	for _, inc := range mapOver.Includes {
+		mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
+		includes = append(includes, mappedLabel)
+	}
+	// mapFn is not applied over excludes, but they are propagated as-is.
+	return LabelList{Includes: includes, Excludes: mapOver.Excludes}
+}
+
+// Map a function over all Labels in a LabelListAttribute
+func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
+	var result LabelListAttribute
+
+	result.Value = MapLabelList(mapOver.Value, mapFn)
+
+	for arch := range PlatformArchMap {
+		result.SetValueForArch(arch, MapLabelList(mapOver.GetValueForArch(arch), mapFn))
+	}
+
+	for os := range PlatformOsMap {
+		result.SetOsValueForTarget(os, MapLabelList(mapOver.GetOsValueForTarget(os), mapFn))
+
+		// TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
+		for _, arch := range AllArches {
+			result.SetOsArchValueForTarget(os, arch, MapLabelList(mapOver.GetOsArchValueForTarget(os, arch), mapFn))
+		}
+	}
+
+	return result
+}
+
 // Return all needles in a given haystack, where needleFn is true for needles.
 func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
 	var includes []Label
@@ -145,6 +178,7 @@
 			includes = append(includes, inc)
 		}
 	}
+	// needleFn is not applied over excludes, but they are propagated as-is.
 	return LabelList{Includes: includes, Excludes: haystack.Excludes}
 }
 
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index b87d713..c8ae031 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -47,6 +47,7 @@
 
 func registerCcLibraryModuleTypes(ctx android.RegistrationContext) {
 	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
@@ -535,6 +536,139 @@
 	})
 }
 
+func TestCcLibrarySharedStaticPropsWithMixedSources(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library shared/static props with c/cpp/s mixed sources",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/both_source.cpp":   "",
+			"foo/bar/both_source.cc":    "",
+			"foo/bar/both_source.c":     "",
+			"foo/bar/both_source.s":     "",
+			"foo/bar/both_source.S":     "",
+			"foo/bar/shared_source.cpp": "",
+			"foo/bar/shared_source.cc":  "",
+			"foo/bar/shared_source.c":   "",
+			"foo/bar/shared_source.s":   "",
+			"foo/bar/shared_source.S":   "",
+			"foo/bar/static_source.cpp": "",
+			"foo/bar/static_source.cc":  "",
+			"foo/bar/static_source.c":   "",
+			"foo/bar/static_source.s":   "",
+			"foo/bar/static_source.S":   "",
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    srcs: [
+		"both_source.cpp",
+		"both_source.cc",
+		"both_source.c",
+		"both_source.s",
+		"both_source.S",
+        ":both_filegroup",
+	],
+    static: {
+		srcs: [
+			"static_source.cpp",
+			"static_source.cc",
+			"static_source.c",
+			"static_source.s",
+			"static_source.S",
+			":static_filegroup",
+		],
+    },
+    shared: {
+		srcs: [
+			"shared_source.cpp",
+			"shared_source.cc",
+			"shared_source.c",
+			"shared_source.s",
+			"shared_source.S",
+			":shared_filegroup",
+		],
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+filegroup {
+    name: "both_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+	],
+}
+
+filegroup {
+    name: "shared_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+	],
+}
+
+filegroup {
+    name: "static_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+	],
+}
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    shared_srcs = [
+        ":shared_filegroup_cpp_srcs",
+        "shared_source.cc",
+        "shared_source.cpp",
+    ],
+    shared_srcs_as = [
+        "shared_source.s",
+        "shared_source.S",
+        ":shared_filegroup_as_srcs",
+    ],
+    shared_srcs_c = [
+        "shared_source.c",
+        ":shared_filegroup_c_srcs",
+    ],
+    srcs = [
+        ":both_filegroup_cpp_srcs",
+        "both_source.cc",
+        "both_source.cpp",
+    ],
+    srcs_as = [
+        "both_source.s",
+        "both_source.S",
+        ":both_filegroup_as_srcs",
+    ],
+    srcs_c = [
+        "both_source.c",
+        ":both_filegroup_c_srcs",
+    ],
+    static_srcs = [
+        ":static_filegroup_cpp_srcs",
+        "static_source.cc",
+        "static_source.cpp",
+    ],
+    static_srcs_as = [
+        "static_source.s",
+        "static_source.S",
+        ":static_filegroup_as_srcs",
+    ],
+    static_srcs_c = [
+        "static_source.c",
+        ":static_filegroup_c_srcs",
+    ],
+)`},
+	})
+}
+
 func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
 		description:                        "cc_library non-configured version script",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index fed9936..a156d54 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -14,6 +14,7 @@
 package cc
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -164,13 +165,86 @@
 // staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
 // properties which apply to either the shared or static version of a cc_library module.
 type staticOrSharedAttributes struct {
-	copts            bazel.StringListAttribute
-	srcs             bazel.LabelListAttribute
+	srcs    bazel.LabelListAttribute
+	srcs_c  bazel.LabelListAttribute
+	srcs_as bazel.LabelListAttribute
+
+	copts bazel.StringListAttribute
+
 	staticDeps       bazel.LabelListAttribute
 	dynamicDeps      bazel.LabelListAttribute
 	wholeArchiveDeps bazel.LabelListAttribute
 }
 
+func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
+	// Branch srcs into three language-specific groups.
+	// 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.
+	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
+	isCSrcOrFilegroup := func(s string) bool {
+		return strings.HasSuffix(s, ".c") || strings.HasSuffix(s, "_c_srcs")
+	}
+
+	isAsmSrcOrFilegroup := func(s string) bool {
+		return strings.HasSuffix(s, ".S") || strings.HasSuffix(s, ".s") || strings.HasSuffix(s, "_as_srcs")
+	}
+
+	// Check that a module is a filegroup type named <label>.
+	isFilegroupNamed := func(m android.Module, fullLabel string) bool {
+		if ctx.OtherModuleType(m) != "filegroup" {
+			return false
+		}
+		labelParts := strings.Split(fullLabel, ":")
+		if len(labelParts) > 2 {
+			// There should not be more than one colon in a label.
+			panic(fmt.Errorf("%s is not a valid Bazel label for a filegroup", fullLabel))
+		} else {
+			return m.Name() == labelParts[len(labelParts)-1]
+		}
+	}
+
+	// Convert the filegroup dependencies into the extension-specific filegroups
+	// filtered in the filegroup.bzl macro.
+	cppFilegroup := func(label string) string {
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if isFilegroupNamed(m, label) {
+				label = label + "_cpp_srcs"
+				return
+			}
+		})
+		return label
+	}
+	cFilegroup := func(label string) string {
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if isFilegroupNamed(m, label) {
+				label = label + "_c_srcs"
+				return
+			}
+		})
+		return label
+	}
+	asFilegroup := func(label string) string {
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if isFilegroupNamed(m, label) {
+				label = label + "_as_srcs"
+				return
+			}
+		})
+		return label
+	}
+
+	cSrcs = bazel.MapLabelListAttribute(srcs, cFilegroup)
+	cSrcs = bazel.FilterLabelListAttribute(cSrcs, isCSrcOrFilegroup)
+
+	asSrcs = bazel.MapLabelListAttribute(srcs, asFilegroup)
+	asSrcs = bazel.FilterLabelListAttribute(asSrcs, isAsmSrcOrFilegroup)
+
+	cppSrcs = bazel.MapLabelListAttribute(srcs, cppFilegroup)
+	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, cSrcs)
+	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, asSrcs)
+	return
+}
+
 // bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
 func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
@@ -265,6 +339,11 @@
 		}
 	}
 
+	cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.srcs)
+	attrs.srcs = cppSrcs
+	attrs.srcs_c = cSrcs
+	attrs.srcs_as = asSrcs
+
 	return attrs
 }
 
@@ -528,20 +607,8 @@
 		}
 	}
 
-	// Branch srcs into three language-specific groups.
-	// 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.
-	// TODO(b/): Handle language detection of sources in a Bazel rule.
-	isCSrc := func(s string) bool {
-		return strings.HasSuffix(s, ".c")
-	}
-	isAsmSrc := func(s string) bool {
-		return strings.HasSuffix(s, ".S") || strings.HasSuffix(s, ".s")
-	}
-	cSrcs := bazel.FilterLabelListAttribute(srcs, isCSrc)
-	asSrcs := bazel.FilterLabelListAttribute(srcs, isAsmSrc)
-	srcs = bazel.SubtractBazelLabelListAttribute(srcs, cSrcs)
-	srcs = bazel.SubtractBazelLabelListAttribute(srcs, asSrcs)
+	srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
+
 	return compilerAttributes{
 		copts:      copts,
 		srcs:       srcs,
diff --git a/cc/library.go b/cc/library.go
index 5e70c51..cdac01c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -221,25 +221,29 @@
 // For bp2build conversion.
 type bazelCcLibraryAttributes struct {
 	// Attributes pertaining to both static and shared variants.
-	Srcs                bazel.LabelListAttribute
+	Srcs    bazel.LabelListAttribute
+	Srcs_c  bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
+
+	Copts      bazel.StringListAttribute
+	Cppflags   bazel.StringListAttribute
+	Conlyflags bazel.StringListAttribute
+	Asflags    bazel.StringListAttribute
+
 	Hdrs                bazel.LabelListAttribute
 	Deps                bazel.LabelListAttribute
 	Implementation_deps bazel.LabelListAttribute
 	Dynamic_deps        bazel.LabelListAttribute
 	Whole_archive_deps  bazel.LabelListAttribute
-	Copts               bazel.StringListAttribute
 	Includes            bazel.StringListAttribute
 	Linkopts            bazel.StringListAttribute
 
-	Cppflags   bazel.StringListAttribute
-	Srcs_c     bazel.LabelListAttribute
-	Conlyflags bazel.StringListAttribute
-	Srcs_as    bazel.LabelListAttribute
-	Asflags    bazel.StringListAttribute
-
 	// Attributes pertaining to shared variant.
-	Shared_copts                  bazel.StringListAttribute
-	Shared_srcs                   bazel.LabelListAttribute
+	Shared_srcs    bazel.LabelListAttribute
+	Shared_srcs_c  bazel.LabelListAttribute
+	Shared_srcs_as bazel.LabelListAttribute
+	Shared_copts   bazel.StringListAttribute
+
 	Exported_deps_for_shared      bazel.LabelListAttribute
 	Static_deps_for_shared        bazel.LabelListAttribute
 	Dynamic_deps_for_shared       bazel.LabelListAttribute
@@ -248,8 +252,11 @@
 	Version_script                bazel.LabelAttribute
 
 	// Attributes pertaining to static variant.
-	Static_copts                  bazel.StringListAttribute
-	Static_srcs                   bazel.LabelListAttribute
+	Static_srcs    bazel.LabelListAttribute
+	Static_srcs_c  bazel.LabelListAttribute
+	Static_srcs_as bazel.LabelListAttribute
+	Static_copts   bazel.StringListAttribute
+
 	Exported_deps_for_static      bazel.LabelListAttribute
 	Static_deps_for_static        bazel.LabelListAttribute
 	Dynamic_deps_for_static       bazel.LabelListAttribute
@@ -302,29 +309,35 @@
 	srcs.Append(compilerAttrs.srcs)
 
 	attrs := &bazelCcLibraryAttributes{
-		Srcs:                srcs,
+		Srcs:    srcs,
+		Srcs_c:  compilerAttrs.cSrcs,
+		Srcs_as: compilerAttrs.asSrcs,
+
+		Copts:      compilerAttrs.copts,
+		Cppflags:   compilerAttrs.cppFlags,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Asflags:    compilerAttrs.asFlags,
+
 		Implementation_deps: linkerAttrs.deps,
 		Deps:                linkerAttrs.exportedDeps,
 		Dynamic_deps:        linkerAttrs.dynamicDeps,
 		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
-		Copts:               compilerAttrs.copts,
 		Includes:            exportedIncludes,
 		Linkopts:            linkerAttrs.linkopts,
-		Cppflags:            compilerAttrs.cppFlags,
-		Srcs_c:              compilerAttrs.cSrcs,
-		Conlyflags:          compilerAttrs.conlyFlags,
-		Srcs_as:             compilerAttrs.asSrcs,
-		Asflags:             compilerAttrs.asFlags,
 
-		Shared_copts:                  sharedAttrs.copts,
 		Shared_srcs:                   sharedAttrs.srcs,
+		Shared_srcs_c:                 sharedAttrs.srcs_c,
+		Shared_srcs_as:                sharedAttrs.srcs_as,
+		Shared_copts:                  sharedAttrs.copts,
 		Static_deps_for_shared:        sharedAttrs.staticDeps,
 		Whole_archive_deps_for_shared: sharedAttrs.wholeArchiveDeps,
 		Dynamic_deps_for_shared:       sharedAttrs.dynamicDeps,
 		Version_script:                linkerAttrs.versionScript,
 
-		Static_copts:                  staticAttrs.copts,
 		Static_srcs:                   staticAttrs.srcs,
+		Static_srcs_c:                 staticAttrs.srcs_c,
+		Static_srcs_as:                staticAttrs.srcs_as,
+		Static_copts:                  staticAttrs.copts,
 		Static_deps_for_static:        staticAttrs.staticDeps,
 		Whole_archive_deps_for_static: staticAttrs.wholeArchiveDeps,
 		Dynamic_deps_for_static:       staticAttrs.dynamicDeps,