blob: 7093d6cdfc4a63f7daf2e2d25759dcfaeea781ea [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
Jingwen Chenfb4692a2021-02-07 10:05:16 -050034const BazelTargetModuleNamePrefix = "__bp2build__"
35
Liz Kammera060c452021-03-24 10:14:47 -040036var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
37
Jingwen Chen38e62642021-04-19 05:00:15 +000038// Label is used to represent a Bazel compatible Label. Also stores the original
39// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050040type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000041 // The string representation of a Bazel target label. This can be a relative
42 // or fully qualified label. These labels are used for generating BUILD
43 // files with bp2build.
44 Label string
45
46 // The original Soong/Blueprint module name that the label was derived from.
47 // This is used for replacing references to the original name with the new
48 // label, for example in genrule cmds.
49 //
50 // While there is a reversible 1:1 mapping from the module name to Bazel
51 // label with bp2build that could make computing the original module name
52 // from the label automatic, it is not the case for handcrafted targets,
53 // where modules can have a custom label mapping through the { bazel_module:
54 // { label: <label> } } property.
55 //
56 // With handcrafted labels, those modules don't go through bp2build
57 // conversion, but relies on handcrafted targets in the source tree.
58 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050059}
60
61// LabelList is used to represent a list of Bazel labels.
62type LabelList struct {
63 Includes []Label
64 Excludes []Label
65}
66
Liz Kammer9abd62d2021-05-21 08:37:59 -040067func (ll *LabelList) IsNil() bool {
68 return ll.Includes == nil && ll.Excludes == nil
69}
70
Liz Kammer74deed42021-06-02 13:02:03 -040071func (ll *LabelList) deepCopy() LabelList {
72 return LabelList{
73 Includes: ll.Includes[:],
74 Excludes: ll.Excludes[:],
75 }
76}
77
Jingwen Chen63930982021-03-24 10:04:33 -040078// uniqueParentDirectories returns a list of the unique parent directories for
79// all files in ll.Includes.
80func (ll *LabelList) uniqueParentDirectories() []string {
81 dirMap := map[string]bool{}
82 for _, label := range ll.Includes {
83 dirMap[filepath.Dir(label.Label)] = true
84 }
85 dirs := []string{}
86 for dir := range dirMap {
87 dirs = append(dirs, dir)
88 }
89 return dirs
90}
91
Liz Kammer356f7d42021-01-26 09:18:53 -050092// Append appends the fields of other labelList to the corresponding fields of ll.
93func (ll *LabelList) Append(other LabelList) {
94 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
95 ll.Includes = append(ll.Includes, other.Includes...)
96 }
97 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
98 ll.Excludes = append(other.Excludes, other.Excludes...)
99 }
100}
Jingwen Chen5d864492021-02-24 07:20:12 -0500101
Jingwen Chened9c17d2021-04-13 07:14:55 +0000102// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
103// the slice in a sorted order.
104func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000105 uniqueLabelsSet := make(map[Label]bool)
106 for _, l := range originalLabels {
107 uniqueLabelsSet[l] = true
108 }
109 var uniqueLabels []Label
110 for l, _ := range uniqueLabelsSet {
111 uniqueLabels = append(uniqueLabels, l)
112 }
113 sort.SliceStable(uniqueLabels, func(i, j int) bool {
114 return uniqueLabels[i].Label < uniqueLabels[j].Label
115 })
116 return uniqueLabels
117}
118
Liz Kammer9abd62d2021-05-21 08:37:59 -0400119func FirstUniqueBazelLabels(originalLabels []Label) []Label {
120 var labels []Label
121 found := make(map[Label]bool, len(originalLabels))
122 for _, l := range originalLabels {
123 if _, ok := found[l]; ok {
124 continue
125 }
126 labels = append(labels, l)
127 found[l] = true
128 }
129 return labels
130}
131
132func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
133 var uniqueLabelList LabelList
134 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
135 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
136 return uniqueLabelList
137}
138
139func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000140 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000141 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
142 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000143 return uniqueLabelList
144}
145
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000146// Subtract needle from haystack
147func SubtractStrings(haystack []string, needle []string) []string {
148 // This is really a set
149 remainder := make(map[string]bool)
150
151 for _, s := range haystack {
152 remainder[s] = true
153 }
154 for _, s := range needle {
155 delete(remainder, s)
156 }
157
158 var strings []string
159 for s, _ := range remainder {
160 strings = append(strings, s)
161 }
162
163 sort.SliceStable(strings, func(i, j int) bool {
164 return strings[i] < strings[j]
165 })
166
167 return strings
168}
169
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000170// Map a function over all labels in a LabelList.
171func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
172 var includes []Label
173 for _, inc := range mapOver.Includes {
174 mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
175 includes = append(includes, mappedLabel)
176 }
177 // mapFn is not applied over excludes, but they are propagated as-is.
178 return LabelList{Includes: includes, Excludes: mapOver.Excludes}
179}
180
181// Map a function over all Labels in a LabelListAttribute
182func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
183 var result LabelListAttribute
184
185 result.Value = MapLabelList(mapOver.Value, mapFn)
186
Liz Kammer9abd62d2021-05-21 08:37:59 -0400187 for axis, configToLabels := range mapOver.ConfigurableValues {
188 for config, value := range configToLabels {
189 result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000190 }
191 }
192
193 return result
194}
195
Chris Parsons990c4f42021-05-25 12:10:58 -0400196// Return all needles in a given haystack, where needleFn is true for needles.
197func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
198 var includes []Label
Chris Parsons990c4f42021-05-25 12:10:58 -0400199 for _, inc := range haystack.Includes {
200 if needleFn(inc.Label) {
201 includes = append(includes, inc)
202 }
203 }
Jingwen Chen14a8bda2021-06-02 11:10:02 +0000204 // needleFn is not applied over excludes, but they are propagated as-is.
Chris Parsons990c4f42021-05-25 12:10:58 -0400205 return LabelList{Includes: includes, Excludes: haystack.Excludes}
206}
207
208// Return all needles in a given haystack, where needleFn is true for needles.
209func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400210 result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
Chris Parsons990c4f42021-05-25 12:10:58 -0400211
Liz Kammer9abd62d2021-05-21 08:37:59 -0400212 for config, selects := range haystack.ConfigurableValues {
213 newSelects := make(labelListSelectValues, len(selects))
214 for k, v := range selects {
215 newSelects[k] = FilterLabelList(v, needleFn)
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400216 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400217 result.ConfigurableValues[config] = newSelects
Chris Parsons990c4f42021-05-25 12:10:58 -0400218 }
219
220 return result
221}
222
223// Subtract needle from haystack
224func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400225 result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
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 needleSelects := needle.ConfigurableValues[config]
Chris Parsons990c4f42021-05-25 12:10:58 -0400230
Liz Kammer9abd62d2021-05-21 08:37:59 -0400231 for k, v := range selects {
232 newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400233 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400234 result.ConfigurableValues[config] = newSelects
Chris Parsons990c4f42021-05-25 12:10:58 -0400235 }
236
Chris Parsons990c4f42021-05-25 12:10:58 -0400237 return result
238}
239
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000240// Subtract needle from haystack
241func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
242 // This is really a set
243 remainder := make(map[Label]bool)
244
245 for _, label := range haystack {
246 remainder[label] = true
247 }
248 for _, label := range needle {
249 delete(remainder, label)
250 }
251
252 var labels []Label
253 for label, _ := range remainder {
254 labels = append(labels, label)
255 }
256
257 sort.SliceStable(labels, func(i, j int) bool {
258 return labels[i].Label < labels[j].Label
259 })
260
261 return labels
262}
263
Chris Parsons484e50a2021-05-13 15:13:04 -0400264// Appends two LabelLists, returning the combined list.
265func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
266 var result LabelList
267 result.Includes = append(a.Includes, b.Includes...)
268 result.Excludes = append(a.Excludes, b.Excludes...)
269 return result
270}
271
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000272// Subtract needle from haystack
273func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
274 var result LabelList
275 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
276 // NOTE: Excludes are intentionally not subtracted
277 result.Excludes = haystack.Excludes
278 return result
279}
280
Jingwen Chenc1c26502021-04-05 10:35:13 +0000281type Attribute interface {
282 HasConfigurableValues() bool
283}
284
Liz Kammer9abd62d2021-05-21 08:37:59 -0400285type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400286
Liz Kammer9abd62d2021-05-21 08:37:59 -0400287type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400288
Liz Kammer9abd62d2021-05-21 08:37:59 -0400289func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
290 if cl[axis] == nil {
291 cl[axis] = make(labelSelectValues)
292 }
293 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400294}
295
296// Represents an attribute whose value is a single label
297type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400298 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400299
Liz Kammer9abd62d2021-05-21 08:37:59 -0400300 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200301}
302
Liz Kammer9abd62d2021-05-21 08:37:59 -0400303// HasConfigurableValues returns whether there are configurable values set for this label.
304func (la LabelAttribute) HasConfigurableValues() bool {
305 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200306}
307
Liz Kammer9abd62d2021-05-21 08:37:59 -0400308// SetValue sets the base, non-configured value for the Label
309func (la *LabelAttribute) SetValue(value Label) {
310 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400311}
312
Liz Kammer9abd62d2021-05-21 08:37:59 -0400313// SetSelectValue set a value for a bazel select for the given axis, config and value.
314func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
315 axis.validateConfig(config)
316 switch axis.configurationType {
317 case noConfig:
318 la.Value = &value
319 case arch, os, osArch, productVariables:
320 if la.ConfigurableValues == nil {
321 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400322 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400323 la.ConfigurableValues.setValueForAxis(axis, config, &value)
324 default:
325 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
326 }
327}
328
329// SelectValue gets a value for a bazel select for the given axis and config.
330func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
331 axis.validateConfig(config)
332 switch axis.configurationType {
333 case noConfig:
334 return *la.Value
335 case arch, os, osArch, productVariables:
336 return *la.ConfigurableValues[axis][config]
337 default:
338 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
339 }
340}
341
342// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
343func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
344 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
345 for k := range la.ConfigurableValues {
346 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400347 }
348
Liz Kammer9abd62d2021-05-21 08:37:59 -0400349 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
350 return keys
351}
352
Liz Kammerd366c902021-06-03 13:43:01 -0400353type configToBools map[string]bool
354
355func (ctb configToBools) setValue(config string, value *bool) {
356 if value == nil {
357 if _, ok := ctb[config]; ok {
358 delete(ctb, config)
359 }
360 return
361 }
362 ctb[config] = *value
363}
364
365type configurableBools map[ConfigurationAxis]configToBools
366
367func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
368 if cb[axis] == nil {
369 cb[axis] = make(configToBools)
370 }
371 cb[axis].setValue(config, value)
372}
373
374// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
375type BoolAttribute struct {
376 Value *bool
377
378 ConfigurableValues configurableBools
379}
380
381// HasConfigurableValues returns whether there are configurable values for this attribute.
382func (ba BoolAttribute) HasConfigurableValues() bool {
383 return len(ba.ConfigurableValues) > 0
384}
385
386// SetSelectValue sets value for the given axis/config.
387func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
388 axis.validateConfig(config)
389 switch axis.configurationType {
390 case noConfig:
391 ba.Value = value
392 case arch, os, osArch, productVariables:
393 if ba.ConfigurableValues == nil {
394 ba.ConfigurableValues = make(configurableBools)
395 }
396 ba.ConfigurableValues.setValueForAxis(axis, config, value)
397 default:
398 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
399 }
400}
401
402// SelectValue gets the value for the given axis/config.
403func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
404 axis.validateConfig(config)
405 switch axis.configurationType {
406 case noConfig:
407 return ba.Value
408 case arch, os, osArch, productVariables:
409 if v, ok := ba.ConfigurableValues[axis][config]; ok {
410 return &v
411 } else {
412 return nil
413 }
414 default:
415 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
416 }
417}
418
419// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
420func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
421 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
422 for k := range ba.ConfigurableValues {
423 keys = append(keys, k)
424 }
425
426 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
427 return keys
428}
429
Liz Kammer9abd62d2021-05-21 08:37:59 -0400430// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
431type labelListSelectValues map[string]LabelList
432
433func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
434 for k, v := range other {
435 l := ll[k]
436 (&l).Append(v)
437 ll[k] = l
438 }
439}
440
441// HasConfigurableValues returns whether there are configurable values within this set of selects.
442func (ll labelListSelectValues) HasConfigurableValues() bool {
443 for _, v := range ll {
444 if len(v.Includes) > 0 {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400445 return true
446 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400447 }
448 return false
449}
450
Jingwen Chen07027912021-03-15 06:02:43 -0400451// LabelListAttribute is used to represent a list of Bazel labels as an
452// attribute.
453type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400454 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400455 Value LabelList
456
Liz Kammer9abd62d2021-05-21 08:37:59 -0400457 // The configured attribute label list Values. Optional
458 // a map of independent configurability axes
459 ConfigurableValues configurableLabelLists
460}
Jingwen Chen91220d72021-03-24 02:18:33 -0400461
Liz Kammer9abd62d2021-05-21 08:37:59 -0400462type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
463
464func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
465 if list.IsNil() {
466 if _, ok := cll[axis][config]; ok {
467 delete(cll[axis], config)
468 }
469 return
470 }
471 if cll[axis] == nil {
472 cll[axis] = make(labelListSelectValues)
473 }
474
475 cll[axis][config] = list
476}
477
478func (cll configurableLabelLists) Append(other configurableLabelLists) {
479 for axis, otherSelects := range other {
480 selects := cll[axis]
481 if selects == nil {
482 selects = make(labelListSelectValues, len(otherSelects))
483 }
484 selects.appendSelects(otherSelects)
485 cll[axis] = selects
486 }
Jingwen Chen07027912021-03-15 06:02:43 -0400487}
488
489// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
490func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400491 return LabelListAttribute{
492 Value: value,
493 ConfigurableValues: make(configurableLabelLists),
494 }
495}
496
497func (lla *LabelListAttribute) SetValue(list LabelList) {
498 lla.SetSelectValue(NoConfigAxis, "", list)
499}
500
501// SetSelectValue set a value for a bazel select for the given axis, config and value.
502func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
503 axis.validateConfig(config)
504 switch axis.configurationType {
505 case noConfig:
506 lla.Value = list
507 case arch, os, osArch, productVariables:
508 if lla.ConfigurableValues == nil {
509 lla.ConfigurableValues = make(configurableLabelLists)
510 }
511 lla.ConfigurableValues.setValueForAxis(axis, config, list)
512 default:
513 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
514 }
515}
516
517// SelectValue gets a value for a bazel select for the given axis and config.
518func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
519 axis.validateConfig(config)
520 switch axis.configurationType {
521 case noConfig:
522 return lla.Value
523 case arch, os, osArch, productVariables:
524 return lla.ConfigurableValues[axis][config]
525 default:
526 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
527 }
528}
529
530// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
531func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
532 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
533 for k := range lla.ConfigurableValues {
534 keys = append(keys, k)
535 }
536
537 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
538 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400539}
540
Jingwen Chened9c17d2021-04-13 07:14:55 +0000541// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400542// LabelListAttribute to this LabelListAttribute.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400543func (lla *LabelListAttribute) Append(other LabelListAttribute) {
544 lla.Value.Append(other.Value)
545 if lla.ConfigurableValues == nil {
546 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400547 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400548 lla.ConfigurableValues.Append(other.ConfigurableValues)
Jingwen Chen63930982021-03-24 10:04:33 -0400549}
550
Liz Kammer9abd62d2021-05-21 08:37:59 -0400551// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
552func (lla LabelListAttribute) HasConfigurableValues() bool {
553 return len(lla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400554}
555
Liz Kammer74deed42021-06-02 13:02:03 -0400556// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
557// the base value and included in default values as appropriate.
558func (lla *LabelListAttribute) ResolveExcludes() {
559 for axis, configToLabels := range lla.ConfigurableValues {
560 baseLabels := lla.Value.deepCopy()
561 for config, val := range configToLabels {
562 // Exclude config-specific excludes from base value
563 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
564
565 // add base values to config specific to add labels excluded by others in this axis
566 // then remove all config-specific excludes
567 allLabels := baseLabels.deepCopy()
568 allLabels.Append(val)
569 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
570 }
571
572 // After going through all configs, delete the duplicates in the config
573 // values that are already in the base Value.
574 for config, val := range configToLabels {
575 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
576 }
577
578 // Now that the Value list is finalized for this axis, compare it with the original
579 // list, and put the difference into the default condition for the axis.
580 lla.ConfigurableValues[axis][conditionsDefault] = SubtractBazelLabelList(baseLabels, lla.Value)
581
582 // if everything ends up without includes, just delete the axis
583 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
584 delete(lla.ConfigurableValues, axis)
585 }
586 }
587}
588
Jingwen Chen5d864492021-02-24 07:20:12 -0500589// StringListAttribute corresponds to the string_list Bazel attribute type with
590// support for additional metadata, like configurations.
591type StringListAttribute struct {
592 // The base value of the string list attribute.
593 Value []string
594
Liz Kammer9abd62d2021-05-21 08:37:59 -0400595 // The configured attribute label list Values. Optional
596 // a map of independent configurability axes
597 ConfigurableValues configurableStringLists
598}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000599
Liz Kammer9abd62d2021-05-21 08:37:59 -0400600type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400601
Liz Kammer9abd62d2021-05-21 08:37:59 -0400602func (csl configurableStringLists) Append(other configurableStringLists) {
603 for axis, otherSelects := range other {
604 selects := csl[axis]
605 if selects == nil {
606 selects = make(stringListSelectValues, len(otherSelects))
607 }
608 selects.appendSelects(otherSelects)
609 csl[axis] = selects
610 }
611}
612
613func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
614 if csl[axis] == nil {
615 csl[axis] = make(stringListSelectValues)
616 }
617 csl[axis][config] = list
618}
619
620type stringListSelectValues map[string][]string
621
622func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
623 for k, v := range other {
624 sl[k] = append(sl[k], v...)
625 }
626}
627
628func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
629 for _, val := range sl {
630 if len(val) > 0 {
631 return true
632 }
633 }
634 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500635}
636
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000637// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
638func MakeStringListAttribute(value []string) StringListAttribute {
639 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400640 return StringListAttribute{
641 Value: value,
642 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400643 }
644}
645
Liz Kammer9abd62d2021-05-21 08:37:59 -0400646// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
647func (sla StringListAttribute) HasConfigurableValues() bool {
648 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400649}
650
Jingwen Chened9c17d2021-04-13 07:14:55 +0000651// Append appends all values, including os and arch specific ones, from another
652// StringListAttribute to this StringListAttribute
Liz Kammer9abd62d2021-05-21 08:37:59 -0400653func (sla *StringListAttribute) Append(other StringListAttribute) {
654 sla.Value = append(sla.Value, other.Value...)
655 if sla.ConfigurableValues == nil {
656 sla.ConfigurableValues = make(configurableStringLists)
657 }
658 sla.ConfigurableValues.Append(other.ConfigurableValues)
659}
660
661// SetSelectValue set a value for a bazel select for the given axis, config and value.
662func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
663 axis.validateConfig(config)
664 switch axis.configurationType {
665 case noConfig:
666 sla.Value = list
667 case arch, os, osArch, productVariables:
668 if sla.ConfigurableValues == nil {
669 sla.ConfigurableValues = make(configurableStringLists)
670 }
671 sla.ConfigurableValues.setValueForAxis(axis, config, list)
672 default:
673 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
674 }
675}
676
677// SelectValue gets a value for a bazel select for the given axis and config.
678func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
679 axis.validateConfig(config)
680 switch axis.configurationType {
681 case noConfig:
682 return sla.Value
683 case arch, os, osArch, productVariables:
684 return sla.ConfigurableValues[axis][config]
685 default:
686 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
687 }
688}
689
690// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
691func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
692 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
693 for k := range sla.ConfigurableValues {
694 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000695 }
696
Liz Kammer9abd62d2021-05-21 08:37:59 -0400697 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
698 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000699}
700
Liz Kammera060c452021-03-24 10:14:47 -0400701// TryVariableSubstitution, replace string substitution formatting within each string in slice with
702// Starlark string.format compatible tag for productVariable.
703func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
704 ret := make([]string, 0, len(slice))
705 changesMade := false
706 for _, s := range slice {
707 newS, changed := TryVariableSubstitution(s, productVariable)
708 ret = append(ret, newS)
709 changesMade = changesMade || changed
710 }
711 return ret, changesMade
712}
713
714// TryVariableSubstitution, replace string substitution formatting within s with Starlark
715// string.format compatible tag for productVariable.
716func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400717 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400718 return sub, s != sub
719}