blob: e29b9e1372f4053e8422ad40121e0c26928fbe7d [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
Sam Delmericoc0161432022-02-25 21:34:51 +000068// MakeLabelList creates a LabelList from a list Label
69func MakeLabelList(labels []Label) LabelList {
70 return LabelList{
71 Includes: labels,
72 Excludes: nil,
73 }
74}
75
Chris Parsons51f8c392021-08-03 21:01:05 -040076func (ll *LabelList) Equals(other LabelList) bool {
77 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
78 return false
79 }
80 for i, _ := range ll.Includes {
81 if ll.Includes[i] != other.Includes[i] {
82 return false
83 }
84 }
85 for i, _ := range ll.Excludes {
86 if ll.Excludes[i] != other.Excludes[i] {
87 return false
88 }
89 }
90 return true
91}
92
Liz Kammer9abd62d2021-05-21 08:37:59 -040093func (ll *LabelList) IsNil() bool {
94 return ll.Includes == nil && ll.Excludes == nil
95}
96
Liz Kammer74deed42021-06-02 13:02:03 -040097func (ll *LabelList) deepCopy() LabelList {
98 return LabelList{
99 Includes: ll.Includes[:],
100 Excludes: ll.Excludes[:],
101 }
102}
103
Jingwen Chen63930982021-03-24 10:04:33 -0400104// uniqueParentDirectories returns a list of the unique parent directories for
105// all files in ll.Includes.
106func (ll *LabelList) uniqueParentDirectories() []string {
107 dirMap := map[string]bool{}
108 for _, label := range ll.Includes {
109 dirMap[filepath.Dir(label.Label)] = true
110 }
111 dirs := []string{}
112 for dir := range dirMap {
113 dirs = append(dirs, dir)
114 }
115 return dirs
116}
117
Liz Kammer12615db2021-09-28 09:19:17 -0400118// Add inserts the label Label at the end of the LabelList.
119func (ll *LabelList) Add(label *Label) {
120 if label == nil {
121 return
122 }
123 ll.Includes = append(ll.Includes, *label)
124}
125
Liz Kammer356f7d42021-01-26 09:18:53 -0500126// Append appends the fields of other labelList to the corresponding fields of ll.
127func (ll *LabelList) Append(other LabelList) {
128 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
129 ll.Includes = append(ll.Includes, other.Includes...)
130 }
131 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
132 ll.Excludes = append(other.Excludes, other.Excludes...)
133 }
134}
Jingwen Chen5d864492021-02-24 07:20:12 -0500135
Jingwen Chened9c17d2021-04-13 07:14:55 +0000136// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
137// the slice in a sorted order.
138func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000139 uniqueLabelsSet := make(map[Label]bool)
140 for _, l := range originalLabels {
141 uniqueLabelsSet[l] = true
142 }
143 var uniqueLabels []Label
144 for l, _ := range uniqueLabelsSet {
145 uniqueLabels = append(uniqueLabels, l)
146 }
147 sort.SliceStable(uniqueLabels, func(i, j int) bool {
148 return uniqueLabels[i].Label < uniqueLabels[j].Label
149 })
150 return uniqueLabels
151}
152
Liz Kammer9abd62d2021-05-21 08:37:59 -0400153func FirstUniqueBazelLabels(originalLabels []Label) []Label {
154 var labels []Label
155 found := make(map[Label]bool, len(originalLabels))
156 for _, l := range originalLabels {
157 if _, ok := found[l]; ok {
158 continue
159 }
160 labels = append(labels, l)
161 found[l] = true
162 }
163 return labels
164}
165
166func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
167 var uniqueLabelList LabelList
168 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
169 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
170 return uniqueLabelList
171}
172
173func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000174 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000175 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
176 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000177 return uniqueLabelList
178}
179
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000180// Subtract needle from haystack
181func SubtractStrings(haystack []string, needle []string) []string {
182 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400183 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000184 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400185 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000186 }
187
188 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400189 for _, s := range haystack {
190 if exclude := needleMap[s]; !exclude {
191 strings = append(strings, s)
192 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000193 }
194
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000195 return strings
196}
197
198// Subtract needle from haystack
199func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
200 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400201 needleMap := make(map[Label]bool)
202 for _, s := range needle {
203 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000204 }
205
206 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400207 for _, label := range haystack {
208 if exclude := needleMap[label]; !exclude {
209 labels = append(labels, label)
210 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000211 }
212
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000213 return labels
214}
215
Chris Parsons484e50a2021-05-13 15:13:04 -0400216// Appends two LabelLists, returning the combined list.
217func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
218 var result LabelList
219 result.Includes = append(a.Includes, b.Includes...)
220 result.Excludes = append(a.Excludes, b.Excludes...)
221 return result
222}
223
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000224// Subtract needle from haystack
225func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
226 var result LabelList
227 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
228 // NOTE: Excludes are intentionally not subtracted
229 result.Excludes = haystack.Excludes
230 return result
231}
232
Jingwen Chenc1c26502021-04-05 10:35:13 +0000233type Attribute interface {
234 HasConfigurableValues() bool
235}
236
Liz Kammer9abd62d2021-05-21 08:37:59 -0400237type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400238
Liz Kammer9abd62d2021-05-21 08:37:59 -0400239type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400240
Liz Kammer9abd62d2021-05-21 08:37:59 -0400241func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
242 if cl[axis] == nil {
243 cl[axis] = make(labelSelectValues)
244 }
245 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400246}
247
248// Represents an attribute whose value is a single label
249type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400250 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400251
Liz Kammer9abd62d2021-05-21 08:37:59 -0400252 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200253}
254
Chris Parsons58852a02021-12-09 18:10:18 -0500255func (la *LabelAttribute) axisTypes() map[configurationType]bool {
256 types := map[configurationType]bool{}
257 for k := range la.ConfigurableValues {
258 if len(la.ConfigurableValues[k]) > 0 {
259 types[k.configurationType] = true
260 }
261 }
262 return types
263}
264
265// Collapse reduces the configurable axes of the label attribute to a single axis.
266// This is necessary for final writing to bp2build, as a configurable label
267// attribute can only be comprised by a single select.
268func (la *LabelAttribute) Collapse() error {
269 axisTypes := la.axisTypes()
270 _, containsOs := axisTypes[os]
271 _, containsArch := axisTypes[arch]
272 _, containsOsArch := axisTypes[osArch]
273 _, containsProductVariables := axisTypes[productVariables]
274 if containsProductVariables {
275 if containsOs || containsArch || containsOsArch {
276 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
277 }
278 }
279 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
280 // If a bool attribute has both os and arch configuration axes, the only
281 // way to successfully union their values is to increase the granularity
282 // of the configuration criteria to os_arch.
283 for osType, supportedArchs := range osToArchMap {
284 for _, supportedArch := range supportedArchs {
285 osArch := osArchString(osType, supportedArch)
286 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
287 // Do nothing, as the arch_os is explicitly defined already.
288 } else {
289 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
290 osVal := la.SelectValue(OsConfigurationAxis, osType)
291 if osVal != nil && archVal != nil {
292 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
293 // runs after os mutator.
294 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
295 } else if osVal != nil && archVal == nil {
296 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
297 } else if osVal == nil && archVal != nil {
298 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
299 }
300 }
301 }
302 }
303 // All os_arch values are now set. Clear os and arch axes.
304 delete(la.ConfigurableValues, ArchConfigurationAxis)
305 delete(la.ConfigurableValues, OsConfigurationAxis)
306 }
307 return nil
308}
309
Liz Kammer9abd62d2021-05-21 08:37:59 -0400310// HasConfigurableValues returns whether there are configurable values set for this label.
311func (la LabelAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500312 for _, selectValues := range la.ConfigurableValues {
313 if len(selectValues) > 0 {
314 return true
315 }
316 }
317 return false
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200318}
319
Liz Kammer9abd62d2021-05-21 08:37:59 -0400320// SetValue sets the base, non-configured value for the Label
321func (la *LabelAttribute) SetValue(value Label) {
322 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400323}
324
Liz Kammer9abd62d2021-05-21 08:37:59 -0400325// SetSelectValue set a value for a bazel select for the given axis, config and value.
326func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
327 axis.validateConfig(config)
328 switch axis.configurationType {
329 case noConfig:
330 la.Value = &value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400331 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400332 if la.ConfigurableValues == nil {
333 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400334 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400335 la.ConfigurableValues.setValueForAxis(axis, config, &value)
336 default:
337 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
338 }
339}
340
341// SelectValue gets a value for a bazel select for the given axis and config.
Chris Parsons58852a02021-12-09 18:10:18 -0500342func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400343 axis.validateConfig(config)
344 switch axis.configurationType {
345 case noConfig:
Chris Parsons58852a02021-12-09 18:10:18 -0500346 return la.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400347 case arch, os, osArch, productVariables:
Chris Parsons58852a02021-12-09 18:10:18 -0500348 return la.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400349 default:
350 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
351 }
352}
353
354// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
355func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
356 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
357 for k := range la.ConfigurableValues {
358 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400359 }
360
Liz Kammer9abd62d2021-05-21 08:37:59 -0400361 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
362 return keys
363}
364
Sam Delmericoc0161432022-02-25 21:34:51 +0000365// MakeLabelAttribute turns a string into a LabelAttribute
366func MakeLabelAttribute(label string) *LabelAttribute {
367 return &LabelAttribute{
368 Value: &Label{
369 Label: label,
370 },
371 }
372}
373
Liz Kammerd366c902021-06-03 13:43:01 -0400374type configToBools map[string]bool
375
376func (ctb configToBools) setValue(config string, value *bool) {
377 if value == nil {
378 if _, ok := ctb[config]; ok {
379 delete(ctb, config)
380 }
381 return
382 }
383 ctb[config] = *value
384}
385
386type configurableBools map[ConfigurationAxis]configToBools
387
388func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
389 if cb[axis] == nil {
390 cb[axis] = make(configToBools)
391 }
392 cb[axis].setValue(config, value)
393}
394
395// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
396type BoolAttribute struct {
397 Value *bool
398
399 ConfigurableValues configurableBools
400}
401
402// HasConfigurableValues returns whether there are configurable values for this attribute.
403func (ba BoolAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500404 for _, cfgToBools := range ba.ConfigurableValues {
405 if len(cfgToBools) > 0 {
406 return true
407 }
408 }
409 return false
Liz Kammerd366c902021-06-03 13:43:01 -0400410}
411
Liz Kammerdfeb1202022-05-13 17:20:20 -0400412// SetValue sets value for the no config axis
413func (ba *BoolAttribute) SetValue(value *bool) {
414 ba.SetSelectValue(NoConfigAxis, "", value)
415}
416
Liz Kammerd366c902021-06-03 13:43:01 -0400417// SetSelectValue sets value for the given axis/config.
418func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
419 axis.validateConfig(config)
420 switch axis.configurationType {
421 case noConfig:
422 ba.Value = value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400423 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400424 if ba.ConfigurableValues == nil {
425 ba.ConfigurableValues = make(configurableBools)
426 }
427 ba.ConfigurableValues.setValueForAxis(axis, config, value)
428 default:
429 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
430 }
431}
432
Chris Parsons58852a02021-12-09 18:10:18 -0500433// ToLabelListAttribute creates and returns a LabelListAttribute from this
434// bool attribute, where each bool in this attribute corresponds to a
435// label list value in the resultant attribute.
436func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
437 getLabelList := func(boolPtr *bool) LabelList {
438 if boolPtr == nil {
439 return LabelList{nil, nil}
440 } else if *boolPtr {
441 return trueVal
442 } else {
443 return falseVal
444 }
445 }
446
447 mainVal := getLabelList(ba.Value)
448 if !ba.HasConfigurableValues() {
449 return MakeLabelListAttribute(mainVal), nil
450 }
451
452 result := LabelListAttribute{}
453 if err := ba.Collapse(); err != nil {
454 return result, err
455 }
456
457 for axis, configToBools := range ba.ConfigurableValues {
458 if len(configToBools) < 1 {
459 continue
460 }
461 for config, boolPtr := range configToBools {
462 val := getLabelList(&boolPtr)
463 if !val.Equals(mainVal) {
464 result.SetSelectValue(axis, config, val)
465 }
466 }
467 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
468 }
469
470 return result, nil
471}
472
473// Collapse reduces the configurable axes of the boolean attribute to a single axis.
474// This is necessary for final writing to bp2build, as a configurable boolean
475// attribute can only be comprised by a single select.
476func (ba *BoolAttribute) Collapse() error {
477 axisTypes := ba.axisTypes()
478 _, containsOs := axisTypes[os]
479 _, containsArch := axisTypes[arch]
480 _, containsOsArch := axisTypes[osArch]
481 _, containsProductVariables := axisTypes[productVariables]
482 if containsProductVariables {
483 if containsOs || containsArch || containsOsArch {
484 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
485 }
486 }
487 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
488 // If a bool attribute has both os and arch configuration axes, the only
489 // way to successfully union their values is to increase the granularity
490 // of the configuration criteria to os_arch.
491 for osType, supportedArchs := range osToArchMap {
492 for _, supportedArch := range supportedArchs {
493 osArch := osArchString(osType, supportedArch)
494 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
495 // Do nothing, as the arch_os is explicitly defined already.
496 } else {
497 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
498 osVal := ba.SelectValue(OsConfigurationAxis, osType)
499 if osVal != nil && archVal != nil {
500 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
501 // runs after os mutator.
502 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
503 } else if osVal != nil && archVal == nil {
504 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
505 } else if osVal == nil && archVal != nil {
506 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
507 }
508 }
509 }
510 }
511 // All os_arch values are now set. Clear os and arch axes.
512 delete(ba.ConfigurableValues, ArchConfigurationAxis)
513 delete(ba.ConfigurableValues, OsConfigurationAxis)
514 // Verify post-condition; this should never fail, provided no additional
515 // axes are introduced.
516 if len(ba.ConfigurableValues) > 1 {
Liz Kammer07e106f2022-01-13 17:00:10 -0500517 panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
Chris Parsons58852a02021-12-09 18:10:18 -0500518 }
519 }
520 return nil
521}
522
523func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
524 types := map[configurationType]bool{}
525 for k := range ba.ConfigurableValues {
526 if len(ba.ConfigurableValues[k]) > 0 {
527 types[k.configurationType] = true
528 }
529 }
530 return types
531}
532
Liz Kammerd366c902021-06-03 13:43:01 -0400533// SelectValue gets the value for the given axis/config.
534func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
535 axis.validateConfig(config)
536 switch axis.configurationType {
537 case noConfig:
538 return ba.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400539 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400540 if v, ok := ba.ConfigurableValues[axis][config]; ok {
541 return &v
542 } else {
543 return nil
544 }
545 default:
546 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
547 }
548}
549
550// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
551func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
552 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
553 for k := range ba.ConfigurableValues {
554 keys = append(keys, k)
555 }
556
557 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
558 return keys
559}
560
Liz Kammer9abd62d2021-05-21 08:37:59 -0400561// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
562type labelListSelectValues map[string]LabelList
563
Liz Kammer12615db2021-09-28 09:19:17 -0400564func (ll labelListSelectValues) addSelects(label labelSelectValues) {
565 for k, v := range label {
566 if label == nil {
567 continue
568 }
569 l := ll[k]
570 (&l).Add(v)
571 ll[k] = l
572 }
573}
574
Chris Parsons77acf2e2021-12-03 17:27:16 -0500575func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400576 for k, v := range other {
577 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500578 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
579 l.Includes = []Label{}
580 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400581 (&l).Append(v)
582 ll[k] = l
583 }
584}
585
586// HasConfigurableValues returns whether there are configurable values within this set of selects.
587func (ll labelListSelectValues) HasConfigurableValues() bool {
588 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400589 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400590 return true
591 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400592 }
593 return false
594}
595
Jingwen Chen07027912021-03-15 06:02:43 -0400596// LabelListAttribute is used to represent a list of Bazel labels as an
597// attribute.
598type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400599 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400600 Value LabelList
601
Liz Kammer9abd62d2021-05-21 08:37:59 -0400602 // The configured attribute label list Values. Optional
603 // a map of independent configurability axes
604 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400605
606 // If true, differentiate between "nil" and "empty" list. nil means that
607 // this attribute should not be specified at all, and "empty" means that
608 // the attribute should be explicitly specified as an empty list.
609 // This mode facilitates use of attribute defaults: an empty list should
610 // override the default.
611 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000612
613 // If true, signal the intent to the code generator to emit all select keys,
614 // even if the Includes list for that key is empty. This mode facilitates
615 // specific select statements where an empty list for a non-default select
616 // key has a meaning.
617 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400618}
Jingwen Chen91220d72021-03-24 02:18:33 -0400619
Liz Kammer9abd62d2021-05-21 08:37:59 -0400620type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
621
622func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
623 if list.IsNil() {
624 if _, ok := cll[axis][config]; ok {
625 delete(cll[axis], config)
626 }
627 return
628 }
629 if cll[axis] == nil {
630 cll[axis] = make(labelListSelectValues)
631 }
632
633 cll[axis][config] = list
634}
635
Chris Parsons77acf2e2021-12-03 17:27:16 -0500636func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400637 for axis, otherSelects := range other {
638 selects := cll[axis]
639 if selects == nil {
640 selects = make(labelListSelectValues, len(otherSelects))
641 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500642 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400643 cll[axis] = selects
644 }
Jingwen Chen07027912021-03-15 06:02:43 -0400645}
646
Chris Parsons77acf2e2021-12-03 17:27:16 -0500647func (lla *LabelListAttribute) Clone() *LabelListAttribute {
648 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
649 return result.Append(*lla)
650}
651
Jingwen Chen07027912021-03-15 06:02:43 -0400652// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
653func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400654 return LabelListAttribute{
655 Value: value,
656 ConfigurableValues: make(configurableLabelLists),
657 }
658}
659
Cole Faust53b62092022-05-12 15:37:02 -0700660// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
661func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
662 return MakeLabelListAttribute(MakeLabelList([]Label{value}))
663}
664
Liz Kammer9abd62d2021-05-21 08:37:59 -0400665func (lla *LabelListAttribute) SetValue(list LabelList) {
666 lla.SetSelectValue(NoConfigAxis, "", list)
667}
668
669// SetSelectValue set a value for a bazel select for the given axis, config and value.
670func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
671 axis.validateConfig(config)
672 switch axis.configurationType {
673 case noConfig:
674 lla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400675 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400676 if lla.ConfigurableValues == nil {
677 lla.ConfigurableValues = make(configurableLabelLists)
678 }
679 lla.ConfigurableValues.setValueForAxis(axis, config, list)
680 default:
681 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
682 }
683}
684
685// SelectValue gets a value for a bazel select for the given axis and config.
686func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
687 axis.validateConfig(config)
688 switch axis.configurationType {
689 case noConfig:
690 return lla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400691 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400692 return lla.ConfigurableValues[axis][config]
693 default:
694 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
695 }
696}
697
698// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
699func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
700 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
701 for k := range lla.ConfigurableValues {
702 keys = append(keys, k)
703 }
704
705 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
706 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400707}
708
Jingwen Chened9c17d2021-04-13 07:14:55 +0000709// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500710// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
711func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
712 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
713 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400714 lla.Value.Includes = []Label{}
715 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400716 lla.Value.Append(other.Value)
717 if lla.ConfigurableValues == nil {
718 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400719 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500720 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
721 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400722}
723
Liz Kammer12615db2021-09-28 09:19:17 -0400724// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
725// LabelList within the LabelListAttribute
726func (lla *LabelListAttribute) Add(label *LabelAttribute) {
727 if label == nil {
728 return
729 }
730
731 lla.Value.Add(label.Value)
732 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
733 lla.ConfigurableValues = make(configurableLabelLists)
734 }
735 for axis, _ := range label.ConfigurableValues {
736 if _, exists := lla.ConfigurableValues[axis]; !exists {
737 lla.ConfigurableValues[axis] = make(labelListSelectValues)
738 }
739 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
740 }
741}
742
Liz Kammer9abd62d2021-05-21 08:37:59 -0400743// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
744func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500745 for _, selectValues := range lla.ConfigurableValues {
746 if len(selectValues) > 0 {
747 return true
748 }
749 }
750 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400751}
752
Chris Parsons69fa9f92021-07-13 11:47:44 -0400753// IsEmpty returns true if the attribute has no values under any configuration.
754func (lla LabelListAttribute) IsEmpty() bool {
755 if len(lla.Value.Includes) > 0 {
756 return false
757 }
758 for axis, _ := range lla.ConfigurableValues {
759 if lla.ConfigurableValues[axis].HasConfigurableValues() {
760 return false
761 }
762 }
763 return true
764}
765
Liz Kammer54309532021-12-14 12:21:22 -0500766// IsNil returns true if the attribute has not been set for any configuration.
767func (lla LabelListAttribute) IsNil() bool {
768 if lla.Value.Includes != nil {
769 return false
770 }
771 return !lla.HasConfigurableValues()
772}
773
774// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
775// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
776// be removed, e.g. if they could cause duplicate element failures.
777func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
778 val := lla.SelectValue(axis, config)
779 newList := SubtractBazelLabelList(val, labelList)
780 newList.Excludes = append(newList.Excludes, labelList.Includes...)
781 lla.SetSelectValue(axis, config, newList)
782}
783
Liz Kammer74deed42021-06-02 13:02:03 -0400784// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
785// the base value and included in default values as appropriate.
786func (lla *LabelListAttribute) ResolveExcludes() {
787 for axis, configToLabels := range lla.ConfigurableValues {
788 baseLabels := lla.Value.deepCopy()
789 for config, val := range configToLabels {
790 // Exclude config-specific excludes from base value
791 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
792
793 // add base values to config specific to add labels excluded by others in this axis
794 // then remove all config-specific excludes
795 allLabels := baseLabels.deepCopy()
796 allLabels.Append(val)
797 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
798 }
799
800 // After going through all configs, delete the duplicates in the config
801 // values that are already in the base Value.
802 for config, val := range configToLabels {
803 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
804 }
805
Jingwen Chen9af49a42021-11-02 10:27:17 +0000806 // Now that the Value list is finalized for this axis, compare it with
807 // the original list, and union the difference with the default
808 // condition for the axis.
809 difference := SubtractBazelLabelList(baseLabels, lla.Value)
810 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
811 existingDefaults.Append(difference)
812 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400813
814 // if everything ends up without includes, just delete the axis
815 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
816 delete(lla.ConfigurableValues, axis)
817 }
818 }
819}
820
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400821// OtherModuleContext is a limited context that has methods with information about other modules.
822type OtherModuleContext interface {
823 ModuleFromName(name string) (blueprint.Module, bool)
824 OtherModuleType(m blueprint.Module) string
825 OtherModuleName(m blueprint.Module) string
826 OtherModuleDir(m blueprint.Module) string
827 ModuleErrorf(fmt string, args ...interface{})
828}
829
830// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
831// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400832type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400833
834// LabelPartition contains descriptions of a partition for labels
835type LabelPartition struct {
836 // Extensions to include in this partition
837 Extensions []string
838 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
839 // the mapped label in the partition
840 LabelMapper LabelMapper
841 // Whether to store files not included in any other partition in a group of LabelPartitions
842 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
843 Keep_remainder bool
844}
845
846// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
847// partition
848type LabelPartitions map[string]LabelPartition
849
850// filter returns a pointer to a label if the label should be included in the partition or nil if
851// not.
852func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
853 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400854 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400855 return &Label{newLabel, label.OriginalModuleName}
856 }
857 }
858 for _, ext := range lf.Extensions {
859 if strings.HasSuffix(label.Label, ext) {
860 return &label
861 }
862 }
863
864 return nil
865}
866
867// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
868type PartitionToLabelListAttribute map[string]LabelListAttribute
869
870type partitionToLabelList map[string]*LabelList
871
872func (p partitionToLabelList) appendIncludes(partition string, label Label) {
873 if _, ok := p[partition]; !ok {
874 p[partition] = &LabelList{}
875 }
876 p[partition].Includes = append(p[partition].Includes, label)
877}
878
879func (p partitionToLabelList) excludes(partition string, excludes []Label) {
880 if _, ok := p[partition]; !ok {
881 p[partition] = &LabelList{}
882 }
883 p[partition].Excludes = excludes
884}
885
886// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
887func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
888 ret := PartitionToLabelListAttribute{}
889 var partitionNames []string
890 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
891 var remainderPartition *string
892 for p, f := range partitions {
893 partitionNames = append(partitionNames, p)
894 if f.Keep_remainder {
895 if remainderPartition != nil {
896 panic("only one partition can store the remainder")
897 }
898 // If we take the address of p in a loop, we'll end up with the last value of p in
899 // remainderPartition, we want the requested partition
900 capturePartition := p
901 remainderPartition = &capturePartition
902 }
903 }
904
905 partitionLabelList := func(axis ConfigurationAxis, config string) {
906 value := lla.SelectValue(axis, config)
907 partitionToLabels := partitionToLabelList{}
908 for _, item := range value.Includes {
909 wasFiltered := false
910 var inPartition *string
911 for partition, f := range partitions {
912 filtered := f.filter(ctx, item)
913 if filtered == nil {
914 // did not match this filter, keep looking
915 continue
916 }
917 wasFiltered = true
918 partitionToLabels.appendIncludes(partition, *filtered)
919 // don't need to check other partitions if this filter used the item,
920 // continue checking if mapped to another name
921 if *filtered == item {
922 if inPartition != nil {
923 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
924 }
925 capturePartition := partition
926 inPartition = &capturePartition
927 }
928 }
929
930 // if not specified in a partition, add to remainder partition if one exists
931 if !wasFiltered && remainderPartition != nil {
932 partitionToLabels.appendIncludes(*remainderPartition, item)
933 }
934 }
935
936 // ensure empty lists are maintained
937 if value.Excludes != nil {
938 for _, partition := range partitionNames {
939 partitionToLabels.excludes(partition, value.Excludes)
940 }
941 }
942
943 for partition, list := range partitionToLabels {
944 val := ret[partition]
945 (&val).SetSelectValue(axis, config, *list)
946 ret[partition] = val
947 }
948 }
949
950 partitionLabelList(NoConfigAxis, "")
951 for axis, configToList := range lla.ConfigurableValues {
952 for config, _ := range configToList {
953 partitionLabelList(axis, config)
954 }
955 }
956 return ret
957}
958
Jingwen Chen5d864492021-02-24 07:20:12 -0500959// StringListAttribute corresponds to the string_list Bazel attribute type with
960// support for additional metadata, like configurations.
961type StringListAttribute struct {
962 // The base value of the string list attribute.
963 Value []string
964
Liz Kammer9abd62d2021-05-21 08:37:59 -0400965 // The configured attribute label list Values. Optional
966 // a map of independent configurability axes
967 ConfigurableValues configurableStringLists
968}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000969
Liz Kammer9abd62d2021-05-21 08:37:59 -0400970type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400971
Liz Kammer9abd62d2021-05-21 08:37:59 -0400972func (csl configurableStringLists) Append(other configurableStringLists) {
973 for axis, otherSelects := range other {
974 selects := csl[axis]
975 if selects == nil {
976 selects = make(stringListSelectValues, len(otherSelects))
977 }
978 selects.appendSelects(otherSelects)
979 csl[axis] = selects
980 }
981}
982
983func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
984 if csl[axis] == nil {
985 csl[axis] = make(stringListSelectValues)
986 }
987 csl[axis][config] = list
988}
989
990type stringListSelectValues map[string][]string
991
992func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
993 for k, v := range other {
994 sl[k] = append(sl[k], v...)
995 }
996}
997
998func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
999 for _, val := range sl {
1000 if len(val) > 0 {
1001 return true
1002 }
1003 }
1004 return false
Jingwen Chen5d864492021-02-24 07:20:12 -05001005}
1006
Rupert Shuttleworthb8151682021-04-06 20:06:21 +00001007// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1008func MakeStringListAttribute(value []string) StringListAttribute {
1009 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -04001010 return StringListAttribute{
1011 Value: value,
1012 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -04001013 }
1014}
1015
Liz Kammer9abd62d2021-05-21 08:37:59 -04001016// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1017func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -05001018 for _, selectValues := range sla.ConfigurableValues {
1019 if len(selectValues) > 0 {
1020 return true
1021 }
1022 }
1023 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -04001024}
1025
Jingwen Chened9c17d2021-04-13 07:14:55 +00001026// Append appends all values, including os and arch specific ones, from another
1027// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001028func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001029 sla.Value = append(sla.Value, other.Value...)
1030 if sla.ConfigurableValues == nil {
1031 sla.ConfigurableValues = make(configurableStringLists)
1032 }
1033 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001034 return sla
1035}
1036
1037func (sla *StringListAttribute) Clone() *StringListAttribute {
1038 result := &StringListAttribute{}
1039 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001040}
1041
1042// SetSelectValue set a value for a bazel select for the given axis, config and value.
1043func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1044 axis.validateConfig(config)
1045 switch axis.configurationType {
1046 case noConfig:
1047 sla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -04001048 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001049 if sla.ConfigurableValues == nil {
1050 sla.ConfigurableValues = make(configurableStringLists)
1051 }
1052 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1053 default:
1054 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1055 }
1056}
1057
1058// SelectValue gets a value for a bazel select for the given axis and config.
1059func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1060 axis.validateConfig(config)
1061 switch axis.configurationType {
1062 case noConfig:
1063 return sla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -04001064 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001065 return sla.ConfigurableValues[axis][config]
1066 default:
1067 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1068 }
1069}
1070
1071// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1072func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1073 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
1074 for k := range sla.ConfigurableValues {
1075 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001076 }
1077
Liz Kammer9abd62d2021-05-21 08:37:59 -04001078 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1079 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +00001080}
1081
Liz Kammer5fad5012021-09-09 14:08:21 -04001082// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1083// configuration-specific values. For example, if we would convert this StringListAttribute as:
1084// ["a", "b", "c"] + select({
1085// "//condition:one": ["a", "d"],
1086// "//conditions:default": [],
1087// })
1088// after this function, we would convert this StringListAttribute as:
1089// ["a", "b", "c"] + select({
1090// "//condition:one": ["d"],
1091// "//conditions:default": [],
1092// })
1093func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1094 base := sla.Value
1095 for axis, configToList := range sla.ConfigurableValues {
1096 for config, list := range configToList {
1097 remaining := SubtractStrings(list, base)
1098 if len(remaining) == 0 {
1099 delete(sla.ConfigurableValues[axis], config)
1100 } else {
1101 sla.ConfigurableValues[axis][config] = remaining
1102 }
1103 }
1104 }
1105}
1106
Liz Kammera060c452021-03-24 10:14:47 -04001107// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1108// Starlark string.format compatible tag for productVariable.
1109func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
1110 ret := make([]string, 0, len(slice))
1111 changesMade := false
1112 for _, s := range slice {
1113 newS, changed := TryVariableSubstitution(s, productVariable)
1114 ret = append(ret, newS)
1115 changesMade = changesMade || changed
1116 }
1117 return ret, changesMade
1118}
1119
1120// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1121// string.format compatible tag for productVariable.
1122func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001123 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001124 return sub, s != sub
1125}