blob: ed4e9fc9acf0bbc9392793bbb3ef22ff22805ab7 [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
Chris Parsons51f8c392021-08-03 21:01:05 -040072func (ll *LabelList) Equals(other LabelList) bool {
73 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
74 return false
75 }
76 for i, _ := range ll.Includes {
77 if ll.Includes[i] != other.Includes[i] {
78 return false
79 }
80 }
81 for i, _ := range ll.Excludes {
82 if ll.Excludes[i] != other.Excludes[i] {
83 return false
84 }
85 }
86 return true
87}
88
Liz Kammer9abd62d2021-05-21 08:37:59 -040089func (ll *LabelList) IsNil() bool {
90 return ll.Includes == nil && ll.Excludes == nil
91}
92
Liz Kammer74deed42021-06-02 13:02:03 -040093func (ll *LabelList) deepCopy() LabelList {
94 return LabelList{
95 Includes: ll.Includes[:],
96 Excludes: ll.Excludes[:],
97 }
98}
99
Jingwen Chen63930982021-03-24 10:04:33 -0400100// uniqueParentDirectories returns a list of the unique parent directories for
101// all files in ll.Includes.
102func (ll *LabelList) uniqueParentDirectories() []string {
103 dirMap := map[string]bool{}
104 for _, label := range ll.Includes {
105 dirMap[filepath.Dir(label.Label)] = true
106 }
107 dirs := []string{}
108 for dir := range dirMap {
109 dirs = append(dirs, dir)
110 }
111 return dirs
112}
113
Liz Kammer356f7d42021-01-26 09:18:53 -0500114// Append appends the fields of other labelList to the corresponding fields of ll.
115func (ll *LabelList) Append(other LabelList) {
116 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
117 ll.Includes = append(ll.Includes, other.Includes...)
118 }
119 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
120 ll.Excludes = append(other.Excludes, other.Excludes...)
121 }
122}
Jingwen Chen5d864492021-02-24 07:20:12 -0500123
Jingwen Chened9c17d2021-04-13 07:14:55 +0000124// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
125// the slice in a sorted order.
126func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000127 uniqueLabelsSet := make(map[Label]bool)
128 for _, l := range originalLabels {
129 uniqueLabelsSet[l] = true
130 }
131 var uniqueLabels []Label
132 for l, _ := range uniqueLabelsSet {
133 uniqueLabels = append(uniqueLabels, l)
134 }
135 sort.SliceStable(uniqueLabels, func(i, j int) bool {
136 return uniqueLabels[i].Label < uniqueLabels[j].Label
137 })
138 return uniqueLabels
139}
140
Liz Kammer9abd62d2021-05-21 08:37:59 -0400141func FirstUniqueBazelLabels(originalLabels []Label) []Label {
142 var labels []Label
143 found := make(map[Label]bool, len(originalLabels))
144 for _, l := range originalLabels {
145 if _, ok := found[l]; ok {
146 continue
147 }
148 labels = append(labels, l)
149 found[l] = true
150 }
151 return labels
152}
153
154func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
155 var uniqueLabelList LabelList
156 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
157 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
158 return uniqueLabelList
159}
160
161func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000162 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000163 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
164 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000165 return uniqueLabelList
166}
167
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000168// Subtract needle from haystack
169func SubtractStrings(haystack []string, needle []string) []string {
170 // This is really a set
171 remainder := make(map[string]bool)
172
173 for _, s := range haystack {
174 remainder[s] = true
175 }
176 for _, s := range needle {
177 delete(remainder, s)
178 }
179
180 var strings []string
181 for s, _ := range remainder {
182 strings = append(strings, s)
183 }
184
185 sort.SliceStable(strings, func(i, j int) bool {
186 return strings[i] < strings[j]
187 })
188
189 return strings
190}
191
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000192// Map a function over all labels in a LabelList.
193func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
194 var includes []Label
195 for _, inc := range mapOver.Includes {
196 mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
197 includes = append(includes, mappedLabel)
198 }
199 // mapFn is not applied over excludes, but they are propagated as-is.
200 return LabelList{Includes: includes, Excludes: mapOver.Excludes}
201}
202
203// Map a function over all Labels in a LabelListAttribute
204func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
205 var result LabelListAttribute
206
207 result.Value = MapLabelList(mapOver.Value, mapFn)
208
Liz Kammer9abd62d2021-05-21 08:37:59 -0400209 for axis, configToLabels := range mapOver.ConfigurableValues {
210 for config, value := range configToLabels {
211 result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000212 }
213 }
214
215 return result
216}
217
Chris Parsons990c4f42021-05-25 12:10:58 -0400218// Return all needles in a given haystack, where needleFn is true for needles.
219func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
220 var includes []Label
Chris Parsons990c4f42021-05-25 12:10:58 -0400221 for _, inc := range haystack.Includes {
222 if needleFn(inc.Label) {
223 includes = append(includes, inc)
224 }
225 }
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000226 // needleFn is not applied over excludes, but they are propagated as-is.
Chris Parsons990c4f42021-05-25 12:10:58 -0400227 return LabelList{Includes: includes, Excludes: haystack.Excludes}
228}
229
230// Return all needles in a given haystack, where needleFn is true for needles.
231func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400232 result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
Chris Parsons990c4f42021-05-25 12:10:58 -0400233
Liz Kammer9abd62d2021-05-21 08:37:59 -0400234 for config, selects := range haystack.ConfigurableValues {
235 newSelects := make(labelListSelectValues, len(selects))
236 for k, v := range selects {
237 newSelects[k] = FilterLabelList(v, needleFn)
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
242 return result
243}
244
245// Subtract needle from haystack
246func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400247 result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
Chris Parsons990c4f42021-05-25 12:10:58 -0400248
Liz Kammer9abd62d2021-05-21 08:37:59 -0400249 for config, selects := range haystack.ConfigurableValues {
250 newSelects := make(labelListSelectValues, len(selects))
251 needleSelects := needle.ConfigurableValues[config]
Chris Parsons990c4f42021-05-25 12:10:58 -0400252
Liz Kammer9abd62d2021-05-21 08:37:59 -0400253 for k, v := range selects {
254 newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400255 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400256 result.ConfigurableValues[config] = newSelects
Chris Parsons990c4f42021-05-25 12:10:58 -0400257 }
258
Chris Parsons990c4f42021-05-25 12:10:58 -0400259 return result
260}
261
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000262// Subtract needle from haystack
263func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
264 // This is really a set
265 remainder := make(map[Label]bool)
266
267 for _, label := range haystack {
268 remainder[label] = true
269 }
270 for _, label := range needle {
271 delete(remainder, label)
272 }
273
274 var labels []Label
275 for label, _ := range remainder {
276 labels = append(labels, label)
277 }
278
279 sort.SliceStable(labels, func(i, j int) bool {
280 return labels[i].Label < labels[j].Label
281 })
282
283 return labels
284}
285
Chris Parsons484e50a2021-05-13 15:13:04 -0400286// Appends two LabelLists, returning the combined list.
287func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
288 var result LabelList
289 result.Includes = append(a.Includes, b.Includes...)
290 result.Excludes = append(a.Excludes, b.Excludes...)
291 return result
292}
293
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000294// Subtract needle from haystack
295func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
296 var result LabelList
297 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
298 // NOTE: Excludes are intentionally not subtracted
299 result.Excludes = haystack.Excludes
300 return result
301}
302
Jingwen Chenc1c26502021-04-05 10:35:13 +0000303type Attribute interface {
304 HasConfigurableValues() bool
305}
306
Liz Kammer9abd62d2021-05-21 08:37:59 -0400307type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400308
Liz Kammer9abd62d2021-05-21 08:37:59 -0400309type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400310
Liz Kammer9abd62d2021-05-21 08:37:59 -0400311func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
312 if cl[axis] == nil {
313 cl[axis] = make(labelSelectValues)
314 }
315 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400316}
317
318// Represents an attribute whose value is a single label
319type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400320 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400321
Liz Kammer9abd62d2021-05-21 08:37:59 -0400322 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200323}
324
Liz Kammer9abd62d2021-05-21 08:37:59 -0400325// HasConfigurableValues returns whether there are configurable values set for this label.
326func (la LabelAttribute) HasConfigurableValues() bool {
327 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200328}
329
Liz Kammer9abd62d2021-05-21 08:37:59 -0400330// SetValue sets the base, non-configured value for the Label
331func (la *LabelAttribute) SetValue(value Label) {
332 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400333}
334
Liz Kammer9abd62d2021-05-21 08:37:59 -0400335// SetSelectValue set a value for a bazel select for the given axis, config and value.
336func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
337 axis.validateConfig(config)
338 switch axis.configurationType {
339 case noConfig:
340 la.Value = &value
Liz Kammer01a16e82021-07-16 16:33:47 -0400341 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400342 if la.ConfigurableValues == nil {
343 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400344 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400345 la.ConfigurableValues.setValueForAxis(axis, config, &value)
346 default:
347 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
348 }
349}
350
351// SelectValue gets a value for a bazel select for the given axis and config.
352func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
353 axis.validateConfig(config)
354 switch axis.configurationType {
355 case noConfig:
356 return *la.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400357 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400358 return *la.ConfigurableValues[axis][config]
359 default:
360 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
361 }
362}
363
364// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
365func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
366 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
367 for k := range la.ConfigurableValues {
368 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400369 }
370
Liz Kammer9abd62d2021-05-21 08:37:59 -0400371 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
372 return keys
373}
374
Liz Kammerd366c902021-06-03 13:43:01 -0400375type configToBools map[string]bool
376
377func (ctb configToBools) setValue(config string, value *bool) {
378 if value == nil {
379 if _, ok := ctb[config]; ok {
380 delete(ctb, config)
381 }
382 return
383 }
384 ctb[config] = *value
385}
386
387type configurableBools map[ConfigurationAxis]configToBools
388
389func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
390 if cb[axis] == nil {
391 cb[axis] = make(configToBools)
392 }
393 cb[axis].setValue(config, value)
394}
395
396// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
397type BoolAttribute struct {
398 Value *bool
399
400 ConfigurableValues configurableBools
401}
402
403// HasConfigurableValues returns whether there are configurable values for this attribute.
404func (ba BoolAttribute) HasConfigurableValues() bool {
405 return len(ba.ConfigurableValues) > 0
406}
407
408// SetSelectValue sets value for the given axis/config.
409func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
410 axis.validateConfig(config)
411 switch axis.configurationType {
412 case noConfig:
413 ba.Value = value
Liz Kammer01a16e82021-07-16 16:33:47 -0400414 case arch, os, osArch, bionic, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400415 if ba.ConfigurableValues == nil {
416 ba.ConfigurableValues = make(configurableBools)
417 }
418 ba.ConfigurableValues.setValueForAxis(axis, config, value)
419 default:
420 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
421 }
422}
423
424// SelectValue gets the value for the given axis/config.
425func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
426 axis.validateConfig(config)
427 switch axis.configurationType {
428 case noConfig:
429 return ba.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400430 case arch, os, osArch, bionic, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400431 if v, ok := ba.ConfigurableValues[axis][config]; ok {
432 return &v
433 } else {
434 return nil
435 }
436 default:
437 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
438 }
439}
440
441// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
442func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
443 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
444 for k := range ba.ConfigurableValues {
445 keys = append(keys, k)
446 }
447
448 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
449 return keys
450}
451
Liz Kammer9abd62d2021-05-21 08:37:59 -0400452// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
453type labelListSelectValues map[string]LabelList
454
455func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
456 for k, v := range other {
457 l := ll[k]
458 (&l).Append(v)
459 ll[k] = l
460 }
461}
462
463// HasConfigurableValues returns whether there are configurable values within this set of selects.
464func (ll labelListSelectValues) HasConfigurableValues() bool {
465 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400466 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400467 return true
468 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400469 }
470 return false
471}
472
Jingwen Chen07027912021-03-15 06:02:43 -0400473// LabelListAttribute is used to represent a list of Bazel labels as an
474// attribute.
475type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400476 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400477 Value LabelList
478
Liz Kammer9abd62d2021-05-21 08:37:59 -0400479 // The configured attribute label list Values. Optional
480 // a map of independent configurability axes
481 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400482
483 // If true, differentiate between "nil" and "empty" list. nil means that
484 // this attribute should not be specified at all, and "empty" means that
485 // the attribute should be explicitly specified as an empty list.
486 // This mode facilitates use of attribute defaults: an empty list should
487 // override the default.
488 ForceSpecifyEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400489}
Jingwen Chen91220d72021-03-24 02:18:33 -0400490
Liz Kammer9abd62d2021-05-21 08:37:59 -0400491type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
492
493func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
494 if list.IsNil() {
495 if _, ok := cll[axis][config]; ok {
496 delete(cll[axis], config)
497 }
498 return
499 }
500 if cll[axis] == nil {
501 cll[axis] = make(labelListSelectValues)
502 }
503
504 cll[axis][config] = list
505}
506
507func (cll configurableLabelLists) Append(other configurableLabelLists) {
508 for axis, otherSelects := range other {
509 selects := cll[axis]
510 if selects == nil {
511 selects = make(labelListSelectValues, len(otherSelects))
512 }
513 selects.appendSelects(otherSelects)
514 cll[axis] = selects
515 }
Jingwen Chen07027912021-03-15 06:02:43 -0400516}
517
518// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
519func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400520 return LabelListAttribute{
521 Value: value,
522 ConfigurableValues: make(configurableLabelLists),
523 }
524}
525
526func (lla *LabelListAttribute) SetValue(list LabelList) {
527 lla.SetSelectValue(NoConfigAxis, "", list)
528}
529
530// SetSelectValue set a value for a bazel select for the given axis, config and value.
531func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
532 axis.validateConfig(config)
533 switch axis.configurationType {
534 case noConfig:
535 lla.Value = list
Liz Kammer01a16e82021-07-16 16:33:47 -0400536 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400537 if lla.ConfigurableValues == nil {
538 lla.ConfigurableValues = make(configurableLabelLists)
539 }
540 lla.ConfigurableValues.setValueForAxis(axis, config, list)
541 default:
542 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
543 }
544}
545
546// SelectValue gets a value for a bazel select for the given axis and config.
547func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
548 axis.validateConfig(config)
549 switch axis.configurationType {
550 case noConfig:
551 return lla.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400552 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400553 return lla.ConfigurableValues[axis][config]
554 default:
555 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
556 }
557}
558
559// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
560func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
561 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
562 for k := range lla.ConfigurableValues {
563 keys = append(keys, k)
564 }
565
566 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
567 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400568}
569
Jingwen Chened9c17d2021-04-13 07:14:55 +0000570// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400571// LabelListAttribute to this LabelListAttribute.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400572func (lla *LabelListAttribute) Append(other LabelListAttribute) {
Chris Parsons51f8c392021-08-03 21:01:05 -0400573 if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
574 lla.Value.Includes = []Label{}
575 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400576 lla.Value.Append(other.Value)
577 if lla.ConfigurableValues == nil {
578 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400579 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400580 lla.ConfigurableValues.Append(other.ConfigurableValues)
Jingwen Chen63930982021-03-24 10:04:33 -0400581}
582
Liz Kammer9abd62d2021-05-21 08:37:59 -0400583// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
584func (lla LabelListAttribute) HasConfigurableValues() bool {
585 return len(lla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400586}
587
Chris Parsons69fa9f92021-07-13 11:47:44 -0400588// IsEmpty returns true if the attribute has no values under any configuration.
589func (lla LabelListAttribute) IsEmpty() bool {
590 if len(lla.Value.Includes) > 0 {
591 return false
592 }
593 for axis, _ := range lla.ConfigurableValues {
594 if lla.ConfigurableValues[axis].HasConfigurableValues() {
595 return false
596 }
597 }
598 return true
599}
600
Liz Kammer74deed42021-06-02 13:02:03 -0400601// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
602// the base value and included in default values as appropriate.
603func (lla *LabelListAttribute) ResolveExcludes() {
604 for axis, configToLabels := range lla.ConfigurableValues {
605 baseLabels := lla.Value.deepCopy()
606 for config, val := range configToLabels {
607 // Exclude config-specific excludes from base value
608 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
609
610 // add base values to config specific to add labels excluded by others in this axis
611 // then remove all config-specific excludes
612 allLabels := baseLabels.deepCopy()
613 allLabels.Append(val)
614 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
615 }
616
617 // After going through all configs, delete the duplicates in the config
618 // values that are already in the base Value.
619 for config, val := range configToLabels {
620 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
621 }
622
623 // Now that the Value list is finalized for this axis, compare it with the original
624 // list, and put the difference into the default condition for the axis.
Chris Parsons51f8c392021-08-03 21:01:05 -0400625 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = SubtractBazelLabelList(baseLabels, lla.Value)
Liz Kammer74deed42021-06-02 13:02:03 -0400626
627 // if everything ends up without includes, just delete the axis
628 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
629 delete(lla.ConfigurableValues, axis)
630 }
631 }
632}
633
Jingwen Chen5d864492021-02-24 07:20:12 -0500634// StringListAttribute corresponds to the string_list Bazel attribute type with
635// support for additional metadata, like configurations.
636type StringListAttribute struct {
637 // The base value of the string list attribute.
638 Value []string
639
Liz Kammer9abd62d2021-05-21 08:37:59 -0400640 // The configured attribute label list Values. Optional
641 // a map of independent configurability axes
642 ConfigurableValues configurableStringLists
643}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000644
Liz Kammer9abd62d2021-05-21 08:37:59 -0400645type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400646
Liz Kammer9abd62d2021-05-21 08:37:59 -0400647func (csl configurableStringLists) Append(other configurableStringLists) {
648 for axis, otherSelects := range other {
649 selects := csl[axis]
650 if selects == nil {
651 selects = make(stringListSelectValues, len(otherSelects))
652 }
653 selects.appendSelects(otherSelects)
654 csl[axis] = selects
655 }
656}
657
658func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
659 if csl[axis] == nil {
660 csl[axis] = make(stringListSelectValues)
661 }
662 csl[axis][config] = list
663}
664
665type stringListSelectValues map[string][]string
666
667func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
668 for k, v := range other {
669 sl[k] = append(sl[k], v...)
670 }
671}
672
673func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
674 for _, val := range sl {
675 if len(val) > 0 {
676 return true
677 }
678 }
679 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500680}
681
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000682// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
683func MakeStringListAttribute(value []string) StringListAttribute {
684 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400685 return StringListAttribute{
686 Value: value,
687 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400688 }
689}
690
Liz Kammer9abd62d2021-05-21 08:37:59 -0400691// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
692func (sla StringListAttribute) HasConfigurableValues() bool {
693 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400694}
695
Jingwen Chened9c17d2021-04-13 07:14:55 +0000696// Append appends all values, including os and arch specific ones, from another
697// StringListAttribute to this StringListAttribute
Liz Kammer9abd62d2021-05-21 08:37:59 -0400698func (sla *StringListAttribute) Append(other StringListAttribute) {
699 sla.Value = append(sla.Value, other.Value...)
700 if sla.ConfigurableValues == nil {
701 sla.ConfigurableValues = make(configurableStringLists)
702 }
703 sla.ConfigurableValues.Append(other.ConfigurableValues)
704}
705
706// SetSelectValue set a value for a bazel select for the given axis, config and value.
707func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
708 axis.validateConfig(config)
709 switch axis.configurationType {
710 case noConfig:
711 sla.Value = list
Liz Kammer01a16e82021-07-16 16:33:47 -0400712 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400713 if sla.ConfigurableValues == nil {
714 sla.ConfigurableValues = make(configurableStringLists)
715 }
716 sla.ConfigurableValues.setValueForAxis(axis, config, list)
717 default:
718 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
719 }
720}
721
722// SelectValue gets a value for a bazel select for the given axis and config.
723func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
724 axis.validateConfig(config)
725 switch axis.configurationType {
726 case noConfig:
727 return sla.Value
Liz Kammer01a16e82021-07-16 16:33:47 -0400728 case arch, os, osArch, bionic, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400729 return sla.ConfigurableValues[axis][config]
730 default:
731 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
732 }
733}
734
735// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
736func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
737 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
738 for k := range sla.ConfigurableValues {
739 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000740 }
741
Liz Kammer9abd62d2021-05-21 08:37:59 -0400742 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
743 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000744}
745
Liz Kammer5fad5012021-09-09 14:08:21 -0400746// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
747// configuration-specific values. For example, if we would convert this StringListAttribute as:
748// ["a", "b", "c"] + select({
749// "//condition:one": ["a", "d"],
750// "//conditions:default": [],
751// })
752// after this function, we would convert this StringListAttribute as:
753// ["a", "b", "c"] + select({
754// "//condition:one": ["d"],
755// "//conditions:default": [],
756// })
757func (sla *StringListAttribute) DeduplicateAxesFromBase() {
758 base := sla.Value
759 for axis, configToList := range sla.ConfigurableValues {
760 for config, list := range configToList {
761 remaining := SubtractStrings(list, base)
762 if len(remaining) == 0 {
763 delete(sla.ConfigurableValues[axis], config)
764 } else {
765 sla.ConfigurableValues[axis][config] = remaining
766 }
767 }
768 }
769}
770
Liz Kammera060c452021-03-24 10:14:47 -0400771// TryVariableSubstitution, replace string substitution formatting within each string in slice with
772// Starlark string.format compatible tag for productVariable.
773func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
774 ret := make([]string, 0, len(slice))
775 changesMade := false
776 for _, s := range slice {
777 newS, changed := TryVariableSubstitution(s, productVariable)
778 ret = append(ret, newS)
779 changesMade = changesMade || changed
780 }
781 return ret, changesMade
782}
783
784// TryVariableSubstitution, replace string substitution formatting within s with Starlark
785// string.format compatible tag for productVariable.
786func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400787 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400788 return sub, s != sub
789}