blob: bffd97bc5627283238243f4178d1148b15b57746 [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
Sam Delmerico3177a6e2022-06-21 19:28:33 +000097func (ll *LabelList) IsEmpty() bool {
98 return len(ll.Includes) == 0 && len(ll.Excludes) == 0
99}
100
Liz Kammer74deed42021-06-02 13:02:03 -0400101func (ll *LabelList) deepCopy() LabelList {
102 return LabelList{
103 Includes: ll.Includes[:],
104 Excludes: ll.Excludes[:],
105 }
106}
107
Jingwen Chen63930982021-03-24 10:04:33 -0400108// uniqueParentDirectories returns a list of the unique parent directories for
109// all files in ll.Includes.
110func (ll *LabelList) uniqueParentDirectories() []string {
111 dirMap := map[string]bool{}
112 for _, label := range ll.Includes {
113 dirMap[filepath.Dir(label.Label)] = true
114 }
115 dirs := []string{}
116 for dir := range dirMap {
117 dirs = append(dirs, dir)
118 }
119 return dirs
120}
121
Liz Kammer12615db2021-09-28 09:19:17 -0400122// Add inserts the label Label at the end of the LabelList.
123func (ll *LabelList) Add(label *Label) {
124 if label == nil {
125 return
126 }
127 ll.Includes = append(ll.Includes, *label)
128}
129
Liz Kammer356f7d42021-01-26 09:18:53 -0500130// Append appends the fields of other labelList to the corresponding fields of ll.
131func (ll *LabelList) Append(other LabelList) {
132 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
133 ll.Includes = append(ll.Includes, other.Includes...)
134 }
135 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
136 ll.Excludes = append(other.Excludes, other.Excludes...)
137 }
138}
Jingwen Chen5d864492021-02-24 07:20:12 -0500139
Jingwen Chened9c17d2021-04-13 07:14:55 +0000140// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
141// the slice in a sorted order.
142func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000143 uniqueLabelsSet := make(map[Label]bool)
144 for _, l := range originalLabels {
145 uniqueLabelsSet[l] = true
146 }
147 var uniqueLabels []Label
148 for l, _ := range uniqueLabelsSet {
149 uniqueLabels = append(uniqueLabels, l)
150 }
151 sort.SliceStable(uniqueLabels, func(i, j int) bool {
152 return uniqueLabels[i].Label < uniqueLabels[j].Label
153 })
154 return uniqueLabels
155}
156
Liz Kammer9abd62d2021-05-21 08:37:59 -0400157func FirstUniqueBazelLabels(originalLabels []Label) []Label {
158 var labels []Label
159 found := make(map[Label]bool, len(originalLabels))
160 for _, l := range originalLabels {
161 if _, ok := found[l]; ok {
162 continue
163 }
164 labels = append(labels, l)
165 found[l] = true
166 }
167 return labels
168}
169
170func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
171 var uniqueLabelList LabelList
172 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
173 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
174 return uniqueLabelList
175}
176
177func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000178 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000179 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
180 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000181 return uniqueLabelList
182}
183
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000184// Subtract needle from haystack
185func SubtractStrings(haystack []string, needle []string) []string {
186 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400187 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000188 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400189 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000190 }
191
192 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400193 for _, s := range haystack {
194 if exclude := needleMap[s]; !exclude {
195 strings = append(strings, s)
196 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000197 }
198
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000199 return strings
200}
201
202// Subtract needle from haystack
203func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
204 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400205 needleMap := make(map[Label]bool)
206 for _, s := range needle {
207 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000208 }
209
210 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400211 for _, label := range haystack {
212 if exclude := needleMap[label]; !exclude {
213 labels = append(labels, label)
214 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000215 }
216
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000217 return labels
218}
219
Chris Parsons484e50a2021-05-13 15:13:04 -0400220// Appends two LabelLists, returning the combined list.
221func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
222 var result LabelList
223 result.Includes = append(a.Includes, b.Includes...)
224 result.Excludes = append(a.Excludes, b.Excludes...)
225 return result
226}
227
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000228// Subtract needle from haystack
229func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
230 var result LabelList
231 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
232 // NOTE: Excludes are intentionally not subtracted
233 result.Excludes = haystack.Excludes
234 return result
235}
236
Jingwen Chenc1c26502021-04-05 10:35:13 +0000237type Attribute interface {
238 HasConfigurableValues() bool
239}
240
Liz Kammer9abd62d2021-05-21 08:37:59 -0400241type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400242
Liz Kammer9abd62d2021-05-21 08:37:59 -0400243type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400244
Liz Kammer9abd62d2021-05-21 08:37:59 -0400245func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
246 if cl[axis] == nil {
247 cl[axis] = make(labelSelectValues)
248 }
249 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400250}
251
252// Represents an attribute whose value is a single label
253type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400254 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400255
Liz Kammer9abd62d2021-05-21 08:37:59 -0400256 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200257}
258
Chris Parsons58852a02021-12-09 18:10:18 -0500259func (la *LabelAttribute) axisTypes() map[configurationType]bool {
260 types := map[configurationType]bool{}
261 for k := range la.ConfigurableValues {
262 if len(la.ConfigurableValues[k]) > 0 {
263 types[k.configurationType] = true
264 }
265 }
266 return types
267}
268
269// Collapse reduces the configurable axes of the label attribute to a single axis.
270// This is necessary for final writing to bp2build, as a configurable label
271// attribute can only be comprised by a single select.
272func (la *LabelAttribute) Collapse() error {
273 axisTypes := la.axisTypes()
274 _, containsOs := axisTypes[os]
275 _, containsArch := axisTypes[arch]
276 _, containsOsArch := axisTypes[osArch]
277 _, containsProductVariables := axisTypes[productVariables]
278 if containsProductVariables {
279 if containsOs || containsArch || containsOsArch {
280 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
281 }
282 }
283 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
284 // If a bool attribute has both os and arch configuration axes, the only
285 // way to successfully union their values is to increase the granularity
286 // of the configuration criteria to os_arch.
287 for osType, supportedArchs := range osToArchMap {
288 for _, supportedArch := range supportedArchs {
289 osArch := osArchString(osType, supportedArch)
290 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
291 // Do nothing, as the arch_os is explicitly defined already.
292 } else {
293 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
294 osVal := la.SelectValue(OsConfigurationAxis, osType)
295 if osVal != nil && archVal != nil {
296 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
297 // runs after os mutator.
298 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
299 } else if osVal != nil && archVal == nil {
300 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
301 } else if osVal == nil && archVal != nil {
302 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
303 }
304 }
305 }
306 }
307 // All os_arch values are now set. Clear os and arch axes.
308 delete(la.ConfigurableValues, ArchConfigurationAxis)
309 delete(la.ConfigurableValues, OsConfigurationAxis)
310 }
311 return nil
312}
313
Liz Kammer9abd62d2021-05-21 08:37:59 -0400314// HasConfigurableValues returns whether there are configurable values set for this label.
315func (la LabelAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500316 for _, selectValues := range la.ConfigurableValues {
317 if len(selectValues) > 0 {
318 return true
319 }
320 }
321 return false
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200322}
323
Liz Kammer9abd62d2021-05-21 08:37:59 -0400324// SetValue sets the base, non-configured value for the Label
325func (la *LabelAttribute) SetValue(value Label) {
326 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400327}
328
Liz Kammer9abd62d2021-05-21 08:37:59 -0400329// SetSelectValue set a value for a bazel select for the given axis, config and value.
330func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
331 axis.validateConfig(config)
332 switch axis.configurationType {
333 case noConfig:
334 la.Value = &value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400335 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400336 if la.ConfigurableValues == nil {
337 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400338 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400339 la.ConfigurableValues.setValueForAxis(axis, config, &value)
340 default:
341 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
342 }
343}
344
345// SelectValue gets a value for a bazel select for the given axis and config.
Chris Parsons58852a02021-12-09 18:10:18 -0500346func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400347 axis.validateConfig(config)
348 switch axis.configurationType {
349 case noConfig:
Chris Parsons58852a02021-12-09 18:10:18 -0500350 return la.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400351 case arch, os, osArch, productVariables:
Chris Parsons58852a02021-12-09 18:10:18 -0500352 return la.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400353 default:
354 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
355 }
356}
357
358// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
359func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
360 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
361 for k := range la.ConfigurableValues {
362 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400363 }
364
Liz Kammer9abd62d2021-05-21 08:37:59 -0400365 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
366 return keys
367}
368
Sam Delmericoc0161432022-02-25 21:34:51 +0000369// MakeLabelAttribute turns a string into a LabelAttribute
370func MakeLabelAttribute(label string) *LabelAttribute {
371 return &LabelAttribute{
372 Value: &Label{
373 Label: label,
374 },
375 }
376}
377
Liz Kammerd366c902021-06-03 13:43:01 -0400378type configToBools map[string]bool
379
380func (ctb configToBools) setValue(config string, value *bool) {
381 if value == nil {
382 if _, ok := ctb[config]; ok {
383 delete(ctb, config)
384 }
385 return
386 }
387 ctb[config] = *value
388}
389
390type configurableBools map[ConfigurationAxis]configToBools
391
392func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
393 if cb[axis] == nil {
394 cb[axis] = make(configToBools)
395 }
396 cb[axis].setValue(config, value)
397}
398
399// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
400type BoolAttribute struct {
401 Value *bool
402
403 ConfigurableValues configurableBools
404}
405
406// HasConfigurableValues returns whether there are configurable values for this attribute.
407func (ba BoolAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500408 for _, cfgToBools := range ba.ConfigurableValues {
409 if len(cfgToBools) > 0 {
410 return true
411 }
412 }
413 return false
Liz Kammerd366c902021-06-03 13:43:01 -0400414}
415
Liz Kammerdfeb1202022-05-13 17:20:20 -0400416// SetValue sets value for the no config axis
417func (ba *BoolAttribute) SetValue(value *bool) {
418 ba.SetSelectValue(NoConfigAxis, "", value)
419}
420
Liz Kammerd366c902021-06-03 13:43:01 -0400421// SetSelectValue sets value for the given axis/config.
422func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
423 axis.validateConfig(config)
424 switch axis.configurationType {
425 case noConfig:
426 ba.Value = value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400427 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400428 if ba.ConfigurableValues == nil {
429 ba.ConfigurableValues = make(configurableBools)
430 }
431 ba.ConfigurableValues.setValueForAxis(axis, config, value)
432 default:
433 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
434 }
435}
436
Chris Parsons58852a02021-12-09 18:10:18 -0500437// ToLabelListAttribute creates and returns a LabelListAttribute from this
438// bool attribute, where each bool in this attribute corresponds to a
439// label list value in the resultant attribute.
440func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
441 getLabelList := func(boolPtr *bool) LabelList {
442 if boolPtr == nil {
443 return LabelList{nil, nil}
444 } else if *boolPtr {
445 return trueVal
446 } else {
447 return falseVal
448 }
449 }
450
451 mainVal := getLabelList(ba.Value)
452 if !ba.HasConfigurableValues() {
453 return MakeLabelListAttribute(mainVal), nil
454 }
455
456 result := LabelListAttribute{}
457 if err := ba.Collapse(); err != nil {
458 return result, err
459 }
460
461 for axis, configToBools := range ba.ConfigurableValues {
462 if len(configToBools) < 1 {
463 continue
464 }
465 for config, boolPtr := range configToBools {
466 val := getLabelList(&boolPtr)
467 if !val.Equals(mainVal) {
468 result.SetSelectValue(axis, config, val)
469 }
470 }
471 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
472 }
473
474 return result, nil
475}
476
477// Collapse reduces the configurable axes of the boolean attribute to a single axis.
478// This is necessary for final writing to bp2build, as a configurable boolean
479// attribute can only be comprised by a single select.
480func (ba *BoolAttribute) Collapse() error {
481 axisTypes := ba.axisTypes()
482 _, containsOs := axisTypes[os]
483 _, containsArch := axisTypes[arch]
484 _, containsOsArch := axisTypes[osArch]
485 _, containsProductVariables := axisTypes[productVariables]
486 if containsProductVariables {
487 if containsOs || containsArch || containsOsArch {
488 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
489 }
490 }
491 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
492 // If a bool attribute has both os and arch configuration axes, the only
493 // way to successfully union their values is to increase the granularity
494 // of the configuration criteria to os_arch.
495 for osType, supportedArchs := range osToArchMap {
496 for _, supportedArch := range supportedArchs {
497 osArch := osArchString(osType, supportedArch)
498 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
499 // Do nothing, as the arch_os is explicitly defined already.
500 } else {
501 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
502 osVal := ba.SelectValue(OsConfigurationAxis, osType)
503 if osVal != nil && archVal != nil {
504 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
505 // runs after os mutator.
506 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
507 } else if osVal != nil && archVal == nil {
508 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
509 } else if osVal == nil && archVal != nil {
510 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
511 }
512 }
513 }
514 }
515 // All os_arch values are now set. Clear os and arch axes.
516 delete(ba.ConfigurableValues, ArchConfigurationAxis)
517 delete(ba.ConfigurableValues, OsConfigurationAxis)
518 // Verify post-condition; this should never fail, provided no additional
519 // axes are introduced.
520 if len(ba.ConfigurableValues) > 1 {
Liz Kammer07e106f2022-01-13 17:00:10 -0500521 panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
Chris Parsons58852a02021-12-09 18:10:18 -0500522 }
523 }
524 return nil
525}
526
527func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
528 types := map[configurationType]bool{}
529 for k := range ba.ConfigurableValues {
530 if len(ba.ConfigurableValues[k]) > 0 {
531 types[k.configurationType] = true
532 }
533 }
534 return types
535}
536
Liz Kammerd366c902021-06-03 13:43:01 -0400537// SelectValue gets the value for the given axis/config.
538func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
539 axis.validateConfig(config)
540 switch axis.configurationType {
541 case noConfig:
542 return ba.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400543 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400544 if v, ok := ba.ConfigurableValues[axis][config]; ok {
545 return &v
546 } else {
547 return nil
548 }
549 default:
550 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
551 }
552}
553
554// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
555func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
556 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
557 for k := range ba.ConfigurableValues {
558 keys = append(keys, k)
559 }
560
561 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
562 return keys
563}
564
Liz Kammer9abd62d2021-05-21 08:37:59 -0400565// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
566type labelListSelectValues map[string]LabelList
567
Liz Kammer12615db2021-09-28 09:19:17 -0400568func (ll labelListSelectValues) addSelects(label labelSelectValues) {
569 for k, v := range label {
570 if label == nil {
571 continue
572 }
573 l := ll[k]
574 (&l).Add(v)
575 ll[k] = l
576 }
577}
578
Chris Parsons77acf2e2021-12-03 17:27:16 -0500579func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400580 for k, v := range other {
581 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500582 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
583 l.Includes = []Label{}
584 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400585 (&l).Append(v)
586 ll[k] = l
587 }
588}
589
590// HasConfigurableValues returns whether there are configurable values within this set of selects.
591func (ll labelListSelectValues) HasConfigurableValues() bool {
592 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400593 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400594 return true
595 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400596 }
597 return false
598}
599
Jingwen Chen07027912021-03-15 06:02:43 -0400600// LabelListAttribute is used to represent a list of Bazel labels as an
601// attribute.
602type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400603 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400604 Value LabelList
605
Liz Kammer9abd62d2021-05-21 08:37:59 -0400606 // The configured attribute label list Values. Optional
607 // a map of independent configurability axes
608 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400609
610 // If true, differentiate between "nil" and "empty" list. nil means that
611 // this attribute should not be specified at all, and "empty" means that
612 // the attribute should be explicitly specified as an empty list.
613 // This mode facilitates use of attribute defaults: an empty list should
614 // override the default.
615 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000616
617 // If true, signal the intent to the code generator to emit all select keys,
618 // even if the Includes list for that key is empty. This mode facilitates
619 // specific select statements where an empty list for a non-default select
620 // key has a meaning.
621 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400622}
Jingwen Chen91220d72021-03-24 02:18:33 -0400623
Liz Kammer9abd62d2021-05-21 08:37:59 -0400624type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
625
626func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
627 if list.IsNil() {
628 if _, ok := cll[axis][config]; ok {
629 delete(cll[axis], config)
630 }
631 return
632 }
633 if cll[axis] == nil {
634 cll[axis] = make(labelListSelectValues)
635 }
636
637 cll[axis][config] = list
638}
639
Chris Parsons77acf2e2021-12-03 17:27:16 -0500640func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400641 for axis, otherSelects := range other {
642 selects := cll[axis]
643 if selects == nil {
644 selects = make(labelListSelectValues, len(otherSelects))
645 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500646 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400647 cll[axis] = selects
648 }
Jingwen Chen07027912021-03-15 06:02:43 -0400649}
650
Chris Parsons77acf2e2021-12-03 17:27:16 -0500651func (lla *LabelListAttribute) Clone() *LabelListAttribute {
652 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
653 return result.Append(*lla)
654}
655
Jingwen Chen07027912021-03-15 06:02:43 -0400656// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
657func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400658 return LabelListAttribute{
659 Value: value,
660 ConfigurableValues: make(configurableLabelLists),
661 }
662}
663
Cole Faust53b62092022-05-12 15:37:02 -0700664// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
665func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
666 return MakeLabelListAttribute(MakeLabelList([]Label{value}))
667}
668
Liz Kammer9abd62d2021-05-21 08:37:59 -0400669func (lla *LabelListAttribute) SetValue(list LabelList) {
670 lla.SetSelectValue(NoConfigAxis, "", list)
671}
672
673// SetSelectValue set a value for a bazel select for the given axis, config and value.
674func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
675 axis.validateConfig(config)
676 switch axis.configurationType {
677 case noConfig:
678 lla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400679 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400680 if lla.ConfigurableValues == nil {
681 lla.ConfigurableValues = make(configurableLabelLists)
682 }
683 lla.ConfigurableValues.setValueForAxis(axis, config, list)
684 default:
685 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
686 }
687}
688
689// SelectValue gets a value for a bazel select for the given axis and config.
690func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
691 axis.validateConfig(config)
692 switch axis.configurationType {
693 case noConfig:
694 return lla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400695 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400696 return lla.ConfigurableValues[axis][config]
697 default:
698 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
699 }
700}
701
702// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
703func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
704 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
705 for k := range lla.ConfigurableValues {
706 keys = append(keys, k)
707 }
708
709 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
710 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400711}
712
Jingwen Chened9c17d2021-04-13 07:14:55 +0000713// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500714// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
715func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
716 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
717 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400718 lla.Value.Includes = []Label{}
719 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400720 lla.Value.Append(other.Value)
721 if lla.ConfigurableValues == nil {
722 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400723 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500724 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
725 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400726}
727
Liz Kammer12615db2021-09-28 09:19:17 -0400728// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
729// LabelList within the LabelListAttribute
730func (lla *LabelListAttribute) Add(label *LabelAttribute) {
731 if label == nil {
732 return
733 }
734
735 lla.Value.Add(label.Value)
736 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
737 lla.ConfigurableValues = make(configurableLabelLists)
738 }
739 for axis, _ := range label.ConfigurableValues {
740 if _, exists := lla.ConfigurableValues[axis]; !exists {
741 lla.ConfigurableValues[axis] = make(labelListSelectValues)
742 }
743 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
744 }
745}
746
Liz Kammer9abd62d2021-05-21 08:37:59 -0400747// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
748func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500749 for _, selectValues := range lla.ConfigurableValues {
750 if len(selectValues) > 0 {
751 return true
752 }
753 }
754 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400755}
756
Chris Parsons69fa9f92021-07-13 11:47:44 -0400757// IsEmpty returns true if the attribute has no values under any configuration.
758func (lla LabelListAttribute) IsEmpty() bool {
759 if len(lla.Value.Includes) > 0 {
760 return false
761 }
762 for axis, _ := range lla.ConfigurableValues {
763 if lla.ConfigurableValues[axis].HasConfigurableValues() {
764 return false
765 }
766 }
767 return true
768}
769
Liz Kammer54309532021-12-14 12:21:22 -0500770// IsNil returns true if the attribute has not been set for any configuration.
771func (lla LabelListAttribute) IsNil() bool {
772 if lla.Value.Includes != nil {
773 return false
774 }
775 return !lla.HasConfigurableValues()
776}
777
778// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
779// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
780// be removed, e.g. if they could cause duplicate element failures.
781func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
782 val := lla.SelectValue(axis, config)
783 newList := SubtractBazelLabelList(val, labelList)
784 newList.Excludes = append(newList.Excludes, labelList.Includes...)
785 lla.SetSelectValue(axis, config, newList)
786}
787
Liz Kammer74deed42021-06-02 13:02:03 -0400788// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
789// the base value and included in default values as appropriate.
790func (lla *LabelListAttribute) ResolveExcludes() {
791 for axis, configToLabels := range lla.ConfigurableValues {
792 baseLabels := lla.Value.deepCopy()
793 for config, val := range configToLabels {
794 // Exclude config-specific excludes from base value
795 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
796
797 // add base values to config specific to add labels excluded by others in this axis
798 // then remove all config-specific excludes
799 allLabels := baseLabels.deepCopy()
800 allLabels.Append(val)
801 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
802 }
803
804 // After going through all configs, delete the duplicates in the config
805 // values that are already in the base Value.
806 for config, val := range configToLabels {
807 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
808 }
809
Jingwen Chen9af49a42021-11-02 10:27:17 +0000810 // Now that the Value list is finalized for this axis, compare it with
811 // the original list, and union the difference with the default
812 // condition for the axis.
813 difference := SubtractBazelLabelList(baseLabels, lla.Value)
814 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
815 existingDefaults.Append(difference)
816 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400817
818 // if everything ends up without includes, just delete the axis
819 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
820 delete(lla.ConfigurableValues, axis)
821 }
822 }
823}
824
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400825// OtherModuleContext is a limited context that has methods with information about other modules.
826type OtherModuleContext interface {
827 ModuleFromName(name string) (blueprint.Module, bool)
828 OtherModuleType(m blueprint.Module) string
829 OtherModuleName(m blueprint.Module) string
830 OtherModuleDir(m blueprint.Module) string
831 ModuleErrorf(fmt string, args ...interface{})
832}
833
834// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
835// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400836type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400837
838// LabelPartition contains descriptions of a partition for labels
839type LabelPartition struct {
840 // Extensions to include in this partition
841 Extensions []string
842 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
843 // the mapped label in the partition
844 LabelMapper LabelMapper
845 // Whether to store files not included in any other partition in a group of LabelPartitions
846 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
847 Keep_remainder bool
848}
849
850// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
851// partition
852type LabelPartitions map[string]LabelPartition
853
854// filter returns a pointer to a label if the label should be included in the partition or nil if
855// not.
856func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
857 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400858 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400859 return &Label{newLabel, label.OriginalModuleName}
860 }
861 }
862 for _, ext := range lf.Extensions {
863 if strings.HasSuffix(label.Label, ext) {
864 return &label
865 }
866 }
867
868 return nil
869}
870
871// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
872type PartitionToLabelListAttribute map[string]LabelListAttribute
873
874type partitionToLabelList map[string]*LabelList
875
876func (p partitionToLabelList) appendIncludes(partition string, label Label) {
877 if _, ok := p[partition]; !ok {
878 p[partition] = &LabelList{}
879 }
880 p[partition].Includes = append(p[partition].Includes, label)
881}
882
883func (p partitionToLabelList) excludes(partition string, excludes []Label) {
884 if _, ok := p[partition]; !ok {
885 p[partition] = &LabelList{}
886 }
887 p[partition].Excludes = excludes
888}
889
890// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
891func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
892 ret := PartitionToLabelListAttribute{}
893 var partitionNames []string
894 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
895 var remainderPartition *string
896 for p, f := range partitions {
897 partitionNames = append(partitionNames, p)
898 if f.Keep_remainder {
899 if remainderPartition != nil {
900 panic("only one partition can store the remainder")
901 }
902 // If we take the address of p in a loop, we'll end up with the last value of p in
903 // remainderPartition, we want the requested partition
904 capturePartition := p
905 remainderPartition = &capturePartition
906 }
907 }
908
909 partitionLabelList := func(axis ConfigurationAxis, config string) {
910 value := lla.SelectValue(axis, config)
911 partitionToLabels := partitionToLabelList{}
912 for _, item := range value.Includes {
913 wasFiltered := false
914 var inPartition *string
915 for partition, f := range partitions {
916 filtered := f.filter(ctx, item)
917 if filtered == nil {
918 // did not match this filter, keep looking
919 continue
920 }
921 wasFiltered = true
922 partitionToLabels.appendIncludes(partition, *filtered)
923 // don't need to check other partitions if this filter used the item,
924 // continue checking if mapped to another name
925 if *filtered == item {
926 if inPartition != nil {
927 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
928 }
929 capturePartition := partition
930 inPartition = &capturePartition
931 }
932 }
933
934 // if not specified in a partition, add to remainder partition if one exists
935 if !wasFiltered && remainderPartition != nil {
936 partitionToLabels.appendIncludes(*remainderPartition, item)
937 }
938 }
939
940 // ensure empty lists are maintained
941 if value.Excludes != nil {
942 for _, partition := range partitionNames {
943 partitionToLabels.excludes(partition, value.Excludes)
944 }
945 }
946
947 for partition, list := range partitionToLabels {
948 val := ret[partition]
949 (&val).SetSelectValue(axis, config, *list)
950 ret[partition] = val
951 }
952 }
953
954 partitionLabelList(NoConfigAxis, "")
955 for axis, configToList := range lla.ConfigurableValues {
956 for config, _ := range configToList {
957 partitionLabelList(axis, config)
958 }
959 }
960 return ret
961}
962
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +0000963// StringAttribute corresponds to the string Bazel attribute type with
964// support for additional metadata, like configurations.
965type StringAttribute struct {
966 // The base value of the string attribute.
967 Value *string
968
969 // The configured attribute label list Values. Optional
970 // a map of independent configurability axes
971 ConfigurableValues configurableStrings
972}
973
974type configurableStrings map[ConfigurationAxis]stringSelectValues
975
976func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
977 if cs[axis] == nil {
978 cs[axis] = make(stringSelectValues)
979 }
980 var v = ""
981 if str != nil {
982 v = *str
983 }
984 cs[axis][config] = v
985}
986
987type stringSelectValues map[string]string
988
989// HasConfigurableValues returns true if the attribute contains axis-specific string values.
990func (sa StringAttribute) HasConfigurableValues() bool {
991 for _, selectValues := range sa.ConfigurableValues {
992 if len(selectValues) > 0 {
993 return true
994 }
995 }
996 return false
997}
998
999// SetSelectValue set a value for a bazel select for the given axis, config and value.
1000func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
1001 axis.validateConfig(config)
1002 switch axis.configurationType {
1003 case noConfig:
1004 sa.Value = str
1005 case arch, os, osArch, productVariables:
1006 if sa.ConfigurableValues == nil {
1007 sa.ConfigurableValues = make(configurableStrings)
1008 }
1009 sa.ConfigurableValues.setValueForAxis(axis, config, str)
1010 default:
1011 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1012 }
1013}
1014
1015// SelectValue gets a value for a bazel select for the given axis and config.
1016func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
1017 axis.validateConfig(config)
1018 switch axis.configurationType {
1019 case noConfig:
1020 return sa.Value
1021 case arch, os, osArch, productVariables:
1022 if v, ok := sa.ConfigurableValues[axis][config]; ok {
1023 return &v
1024 } else {
1025 return nil
1026 }
1027 default:
1028 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1029 }
1030}
1031
1032// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1033func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1034 keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
1035 for k := range sa.ConfigurableValues {
1036 keys = append(keys, k)
1037 }
1038
1039 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1040 return keys
1041}
1042
1043// Collapse reduces the configurable axes of the string attribute to a single axis.
1044// This is necessary for final writing to bp2build, as a configurable string
1045// attribute can only be comprised by a single select.
1046func (sa *StringAttribute) Collapse() error {
1047 axisTypes := sa.axisTypes()
1048 _, containsOs := axisTypes[os]
1049 _, containsArch := axisTypes[arch]
1050 _, containsOsArch := axisTypes[osArch]
1051 _, containsProductVariables := axisTypes[productVariables]
1052 if containsProductVariables {
1053 if containsOs || containsArch || containsOsArch {
1054 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
1055 }
1056 }
1057 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
1058 // If a bool attribute has both os and arch configuration axes, the only
1059 // way to successfully union their values is to increase the granularity
1060 // of the configuration criteria to os_arch.
1061 for osType, supportedArchs := range osToArchMap {
1062 for _, supportedArch := range supportedArchs {
1063 osArch := osArchString(osType, supportedArch)
1064 if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
1065 // Do nothing, as the arch_os is explicitly defined already.
1066 } else {
1067 archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
1068 osVal := sa.SelectValue(OsConfigurationAxis, osType)
1069 if osVal != nil && archVal != nil {
1070 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
1071 // runs after os mutator.
1072 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1073 } else if osVal != nil && archVal == nil {
1074 sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
1075 } else if osVal == nil && archVal != nil {
1076 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1077 }
1078 }
1079 }
1080 }
1081 // All os_arch values are now set. Clear os and arch axes.
1082 delete(sa.ConfigurableValues, ArchConfigurationAxis)
1083 delete(sa.ConfigurableValues, OsConfigurationAxis)
1084 // Verify post-condition; this should never fail, provided no additional
1085 // axes are introduced.
1086 if len(sa.ConfigurableValues) > 1 {
1087 panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
1088 }
1089 }
1090 return nil
1091}
1092
1093func (sa *StringAttribute) axisTypes() map[configurationType]bool {
1094 types := map[configurationType]bool{}
1095 for k := range sa.ConfigurableValues {
1096 if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
1097 types[k.configurationType] = true
1098 }
1099 }
1100 return types
1101}
1102
Jingwen Chen5d864492021-02-24 07:20:12 -05001103// StringListAttribute corresponds to the string_list Bazel attribute type with
1104// support for additional metadata, like configurations.
1105type StringListAttribute struct {
1106 // The base value of the string list attribute.
1107 Value []string
1108
Liz Kammer9abd62d2021-05-21 08:37:59 -04001109 // The configured attribute label list Values. Optional
1110 // a map of independent configurability axes
1111 ConfigurableValues configurableStringLists
1112}
Jingwen Chenc1c26502021-04-05 10:35:13 +00001113
Liz Kammer9abd62d2021-05-21 08:37:59 -04001114type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -04001115
Liz Kammer9abd62d2021-05-21 08:37:59 -04001116func (csl configurableStringLists) Append(other configurableStringLists) {
1117 for axis, otherSelects := range other {
1118 selects := csl[axis]
1119 if selects == nil {
1120 selects = make(stringListSelectValues, len(otherSelects))
1121 }
1122 selects.appendSelects(otherSelects)
1123 csl[axis] = selects
1124 }
1125}
1126
1127func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
1128 if csl[axis] == nil {
1129 csl[axis] = make(stringListSelectValues)
1130 }
1131 csl[axis][config] = list
1132}
1133
1134type stringListSelectValues map[string][]string
1135
1136func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
1137 for k, v := range other {
1138 sl[k] = append(sl[k], v...)
1139 }
1140}
1141
1142func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
1143 for _, val := range sl {
1144 if len(val) > 0 {
1145 return true
1146 }
1147 }
1148 return false
Jingwen Chen5d864492021-02-24 07:20:12 -05001149}
1150
Rupert Shuttleworthb8151682021-04-06 20:06:21 +00001151// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1152func MakeStringListAttribute(value []string) StringListAttribute {
1153 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -04001154 return StringListAttribute{
1155 Value: value,
1156 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -04001157 }
1158}
1159
Liz Kammer9abd62d2021-05-21 08:37:59 -04001160// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1161func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -05001162 for _, selectValues := range sla.ConfigurableValues {
1163 if len(selectValues) > 0 {
1164 return true
1165 }
1166 }
1167 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -04001168}
1169
Jingwen Chened9c17d2021-04-13 07:14:55 +00001170// Append appends all values, including os and arch specific ones, from another
1171// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001172func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001173 sla.Value = append(sla.Value, other.Value...)
1174 if sla.ConfigurableValues == nil {
1175 sla.ConfigurableValues = make(configurableStringLists)
1176 }
1177 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001178 return sla
1179}
1180
1181func (sla *StringListAttribute) Clone() *StringListAttribute {
1182 result := &StringListAttribute{}
1183 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001184}
1185
1186// SetSelectValue set a value for a bazel select for the given axis, config and value.
1187func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1188 axis.validateConfig(config)
1189 switch axis.configurationType {
1190 case noConfig:
1191 sla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -04001192 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001193 if sla.ConfigurableValues == nil {
1194 sla.ConfigurableValues = make(configurableStringLists)
1195 }
1196 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1197 default:
1198 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1199 }
1200}
1201
1202// SelectValue gets a value for a bazel select for the given axis and config.
1203func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1204 axis.validateConfig(config)
1205 switch axis.configurationType {
1206 case noConfig:
1207 return sla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -04001208 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001209 return sla.ConfigurableValues[axis][config]
1210 default:
1211 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1212 }
1213}
1214
1215// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1216func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1217 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
1218 for k := range sla.ConfigurableValues {
1219 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001220 }
1221
Liz Kammer9abd62d2021-05-21 08:37:59 -04001222 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1223 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +00001224}
1225
Liz Kammer5fad5012021-09-09 14:08:21 -04001226// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1227// configuration-specific values. For example, if we would convert this StringListAttribute as:
1228// ["a", "b", "c"] + select({
1229// "//condition:one": ["a", "d"],
1230// "//conditions:default": [],
1231// })
1232// after this function, we would convert this StringListAttribute as:
1233// ["a", "b", "c"] + select({
1234// "//condition:one": ["d"],
1235// "//conditions:default": [],
1236// })
1237func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1238 base := sla.Value
1239 for axis, configToList := range sla.ConfigurableValues {
1240 for config, list := range configToList {
1241 remaining := SubtractStrings(list, base)
1242 if len(remaining) == 0 {
1243 delete(sla.ConfigurableValues[axis], config)
1244 } else {
1245 sla.ConfigurableValues[axis][config] = remaining
1246 }
1247 }
1248 }
1249}
1250
Liz Kammera060c452021-03-24 10:14:47 -04001251// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1252// Starlark string.format compatible tag for productVariable.
1253func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
1254 ret := make([]string, 0, len(slice))
1255 changesMade := false
1256 for _, s := range slice {
1257 newS, changed := TryVariableSubstitution(s, productVariable)
1258 ret = append(ret, newS)
1259 changesMade = changesMade || changed
1260 }
1261 return ret, changesMade
1262}
1263
1264// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1265// string.format compatible tag for productVariable.
1266func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001267 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001268 return sub, s != sub
1269}