blob: 76be0581bc01111bc279076b0b50c9b0cbbca148 [file] [log] [blame]
Jingwen Chen30f5aaa2020-11-19 05:38:02 -05001// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package bazel
16
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000017import (
18 "fmt"
Jingwen Chen63930982021-03-24 10:04:33 -040019 "path/filepath"
Liz Kammera060c452021-03-24 10:14:47 -040020 "regexp"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000021 "sort"
Liz Kammer57e2e7a2021-09-20 12:55:02 -040022 "strings"
23
24 "github.com/google/blueprint"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000025)
Jingwen Chen5d864492021-02-24 07:20:12 -050026
Jingwen Chen73850672020-12-14 08:25:34 -050027// BazelTargetModuleProperties contain properties and metadata used for
28// Blueprint to BUILD file conversion.
29type BazelTargetModuleProperties struct {
30 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050031 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050032
33 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050034 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050035}
Liz Kammer356f7d42021-01-26 09:18:53 -050036
Liz Kammera060c452021-03-24 10:14:47 -040037var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
38
Jingwen Chen38e62642021-04-19 05:00:15 +000039// Label is used to represent a Bazel compatible Label. Also stores the original
40// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050041type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000042 // The string representation of a Bazel target label. This can be a relative
43 // or fully qualified label. These labels are used for generating BUILD
44 // files with bp2build.
45 Label string
46
47 // The original Soong/Blueprint module name that the label was derived from.
48 // This is used for replacing references to the original name with the new
49 // label, for example in genrule cmds.
50 //
51 // While there is a reversible 1:1 mapping from the module name to Bazel
52 // label with bp2build that could make computing the original module name
53 // from the label automatic, it is not the case for handcrafted targets,
54 // where modules can have a custom label mapping through the { bazel_module:
55 // { label: <label> } } property.
56 //
57 // With handcrafted labels, those modules don't go through bp2build
58 // conversion, but relies on handcrafted targets in the source tree.
59 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050060}
61
62// LabelList is used to represent a list of Bazel labels.
63type LabelList struct {
64 Includes []Label
65 Excludes []Label
66}
67
Chris Parsons51f8c392021-08-03 21:01:05 -040068func (ll *LabelList) Equals(other LabelList) bool {
69 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
70 return false
71 }
72 for i, _ := range ll.Includes {
73 if ll.Includes[i] != other.Includes[i] {
74 return false
75 }
76 }
77 for i, _ := range ll.Excludes {
78 if ll.Excludes[i] != other.Excludes[i] {
79 return false
80 }
81 }
82 return true
83}
84
Liz Kammer9abd62d2021-05-21 08:37:59 -040085func (ll *LabelList) IsNil() bool {
86 return ll.Includes == nil && ll.Excludes == nil
87}
88
Liz Kammer74deed42021-06-02 13:02:03 -040089func (ll *LabelList) deepCopy() LabelList {
90 return LabelList{
91 Includes: ll.Includes[:],
92 Excludes: ll.Excludes[:],
93 }
94}
95
Jingwen Chen63930982021-03-24 10:04:33 -040096// uniqueParentDirectories returns a list of the unique parent directories for
97// all files in ll.Includes.
98func (ll *LabelList) uniqueParentDirectories() []string {
99 dirMap := map[string]bool{}
100 for _, label := range ll.Includes {
101 dirMap[filepath.Dir(label.Label)] = true
102 }
103 dirs := []string{}
104 for dir := range dirMap {
105 dirs = append(dirs, dir)
106 }
107 return dirs
108}
109
Liz Kammer12615db2021-09-28 09:19:17 -0400110// Add inserts the label Label at the end of the LabelList.
111func (ll *LabelList) Add(label *Label) {
112 if label == nil {
113 return
114 }
115 ll.Includes = append(ll.Includes, *label)
116}
117
Liz Kammer356f7d42021-01-26 09:18:53 -0500118// Append appends the fields of other labelList to the corresponding fields of ll.
119func (ll *LabelList) Append(other LabelList) {
120 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
121 ll.Includes = append(ll.Includes, other.Includes...)
122 }
123 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
124 ll.Excludes = append(other.Excludes, other.Excludes...)
125 }
126}
Jingwen Chen5d864492021-02-24 07:20:12 -0500127
Jingwen Chened9c17d2021-04-13 07:14:55 +0000128// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
129// the slice in a sorted order.
130func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000131 uniqueLabelsSet := make(map[Label]bool)
132 for _, l := range originalLabels {
133 uniqueLabelsSet[l] = true
134 }
135 var uniqueLabels []Label
136 for l, _ := range uniqueLabelsSet {
137 uniqueLabels = append(uniqueLabels, l)
138 }
139 sort.SliceStable(uniqueLabels, func(i, j int) bool {
140 return uniqueLabels[i].Label < uniqueLabels[j].Label
141 })
142 return uniqueLabels
143}
144
Liz Kammer9abd62d2021-05-21 08:37:59 -0400145func FirstUniqueBazelLabels(originalLabels []Label) []Label {
146 var labels []Label
147 found := make(map[Label]bool, len(originalLabels))
148 for _, l := range originalLabels {
149 if _, ok := found[l]; ok {
150 continue
151 }
152 labels = append(labels, l)
153 found[l] = true
154 }
155 return labels
156}
157
158func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
159 var uniqueLabelList LabelList
160 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
161 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
162 return uniqueLabelList
163}
164
165func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000166 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000167 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
168 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000169 return uniqueLabelList
170}
171
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000172// Subtract needle from haystack
173func SubtractStrings(haystack []string, needle []string) []string {
174 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400175 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000176 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400177 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000178 }
179
180 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400181 for _, s := range haystack {
182 if exclude := needleMap[s]; !exclude {
183 strings = append(strings, s)
184 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000185 }
186
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000187 return strings
188}
189
190// Subtract needle from haystack
191func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
192 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400193 needleMap := make(map[Label]bool)
194 for _, s := range needle {
195 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000196 }
197
198 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400199 for _, label := range haystack {
200 if exclude := needleMap[label]; !exclude {
201 labels = append(labels, label)
202 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000203 }
204
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000205 return labels
206}
207
Chris Parsons484e50a2021-05-13 15:13:04 -0400208// Appends two LabelLists, returning the combined list.
209func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
210 var result LabelList
211 result.Includes = append(a.Includes, b.Includes...)
212 result.Excludes = append(a.Excludes, b.Excludes...)
213 return result
214}
215
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000216// Subtract needle from haystack
217func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
218 var result LabelList
219 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
220 // NOTE: Excludes are intentionally not subtracted
221 result.Excludes = haystack.Excludes
222 return result
223}
224
Jingwen Chenc1c26502021-04-05 10:35:13 +0000225type Attribute interface {
226 HasConfigurableValues() bool
227}
228
Liz Kammer9abd62d2021-05-21 08:37:59 -0400229type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400230
Liz Kammer9abd62d2021-05-21 08:37:59 -0400231type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400232
Liz Kammer9abd62d2021-05-21 08:37:59 -0400233func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
234 if cl[axis] == nil {
235 cl[axis] = make(labelSelectValues)
236 }
237 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400238}
239
240// Represents an attribute whose value is a single label
241type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400242 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400243
Liz Kammer9abd62d2021-05-21 08:37:59 -0400244 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200245}
246
Liz Kammer9abd62d2021-05-21 08:37:59 -0400247// HasConfigurableValues returns whether there are configurable values set for this label.
248func (la LabelAttribute) HasConfigurableValues() bool {
249 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200250}
251
Liz Kammer9abd62d2021-05-21 08:37:59 -0400252// SetValue sets the base, non-configured value for the Label
253func (la *LabelAttribute) SetValue(value Label) {
254 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400255}
256
Liz Kammer9abd62d2021-05-21 08:37:59 -0400257// SetSelectValue set a value for a bazel select for the given axis, config and value.
258func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
259 axis.validateConfig(config)
260 switch axis.configurationType {
261 case noConfig:
262 la.Value = &value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400263 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400264 if la.ConfigurableValues == nil {
265 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400266 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400267 la.ConfigurableValues.setValueForAxis(axis, config, &value)
268 default:
269 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
270 }
271}
272
273// SelectValue gets a value for a bazel select for the given axis and config.
274func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
275 axis.validateConfig(config)
276 switch axis.configurationType {
277 case noConfig:
278 return *la.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400279 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400280 return *la.ConfigurableValues[axis][config]
281 default:
282 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
283 }
284}
285
286// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
287func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
288 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
289 for k := range la.ConfigurableValues {
290 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400291 }
292
Liz Kammer9abd62d2021-05-21 08:37:59 -0400293 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
294 return keys
295}
296
Liz Kammerd366c902021-06-03 13:43:01 -0400297type configToBools map[string]bool
298
299func (ctb configToBools) setValue(config string, value *bool) {
300 if value == nil {
301 if _, ok := ctb[config]; ok {
302 delete(ctb, config)
303 }
304 return
305 }
306 ctb[config] = *value
307}
308
309type configurableBools map[ConfigurationAxis]configToBools
310
311func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
312 if cb[axis] == nil {
313 cb[axis] = make(configToBools)
314 }
315 cb[axis].setValue(config, value)
316}
317
318// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
319type BoolAttribute struct {
320 Value *bool
321
322 ConfigurableValues configurableBools
323}
324
325// HasConfigurableValues returns whether there are configurable values for this attribute.
326func (ba BoolAttribute) HasConfigurableValues() bool {
327 return len(ba.ConfigurableValues) > 0
328}
329
330// SetSelectValue sets value for the given axis/config.
331func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
332 axis.validateConfig(config)
333 switch axis.configurationType {
334 case noConfig:
335 ba.Value = value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400336 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400337 if ba.ConfigurableValues == nil {
338 ba.ConfigurableValues = make(configurableBools)
339 }
340 ba.ConfigurableValues.setValueForAxis(axis, config, value)
341 default:
342 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
343 }
344}
345
346// SelectValue gets the value for the given axis/config.
347func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
348 axis.validateConfig(config)
349 switch axis.configurationType {
350 case noConfig:
351 return ba.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400352 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400353 if v, ok := ba.ConfigurableValues[axis][config]; ok {
354 return &v
355 } else {
356 return nil
357 }
358 default:
359 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
360 }
361}
362
363// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
364func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
365 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
366 for k := range ba.ConfigurableValues {
367 keys = append(keys, k)
368 }
369
370 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
371 return keys
372}
373
Liz Kammer9abd62d2021-05-21 08:37:59 -0400374// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
375type labelListSelectValues map[string]LabelList
376
Liz Kammer12615db2021-09-28 09:19:17 -0400377func (ll labelListSelectValues) addSelects(label labelSelectValues) {
378 for k, v := range label {
379 if label == nil {
380 continue
381 }
382 l := ll[k]
383 (&l).Add(v)
384 ll[k] = l
385 }
386}
387
Chris Parsons77acf2e2021-12-03 17:27:16 -0500388func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400389 for k, v := range other {
390 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500391 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
392 l.Includes = []Label{}
393 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400394 (&l).Append(v)
395 ll[k] = l
396 }
397}
398
399// HasConfigurableValues returns whether there are configurable values within this set of selects.
400func (ll labelListSelectValues) HasConfigurableValues() bool {
401 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400402 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400403 return true
404 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400405 }
406 return false
407}
408
Jingwen Chen07027912021-03-15 06:02:43 -0400409// LabelListAttribute is used to represent a list of Bazel labels as an
410// attribute.
411type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400412 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400413 Value LabelList
414
Liz Kammer9abd62d2021-05-21 08:37:59 -0400415 // The configured attribute label list Values. Optional
416 // a map of independent configurability axes
417 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400418
419 // If true, differentiate between "nil" and "empty" list. nil means that
420 // this attribute should not be specified at all, and "empty" means that
421 // the attribute should be explicitly specified as an empty list.
422 // This mode facilitates use of attribute defaults: an empty list should
423 // override the default.
424 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000425
426 // If true, signal the intent to the code generator to emit all select keys,
427 // even if the Includes list for that key is empty. This mode facilitates
428 // specific select statements where an empty list for a non-default select
429 // key has a meaning.
430 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400431}
Jingwen Chen91220d72021-03-24 02:18:33 -0400432
Liz Kammer9abd62d2021-05-21 08:37:59 -0400433type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
434
435func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
436 if list.IsNil() {
437 if _, ok := cll[axis][config]; ok {
438 delete(cll[axis], config)
439 }
440 return
441 }
442 if cll[axis] == nil {
443 cll[axis] = make(labelListSelectValues)
444 }
445
446 cll[axis][config] = list
447}
448
Chris Parsons77acf2e2021-12-03 17:27:16 -0500449func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400450 for axis, otherSelects := range other {
451 selects := cll[axis]
452 if selects == nil {
453 selects = make(labelListSelectValues, len(otherSelects))
454 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500455 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400456 cll[axis] = selects
457 }
Jingwen Chen07027912021-03-15 06:02:43 -0400458}
459
Chris Parsons77acf2e2021-12-03 17:27:16 -0500460func (lla *LabelListAttribute) Clone() *LabelListAttribute {
461 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
462 return result.Append(*lla)
463}
464
Jingwen Chen07027912021-03-15 06:02:43 -0400465// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
466func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400467 return LabelListAttribute{
468 Value: value,
469 ConfigurableValues: make(configurableLabelLists),
470 }
471}
472
473func (lla *LabelListAttribute) SetValue(list LabelList) {
474 lla.SetSelectValue(NoConfigAxis, "", list)
475}
476
477// SetSelectValue set a value for a bazel select for the given axis, config and value.
478func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
479 axis.validateConfig(config)
480 switch axis.configurationType {
481 case noConfig:
482 lla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400483 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400484 if lla.ConfigurableValues == nil {
485 lla.ConfigurableValues = make(configurableLabelLists)
486 }
487 lla.ConfigurableValues.setValueForAxis(axis, config, list)
488 default:
489 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
490 }
491}
492
493// SelectValue gets a value for a bazel select for the given axis and config.
494func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
495 axis.validateConfig(config)
496 switch axis.configurationType {
497 case noConfig:
498 return lla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400499 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400500 return lla.ConfigurableValues[axis][config]
501 default:
502 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
503 }
504}
505
506// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
507func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
508 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
509 for k := range lla.ConfigurableValues {
510 keys = append(keys, k)
511 }
512
513 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
514 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400515}
516
Jingwen Chened9c17d2021-04-13 07:14:55 +0000517// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500518// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
519func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
520 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
521 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400522 lla.Value.Includes = []Label{}
523 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400524 lla.Value.Append(other.Value)
525 if lla.ConfigurableValues == nil {
526 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400527 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500528 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
529 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400530}
531
Liz Kammer12615db2021-09-28 09:19:17 -0400532// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
533// LabelList within the LabelListAttribute
534func (lla *LabelListAttribute) Add(label *LabelAttribute) {
535 if label == nil {
536 return
537 }
538
539 lla.Value.Add(label.Value)
540 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
541 lla.ConfigurableValues = make(configurableLabelLists)
542 }
543 for axis, _ := range label.ConfigurableValues {
544 if _, exists := lla.ConfigurableValues[axis]; !exists {
545 lla.ConfigurableValues[axis] = make(labelListSelectValues)
546 }
547 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
548 }
549}
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
Chris Parsons69fa9f92021-07-13 11:47:44 -0400556// IsEmpty returns true if the attribute has no values under any configuration.
557func (lla LabelListAttribute) IsEmpty() bool {
558 if len(lla.Value.Includes) > 0 {
559 return false
560 }
561 for axis, _ := range lla.ConfigurableValues {
562 if lla.ConfigurableValues[axis].HasConfigurableValues() {
563 return false
564 }
565 }
566 return true
567}
568
Liz Kammer74deed42021-06-02 13:02:03 -0400569// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
570// the base value and included in default values as appropriate.
571func (lla *LabelListAttribute) ResolveExcludes() {
572 for axis, configToLabels := range lla.ConfigurableValues {
573 baseLabels := lla.Value.deepCopy()
574 for config, val := range configToLabels {
575 // Exclude config-specific excludes from base value
576 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
577
578 // add base values to config specific to add labels excluded by others in this axis
579 // then remove all config-specific excludes
580 allLabels := baseLabels.deepCopy()
581 allLabels.Append(val)
582 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
583 }
584
585 // After going through all configs, delete the duplicates in the config
586 // values that are already in the base Value.
587 for config, val := range configToLabels {
588 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
589 }
590
Jingwen Chen9af49a42021-11-02 10:27:17 +0000591 // Now that the Value list is finalized for this axis, compare it with
592 // the original list, and union the difference with the default
593 // condition for the axis.
594 difference := SubtractBazelLabelList(baseLabels, lla.Value)
595 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
596 existingDefaults.Append(difference)
597 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400598
599 // if everything ends up without includes, just delete the axis
600 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
601 delete(lla.ConfigurableValues, axis)
602 }
603 }
604}
605
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400606// OtherModuleContext is a limited context that has methods with information about other modules.
607type OtherModuleContext interface {
608 ModuleFromName(name string) (blueprint.Module, bool)
609 OtherModuleType(m blueprint.Module) string
610 OtherModuleName(m blueprint.Module) string
611 OtherModuleDir(m blueprint.Module) string
612 ModuleErrorf(fmt string, args ...interface{})
613}
614
615// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
616// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400617type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400618
619// LabelPartition contains descriptions of a partition for labels
620type LabelPartition struct {
621 // Extensions to include in this partition
622 Extensions []string
623 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
624 // the mapped label in the partition
625 LabelMapper LabelMapper
626 // Whether to store files not included in any other partition in a group of LabelPartitions
627 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
628 Keep_remainder bool
629}
630
631// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
632// partition
633type LabelPartitions map[string]LabelPartition
634
635// filter returns a pointer to a label if the label should be included in the partition or nil if
636// not.
637func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
638 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400639 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400640 return &Label{newLabel, label.OriginalModuleName}
641 }
642 }
643 for _, ext := range lf.Extensions {
644 if strings.HasSuffix(label.Label, ext) {
645 return &label
646 }
647 }
648
649 return nil
650}
651
652// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
653type PartitionToLabelListAttribute map[string]LabelListAttribute
654
655type partitionToLabelList map[string]*LabelList
656
657func (p partitionToLabelList) appendIncludes(partition string, label Label) {
658 if _, ok := p[partition]; !ok {
659 p[partition] = &LabelList{}
660 }
661 p[partition].Includes = append(p[partition].Includes, label)
662}
663
664func (p partitionToLabelList) excludes(partition string, excludes []Label) {
665 if _, ok := p[partition]; !ok {
666 p[partition] = &LabelList{}
667 }
668 p[partition].Excludes = excludes
669}
670
671// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
672func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
673 ret := PartitionToLabelListAttribute{}
674 var partitionNames []string
675 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
676 var remainderPartition *string
677 for p, f := range partitions {
678 partitionNames = append(partitionNames, p)
679 if f.Keep_remainder {
680 if remainderPartition != nil {
681 panic("only one partition can store the remainder")
682 }
683 // If we take the address of p in a loop, we'll end up with the last value of p in
684 // remainderPartition, we want the requested partition
685 capturePartition := p
686 remainderPartition = &capturePartition
687 }
688 }
689
690 partitionLabelList := func(axis ConfigurationAxis, config string) {
691 value := lla.SelectValue(axis, config)
692 partitionToLabels := partitionToLabelList{}
693 for _, item := range value.Includes {
694 wasFiltered := false
695 var inPartition *string
696 for partition, f := range partitions {
697 filtered := f.filter(ctx, item)
698 if filtered == nil {
699 // did not match this filter, keep looking
700 continue
701 }
702 wasFiltered = true
703 partitionToLabels.appendIncludes(partition, *filtered)
704 // don't need to check other partitions if this filter used the item,
705 // continue checking if mapped to another name
706 if *filtered == item {
707 if inPartition != nil {
708 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
709 }
710 capturePartition := partition
711 inPartition = &capturePartition
712 }
713 }
714
715 // if not specified in a partition, add to remainder partition if one exists
716 if !wasFiltered && remainderPartition != nil {
717 partitionToLabels.appendIncludes(*remainderPartition, item)
718 }
719 }
720
721 // ensure empty lists are maintained
722 if value.Excludes != nil {
723 for _, partition := range partitionNames {
724 partitionToLabels.excludes(partition, value.Excludes)
725 }
726 }
727
728 for partition, list := range partitionToLabels {
729 val := ret[partition]
730 (&val).SetSelectValue(axis, config, *list)
731 ret[partition] = val
732 }
733 }
734
735 partitionLabelList(NoConfigAxis, "")
736 for axis, configToList := range lla.ConfigurableValues {
737 for config, _ := range configToList {
738 partitionLabelList(axis, config)
739 }
740 }
741 return ret
742}
743
Jingwen Chen5d864492021-02-24 07:20:12 -0500744// StringListAttribute corresponds to the string_list Bazel attribute type with
745// support for additional metadata, like configurations.
746type StringListAttribute struct {
747 // The base value of the string list attribute.
748 Value []string
749
Liz Kammer9abd62d2021-05-21 08:37:59 -0400750 // The configured attribute label list Values. Optional
751 // a map of independent configurability axes
752 ConfigurableValues configurableStringLists
753}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000754
Liz Kammer9abd62d2021-05-21 08:37:59 -0400755type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400756
Liz Kammer9abd62d2021-05-21 08:37:59 -0400757func (csl configurableStringLists) Append(other configurableStringLists) {
758 for axis, otherSelects := range other {
759 selects := csl[axis]
760 if selects == nil {
761 selects = make(stringListSelectValues, len(otherSelects))
762 }
763 selects.appendSelects(otherSelects)
764 csl[axis] = selects
765 }
766}
767
768func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
769 if csl[axis] == nil {
770 csl[axis] = make(stringListSelectValues)
771 }
772 csl[axis][config] = list
773}
774
775type stringListSelectValues map[string][]string
776
777func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
778 for k, v := range other {
779 sl[k] = append(sl[k], v...)
780 }
781}
782
783func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
784 for _, val := range sl {
785 if len(val) > 0 {
786 return true
787 }
788 }
789 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500790}
791
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000792// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
793func MakeStringListAttribute(value []string) StringListAttribute {
794 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400795 return StringListAttribute{
796 Value: value,
797 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400798 }
799}
800
Liz Kammer9abd62d2021-05-21 08:37:59 -0400801// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
802func (sla StringListAttribute) HasConfigurableValues() bool {
803 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400804}
805
Jingwen Chened9c17d2021-04-13 07:14:55 +0000806// Append appends all values, including os and arch specific ones, from another
807// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -0500808func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400809 sla.Value = append(sla.Value, other.Value...)
810 if sla.ConfigurableValues == nil {
811 sla.ConfigurableValues = make(configurableStringLists)
812 }
813 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -0500814 return sla
815}
816
817func (sla *StringListAttribute) Clone() *StringListAttribute {
818 result := &StringListAttribute{}
819 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400820}
821
822// SetSelectValue set a value for a bazel select for the given axis, config and value.
823func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
824 axis.validateConfig(config)
825 switch axis.configurationType {
826 case noConfig:
827 sla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400828 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400829 if sla.ConfigurableValues == nil {
830 sla.ConfigurableValues = make(configurableStringLists)
831 }
832 sla.ConfigurableValues.setValueForAxis(axis, config, list)
833 default:
834 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
835 }
836}
837
838// SelectValue gets a value for a bazel select for the given axis and config.
839func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
840 axis.validateConfig(config)
841 switch axis.configurationType {
842 case noConfig:
843 return sla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400844 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400845 return sla.ConfigurableValues[axis][config]
846 default:
847 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
848 }
849}
850
851// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
852func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
853 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
854 for k := range sla.ConfigurableValues {
855 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000856 }
857
Liz Kammer9abd62d2021-05-21 08:37:59 -0400858 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
859 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000860}
861
Liz Kammer5fad5012021-09-09 14:08:21 -0400862// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
863// configuration-specific values. For example, if we would convert this StringListAttribute as:
864// ["a", "b", "c"] + select({
865// "//condition:one": ["a", "d"],
866// "//conditions:default": [],
867// })
868// after this function, we would convert this StringListAttribute as:
869// ["a", "b", "c"] + select({
870// "//condition:one": ["d"],
871// "//conditions:default": [],
872// })
873func (sla *StringListAttribute) DeduplicateAxesFromBase() {
874 base := sla.Value
875 for axis, configToList := range sla.ConfigurableValues {
876 for config, list := range configToList {
877 remaining := SubtractStrings(list, base)
878 if len(remaining) == 0 {
879 delete(sla.ConfigurableValues[axis], config)
880 } else {
881 sla.ConfigurableValues[axis][config] = remaining
882 }
883 }
884 }
885}
886
Liz Kammera060c452021-03-24 10:14:47 -0400887// TryVariableSubstitution, replace string substitution formatting within each string in slice with
888// Starlark string.format compatible tag for productVariable.
889func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
890 ret := make([]string, 0, len(slice))
891 changesMade := false
892 for _, s := range slice {
893 newS, changed := TryVariableSubstitution(s, productVariable)
894 ret = append(ret, newS)
895 changesMade = changesMade || changed
896 }
897 return ret, changesMade
898}
899
900// TryVariableSubstitution, replace string substitution formatting within s with Starlark
901// string.format compatible tag for productVariable.
902func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400903 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400904 return sub, s != sub
905}