blob: 1300a53e22e500f3199912f90caffa519721b04c [file] [log] [blame]
Jingwen Chen30f5aaa2020-11-19 05:38:02 -05001// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package bazel
16
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000017import (
18 "fmt"
Jingwen Chen63930982021-03-24 10:04:33 -040019 "path/filepath"
Liz Kammera060c452021-03-24 10:14:47 -040020 "regexp"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000021 "sort"
Liz Kammer57e2e7a2021-09-20 12:55:02 -040022 "strings"
23
24 "github.com/google/blueprint"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000025)
Jingwen Chen5d864492021-02-24 07:20:12 -050026
Jingwen Chen73850672020-12-14 08:25:34 -050027// BazelTargetModuleProperties contain properties and metadata used for
28// Blueprint to BUILD file conversion.
29type BazelTargetModuleProperties struct {
30 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050031 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050032
33 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050034 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050035}
Liz Kammer356f7d42021-01-26 09:18:53 -050036
Liz Kammera060c452021-03-24 10:14:47 -040037var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
38
Jingwen Chen38e62642021-04-19 05:00:15 +000039// Label is used to represent a Bazel compatible Label. Also stores the original
40// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050041type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000042 // The string representation of a Bazel target label. This can be a relative
43 // or fully qualified label. These labels are used for generating BUILD
44 // files with bp2build.
45 Label string
46
47 // The original Soong/Blueprint module name that the label was derived from.
48 // This is used for replacing references to the original name with the new
49 // label, for example in genrule cmds.
50 //
51 // While there is a reversible 1:1 mapping from the module name to Bazel
52 // label with bp2build that could make computing the original module name
53 // from the label automatic, it is not the case for handcrafted targets,
54 // where modules can have a custom label mapping through the { bazel_module:
55 // { label: <label> } } property.
56 //
57 // With handcrafted labels, those modules don't go through bp2build
58 // conversion, but relies on handcrafted targets in the source tree.
59 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050060}
61
62// LabelList is used to represent a list of Bazel labels.
63type LabelList struct {
64 Includes []Label
65 Excludes []Label
66}
67
Chris Parsons51f8c392021-08-03 21:01:05 -040068func (ll *LabelList) Equals(other LabelList) bool {
69 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
70 return false
71 }
72 for i, _ := range ll.Includes {
73 if ll.Includes[i] != other.Includes[i] {
74 return false
75 }
76 }
77 for i, _ := range ll.Excludes {
78 if ll.Excludes[i] != other.Excludes[i] {
79 return false
80 }
81 }
82 return true
83}
84
Liz Kammer9abd62d2021-05-21 08:37:59 -040085func (ll *LabelList) IsNil() bool {
86 return ll.Includes == nil && ll.Excludes == nil
87}
88
Liz Kammer74deed42021-06-02 13:02:03 -040089func (ll *LabelList) deepCopy() LabelList {
90 return LabelList{
91 Includes: ll.Includes[:],
92 Excludes: ll.Excludes[:],
93 }
94}
95
Jingwen Chen63930982021-03-24 10:04:33 -040096// uniqueParentDirectories returns a list of the unique parent directories for
97// all files in ll.Includes.
98func (ll *LabelList) uniqueParentDirectories() []string {
99 dirMap := map[string]bool{}
100 for _, label := range ll.Includes {
101 dirMap[filepath.Dir(label.Label)] = true
102 }
103 dirs := []string{}
104 for dir := range dirMap {
105 dirs = append(dirs, dir)
106 }
107 return dirs
108}
109
Liz Kammer12615db2021-09-28 09:19:17 -0400110// Add inserts the label Label at the end of the LabelList.
111func (ll *LabelList) Add(label *Label) {
112 if label == nil {
113 return
114 }
115 ll.Includes = append(ll.Includes, *label)
116}
117
Liz Kammer356f7d42021-01-26 09:18:53 -0500118// Append appends the fields of other labelList to the corresponding fields of ll.
119func (ll *LabelList) Append(other LabelList) {
120 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
121 ll.Includes = append(ll.Includes, other.Includes...)
122 }
123 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
124 ll.Excludes = append(other.Excludes, other.Excludes...)
125 }
126}
Jingwen Chen5d864492021-02-24 07:20:12 -0500127
Jingwen Chened9c17d2021-04-13 07:14:55 +0000128// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
129// the slice in a sorted order.
130func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000131 uniqueLabelsSet := make(map[Label]bool)
132 for _, l := range originalLabels {
133 uniqueLabelsSet[l] = true
134 }
135 var uniqueLabels []Label
136 for l, _ := range uniqueLabelsSet {
137 uniqueLabels = append(uniqueLabels, l)
138 }
139 sort.SliceStable(uniqueLabels, func(i, j int) bool {
140 return uniqueLabels[i].Label < uniqueLabels[j].Label
141 })
142 return uniqueLabels
143}
144
Liz Kammer9abd62d2021-05-21 08:37:59 -0400145func FirstUniqueBazelLabels(originalLabels []Label) []Label {
146 var labels []Label
147 found := make(map[Label]bool, len(originalLabels))
148 for _, l := range originalLabels {
149 if _, ok := found[l]; ok {
150 continue
151 }
152 labels = append(labels, l)
153 found[l] = true
154 }
155 return labels
156}
157
158func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
159 var uniqueLabelList LabelList
160 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
161 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
162 return uniqueLabelList
163}
164
165func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000166 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000167 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
168 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000169 return uniqueLabelList
170}
171
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000172// Subtract needle from haystack
173func SubtractStrings(haystack []string, needle []string) []string {
174 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400175 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000176 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400177 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000178 }
179
180 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400181 for _, s := range haystack {
182 if exclude := needleMap[s]; !exclude {
183 strings = append(strings, s)
184 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000185 }
186
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000187 return strings
188}
189
190// Subtract needle from haystack
191func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
192 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400193 needleMap := make(map[Label]bool)
194 for _, s := range needle {
195 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000196 }
197
198 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400199 for _, label := range haystack {
200 if exclude := needleMap[label]; !exclude {
201 labels = append(labels, label)
202 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000203 }
204
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000205 return labels
206}
207
Chris Parsons484e50a2021-05-13 15:13:04 -0400208// Appends two LabelLists, returning the combined list.
209func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
210 var result LabelList
211 result.Includes = append(a.Includes, b.Includes...)
212 result.Excludes = append(a.Excludes, b.Excludes...)
213 return result
214}
215
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000216// Subtract needle from haystack
217func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
218 var result LabelList
219 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
220 // NOTE: Excludes are intentionally not subtracted
221 result.Excludes = haystack.Excludes
222 return result
223}
224
Jingwen Chenc1c26502021-04-05 10:35:13 +0000225type Attribute interface {
226 HasConfigurableValues() bool
227}
228
Liz Kammer9abd62d2021-05-21 08:37:59 -0400229type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400230
Liz Kammer9abd62d2021-05-21 08:37:59 -0400231type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400232
Liz Kammer9abd62d2021-05-21 08:37:59 -0400233func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
234 if cl[axis] == nil {
235 cl[axis] = make(labelSelectValues)
236 }
237 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400238}
239
240// Represents an attribute whose value is a single label
241type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400242 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400243
Liz Kammer9abd62d2021-05-21 08:37:59 -0400244 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200245}
246
Chris Parsons58852a02021-12-09 18:10:18 -0500247func (la *LabelAttribute) axisTypes() map[configurationType]bool {
248 types := map[configurationType]bool{}
249 for k := range la.ConfigurableValues {
250 if len(la.ConfigurableValues[k]) > 0 {
251 types[k.configurationType] = true
252 }
253 }
254 return types
255}
256
257// Collapse reduces the configurable axes of the label attribute to a single axis.
258// This is necessary for final writing to bp2build, as a configurable label
259// attribute can only be comprised by a single select.
260func (la *LabelAttribute) Collapse() error {
261 axisTypes := la.axisTypes()
262 _, containsOs := axisTypes[os]
263 _, containsArch := axisTypes[arch]
264 _, containsOsArch := axisTypes[osArch]
265 _, containsProductVariables := axisTypes[productVariables]
266 if containsProductVariables {
267 if containsOs || containsArch || containsOsArch {
268 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
269 }
270 }
271 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
272 // If a bool attribute has both os and arch configuration axes, the only
273 // way to successfully union their values is to increase the granularity
274 // of the configuration criteria to os_arch.
275 for osType, supportedArchs := range osToArchMap {
276 for _, supportedArch := range supportedArchs {
277 osArch := osArchString(osType, supportedArch)
278 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
279 // Do nothing, as the arch_os is explicitly defined already.
280 } else {
281 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
282 osVal := la.SelectValue(OsConfigurationAxis, osType)
283 if osVal != nil && archVal != nil {
284 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
285 // runs after os mutator.
286 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
287 } else if osVal != nil && archVal == nil {
288 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
289 } else if osVal == nil && archVal != nil {
290 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
291 }
292 }
293 }
294 }
295 // All os_arch values are now set. Clear os and arch axes.
296 delete(la.ConfigurableValues, ArchConfigurationAxis)
297 delete(la.ConfigurableValues, OsConfigurationAxis)
298 }
299 return nil
300}
301
Liz Kammer9abd62d2021-05-21 08:37:59 -0400302// HasConfigurableValues returns whether there are configurable values set for this label.
303func (la LabelAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500304 for _, selectValues := range la.ConfigurableValues {
305 if len(selectValues) > 0 {
306 return true
307 }
308 }
309 return false
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200310}
311
Liz Kammer9abd62d2021-05-21 08:37:59 -0400312// SetValue sets the base, non-configured value for the Label
313func (la *LabelAttribute) SetValue(value Label) {
314 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400315}
316
Liz Kammer9abd62d2021-05-21 08:37:59 -0400317// SetSelectValue set a value for a bazel select for the given axis, config and value.
318func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
319 axis.validateConfig(config)
320 switch axis.configurationType {
321 case noConfig:
322 la.Value = &value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400323 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400324 if la.ConfigurableValues == nil {
325 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400326 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400327 la.ConfigurableValues.setValueForAxis(axis, config, &value)
328 default:
329 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
330 }
331}
332
333// SelectValue gets a value for a bazel select for the given axis and config.
Chris Parsons58852a02021-12-09 18:10:18 -0500334func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400335 axis.validateConfig(config)
336 switch axis.configurationType {
337 case noConfig:
Chris Parsons58852a02021-12-09 18:10:18 -0500338 return la.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400339 case arch, os, osArch, productVariables:
Chris Parsons58852a02021-12-09 18:10:18 -0500340 return la.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400341 default:
342 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
343 }
344}
345
346// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
347func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
348 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
349 for k := range la.ConfigurableValues {
350 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400351 }
352
Liz Kammer9abd62d2021-05-21 08:37:59 -0400353 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
354 return keys
355}
356
Liz Kammerd366c902021-06-03 13:43:01 -0400357type configToBools map[string]bool
358
359func (ctb configToBools) setValue(config string, value *bool) {
360 if value == nil {
361 if _, ok := ctb[config]; ok {
362 delete(ctb, config)
363 }
364 return
365 }
366 ctb[config] = *value
367}
368
369type configurableBools map[ConfigurationAxis]configToBools
370
371func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
372 if cb[axis] == nil {
373 cb[axis] = make(configToBools)
374 }
375 cb[axis].setValue(config, value)
376}
377
378// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
379type BoolAttribute struct {
380 Value *bool
381
382 ConfigurableValues configurableBools
383}
384
385// HasConfigurableValues returns whether there are configurable values for this attribute.
386func (ba BoolAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500387 for _, cfgToBools := range ba.ConfigurableValues {
388 if len(cfgToBools) > 0 {
389 return true
390 }
391 }
392 return false
Liz Kammerd366c902021-06-03 13:43:01 -0400393}
394
395// SetSelectValue sets value for the given axis/config.
396func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
397 axis.validateConfig(config)
398 switch axis.configurationType {
399 case noConfig:
400 ba.Value = value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400401 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400402 if ba.ConfigurableValues == nil {
403 ba.ConfigurableValues = make(configurableBools)
404 }
405 ba.ConfigurableValues.setValueForAxis(axis, config, value)
406 default:
407 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
408 }
409}
410
Chris Parsons58852a02021-12-09 18:10:18 -0500411// ToLabelListAttribute creates and returns a LabelListAttribute from this
412// bool attribute, where each bool in this attribute corresponds to a
413// label list value in the resultant attribute.
414func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
415 getLabelList := func(boolPtr *bool) LabelList {
416 if boolPtr == nil {
417 return LabelList{nil, nil}
418 } else if *boolPtr {
419 return trueVal
420 } else {
421 return falseVal
422 }
423 }
424
425 mainVal := getLabelList(ba.Value)
426 if !ba.HasConfigurableValues() {
427 return MakeLabelListAttribute(mainVal), nil
428 }
429
430 result := LabelListAttribute{}
431 if err := ba.Collapse(); err != nil {
432 return result, err
433 }
434
435 for axis, configToBools := range ba.ConfigurableValues {
436 if len(configToBools) < 1 {
437 continue
438 }
439 for config, boolPtr := range configToBools {
440 val := getLabelList(&boolPtr)
441 if !val.Equals(mainVal) {
442 result.SetSelectValue(axis, config, val)
443 }
444 }
445 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
446 }
447
448 return result, nil
449}
450
451// Collapse reduces the configurable axes of the boolean attribute to a single axis.
452// This is necessary for final writing to bp2build, as a configurable boolean
453// attribute can only be comprised by a single select.
454func (ba *BoolAttribute) Collapse() error {
455 axisTypes := ba.axisTypes()
456 _, containsOs := axisTypes[os]
457 _, containsArch := axisTypes[arch]
458 _, containsOsArch := axisTypes[osArch]
459 _, containsProductVariables := axisTypes[productVariables]
460 if containsProductVariables {
461 if containsOs || containsArch || containsOsArch {
462 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
463 }
464 }
465 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
466 // If a bool attribute has both os and arch configuration axes, the only
467 // way to successfully union their values is to increase the granularity
468 // of the configuration criteria to os_arch.
469 for osType, supportedArchs := range osToArchMap {
470 for _, supportedArch := range supportedArchs {
471 osArch := osArchString(osType, supportedArch)
472 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
473 // Do nothing, as the arch_os is explicitly defined already.
474 } else {
475 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
476 osVal := ba.SelectValue(OsConfigurationAxis, osType)
477 if osVal != nil && archVal != nil {
478 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
479 // runs after os mutator.
480 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
481 } else if osVal != nil && archVal == nil {
482 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
483 } else if osVal == nil && archVal != nil {
484 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
485 }
486 }
487 }
488 }
489 // All os_arch values are now set. Clear os and arch axes.
490 delete(ba.ConfigurableValues, ArchConfigurationAxis)
491 delete(ba.ConfigurableValues, OsConfigurationAxis)
492 // Verify post-condition; this should never fail, provided no additional
493 // axes are introduced.
494 if len(ba.ConfigurableValues) > 1 {
Liz Kammer07e106f2022-01-13 17:00:10 -0500495 panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
Chris Parsons58852a02021-12-09 18:10:18 -0500496 }
497 }
498 return nil
499}
500
501func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
502 types := map[configurationType]bool{}
503 for k := range ba.ConfigurableValues {
504 if len(ba.ConfigurableValues[k]) > 0 {
505 types[k.configurationType] = true
506 }
507 }
508 return types
509}
510
Liz Kammerd366c902021-06-03 13:43:01 -0400511// SelectValue gets the value for the given axis/config.
512func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
513 axis.validateConfig(config)
514 switch axis.configurationType {
515 case noConfig:
516 return ba.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400517 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400518 if v, ok := ba.ConfigurableValues[axis][config]; ok {
519 return &v
520 } else {
521 return nil
522 }
523 default:
524 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
525 }
526}
527
528// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
529func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
530 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
531 for k := range ba.ConfigurableValues {
532 keys = append(keys, k)
533 }
534
535 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
536 return keys
537}
538
Liz Kammer9abd62d2021-05-21 08:37:59 -0400539// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
540type labelListSelectValues map[string]LabelList
541
Liz Kammer12615db2021-09-28 09:19:17 -0400542func (ll labelListSelectValues) addSelects(label labelSelectValues) {
543 for k, v := range label {
544 if label == nil {
545 continue
546 }
547 l := ll[k]
548 (&l).Add(v)
549 ll[k] = l
550 }
551}
552
Chris Parsons77acf2e2021-12-03 17:27:16 -0500553func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400554 for k, v := range other {
555 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500556 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
557 l.Includes = []Label{}
558 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400559 (&l).Append(v)
560 ll[k] = l
561 }
562}
563
564// HasConfigurableValues returns whether there are configurable values within this set of selects.
565func (ll labelListSelectValues) HasConfigurableValues() bool {
566 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400567 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400568 return true
569 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400570 }
571 return false
572}
573
Jingwen Chen07027912021-03-15 06:02:43 -0400574// LabelListAttribute is used to represent a list of Bazel labels as an
575// attribute.
576type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400577 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400578 Value LabelList
579
Liz Kammer9abd62d2021-05-21 08:37:59 -0400580 // The configured attribute label list Values. Optional
581 // a map of independent configurability axes
582 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400583
584 // If true, differentiate between "nil" and "empty" list. nil means that
585 // this attribute should not be specified at all, and "empty" means that
586 // the attribute should be explicitly specified as an empty list.
587 // This mode facilitates use of attribute defaults: an empty list should
588 // override the default.
589 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000590
591 // If true, signal the intent to the code generator to emit all select keys,
592 // even if the Includes list for that key is empty. This mode facilitates
593 // specific select statements where an empty list for a non-default select
594 // key has a meaning.
595 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400596}
Jingwen Chen91220d72021-03-24 02:18:33 -0400597
Liz Kammer9abd62d2021-05-21 08:37:59 -0400598type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
599
600func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
601 if list.IsNil() {
602 if _, ok := cll[axis][config]; ok {
603 delete(cll[axis], config)
604 }
605 return
606 }
607 if cll[axis] == nil {
608 cll[axis] = make(labelListSelectValues)
609 }
610
611 cll[axis][config] = list
612}
613
Chris Parsons77acf2e2021-12-03 17:27:16 -0500614func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400615 for axis, otherSelects := range other {
616 selects := cll[axis]
617 if selects == nil {
618 selects = make(labelListSelectValues, len(otherSelects))
619 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500620 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400621 cll[axis] = selects
622 }
Jingwen Chen07027912021-03-15 06:02:43 -0400623}
624
Chris Parsons77acf2e2021-12-03 17:27:16 -0500625func (lla *LabelListAttribute) Clone() *LabelListAttribute {
626 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
627 return result.Append(*lla)
628}
629
Jingwen Chen07027912021-03-15 06:02:43 -0400630// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
631func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400632 return LabelListAttribute{
633 Value: value,
634 ConfigurableValues: make(configurableLabelLists),
635 }
636}
637
638func (lla *LabelListAttribute) SetValue(list LabelList) {
639 lla.SetSelectValue(NoConfigAxis, "", list)
640}
641
642// SetSelectValue set a value for a bazel select for the given axis, config and value.
643func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
644 axis.validateConfig(config)
645 switch axis.configurationType {
646 case noConfig:
647 lla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400648 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400649 if lla.ConfigurableValues == nil {
650 lla.ConfigurableValues = make(configurableLabelLists)
651 }
652 lla.ConfigurableValues.setValueForAxis(axis, config, list)
653 default:
654 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
655 }
656}
657
658// SelectValue gets a value for a bazel select for the given axis and config.
659func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
660 axis.validateConfig(config)
661 switch axis.configurationType {
662 case noConfig:
663 return lla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400664 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400665 return lla.ConfigurableValues[axis][config]
666 default:
667 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
668 }
669}
670
671// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
672func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
673 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
674 for k := range lla.ConfigurableValues {
675 keys = append(keys, k)
676 }
677
678 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
679 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400680}
681
Jingwen Chened9c17d2021-04-13 07:14:55 +0000682// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500683// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
684func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
685 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
686 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400687 lla.Value.Includes = []Label{}
688 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400689 lla.Value.Append(other.Value)
690 if lla.ConfigurableValues == nil {
691 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400692 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500693 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
694 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400695}
696
Liz Kammer12615db2021-09-28 09:19:17 -0400697// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
698// LabelList within the LabelListAttribute
699func (lla *LabelListAttribute) Add(label *LabelAttribute) {
700 if label == nil {
701 return
702 }
703
704 lla.Value.Add(label.Value)
705 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
706 lla.ConfigurableValues = make(configurableLabelLists)
707 }
708 for axis, _ := range label.ConfigurableValues {
709 if _, exists := lla.ConfigurableValues[axis]; !exists {
710 lla.ConfigurableValues[axis] = make(labelListSelectValues)
711 }
712 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
713 }
714}
715
Liz Kammer9abd62d2021-05-21 08:37:59 -0400716// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
717func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500718 for _, selectValues := range lla.ConfigurableValues {
719 if len(selectValues) > 0 {
720 return true
721 }
722 }
723 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400724}
725
Chris Parsons69fa9f92021-07-13 11:47:44 -0400726// IsEmpty returns true if the attribute has no values under any configuration.
727func (lla LabelListAttribute) IsEmpty() bool {
728 if len(lla.Value.Includes) > 0 {
729 return false
730 }
731 for axis, _ := range lla.ConfigurableValues {
732 if lla.ConfigurableValues[axis].HasConfigurableValues() {
733 return false
734 }
735 }
736 return true
737}
738
Liz Kammer54309532021-12-14 12:21:22 -0500739// IsNil returns true if the attribute has not been set for any configuration.
740func (lla LabelListAttribute) IsNil() bool {
741 if lla.Value.Includes != nil {
742 return false
743 }
744 return !lla.HasConfigurableValues()
745}
746
747// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
748// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
749// be removed, e.g. if they could cause duplicate element failures.
750func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
751 val := lla.SelectValue(axis, config)
752 newList := SubtractBazelLabelList(val, labelList)
753 newList.Excludes = append(newList.Excludes, labelList.Includes...)
754 lla.SetSelectValue(axis, config, newList)
755}
756
Liz Kammer74deed42021-06-02 13:02:03 -0400757// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
758// the base value and included in default values as appropriate.
759func (lla *LabelListAttribute) ResolveExcludes() {
760 for axis, configToLabels := range lla.ConfigurableValues {
761 baseLabels := lla.Value.deepCopy()
762 for config, val := range configToLabels {
763 // Exclude config-specific excludes from base value
764 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
765
766 // add base values to config specific to add labels excluded by others in this axis
767 // then remove all config-specific excludes
768 allLabels := baseLabels.deepCopy()
769 allLabels.Append(val)
770 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
771 }
772
773 // After going through all configs, delete the duplicates in the config
774 // values that are already in the base Value.
775 for config, val := range configToLabels {
776 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
777 }
778
Jingwen Chen9af49a42021-11-02 10:27:17 +0000779 // Now that the Value list is finalized for this axis, compare it with
780 // the original list, and union the difference with the default
781 // condition for the axis.
782 difference := SubtractBazelLabelList(baseLabels, lla.Value)
783 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
784 existingDefaults.Append(difference)
785 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400786
787 // if everything ends up without includes, just delete the axis
788 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
789 delete(lla.ConfigurableValues, axis)
790 }
791 }
792}
793
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400794// OtherModuleContext is a limited context that has methods with information about other modules.
795type OtherModuleContext interface {
796 ModuleFromName(name string) (blueprint.Module, bool)
797 OtherModuleType(m blueprint.Module) string
798 OtherModuleName(m blueprint.Module) string
799 OtherModuleDir(m blueprint.Module) string
800 ModuleErrorf(fmt string, args ...interface{})
801}
802
803// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
804// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400805type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400806
807// LabelPartition contains descriptions of a partition for labels
808type LabelPartition struct {
809 // Extensions to include in this partition
810 Extensions []string
811 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
812 // the mapped label in the partition
813 LabelMapper LabelMapper
814 // Whether to store files not included in any other partition in a group of LabelPartitions
815 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
816 Keep_remainder bool
817}
818
819// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
820// partition
821type LabelPartitions map[string]LabelPartition
822
823// filter returns a pointer to a label if the label should be included in the partition or nil if
824// not.
825func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
826 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400827 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400828 return &Label{newLabel, label.OriginalModuleName}
829 }
830 }
831 for _, ext := range lf.Extensions {
832 if strings.HasSuffix(label.Label, ext) {
833 return &label
834 }
835 }
836
837 return nil
838}
839
840// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
841type PartitionToLabelListAttribute map[string]LabelListAttribute
842
843type partitionToLabelList map[string]*LabelList
844
845func (p partitionToLabelList) appendIncludes(partition string, label Label) {
846 if _, ok := p[partition]; !ok {
847 p[partition] = &LabelList{}
848 }
849 p[partition].Includes = append(p[partition].Includes, label)
850}
851
852func (p partitionToLabelList) excludes(partition string, excludes []Label) {
853 if _, ok := p[partition]; !ok {
854 p[partition] = &LabelList{}
855 }
856 p[partition].Excludes = excludes
857}
858
859// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
860func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
861 ret := PartitionToLabelListAttribute{}
862 var partitionNames []string
863 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
864 var remainderPartition *string
865 for p, f := range partitions {
866 partitionNames = append(partitionNames, p)
867 if f.Keep_remainder {
868 if remainderPartition != nil {
869 panic("only one partition can store the remainder")
870 }
871 // If we take the address of p in a loop, we'll end up with the last value of p in
872 // remainderPartition, we want the requested partition
873 capturePartition := p
874 remainderPartition = &capturePartition
875 }
876 }
877
878 partitionLabelList := func(axis ConfigurationAxis, config string) {
879 value := lla.SelectValue(axis, config)
880 partitionToLabels := partitionToLabelList{}
881 for _, item := range value.Includes {
882 wasFiltered := false
883 var inPartition *string
884 for partition, f := range partitions {
885 filtered := f.filter(ctx, item)
886 if filtered == nil {
887 // did not match this filter, keep looking
888 continue
889 }
890 wasFiltered = true
891 partitionToLabels.appendIncludes(partition, *filtered)
892 // don't need to check other partitions if this filter used the item,
893 // continue checking if mapped to another name
894 if *filtered == item {
895 if inPartition != nil {
896 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
897 }
898 capturePartition := partition
899 inPartition = &capturePartition
900 }
901 }
902
903 // if not specified in a partition, add to remainder partition if one exists
904 if !wasFiltered && remainderPartition != nil {
905 partitionToLabels.appendIncludes(*remainderPartition, item)
906 }
907 }
908
909 // ensure empty lists are maintained
910 if value.Excludes != nil {
911 for _, partition := range partitionNames {
912 partitionToLabels.excludes(partition, value.Excludes)
913 }
914 }
915
916 for partition, list := range partitionToLabels {
917 val := ret[partition]
918 (&val).SetSelectValue(axis, config, *list)
919 ret[partition] = val
920 }
921 }
922
923 partitionLabelList(NoConfigAxis, "")
924 for axis, configToList := range lla.ConfigurableValues {
925 for config, _ := range configToList {
926 partitionLabelList(axis, config)
927 }
928 }
929 return ret
930}
931
Jingwen Chen5d864492021-02-24 07:20:12 -0500932// StringListAttribute corresponds to the string_list Bazel attribute type with
933// support for additional metadata, like configurations.
934type StringListAttribute struct {
935 // The base value of the string list attribute.
936 Value []string
937
Liz Kammer9abd62d2021-05-21 08:37:59 -0400938 // The configured attribute label list Values. Optional
939 // a map of independent configurability axes
940 ConfigurableValues configurableStringLists
941}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000942
Liz Kammer9abd62d2021-05-21 08:37:59 -0400943type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400944
Liz Kammer9abd62d2021-05-21 08:37:59 -0400945func (csl configurableStringLists) Append(other configurableStringLists) {
946 for axis, otherSelects := range other {
947 selects := csl[axis]
948 if selects == nil {
949 selects = make(stringListSelectValues, len(otherSelects))
950 }
951 selects.appendSelects(otherSelects)
952 csl[axis] = selects
953 }
954}
955
956func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
957 if csl[axis] == nil {
958 csl[axis] = make(stringListSelectValues)
959 }
960 csl[axis][config] = list
961}
962
963type stringListSelectValues map[string][]string
964
965func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
966 for k, v := range other {
967 sl[k] = append(sl[k], v...)
968 }
969}
970
971func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
972 for _, val := range sl {
973 if len(val) > 0 {
974 return true
975 }
976 }
977 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500978}
979
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000980// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
981func MakeStringListAttribute(value []string) StringListAttribute {
982 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400983 return StringListAttribute{
984 Value: value,
985 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400986 }
987}
988
Liz Kammer9abd62d2021-05-21 08:37:59 -0400989// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
990func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500991 for _, selectValues := range sla.ConfigurableValues {
992 if len(selectValues) > 0 {
993 return true
994 }
995 }
996 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400997}
998
Jingwen Chened9c17d2021-04-13 07:14:55 +0000999// Append appends all values, including os and arch specific ones, from another
1000// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001001func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001002 sla.Value = append(sla.Value, other.Value...)
1003 if sla.ConfigurableValues == nil {
1004 sla.ConfigurableValues = make(configurableStringLists)
1005 }
1006 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001007 return sla
1008}
1009
1010func (sla *StringListAttribute) Clone() *StringListAttribute {
1011 result := &StringListAttribute{}
1012 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001013}
1014
1015// SetSelectValue set a value for a bazel select for the given axis, config and value.
1016func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1017 axis.validateConfig(config)
1018 switch axis.configurationType {
1019 case noConfig:
1020 sla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -04001021 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001022 if sla.ConfigurableValues == nil {
1023 sla.ConfigurableValues = make(configurableStringLists)
1024 }
1025 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1026 default:
1027 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1028 }
1029}
1030
1031// SelectValue gets a value for a bazel select for the given axis and config.
1032func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1033 axis.validateConfig(config)
1034 switch axis.configurationType {
1035 case noConfig:
1036 return sla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -04001037 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001038 return sla.ConfigurableValues[axis][config]
1039 default:
1040 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1041 }
1042}
1043
1044// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1045func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1046 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
1047 for k := range sla.ConfigurableValues {
1048 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001049 }
1050
Liz Kammer9abd62d2021-05-21 08:37:59 -04001051 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1052 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +00001053}
1054
Liz Kammer5fad5012021-09-09 14:08:21 -04001055// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1056// configuration-specific values. For example, if we would convert this StringListAttribute as:
1057// ["a", "b", "c"] + select({
1058// "//condition:one": ["a", "d"],
1059// "//conditions:default": [],
1060// })
1061// after this function, we would convert this StringListAttribute as:
1062// ["a", "b", "c"] + select({
1063// "//condition:one": ["d"],
1064// "//conditions:default": [],
1065// })
1066func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1067 base := sla.Value
1068 for axis, configToList := range sla.ConfigurableValues {
1069 for config, list := range configToList {
1070 remaining := SubtractStrings(list, base)
1071 if len(remaining) == 0 {
1072 delete(sla.ConfigurableValues[axis], config)
1073 } else {
1074 sla.ConfigurableValues[axis][config] = remaining
1075 }
1076 }
1077 }
1078}
1079
Liz Kammera060c452021-03-24 10:14:47 -04001080// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1081// Starlark string.format compatible tag for productVariable.
1082func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
1083 ret := make([]string, 0, len(slice))
1084 changesMade := false
1085 for _, s := range slice {
1086 newS, changed := TryVariableSubstitution(s, productVariable)
1087 ret = append(ret, newS)
1088 changesMade = changesMade || changed
1089 }
1090 return ret, changesMade
1091}
1092
1093// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1094// string.format compatible tag for productVariable.
1095func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001096 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001097 return sub, s != sub
1098}