From c1130dc721438d971c23f1beec4088904507523b Mon Sep 17 00:00:00 2001 From: Sam Delmerico Date: Thu, 25 Aug 2022 14:43:54 -0400 Subject: [PATCH] add Partition method to LabelListAttribute It can be tedious to split a LabelListAttribute on all of the configuration axes, so LabelListAttribute.Partition can be used to do this based on a predicate function. Test: go test ./bazel Change-Id: I78ff8c24ce9c86bab5896290d8afe979d56545b6 --- bazel/properties.go | 57 ++++++++++++++++- bazel/properties_test.go | 128 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) diff --git a/bazel/properties.go b/bazel/properties.go index 963e27bdd..7bbfea40c 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -119,7 +119,7 @@ func (ll *LabelList) uniqueParentDirectories() []string { return dirs } -// Add inserts the label Label at the end of the LabelList. +// Add inserts the label Label at the end of the LabelList.Includes. func (ll *LabelList) Add(label *Label) { if label == nil { return @@ -127,6 +127,14 @@ func (ll *LabelList) Add(label *Label) { ll.Includes = append(ll.Includes, *label) } +// AddExclude inserts the label Label at the end of the LabelList.Excludes. +func (ll *LabelList) AddExclude(label *Label) { + if label == nil { + return + } + ll.Excludes = append(ll.Excludes, *label) +} + // Append appends the fields of other labelList to the corresponding fields of ll. func (ll *LabelList) Append(other LabelList) { if len(ll.Includes) > 0 || len(other.Includes) > 0 { @@ -137,6 +145,30 @@ func (ll *LabelList) Append(other LabelList) { } } +// Partition splits a LabelList into two LabelLists depending on the return value +// of the predicate. +// This function preserves the Includes and Excludes, but it does not provide +// that information to the partition function. +func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) { + predicated := LabelList{} + unpredicated := LabelList{} + for _, include := range ll.Includes { + if predicate(include) { + predicated.Add(&include) + } else { + unpredicated.Add(&include) + } + } + for _, exclude := range ll.Excludes { + if predicate(exclude) { + predicated.AddExclude(&exclude) + } else { + unpredicated.AddExclude(&exclude) + } + } + return predicated, unpredicated +} + // UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns // the slice in a sorted order. func UniqueSortedBazelLabels(originalLabels []Label) []Label { @@ -822,6 +854,29 @@ func (lla *LabelListAttribute) ResolveExcludes() { } } +// Partition splits a LabelListAttribute into two LabelListAttributes depending +// on the return value of the predicate. +// This function preserves the Includes and Excludes, but it does not provide +// that information to the partition function. +func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) { + predicated := LabelListAttribute{} + unpredicated := LabelListAttribute{} + + valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate) + predicated.SetValue(valuePartitionTrue) + unpredicated.SetValue(valuePartitionFalse) + + for axis, selectValueLabelLists := range lla.ConfigurableValues { + for config, labelList := range selectValueLabelLists { + configPredicated, configUnpredicated := labelList.Partition(predicate) + predicated.SetSelectValue(axis, config, configPredicated) + unpredicated.SetSelectValue(axis, config, configUnpredicated) + } + } + + return predicated, unpredicated +} + // OtherModuleContext is a limited context that has methods with information about other modules. type OtherModuleContext interface { ModuleFromName(name string) (blueprint.Module, bool) diff --git a/bazel/properties_test.go b/bazel/properties_test.go index 7b76b747b..dc4d5b0f5 100644 --- a/bazel/properties_test.go +++ b/bazel/properties_test.go @@ -310,6 +310,134 @@ func TestResolveExcludes(t *testing.T) { } } +func TestLabelListAttributePartition(t *testing.T) { + testCases := []struct { + name string + input LabelListAttribute + predicated LabelListAttribute + unpredicated LabelListAttribute + predicate func(label Label) bool + }{ + { + name: "move all to predicated partition", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + unpredicated: LabelListAttribute{}, + predicate: func(label Label) bool { + return true + }, + }, + { + name: "move all to unpredicated partition", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: LabelListAttribute{}, + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicate: func(label Label) bool { + return false + }, + }, + { + name: "partition includes and excludes", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "keep2"}, + []string{"keep1", "keep2"}, + )), + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{"throw1", "throw2"}, + []string{"throw1", "throw2"}, + )), + predicate: func(label Label) bool { + return strings.HasPrefix(label.Label, "keep") + }, + }, + { + name: "partition excludes only", + input: MakeLabelListAttribute(makeLabelList( + []string{}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{}, + []string{"keep1", "keep2"}, + )), + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{}, + []string{"throw1", "throw2"}, + )), + predicate: func(label Label) bool { + return strings.HasPrefix(label.Label, "keep") + }, + }, + { + name: "partition includes only", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "keep2"}, + []string{}, + )), + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{"throw1", "throw2"}, + []string{}, + )), + predicate: func(label Label) bool { + return strings.HasPrefix(label.Label, "keep") + }, + }, + { + name: "empty partition", + input: MakeLabelListAttribute(makeLabelList([]string{}, []string{})), + predicated: LabelListAttribute{}, + unpredicated: LabelListAttribute{}, + predicate: func(label Label) bool { + return true + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + predicated, unpredicated := tc.input.Partition(tc.predicate) + if !predicated.Value.Equals(tc.predicated.Value) { + t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated) + } + for axis, configs := range predicated.ConfigurableValues { + tcConfigs, ok := tc.predicated.ConfigurableValues[axis] + if !ok || !reflect.DeepEqual(configs, tcConfigs) { + t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated) + } + } + if !unpredicated.Value.Equals(tc.unpredicated.Value) { + t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated) + } + for axis, configs := range unpredicated.ConfigurableValues { + tcConfigs, ok := tc.unpredicated.ConfigurableValues[axis] + if !ok || !reflect.DeepEqual(configs, tcConfigs) { + t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated) + } + } + }) + } +} + // labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of // typ func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {