Refactor code for partitions c srcs

To support protos (and other srcs that generate sources), we need to
partition further. Separate out into a separate common function.

Bug: 200601772
Test: build/bazel/ci/bp2build.sh
Change-Id: I7bf4cd96fd9a9fca4ccb3c96f21a04303201f891
This commit is contained in:
Liz Kammer 2021-09-20 12:55:02 -04:00
parent 5b780b1ae2
commit 57e2e7a78f
6 changed files with 497 additions and 128 deletions

View file

@ -14,6 +14,7 @@ bootstrap_go_package {
testSrcs: [
"aquery_test.go",
"properties_test.go",
"testing.go",
],
pluginFor: [
"soong_build",

View file

@ -19,6 +19,9 @@ import (
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/google/blueprint"
)
// BazelTargetModuleProperties contain properties and metadata used for
@ -182,76 +185,6 @@ func SubtractStrings(haystack []string, needle []string) []string {
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 axis, configToLabels := range mapOver.ConfigurableValues {
for config, value := range configToLabels {
result.SetSelectValue(axis, config, MapLabelList(value, 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
for _, inc := range haystack.Includes {
if needleFn(inc.Label) {
includes = append(includes, inc)
}
}
// needleFn is not applied over excludes, but they are propagated as-is.
return LabelList{Includes: includes, Excludes: haystack.Excludes}
}
// Return all needles in a given haystack, where needleFn is true for needles.
func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
for config, selects := range haystack.ConfigurableValues {
newSelects := make(labelListSelectValues, len(selects))
for k, v := range selects {
newSelects[k] = FilterLabelList(v, needleFn)
}
result.ConfigurableValues[config] = newSelects
}
return result
}
// Subtract needle from haystack
func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
for config, selects := range haystack.ConfigurableValues {
newSelects := make(labelListSelectValues, len(selects))
needleSelects := needle.ConfigurableValues[config]
for k, v := range selects {
newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
}
result.ConfigurableValues[config] = newSelects
}
return result
}
// Subtract needle from haystack
func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
// This is really a set
@ -624,6 +557,144 @@ func (lla *LabelListAttribute) ResolveExcludes() {
}
}
// OtherModuleContext is a limited context that has methods with information about other modules.
type OtherModuleContext interface {
ModuleFromName(name string) (blueprint.Module, bool)
OtherModuleType(m blueprint.Module) string
OtherModuleName(m blueprint.Module) string
OtherModuleDir(m blueprint.Module) string
ModuleErrorf(fmt string, args ...interface{})
}
// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
// label and whether it was changed.
type LabelMapper func(OtherModuleContext, string) (string, bool)
// LabelPartition contains descriptions of a partition for labels
type LabelPartition struct {
// Extensions to include in this partition
Extensions []string
// LabelMapper is a function that can map a label to a new label, and indicate whether to include
// the mapped label in the partition
LabelMapper LabelMapper
// Whether to store files not included in any other partition in a group of LabelPartitions
// Only one partition in a group of LabelPartitions can enabled Keep_remainder
Keep_remainder bool
}
// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
// partition
type LabelPartitions map[string]LabelPartition
// filter returns a pointer to a label if the label should be included in the partition or nil if
// not.
func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
if lf.LabelMapper != nil {
if newLabel, changed := lf.LabelMapper(ctx, label.Label); changed {
return &Label{newLabel, label.OriginalModuleName}
}
}
for _, ext := range lf.Extensions {
if strings.HasSuffix(label.Label, ext) {
return &label
}
}
return nil
}
// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
type PartitionToLabelListAttribute map[string]LabelListAttribute
type partitionToLabelList map[string]*LabelList
func (p partitionToLabelList) appendIncludes(partition string, label Label) {
if _, ok := p[partition]; !ok {
p[partition] = &LabelList{}
}
p[partition].Includes = append(p[partition].Includes, label)
}
func (p partitionToLabelList) excludes(partition string, excludes []Label) {
if _, ok := p[partition]; !ok {
p[partition] = &LabelList{}
}
p[partition].Excludes = excludes
}
// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
ret := PartitionToLabelListAttribute{}
var partitionNames []string
// Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
var remainderPartition *string
for p, f := range partitions {
partitionNames = append(partitionNames, p)
if f.Keep_remainder {
if remainderPartition != nil {
panic("only one partition can store the remainder")
}
// If we take the address of p in a loop, we'll end up with the last value of p in
// remainderPartition, we want the requested partition
capturePartition := p
remainderPartition = &capturePartition
}
}
partitionLabelList := func(axis ConfigurationAxis, config string) {
value := lla.SelectValue(axis, config)
partitionToLabels := partitionToLabelList{}
for _, item := range value.Includes {
wasFiltered := false
var inPartition *string
for partition, f := range partitions {
filtered := f.filter(ctx, item)
if filtered == nil {
// did not match this filter, keep looking
continue
}
wasFiltered = true
partitionToLabels.appendIncludes(partition, *filtered)
// don't need to check other partitions if this filter used the item,
// continue checking if mapped to another name
if *filtered == item {
if inPartition != nil {
ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
}
capturePartition := partition
inPartition = &capturePartition
}
}
// if not specified in a partition, add to remainder partition if one exists
if !wasFiltered && remainderPartition != nil {
partitionToLabels.appendIncludes(*remainderPartition, item)
}
}
// ensure empty lists are maintained
if value.Excludes != nil {
for _, partition := range partitionNames {
partitionToLabels.excludes(partition, value.Excludes)
}
}
for partition, list := range partitionToLabels {
val := ret[partition]
(&val).SetSelectValue(axis, config, *list)
ret[partition] = val
}
}
partitionLabelList(NoConfigAxis, "")
for axis, configToList := range lla.ConfigurableValues {
for config, _ := range configToList {
partitionLabelList(axis, config)
}
}
return ret
}
// StringListAttribute corresponds to the string_list Bazel attribute type with
// support for additional metadata, like configurations.
type StringListAttribute struct {

View file

@ -16,7 +16,10 @@ package bazel
import (
"reflect"
"strings"
"testing"
"github.com/google/blueprint/proptools"
)
func TestUniqueBazelLabels(t *testing.T) {
@ -294,6 +297,222 @@ func TestResolveExcludes(t *testing.T) {
}
}
// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
// typ
func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
return func(omc OtherModuleContext, label string) (string, bool) {
m, ok := omc.ModuleFromName(label)
if !ok {
return label, false
}
mTyp := omc.OtherModuleType(m)
if typ == mTyp {
return label + suffix, true
}
return label, false
}
}
func TestPartitionLabelListAttribute(t *testing.T) {
testCases := []struct {
name string
ctx *otherModuleTestContext
labelList LabelListAttribute
filters LabelPartitions
expected PartitionToLabelListAttribute
expectedErrMsg *string
}{
{
name: "no configurable values",
ctx: &otherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
},
filters: LabelPartitions{
"A": LabelPartition{Extensions: []string{".a"}},
"B": LabelPartition{Extensions: []string{".b"}},
"C": LabelPartition{Extensions: []string{".c"}},
},
expected: PartitionToLabelListAttribute{
"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
},
},
{
name: "no configurable values, remainder partition",
ctx: &otherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
},
filters: LabelPartitions{
"A": LabelPartition{Extensions: []string{".a"}, Keep_remainder: true},
"B": LabelPartition{Extensions: []string{".b"}},
"C": LabelPartition{Extensions: []string{".c"}},
},
expected: PartitionToLabelListAttribute{
"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "d.d", "e.e"}, []string{})},
"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
},
},
{
name: "no configurable values, empty partition",
ctx: &otherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "c.c"}, []string{}),
},
filters: LabelPartitions{
"A": LabelPartition{Extensions: []string{".a"}},
"B": LabelPartition{Extensions: []string{".b"}},
"C": LabelPartition{Extensions: []string{".c"}},
},
expected: PartitionToLabelListAttribute{
"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
},
},
{
name: "no configurable values, has map",
ctx: &otherModuleTestContext{
modules: []testModuleInfo{testModuleInfo{name: "srcs", typ: "fg", dir: "dir"}},
},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}),
},
filters: LabelPartitions{
"A": LabelPartition{Extensions: []string{".a"}, LabelMapper: labelAddSuffixForTypeMapper("_a", "fg")},
"B": LabelPartition{Extensions: []string{".b"}},
"C": LabelPartition{Extensions: []string{".c"}},
},
expected: PartitionToLabelListAttribute{
"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "srcs_a"}, []string{})},
"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
},
},
{
name: "configurable values, keeps empty if excludes",
ctx: &otherModuleTestContext{},
labelList: LabelListAttribute{
ConfigurableValues: configurableLabelLists{
ArchConfigurationAxis: labelListSelectValues{
"x86": makeLabelList([]string{"a.a", "c.c"}, []string{}),
"arm": makeLabelList([]string{"b.b"}, []string{}),
"x86_64": makeLabelList([]string{"b.b"}, []string{"d.d"}),
},
},
},
filters: LabelPartitions{
"A": LabelPartition{Extensions: []string{".a"}},
"B": LabelPartition{Extensions: []string{".b"}},
"C": LabelPartition{Extensions: []string{".c"}},
},
expected: PartitionToLabelListAttribute{
"A": LabelListAttribute{
ConfigurableValues: configurableLabelLists{
ArchConfigurationAxis: labelListSelectValues{
"x86": makeLabelList([]string{"a.a"}, []string{}),
"x86_64": makeLabelList([]string{}, []string{"c.c"}),
},
},
},
"B": LabelListAttribute{
ConfigurableValues: configurableLabelLists{
ArchConfigurationAxis: labelListSelectValues{
"arm": makeLabelList([]string{"b.b"}, []string{}),
"x86_64": makeLabelList([]string{"b.b"}, []string{"c.c"}),
},
},
},
"C": LabelListAttribute{
ConfigurableValues: configurableLabelLists{
ArchConfigurationAxis: labelListSelectValues{
"x86": makeLabelList([]string{"c.c"}, []string{}),
"x86_64": makeLabelList([]string{}, []string{"c.c"}),
},
},
},
},
},
{
name: "error for multiple partitions same value",
ctx: &otherModuleTestContext{},
labelList: LabelListAttribute{
Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
},
filters: LabelPartitions{
"A": LabelPartition{Extensions: []string{".a"}},
"other A": LabelPartition{Extensions: []string{".a"}},
},
expected: PartitionToLabelListAttribute{},
expectedErrMsg: proptools.StringPtr(`"a.a" was found in multiple partitions:`),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := PartitionLabelListAttribute(tc.ctx, &tc.labelList, tc.filters)
if hasErrors, expectsErr := len(tc.ctx.errors) > 0, tc.expectedErrMsg != nil; hasErrors != expectsErr {
t.Errorf("Unexpected error(s): %q, expected: %q", tc.ctx.errors, *tc.expectedErrMsg)
} else if tc.expectedErrMsg != nil {
found := false
for _, err := range tc.ctx.errors {
if strings.Contains(err, *tc.expectedErrMsg) {
found = true
break
}
}
if !found {
t.Errorf("Expected error message: %q, got %q", *tc.expectedErrMsg, tc.ctx.errors)
}
return
}
if len(tc.expected) != len(got) {
t.Errorf("Expected %d partitions, got %d partitions", len(tc.expected), len(got))
}
for partition, expectedLla := range tc.expected {
gotLla, ok := got[partition]
if !ok {
t.Errorf("Expected partition %q, but it was not found %v", partition, got)
continue
}
expectedLabelList := expectedLla.Value
gotLabelList := gotLla.Value
if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
t.Errorf("Expected no config includes %v, got %v", expectedLabelList.Includes, gotLabelList.Includes)
}
expectedAxes := expectedLla.SortedConfigurationAxes()
gotAxes := gotLla.SortedConfigurationAxes()
if !reflect.DeepEqual(expectedAxes, gotAxes) {
t.Errorf("Expected axes %v, got %v (%#v)", expectedAxes, gotAxes, gotLla)
}
for _, axis := range expectedLla.SortedConfigurationAxes() {
if _, exists := gotLla.ConfigurableValues[axis]; !exists {
t.Errorf("Expected %s to be a supported axis, but it was not found", axis)
}
if expected, got := expectedLla.ConfigurableValues[axis], gotLla.ConfigurableValues[axis]; len(expected) != len(got) {
t.Errorf("For axis %q: expected configs %v, got %v", axis, expected, got)
}
for config, expectedLabelList := range expectedLla.ConfigurableValues[axis] {
gotLabelList, exists := gotLla.ConfigurableValues[axis][config]
if !exists {
t.Errorf("Expected %s to be a supported config, but config was not found", config)
continue
}
if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
t.Errorf("Expected %s %s includes %v, got %v", axis, config, expectedLabelList.Includes, gotLabelList.Includes)
}
}
}
}
})
}
}
func TestDeduplicateAxesFromBase(t *testing.T) {
attr := StringListAttribute{
Value: []string{

105
bazel/testing.go Normal file
View file

@ -0,0 +1,105 @@
// Copyright 2021 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 bazel
import (
"fmt"
"github.com/google/blueprint"
)
// testModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
// a blueprint ModuleContext
type testModuleInfo struct {
name string
typ string
dir string
}
// Name returns name for testModuleInfo -- required to implement blueprint.Module
func (mi testModuleInfo) Name() string {
return mi.name
}
// GenerateBuildActions unused, but required to implmeent blueprint.Module
func (mi testModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
func (mi testModuleInfo) equals(other testModuleInfo) bool {
return mi.name == other.name && mi.typ == other.typ && mi.dir == other.dir
}
// ensure testModuleInfo implements blueprint.Module
var _ blueprint.Module = testModuleInfo{}
// otherModuleTestContext is a mock context that implements OtherModuleContext
type otherModuleTestContext struct {
modules []testModuleInfo
errors []string
}
// ModuleFromName retrieves the testModuleInfo corresponding to name, if it exists
func (omc *otherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
for _, m := range omc.modules {
if m.name == name {
return m, true
}
}
return testModuleInfo{}, false
}
// testModuleInfo returns the testModuleInfo corresponding to a blueprint.Module if it exists in omc
func (omc *otherModuleTestContext) testModuleInfo(m blueprint.Module) (testModuleInfo, bool) {
mi, ok := m.(testModuleInfo)
if !ok {
return testModuleInfo{}, false
}
for _, other := range omc.modules {
if other.equals(mi) {
return mi, true
}
}
return testModuleInfo{}, false
}
// OtherModuleType returns type of m if it exists in omc
func (omc *otherModuleTestContext) OtherModuleType(m blueprint.Module) string {
if mi, ok := omc.testModuleInfo(m); ok {
return mi.typ
}
return ""
}
// OtherModuleName returns name of m if it exists in omc
func (omc *otherModuleTestContext) OtherModuleName(m blueprint.Module) string {
if mi, ok := omc.testModuleInfo(m); ok {
return mi.name
}
return ""
}
// OtherModuleDir returns dir of m if it exists in omc
func (omc *otherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
if mi, ok := omc.testModuleInfo(m); ok {
return mi.dir
}
return ""
}
func (omc *otherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
omc.errors = append(omc.errors, fmt.Sprintf(format, args...))
}
// Ensure otherModuleTestContext implements OtherModuleContext
var _ OtherModuleContext = &otherModuleTestContext{}

View file

@ -580,7 +580,7 @@ cc_library {
"both_source.c",
"both_source.s",
"both_source.S",
":both_filegroup",
":both_filegroup",
],
static: {
srcs: [
@ -633,9 +633,9 @@ filegroup {
local_includes = ["."],
shared = {
"srcs": [
":shared_filegroup_cpp_srcs",
"shared_source.cc",
"shared_source.cpp",
"shared_source.cc",
":shared_filegroup_cpp_srcs",
],
"srcs_as": [
"shared_source.s",
@ -648,9 +648,9 @@ filegroup {
],
},
srcs = [
":both_filegroup_cpp_srcs",
"both_source.cc",
"both_source.cpp",
"both_source.cc",
":both_filegroup_cpp_srcs",
],
srcs_as = [
"both_source.s",
@ -663,9 +663,9 @@ filegroup {
],
static = {
"srcs": [
":static_filegroup_cpp_srcs",
"static_source.cc",
"static_source.cpp",
"static_source.cc",
":static_filegroup_cpp_srcs",
],
"srcs_as": [
"static_source.s",

View file

@ -14,12 +14,12 @@
package cc
import (
"fmt"
"path/filepath"
"strings"
"android/soong/android"
"android/soong/bazel"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@ -41,18 +41,6 @@ type staticOrSharedAttributes struct {
}
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" {
@ -61,54 +49,39 @@ func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelLis
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]
ctx.ModuleErrorf("%s is not a valid Bazel label for a filegroup", fullLabel)
}
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 {
m, exists := ctx.ModuleFromName(label)
if exists {
aModule, _ := m.(android.Module)
if isFilegroupNamed(aModule, label) {
label = label + "_cpp_srcs"
// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
// macro.
addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
return func(ctx bazel.OtherModuleContext, label string) (string, bool) {
m, exists := ctx.ModuleFromName(label)
if !exists {
return label, false
}
}
return label
}
cFilegroup := func(label string) string {
m, exists := ctx.ModuleFromName(label)
if exists {
aModule, _ := m.(android.Module)
if isFilegroupNamed(aModule, label) {
label = label + "_c_srcs"
if !isFilegroupNamed(aModule, label) {
return label, false
}
return label + suffix, true
}
return label
}
asFilegroup := func(label string) string {
m, exists := ctx.ModuleFromName(label)
if exists {
aModule, _ := m.(android.Module)
if isFilegroupNamed(aModule, label) {
label = label + "_as_srcs"
}
}
return label
}
cSrcs = bazel.MapLabelListAttribute(srcs, cFilegroup)
cSrcs = bazel.FilterLabelListAttribute(cSrcs, isCSrcOrFilegroup)
// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
"c": bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
"as": bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
// 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.
"cpp": bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
})
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)
cSrcs = partitioned["c"]
asSrcs = partitioned["as"]
cppSrcs = partitioned["cpp"]
return
}