blob: d3b40a242a744f72e957fd22574cbb57346986c1 [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"
22)
Jingwen Chen5d864492021-02-24 07:20:12 -050023
Jingwen Chen73850672020-12-14 08:25:34 -050024// BazelTargetModuleProperties contain properties and metadata used for
25// Blueprint to BUILD file conversion.
26type BazelTargetModuleProperties struct {
27 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050028 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050029
30 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050031 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050032}
Liz Kammer356f7d42021-01-26 09:18:53 -050033
Liz Kammera060c452021-03-24 10:14:47 -040034var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
35
Jingwen Chen38e62642021-04-19 05:00:15 +000036// Label is used to represent a Bazel compatible Label. Also stores the original
37// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050038type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000039 // The string representation of a Bazel target label. This can be a relative
40 // or fully qualified label. These labels are used for generating BUILD
41 // files with bp2build.
42 Label string
43
44 // The original Soong/Blueprint module name that the label was derived from.
45 // This is used for replacing references to the original name with the new
46 // label, for example in genrule cmds.
47 //
48 // While there is a reversible 1:1 mapping from the module name to Bazel
49 // label with bp2build that could make computing the original module name
50 // from the label automatic, it is not the case for handcrafted targets,
51 // where modules can have a custom label mapping through the { bazel_module:
52 // { label: <label> } } property.
53 //
54 // With handcrafted labels, those modules don't go through bp2build
55 // conversion, but relies on handcrafted targets in the source tree.
56 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050057}
58
59// LabelList is used to represent a list of Bazel labels.
60type LabelList struct {
61 Includes []Label
62 Excludes []Label
63}
64
Chris Parsons51f8c392021-08-03 21:01:05 -040065func (ll *LabelList) Equals(other LabelList) bool {
66 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
67 return false
68 }
69 for i, _ := range ll.Includes {
70 if ll.Includes[i] != other.Includes[i] {
71 return false
72 }
73 }
74 for i, _ := range ll.Excludes {
75 if ll.Excludes[i] != other.Excludes[i] {
76 return false
77 }
78 }
79 return true
80}
81
Liz Kammer9abd62d2021-05-21 08:37:59 -040082func (ll *LabelList) IsNil() bool {
83 return ll.Includes == nil && ll.Excludes == nil
84}
85
Liz Kammer74deed42021-06-02 13:02:03 -040086func (ll *LabelList) deepCopy() LabelList {
87 return LabelList{
88 Includes: ll.Includes[:],
89 Excludes: ll.Excludes[:],
90 }
91}
92
Jingwen Chen63930982021-03-24 10:04:33 -040093// uniqueParentDirectories returns a list of the unique parent directories for
94// all files in ll.Includes.
95func (ll *LabelList) uniqueParentDirectories() []string {
96 dirMap := map[string]bool{}
97 for _, label := range ll.Includes {
98 dirMap[filepath.Dir(label.Label)] = true
99 }
100 dirs := []string{}
101 for dir := range dirMap {
102 dirs = append(dirs, dir)
103 }
104 return dirs
105}
106
Liz Kammer356f7d42021-01-26 09:18:53 -0500107// Append appends the fields of other labelList to the corresponding fields of ll.
108func (ll *LabelList) Append(other LabelList) {
109 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
110 ll.Includes = append(ll.Includes, other.Includes...)
111 }
112 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
113 ll.Excludes = append(other.Excludes, other.Excludes...)
114 }
115}
Jingwen Chen5d864492021-02-24 07:20:12 -0500116
Jingwen Chened9c17d2021-04-13 07:14:55 +0000117// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
118// the slice in a sorted order.
119func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000120 uniqueLabelsSet := make(map[Label]bool)
121 for _, l := range originalLabels {
122 uniqueLabelsSet[l] = true
123 }
124 var uniqueLabels []Label
125 for l, _ := range uniqueLabelsSet {
126 uniqueLabels = append(uniqueLabels, l)
127 }
128 sort.SliceStable(uniqueLabels, func(i, j int) bool {
129 return uniqueLabels[i].Label < uniqueLabels[j].Label
130 })
131 return uniqueLabels
132}
133
Liz Kammer9abd62d2021-05-21 08:37:59 -0400134func FirstUniqueBazelLabels(originalLabels []Label) []Label {
135 var labels []Label
136 found := make(map[Label]bool, len(originalLabels))
137 for _, l := range originalLabels {
138 if _, ok := found[l]; ok {
139 continue
140 }
141 labels = append(labels, l)
142 found[l] = true
143 }
144 return labels
145}
146
147func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
148 var uniqueLabelList LabelList
149 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
150 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
151 return uniqueLabelList
152}
153
154func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000155 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000156 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
157 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000158 return uniqueLabelList
159}
160
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000161// Subtract needle from haystack
162func SubtractStrings(haystack []string, needle []string) []string {
163 // This is really a set
164 remainder := make(map[string]bool)
165
166 for _, s := range haystack {
167 remainder[s] = true
168 }
169 for _, s := range needle {
170 delete(remainder, s)
171 }
172
173 var strings []string
174 for s, _ := range remainder {
175 strings = append(strings, s)
176 }
177
178 sort.SliceStable(strings, func(i, j int) bool {
179 return strings[i] < strings[j]
180 })
181
182 return strings
183}
184
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000185// Map a function over all labels in a LabelList.
186func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
187 var includes []Label
188 for _, inc := range mapOver.Includes {
189 mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
190 includes = append(includes, mappedLabel)
191 }
192 // mapFn is not applied over excludes, but they are propagated as-is.
193 return LabelList{Includes: includes, Excludes: mapOver.Excludes}
194}
195
196// Map a function over all Labels in a LabelListAttribute
197func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
198 var result LabelListAttribute
199
200 result.Value = MapLabelList(mapOver.Value, mapFn)
201
Liz Kammer9abd62d2021-05-21 08:37:59 -0400202 for axis, configToLabels := range mapOver.ConfigurableValues {
203 for config, value := range configToLabels {
204 result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000205 }
206 }
207
208 return result
209}
210
Chris Parsons990c4f42021-05-25 12:10:58 -0400211// Return all needles in a given haystack, where needleFn is true for needles.
212func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
213 var includes []Label
Chris Parsons990c4f42021-05-25 12:10:58 -0400214 for _, inc := range haystack.Includes {
215 if needleFn(inc.Label) {
216 includes = append(includes, inc)
217 }
218 }
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000219 // needleFn is not applied over excludes, but they are propagated as-is.
Chris Parsons990c4f42021-05-25 12:10:58 -0400220 return LabelList{Includes: includes, Excludes: haystack.Excludes}
221}
222
223// Return all needles in a given haystack, where needleFn is true for needles.
224func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400225 result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
Chris Parsons990c4f42021-05-25 12:10:58 -0400226
Liz Kammer9abd62d2021-05-21 08:37:59 -0400227 for config, selects := range haystack.ConfigurableValues {
228 newSelects := make(labelListSelectValues, len(selects))
229 for k, v := range selects {
230 newSelects[k] = FilterLabelList(v, needleFn)
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400231 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400232 result.ConfigurableValues[config] = newSelects
Chris Parsons990c4f42021-05-25 12:10:58 -0400233 }
234
235 return result
236}
237
238// Subtract needle from haystack
239func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400240 result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
Chris Parsons990c4f42021-05-25 12:10:58 -0400241
Liz Kammer9abd62d2021-05-21 08:37:59 -0400242 for config, selects := range haystack.ConfigurableValues {
243 newSelects := make(labelListSelectValues, len(selects))
244 needleSelects := needle.ConfigurableValues[config]
Chris Parsons990c4f42021-05-25 12:10:58 -0400245
Liz Kammer9abd62d2021-05-21 08:37:59 -0400246 for k, v := range selects {
247 newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400248 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400249 result.ConfigurableValues[config] = newSelects
Chris Parsons990c4f42021-05-25 12:10:58 -0400250 }
251
Chris Parsons990c4f42021-05-25 12:10:58 -0400252 return result
253}
254
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000255// Subtract needle from haystack
256func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
257 // This is really a set
258 remainder := make(map[Label]bool)
259
260 for _, label := range haystack {
261 remainder[label] = true
262 }
263 for _, label := range needle {
264 delete(remainder, label)
265 }
266
267 var labels []Label
268 for label, _ := range remainder {
269 labels = append(labels, label)
270 }
271
272 sort.SliceStable(labels, func(i, j int) bool {
273 return labels[i].Label < labels[j].Label
274 })
275
276 return labels
277}
278
Chris Parsons484e50a2021-05-13 15:13:04 -0400279// Appends two LabelLists, returning the combined list.
280func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
281 var result LabelList
282 result.Includes = append(a.Includes, b.Includes...)
283 result.Excludes = append(a.Excludes, b.Excludes...)
284 return result
285}
286
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000287// Subtract needle from haystack
288func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
289 var result LabelList
290 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
291 // NOTE: Excludes are intentionally not subtracted
292 result.Excludes = haystack.Excludes
293 return result
294}
295
Jingwen Chenc1c26502021-04-05 10:35:13 +0000296type Attribute interface {
297 HasConfigurableValues() bool
298}
299
Liz Kammer9abd62d2021-05-21 08:37:59 -0400300type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400301
Liz Kammer9abd62d2021-05-21 08:37:59 -0400302type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400303
Liz Kammer9abd62d2021-05-21 08:37:59 -0400304func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
305 if cl[axis] == nil {
306 cl[axis] = make(labelSelectValues)
307 }
308 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400309}
310
311// Represents an attribute whose value is a single label
312type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400313 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400314
Liz Kammer9abd62d2021-05-21 08:37:59 -0400315 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200316}
317
Liz Kammer9abd62d2021-05-21 08:37:59 -0400318// HasConfigurableValues returns whether there are configurable values set for this label.
319func (la LabelAttribute) HasConfigurableValues() bool {
320 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200321}
322
Liz Kammer9abd62d2021-05-21 08:37:59 -0400323// SetValue sets the base, non-configured value for the Label
324func (la *LabelAttribute) SetValue(value Label) {
325 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400326}
327
Liz Kammer9abd62d2021-05-21 08:37:59 -0400328// SetSelectValue set a value for a bazel select for the given axis, config and value.
329func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
330 axis.validateConfig(config)
331 switch axis.configurationType {
332 case noConfig:
333 la.Value = &value
Liz Kammer01a16e82021-07-16 16:33:47 -0400334 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400335 if la.ConfigurableValues == nil {
336 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400337 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400338 la.ConfigurableValues.setValueForAxis(axis, config, &value)
339 default:
340 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
341 }
342}
343
344// SelectValue gets a value for a bazel select for the given axis and config.
345func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
346 axis.validateConfig(config)
347 switch axis.configurationType {
348 case noConfig:
349 return *la.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400350 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400351 return *la.ConfigurableValues[axis][config]
352 default:
353 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
354 }
355}
356
357// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
358func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
359 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
360 for k := range la.ConfigurableValues {
361 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400362 }
363
Liz Kammer9abd62d2021-05-21 08:37:59 -0400364 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
365 return keys
366}
367
Liz Kammerd366c902021-06-03 13:43:01 -0400368type configToBools map[string]bool
369
370func (ctb configToBools) setValue(config string, value *bool) {
371 if value == nil {
372 if _, ok := ctb[config]; ok {
373 delete(ctb, config)
374 }
375 return
376 }
377 ctb[config] = *value
378}
379
380type configurableBools map[ConfigurationAxis]configToBools
381
382func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
383 if cb[axis] == nil {
384 cb[axis] = make(configToBools)
385 }
386 cb[axis].setValue(config, value)
387}
388
389// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
390type BoolAttribute struct {
391 Value *bool
392
393 ConfigurableValues configurableBools
394}
395
396// HasConfigurableValues returns whether there are configurable values for this attribute.
397func (ba BoolAttribute) HasConfigurableValues() bool {
398 return len(ba.ConfigurableValues) > 0
399}
400
401// SetSelectValue sets value for the given axis/config.
402func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
403 axis.validateConfig(config)
404 switch axis.configurationType {
405 case noConfig:
406 ba.Value = value
Liz Kammer01a16e82021-07-16 16:33:47 -0400407 case arch, os, osArch, bionic, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400408 if ba.ConfigurableValues == nil {
409 ba.ConfigurableValues = make(configurableBools)
410 }
411 ba.ConfigurableValues.setValueForAxis(axis, config, value)
412 default:
413 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
414 }
415}
416
417// SelectValue gets the value for the given axis/config.
418func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
419 axis.validateConfig(config)
420 switch axis.configurationType {
421 case noConfig:
422 return ba.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400423 case arch, os, osArch, bionic, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400424 if v, ok := ba.ConfigurableValues[axis][config]; ok {
425 return &v
426 } else {
427 return nil
428 }
429 default:
430 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
431 }
432}
433
434// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
435func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
436 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
437 for k := range ba.ConfigurableValues {
438 keys = append(keys, k)
439 }
440
441 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
442 return keys
443}
444
Liz Kammer9abd62d2021-05-21 08:37:59 -0400445// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
446type labelListSelectValues map[string]LabelList
447
448func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
449 for k, v := range other {
450 l := ll[k]
451 (&l).Append(v)
452 ll[k] = l
453 }
454}
455
456// HasConfigurableValues returns whether there are configurable values within this set of selects.
457func (ll labelListSelectValues) HasConfigurableValues() bool {
458 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400459 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400460 return true
461 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400462 }
463 return false
464}
465
Jingwen Chen07027912021-03-15 06:02:43 -0400466// LabelListAttribute is used to represent a list of Bazel labels as an
467// attribute.
468type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400469 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400470 Value LabelList
471
Liz Kammer9abd62d2021-05-21 08:37:59 -0400472 // The configured attribute label list Values. Optional
473 // a map of independent configurability axes
474 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400475
476 // If true, differentiate between "nil" and "empty" list. nil means that
477 // this attribute should not be specified at all, and "empty" means that
478 // the attribute should be explicitly specified as an empty list.
479 // This mode facilitates use of attribute defaults: an empty list should
480 // override the default.
481 ForceSpecifyEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400482}
Jingwen Chen91220d72021-03-24 02:18:33 -0400483
Liz Kammer9abd62d2021-05-21 08:37:59 -0400484type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
485
486func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
487 if list.IsNil() {
488 if _, ok := cll[axis][config]; ok {
489 delete(cll[axis], config)
490 }
491 return
492 }
493 if cll[axis] == nil {
494 cll[axis] = make(labelListSelectValues)
495 }
496
497 cll[axis][config] = list
498}
499
500func (cll configurableLabelLists) Append(other configurableLabelLists) {
501 for axis, otherSelects := range other {
502 selects := cll[axis]
503 if selects == nil {
504 selects = make(labelListSelectValues, len(otherSelects))
505 }
506 selects.appendSelects(otherSelects)
507 cll[axis] = selects
508 }
Jingwen Chen07027912021-03-15 06:02:43 -0400509}
510
511// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
512func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400513 return LabelListAttribute{
514 Value: value,
515 ConfigurableValues: make(configurableLabelLists),
516 }
517}
518
519func (lla *LabelListAttribute) SetValue(list LabelList) {
520 lla.SetSelectValue(NoConfigAxis, "", list)
521}
522
523// SetSelectValue set a value for a bazel select for the given axis, config and value.
524func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
525 axis.validateConfig(config)
526 switch axis.configurationType {
527 case noConfig:
528 lla.Value = list
Liz Kammer01a16e82021-07-16 16:33:47 -0400529 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400530 if lla.ConfigurableValues == nil {
531 lla.ConfigurableValues = make(configurableLabelLists)
532 }
533 lla.ConfigurableValues.setValueForAxis(axis, config, list)
534 default:
535 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
536 }
537}
538
539// SelectValue gets a value for a bazel select for the given axis and config.
540func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
541 axis.validateConfig(config)
542 switch axis.configurationType {
543 case noConfig:
544 return lla.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400545 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400546 return lla.ConfigurableValues[axis][config]
547 default:
548 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
549 }
550}
551
552// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
553func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
554 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
555 for k := range lla.ConfigurableValues {
556 keys = append(keys, k)
557 }
558
559 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
560 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400561}
562
Jingwen Chened9c17d2021-04-13 07:14:55 +0000563// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400564// LabelListAttribute to this LabelListAttribute.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400565func (lla *LabelListAttribute) Append(other LabelListAttribute) {
Chris Parsons51f8c392021-08-03 21:01:05 -0400566 if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
567 lla.Value.Includes = []Label{}
568 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400569 lla.Value.Append(other.Value)
570 if lla.ConfigurableValues == nil {
571 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400572 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400573 lla.ConfigurableValues.Append(other.ConfigurableValues)
Jingwen Chen63930982021-03-24 10:04:33 -0400574}
575
Liz Kammer9abd62d2021-05-21 08:37:59 -0400576// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
577func (lla LabelListAttribute) HasConfigurableValues() bool {
578 return len(lla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400579}
580
Chris Parsons69fa9f92021-07-13 11:47:44 -0400581// IsEmpty returns true if the attribute has no values under any configuration.
582func (lla LabelListAttribute) IsEmpty() bool {
583 if len(lla.Value.Includes) > 0 {
584 return false
585 }
586 for axis, _ := range lla.ConfigurableValues {
587 if lla.ConfigurableValues[axis].HasConfigurableValues() {
588 return false
589 }
590 }
591 return true
592}
593
Liz Kammer74deed42021-06-02 13:02:03 -0400594// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
595// the base value and included in default values as appropriate.
596func (lla *LabelListAttribute) ResolveExcludes() {
597 for axis, configToLabels := range lla.ConfigurableValues {
598 baseLabels := lla.Value.deepCopy()
599 for config, val := range configToLabels {
600 // Exclude config-specific excludes from base value
601 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
602
603 // add base values to config specific to add labels excluded by others in this axis
604 // then remove all config-specific excludes
605 allLabels := baseLabels.deepCopy()
606 allLabels.Append(val)
607 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
608 }
609
610 // After going through all configs, delete the duplicates in the config
611 // values that are already in the base Value.
612 for config, val := range configToLabels {
613 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
614 }
615
616 // Now that the Value list is finalized for this axis, compare it with the original
617 // list, and put the difference into the default condition for the axis.
Chris Parsons51f8c392021-08-03 21:01:05 -0400618 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = SubtractBazelLabelList(baseLabels, lla.Value)
Liz Kammer74deed42021-06-02 13:02:03 -0400619
620 // if everything ends up without includes, just delete the axis
621 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
622 delete(lla.ConfigurableValues, axis)
623 }
624 }
625}
626
Jingwen Chen5d864492021-02-24 07:20:12 -0500627// StringListAttribute corresponds to the string_list Bazel attribute type with
628// support for additional metadata, like configurations.
629type StringListAttribute struct {
630 // The base value of the string list attribute.
631 Value []string
632
Liz Kammer9abd62d2021-05-21 08:37:59 -0400633 // The configured attribute label list Values. Optional
634 // a map of independent configurability axes
635 ConfigurableValues configurableStringLists
636}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000637
Liz Kammer9abd62d2021-05-21 08:37:59 -0400638type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400639
Liz Kammer9abd62d2021-05-21 08:37:59 -0400640func (csl configurableStringLists) Append(other configurableStringLists) {
641 for axis, otherSelects := range other {
642 selects := csl[axis]
643 if selects == nil {
644 selects = make(stringListSelectValues, len(otherSelects))
645 }
646 selects.appendSelects(otherSelects)
647 csl[axis] = selects
648 }
649}
650
651func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
652 if csl[axis] == nil {
653 csl[axis] = make(stringListSelectValues)
654 }
655 csl[axis][config] = list
656}
657
658type stringListSelectValues map[string][]string
659
660func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
661 for k, v := range other {
662 sl[k] = append(sl[k], v...)
663 }
664}
665
666func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
667 for _, val := range sl {
668 if len(val) > 0 {
669 return true
670 }
671 }
672 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500673}
674
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000675// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
676func MakeStringListAttribute(value []string) StringListAttribute {
677 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400678 return StringListAttribute{
679 Value: value,
680 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400681 }
682}
683
Liz Kammer9abd62d2021-05-21 08:37:59 -0400684// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
685func (sla StringListAttribute) HasConfigurableValues() bool {
686 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400687}
688
Jingwen Chened9c17d2021-04-13 07:14:55 +0000689// Append appends all values, including os and arch specific ones, from another
690// StringListAttribute to this StringListAttribute
Liz Kammer9abd62d2021-05-21 08:37:59 -0400691func (sla *StringListAttribute) Append(other StringListAttribute) {
692 sla.Value = append(sla.Value, other.Value...)
693 if sla.ConfigurableValues == nil {
694 sla.ConfigurableValues = make(configurableStringLists)
695 }
696 sla.ConfigurableValues.Append(other.ConfigurableValues)
697}
698
699// SetSelectValue set a value for a bazel select for the given axis, config and value.
700func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
701 axis.validateConfig(config)
702 switch axis.configurationType {
703 case noConfig:
704 sla.Value = list
Liz Kammer01a16e82021-07-16 16:33:47 -0400705 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400706 if sla.ConfigurableValues == nil {
707 sla.ConfigurableValues = make(configurableStringLists)
708 }
709 sla.ConfigurableValues.setValueForAxis(axis, config, list)
710 default:
711 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
712 }
713}
714
715// SelectValue gets a value for a bazel select for the given axis and config.
716func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
717 axis.validateConfig(config)
718 switch axis.configurationType {
719 case noConfig:
720 return sla.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400721 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400722 return sla.ConfigurableValues[axis][config]
723 default:
724 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
725 }
726}
727
728// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
729func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
730 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
731 for k := range sla.ConfigurableValues {
732 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000733 }
734
Liz Kammer9abd62d2021-05-21 08:37:59 -0400735 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
736 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000737}
738
Liz Kammer5fad5012021-09-09 14:08:21 -0400739// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
740// configuration-specific values. For example, if we would convert this StringListAttribute as:
741// ["a", "b", "c"] + select({
742// "//condition:one": ["a", "d"],
743// "//conditions:default": [],
744// })
745// after this function, we would convert this StringListAttribute as:
746// ["a", "b", "c"] + select({
747// "//condition:one": ["d"],
748// "//conditions:default": [],
749// })
750func (sla *StringListAttribute) DeduplicateAxesFromBase() {
751 base := sla.Value
752 for axis, configToList := range sla.ConfigurableValues {
753 for config, list := range configToList {
754 remaining := SubtractStrings(list, base)
755 if len(remaining) == 0 {
756 delete(sla.ConfigurableValues[axis], config)
757 } else {
758 sla.ConfigurableValues[axis][config] = remaining
759 }
760 }
761 }
762}
763
Liz Kammera060c452021-03-24 10:14:47 -0400764// TryVariableSubstitution, replace string substitution formatting within each string in slice with
765// Starlark string.format compatible tag for productVariable.
766func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
767 ret := make([]string, 0, len(slice))
768 changesMade := false
769 for _, s := range slice {
770 newS, changed := TryVariableSubstitution(s, productVariable)
771 ret = append(ret, newS)
772 changesMade = changesMade || changed
773 }
774 return ret, changesMade
775}
776
777// TryVariableSubstitution, replace string substitution formatting within s with Starlark
778// string.format compatible tag for productVariable.
779func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400780 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400781 return sub, s != sub
782}