blob: 7ecc92bdb2e9e5643fcddda299a323565ee3b098 [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"
Jingwen Chenc63677b2021-06-17 05:43:19 +000022 "strings"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000023)
Jingwen Chen5d864492021-02-24 07:20:12 -050024
Jingwen Chen73850672020-12-14 08:25:34 -050025// BazelTargetModuleProperties contain properties and metadata used for
26// Blueprint to BUILD file conversion.
27type BazelTargetModuleProperties struct {
28 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050029 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050030
31 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050032 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050033}
Liz Kammer356f7d42021-01-26 09:18:53 -050034
Jingwen Chenfb4692a2021-02-07 10:05:16 -050035const BazelTargetModuleNamePrefix = "__bp2build__"
36
Jingwen Chenc63677b2021-06-17 05:43:19 +000037func StripNamePrefix(moduleName string) string {
38 return strings.TrimPrefix(moduleName, BazelTargetModuleNamePrefix)
39}
40
Liz Kammera060c452021-03-24 10:14:47 -040041var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
42
Jingwen Chen38e62642021-04-19 05:00:15 +000043// Label is used to represent a Bazel compatible Label. Also stores the original
44// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050045type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000046 // The string representation of a Bazel target label. This can be a relative
47 // or fully qualified label. These labels are used for generating BUILD
48 // files with bp2build.
49 Label string
50
51 // The original Soong/Blueprint module name that the label was derived from.
52 // This is used for replacing references to the original name with the new
53 // label, for example in genrule cmds.
54 //
55 // While there is a reversible 1:1 mapping from the module name to Bazel
56 // label with bp2build that could make computing the original module name
57 // from the label automatic, it is not the case for handcrafted targets,
58 // where modules can have a custom label mapping through the { bazel_module:
59 // { label: <label> } } property.
60 //
61 // With handcrafted labels, those modules don't go through bp2build
62 // conversion, but relies on handcrafted targets in the source tree.
63 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050064}
65
66// LabelList is used to represent a list of Bazel labels.
67type LabelList struct {
68 Includes []Label
69 Excludes []Label
70}
71
Liz Kammer9abd62d2021-05-21 08:37:59 -040072func (ll *LabelList) IsNil() bool {
73 return ll.Includes == nil && ll.Excludes == nil
74}
75
Liz Kammer74deed42021-06-02 13:02:03 -040076func (ll *LabelList) deepCopy() LabelList {
77 return LabelList{
78 Includes: ll.Includes[:],
79 Excludes: ll.Excludes[:],
80 }
81}
82
Jingwen Chen63930982021-03-24 10:04:33 -040083// uniqueParentDirectories returns a list of the unique parent directories for
84// all files in ll.Includes.
85func (ll *LabelList) uniqueParentDirectories() []string {
86 dirMap := map[string]bool{}
87 for _, label := range ll.Includes {
88 dirMap[filepath.Dir(label.Label)] = true
89 }
90 dirs := []string{}
91 for dir := range dirMap {
92 dirs = append(dirs, dir)
93 }
94 return dirs
95}
96
Liz Kammer356f7d42021-01-26 09:18:53 -050097// Append appends the fields of other labelList to the corresponding fields of ll.
98func (ll *LabelList) Append(other LabelList) {
99 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
100 ll.Includes = append(ll.Includes, other.Includes...)
101 }
102 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
103 ll.Excludes = append(other.Excludes, other.Excludes...)
104 }
105}
Jingwen Chen5d864492021-02-24 07:20:12 -0500106
Jingwen Chened9c17d2021-04-13 07:14:55 +0000107// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
108// the slice in a sorted order.
109func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000110 uniqueLabelsSet := make(map[Label]bool)
111 for _, l := range originalLabels {
112 uniqueLabelsSet[l] = true
113 }
114 var uniqueLabels []Label
115 for l, _ := range uniqueLabelsSet {
116 uniqueLabels = append(uniqueLabels, l)
117 }
118 sort.SliceStable(uniqueLabels, func(i, j int) bool {
119 return uniqueLabels[i].Label < uniqueLabels[j].Label
120 })
121 return uniqueLabels
122}
123
Liz Kammer9abd62d2021-05-21 08:37:59 -0400124func FirstUniqueBazelLabels(originalLabels []Label) []Label {
125 var labels []Label
126 found := make(map[Label]bool, len(originalLabels))
127 for _, l := range originalLabels {
128 if _, ok := found[l]; ok {
129 continue
130 }
131 labels = append(labels, l)
132 found[l] = true
133 }
134 return labels
135}
136
137func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
138 var uniqueLabelList LabelList
139 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
140 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
141 return uniqueLabelList
142}
143
144func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000145 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000146 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
147 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000148 return uniqueLabelList
149}
150
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000151// Subtract needle from haystack
152func SubtractStrings(haystack []string, needle []string) []string {
153 // This is really a set
154 remainder := make(map[string]bool)
155
156 for _, s := range haystack {
157 remainder[s] = true
158 }
159 for _, s := range needle {
160 delete(remainder, s)
161 }
162
163 var strings []string
164 for s, _ := range remainder {
165 strings = append(strings, s)
166 }
167
168 sort.SliceStable(strings, func(i, j int) bool {
169 return strings[i] < strings[j]
170 })
171
172 return strings
173}
174
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000175// Map a function over all labels in a LabelList.
176func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
177 var includes []Label
178 for _, inc := range mapOver.Includes {
179 mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
180 includes = append(includes, mappedLabel)
181 }
182 // mapFn is not applied over excludes, but they are propagated as-is.
183 return LabelList{Includes: includes, Excludes: mapOver.Excludes}
184}
185
186// Map a function over all Labels in a LabelListAttribute
187func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
188 var result LabelListAttribute
189
190 result.Value = MapLabelList(mapOver.Value, mapFn)
191
Liz Kammer9abd62d2021-05-21 08:37:59 -0400192 for axis, configToLabels := range mapOver.ConfigurableValues {
193 for config, value := range configToLabels {
194 result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000195 }
196 }
197
198 return result
199}
200
Chris Parsons990c4f42021-05-25 12:10:58 -0400201// Return all needles in a given haystack, where needleFn is true for needles.
202func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
203 var includes []Label
Chris Parsons990c4f42021-05-25 12:10:58 -0400204 for _, inc := range haystack.Includes {
205 if needleFn(inc.Label) {
206 includes = append(includes, inc)
207 }
208 }
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000209 // needleFn is not applied over excludes, but they are propagated as-is.
Chris Parsons990c4f42021-05-25 12:10:58 -0400210 return LabelList{Includes: includes, Excludes: haystack.Excludes}
211}
212
213// Return all needles in a given haystack, where needleFn is true for needles.
214func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400215 result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
Chris Parsons990c4f42021-05-25 12:10:58 -0400216
Liz Kammer9abd62d2021-05-21 08:37:59 -0400217 for config, selects := range haystack.ConfigurableValues {
218 newSelects := make(labelListSelectValues, len(selects))
219 for k, v := range selects {
220 newSelects[k] = FilterLabelList(v, needleFn)
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400221 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400222 result.ConfigurableValues[config] = newSelects
Chris Parsons990c4f42021-05-25 12:10:58 -0400223 }
224
225 return result
226}
227
228// Subtract needle from haystack
229func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400230 result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
Chris Parsons990c4f42021-05-25 12:10:58 -0400231
Liz Kammer9abd62d2021-05-21 08:37:59 -0400232 for config, selects := range haystack.ConfigurableValues {
233 newSelects := make(labelListSelectValues, len(selects))
234 needleSelects := needle.ConfigurableValues[config]
Chris Parsons990c4f42021-05-25 12:10:58 -0400235
Liz Kammer9abd62d2021-05-21 08:37:59 -0400236 for k, v := range selects {
237 newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400238 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400239 result.ConfigurableValues[config] = newSelects
Chris Parsons990c4f42021-05-25 12:10:58 -0400240 }
241
Chris Parsons990c4f42021-05-25 12:10:58 -0400242 return result
243}
244
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000245// Subtract needle from haystack
246func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
247 // This is really a set
248 remainder := make(map[Label]bool)
249
250 for _, label := range haystack {
251 remainder[label] = true
252 }
253 for _, label := range needle {
254 delete(remainder, label)
255 }
256
257 var labels []Label
258 for label, _ := range remainder {
259 labels = append(labels, label)
260 }
261
262 sort.SliceStable(labels, func(i, j int) bool {
263 return labels[i].Label < labels[j].Label
264 })
265
266 return labels
267}
268
Chris Parsons484e50a2021-05-13 15:13:04 -0400269// Appends two LabelLists, returning the combined list.
270func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
271 var result LabelList
272 result.Includes = append(a.Includes, b.Includes...)
273 result.Excludes = append(a.Excludes, b.Excludes...)
274 return result
275}
276
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000277// Subtract needle from haystack
278func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
279 var result LabelList
280 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
281 // NOTE: Excludes are intentionally not subtracted
282 result.Excludes = haystack.Excludes
283 return result
284}
285
Jingwen Chenc1c26502021-04-05 10:35:13 +0000286type Attribute interface {
287 HasConfigurableValues() bool
288}
289
Liz Kammer9abd62d2021-05-21 08:37:59 -0400290type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400291
Liz Kammer9abd62d2021-05-21 08:37:59 -0400292type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400293
Liz Kammer9abd62d2021-05-21 08:37:59 -0400294func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
295 if cl[axis] == nil {
296 cl[axis] = make(labelSelectValues)
297 }
298 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400299}
300
301// Represents an attribute whose value is a single label
302type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400303 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400304
Liz Kammer9abd62d2021-05-21 08:37:59 -0400305 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200306}
307
Liz Kammer9abd62d2021-05-21 08:37:59 -0400308// HasConfigurableValues returns whether there are configurable values set for this label.
309func (la LabelAttribute) HasConfigurableValues() bool {
310 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200311}
312
Liz Kammer9abd62d2021-05-21 08:37:59 -0400313// SetValue sets the base, non-configured value for the Label
314func (la *LabelAttribute) SetValue(value Label) {
315 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400316}
317
Liz Kammer9abd62d2021-05-21 08:37:59 -0400318// SetSelectValue set a value for a bazel select for the given axis, config and value.
319func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
320 axis.validateConfig(config)
321 switch axis.configurationType {
322 case noConfig:
323 la.Value = &value
324 case arch, os, osArch, productVariables:
325 if la.ConfigurableValues == nil {
326 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400327 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400328 la.ConfigurableValues.setValueForAxis(axis, config, &value)
329 default:
330 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
331 }
332}
333
334// SelectValue gets a value for a bazel select for the given axis and config.
335func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
336 axis.validateConfig(config)
337 switch axis.configurationType {
338 case noConfig:
339 return *la.Value
340 case arch, os, osArch, productVariables:
341 return *la.ConfigurableValues[axis][config]
342 default:
343 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
344 }
345}
346
347// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
348func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
349 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
350 for k := range la.ConfigurableValues {
351 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400352 }
353
Liz Kammer9abd62d2021-05-21 08:37:59 -0400354 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
355 return keys
356}
357
Liz Kammerd366c902021-06-03 13:43:01 -0400358type configToBools map[string]bool
359
360func (ctb configToBools) setValue(config string, value *bool) {
361 if value == nil {
362 if _, ok := ctb[config]; ok {
363 delete(ctb, config)
364 }
365 return
366 }
367 ctb[config] = *value
368}
369
370type configurableBools map[ConfigurationAxis]configToBools
371
372func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
373 if cb[axis] == nil {
374 cb[axis] = make(configToBools)
375 }
376 cb[axis].setValue(config, value)
377}
378
379// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
380type BoolAttribute struct {
381 Value *bool
382
383 ConfigurableValues configurableBools
384}
385
386// HasConfigurableValues returns whether there are configurable values for this attribute.
387func (ba BoolAttribute) HasConfigurableValues() bool {
388 return len(ba.ConfigurableValues) > 0
389}
390
391// SetSelectValue sets value for the given axis/config.
392func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
393 axis.validateConfig(config)
394 switch axis.configurationType {
395 case noConfig:
396 ba.Value = value
397 case arch, os, osArch, productVariables:
398 if ba.ConfigurableValues == nil {
399 ba.ConfigurableValues = make(configurableBools)
400 }
401 ba.ConfigurableValues.setValueForAxis(axis, config, value)
402 default:
403 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
404 }
405}
406
407// SelectValue gets the value for the given axis/config.
408func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
409 axis.validateConfig(config)
410 switch axis.configurationType {
411 case noConfig:
412 return ba.Value
413 case arch, os, osArch, productVariables:
414 if v, ok := ba.ConfigurableValues[axis][config]; ok {
415 return &v
416 } else {
417 return nil
418 }
419 default:
420 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
421 }
422}
423
424// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
425func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
426 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
427 for k := range ba.ConfigurableValues {
428 keys = append(keys, k)
429 }
430
431 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
432 return keys
433}
434
Liz Kammer9abd62d2021-05-21 08:37:59 -0400435// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
436type labelListSelectValues map[string]LabelList
437
438func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
439 for k, v := range other {
440 l := ll[k]
441 (&l).Append(v)
442 ll[k] = l
443 }
444}
445
446// HasConfigurableValues returns whether there are configurable values within this set of selects.
447func (ll labelListSelectValues) HasConfigurableValues() bool {
448 for _, v := range ll {
449 if len(v.Includes) > 0 {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400450 return true
451 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400452 }
453 return false
454}
455
Jingwen Chen07027912021-03-15 06:02:43 -0400456// LabelListAttribute is used to represent a list of Bazel labels as an
457// attribute.
458type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400459 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400460 Value LabelList
461
Liz Kammer9abd62d2021-05-21 08:37:59 -0400462 // The configured attribute label list Values. Optional
463 // a map of independent configurability axes
464 ConfigurableValues configurableLabelLists
465}
Jingwen Chen91220d72021-03-24 02:18:33 -0400466
Liz Kammer9abd62d2021-05-21 08:37:59 -0400467type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
468
469func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
470 if list.IsNil() {
471 if _, ok := cll[axis][config]; ok {
472 delete(cll[axis], config)
473 }
474 return
475 }
476 if cll[axis] == nil {
477 cll[axis] = make(labelListSelectValues)
478 }
479
480 cll[axis][config] = list
481}
482
483func (cll configurableLabelLists) Append(other configurableLabelLists) {
484 for axis, otherSelects := range other {
485 selects := cll[axis]
486 if selects == nil {
487 selects = make(labelListSelectValues, len(otherSelects))
488 }
489 selects.appendSelects(otherSelects)
490 cll[axis] = selects
491 }
Jingwen Chen07027912021-03-15 06:02:43 -0400492}
493
494// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
495func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400496 return LabelListAttribute{
497 Value: value,
498 ConfigurableValues: make(configurableLabelLists),
499 }
500}
501
502func (lla *LabelListAttribute) SetValue(list LabelList) {
503 lla.SetSelectValue(NoConfigAxis, "", list)
504}
505
506// SetSelectValue set a value for a bazel select for the given axis, config and value.
507func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
508 axis.validateConfig(config)
509 switch axis.configurationType {
510 case noConfig:
511 lla.Value = list
512 case arch, os, osArch, productVariables:
513 if lla.ConfigurableValues == nil {
514 lla.ConfigurableValues = make(configurableLabelLists)
515 }
516 lla.ConfigurableValues.setValueForAxis(axis, config, list)
517 default:
518 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
519 }
520}
521
522// SelectValue gets a value for a bazel select for the given axis and config.
523func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
524 axis.validateConfig(config)
525 switch axis.configurationType {
526 case noConfig:
527 return lla.Value
528 case arch, os, osArch, productVariables:
529 return lla.ConfigurableValues[axis][config]
530 default:
531 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
532 }
533}
534
535// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
536func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
537 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
538 for k := range lla.ConfigurableValues {
539 keys = append(keys, k)
540 }
541
542 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
543 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400544}
545
Jingwen Chened9c17d2021-04-13 07:14:55 +0000546// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400547// LabelListAttribute to this LabelListAttribute.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400548func (lla *LabelListAttribute) Append(other LabelListAttribute) {
549 lla.Value.Append(other.Value)
550 if lla.ConfigurableValues == nil {
551 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400552 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400553 lla.ConfigurableValues.Append(other.ConfigurableValues)
Jingwen Chen63930982021-03-24 10:04:33 -0400554}
555
Liz Kammer9abd62d2021-05-21 08:37:59 -0400556// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
557func (lla LabelListAttribute) HasConfigurableValues() bool {
558 return len(lla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400559}
560
Chris Parsons69fa9f92021-07-13 11:47:44 -0400561// IsEmpty returns true if the attribute has no values under any configuration.
562func (lla LabelListAttribute) IsEmpty() bool {
563 if len(lla.Value.Includes) > 0 {
564 return false
565 }
566 for axis, _ := range lla.ConfigurableValues {
567 if lla.ConfigurableValues[axis].HasConfigurableValues() {
568 return false
569 }
570 }
571 return true
572}
573
Liz Kammer74deed42021-06-02 13:02:03 -0400574// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
575// the base value and included in default values as appropriate.
576func (lla *LabelListAttribute) ResolveExcludes() {
577 for axis, configToLabels := range lla.ConfigurableValues {
578 baseLabels := lla.Value.deepCopy()
579 for config, val := range configToLabels {
580 // Exclude config-specific excludes from base value
581 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
582
583 // add base values to config specific to add labels excluded by others in this axis
584 // then remove all config-specific excludes
585 allLabels := baseLabels.deepCopy()
586 allLabels.Append(val)
587 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
588 }
589
590 // After going through all configs, delete the duplicates in the config
591 // values that are already in the base Value.
592 for config, val := range configToLabels {
593 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
594 }
595
596 // Now that the Value list is finalized for this axis, compare it with the original
597 // list, and put the difference into the default condition for the axis.
598 lla.ConfigurableValues[axis][conditionsDefault] = SubtractBazelLabelList(baseLabels, lla.Value)
599
600 // if everything ends up without includes, just delete the axis
601 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
602 delete(lla.ConfigurableValues, axis)
603 }
604 }
605}
606
Jingwen Chen5d864492021-02-24 07:20:12 -0500607// StringListAttribute corresponds to the string_list Bazel attribute type with
608// support for additional metadata, like configurations.
609type StringListAttribute struct {
610 // The base value of the string list attribute.
611 Value []string
612
Liz Kammer9abd62d2021-05-21 08:37:59 -0400613 // The configured attribute label list Values. Optional
614 // a map of independent configurability axes
615 ConfigurableValues configurableStringLists
616}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000617
Liz Kammer9abd62d2021-05-21 08:37:59 -0400618type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400619
Liz Kammer9abd62d2021-05-21 08:37:59 -0400620func (csl configurableStringLists) Append(other configurableStringLists) {
621 for axis, otherSelects := range other {
622 selects := csl[axis]
623 if selects == nil {
624 selects = make(stringListSelectValues, len(otherSelects))
625 }
626 selects.appendSelects(otherSelects)
627 csl[axis] = selects
628 }
629}
630
631func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
632 if csl[axis] == nil {
633 csl[axis] = make(stringListSelectValues)
634 }
635 csl[axis][config] = list
636}
637
638type stringListSelectValues map[string][]string
639
640func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
641 for k, v := range other {
642 sl[k] = append(sl[k], v...)
643 }
644}
645
646func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
647 for _, val := range sl {
648 if len(val) > 0 {
649 return true
650 }
651 }
652 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500653}
654
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000655// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
656func MakeStringListAttribute(value []string) StringListAttribute {
657 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400658 return StringListAttribute{
659 Value: value,
660 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400661 }
662}
663
Liz Kammer9abd62d2021-05-21 08:37:59 -0400664// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
665func (sla StringListAttribute) HasConfigurableValues() bool {
666 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400667}
668
Jingwen Chened9c17d2021-04-13 07:14:55 +0000669// Append appends all values, including os and arch specific ones, from another
670// StringListAttribute to this StringListAttribute
Liz Kammer9abd62d2021-05-21 08:37:59 -0400671func (sla *StringListAttribute) Append(other StringListAttribute) {
672 sla.Value = append(sla.Value, other.Value...)
673 if sla.ConfigurableValues == nil {
674 sla.ConfigurableValues = make(configurableStringLists)
675 }
676 sla.ConfigurableValues.Append(other.ConfigurableValues)
677}
678
679// SetSelectValue set a value for a bazel select for the given axis, config and value.
680func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
681 axis.validateConfig(config)
682 switch axis.configurationType {
683 case noConfig:
684 sla.Value = list
685 case arch, os, osArch, productVariables:
686 if sla.ConfigurableValues == nil {
687 sla.ConfigurableValues = make(configurableStringLists)
688 }
689 sla.ConfigurableValues.setValueForAxis(axis, config, list)
690 default:
691 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
692 }
693}
694
695// SelectValue gets a value for a bazel select for the given axis and config.
696func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
697 axis.validateConfig(config)
698 switch axis.configurationType {
699 case noConfig:
700 return sla.Value
701 case arch, os, osArch, productVariables:
702 return sla.ConfigurableValues[axis][config]
703 default:
704 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
705 }
706}
707
708// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
709func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
710 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
711 for k := range sla.ConfigurableValues {
712 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000713 }
714
Liz Kammer9abd62d2021-05-21 08:37:59 -0400715 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
716 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000717}
718
Liz Kammera060c452021-03-24 10:14:47 -0400719// TryVariableSubstitution, replace string substitution formatting within each string in slice with
720// Starlark string.format compatible tag for productVariable.
721func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
722 ret := make([]string, 0, len(slice))
723 changesMade := false
724 for _, s := range slice {
725 newS, changed := TryVariableSubstitution(s, productVariable)
726 ret = append(ret, newS)
727 changesMade = changesMade || changed
728 }
729 return ret, changesMade
730}
731
732// TryVariableSubstitution, replace string substitution formatting within s with Starlark
733// string.format compatible tag for productVariable.
734func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400735 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400736 return sub, s != sub
737}