blob: b370bbfb27db4a56934938f26c0a02fc7895222f [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 Kammer356f7d42021-01-26 09:18:53 -0500110// Append appends the fields of other labelList to the corresponding fields of ll.
111func (ll *LabelList) Append(other LabelList) {
112 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
113 ll.Includes = append(ll.Includes, other.Includes...)
114 }
115 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
116 ll.Excludes = append(other.Excludes, other.Excludes...)
117 }
118}
Jingwen Chen5d864492021-02-24 07:20:12 -0500119
Jingwen Chened9c17d2021-04-13 07:14:55 +0000120// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
121// the slice in a sorted order.
122func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000123 uniqueLabelsSet := make(map[Label]bool)
124 for _, l := range originalLabels {
125 uniqueLabelsSet[l] = true
126 }
127 var uniqueLabels []Label
128 for l, _ := range uniqueLabelsSet {
129 uniqueLabels = append(uniqueLabels, l)
130 }
131 sort.SliceStable(uniqueLabels, func(i, j int) bool {
132 return uniqueLabels[i].Label < uniqueLabels[j].Label
133 })
134 return uniqueLabels
135}
136
Liz Kammer9abd62d2021-05-21 08:37:59 -0400137func FirstUniqueBazelLabels(originalLabels []Label) []Label {
138 var labels []Label
139 found := make(map[Label]bool, len(originalLabels))
140 for _, l := range originalLabels {
141 if _, ok := found[l]; ok {
142 continue
143 }
144 labels = append(labels, l)
145 found[l] = true
146 }
147 return labels
148}
149
150func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
151 var uniqueLabelList LabelList
152 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
153 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
154 return uniqueLabelList
155}
156
157func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000158 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000159 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
160 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000161 return uniqueLabelList
162}
163
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000164// Subtract needle from haystack
165func SubtractStrings(haystack []string, needle []string) []string {
166 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400167 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000168 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400169 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000170 }
171
172 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400173 for _, s := range haystack {
174 if exclude := needleMap[s]; !exclude {
175 strings = append(strings, s)
176 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000177 }
178
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000179 return strings
180}
181
182// Subtract needle from haystack
183func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
184 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400185 needleMap := make(map[Label]bool)
186 for _, s := range needle {
187 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000188 }
189
190 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400191 for _, label := range haystack {
192 if exclude := needleMap[label]; !exclude {
193 labels = append(labels, label)
194 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000195 }
196
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000197 return labels
198}
199
Chris Parsons484e50a2021-05-13 15:13:04 -0400200// Appends two LabelLists, returning the combined list.
201func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
202 var result LabelList
203 result.Includes = append(a.Includes, b.Includes...)
204 result.Excludes = append(a.Excludes, b.Excludes...)
205 return result
206}
207
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000208// Subtract needle from haystack
209func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
210 var result LabelList
211 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
212 // NOTE: Excludes are intentionally not subtracted
213 result.Excludes = haystack.Excludes
214 return result
215}
216
Jingwen Chenc1c26502021-04-05 10:35:13 +0000217type Attribute interface {
218 HasConfigurableValues() bool
219}
220
Liz Kammer9abd62d2021-05-21 08:37:59 -0400221type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400222
Liz Kammer9abd62d2021-05-21 08:37:59 -0400223type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400224
Liz Kammer9abd62d2021-05-21 08:37:59 -0400225func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
226 if cl[axis] == nil {
227 cl[axis] = make(labelSelectValues)
228 }
229 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400230}
231
232// Represents an attribute whose value is a single label
233type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400234 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400235
Liz Kammer9abd62d2021-05-21 08:37:59 -0400236 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200237}
238
Liz Kammer9abd62d2021-05-21 08:37:59 -0400239// HasConfigurableValues returns whether there are configurable values set for this label.
240func (la LabelAttribute) HasConfigurableValues() bool {
241 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200242}
243
Liz Kammer9abd62d2021-05-21 08:37:59 -0400244// SetValue sets the base, non-configured value for the Label
245func (la *LabelAttribute) SetValue(value Label) {
246 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400247}
248
Liz Kammer9abd62d2021-05-21 08:37:59 -0400249// SetSelectValue set a value for a bazel select for the given axis, config and value.
250func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
251 axis.validateConfig(config)
252 switch axis.configurationType {
253 case noConfig:
254 la.Value = &value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400255 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400256 if la.ConfigurableValues == nil {
257 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400258 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400259 la.ConfigurableValues.setValueForAxis(axis, config, &value)
260 default:
261 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
262 }
263}
264
265// SelectValue gets a value for a bazel select for the given axis and config.
266func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
267 axis.validateConfig(config)
268 switch axis.configurationType {
269 case noConfig:
270 return *la.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400271 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400272 return *la.ConfigurableValues[axis][config]
273 default:
274 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
275 }
276}
277
278// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
279func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
280 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
281 for k := range la.ConfigurableValues {
282 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400283 }
284
Liz Kammer9abd62d2021-05-21 08:37:59 -0400285 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
286 return keys
287}
288
Liz Kammerd366c902021-06-03 13:43:01 -0400289type configToBools map[string]bool
290
291func (ctb configToBools) setValue(config string, value *bool) {
292 if value == nil {
293 if _, ok := ctb[config]; ok {
294 delete(ctb, config)
295 }
296 return
297 }
298 ctb[config] = *value
299}
300
301type configurableBools map[ConfigurationAxis]configToBools
302
303func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
304 if cb[axis] == nil {
305 cb[axis] = make(configToBools)
306 }
307 cb[axis].setValue(config, value)
308}
309
310// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
311type BoolAttribute struct {
312 Value *bool
313
314 ConfigurableValues configurableBools
315}
316
317// HasConfigurableValues returns whether there are configurable values for this attribute.
318func (ba BoolAttribute) HasConfigurableValues() bool {
319 return len(ba.ConfigurableValues) > 0
320}
321
322// SetSelectValue sets value for the given axis/config.
323func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
324 axis.validateConfig(config)
325 switch axis.configurationType {
326 case noConfig:
327 ba.Value = value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400328 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400329 if ba.ConfigurableValues == nil {
330 ba.ConfigurableValues = make(configurableBools)
331 }
332 ba.ConfigurableValues.setValueForAxis(axis, config, value)
333 default:
334 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
335 }
336}
337
338// SelectValue gets the value for the given axis/config.
339func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
340 axis.validateConfig(config)
341 switch axis.configurationType {
342 case noConfig:
343 return ba.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400344 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400345 if v, ok := ba.ConfigurableValues[axis][config]; ok {
346 return &v
347 } else {
348 return nil
349 }
350 default:
351 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
352 }
353}
354
355// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
356func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
357 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
358 for k := range ba.ConfigurableValues {
359 keys = append(keys, k)
360 }
361
362 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
363 return keys
364}
365
Liz Kammer9abd62d2021-05-21 08:37:59 -0400366// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
367type labelListSelectValues map[string]LabelList
368
369func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
370 for k, v := range other {
371 l := ll[k]
372 (&l).Append(v)
373 ll[k] = l
374 }
375}
376
377// HasConfigurableValues returns whether there are configurable values within this set of selects.
378func (ll labelListSelectValues) HasConfigurableValues() bool {
379 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400380 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400381 return true
382 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400383 }
384 return false
385}
386
Jingwen Chen07027912021-03-15 06:02:43 -0400387// LabelListAttribute is used to represent a list of Bazel labels as an
388// attribute.
389type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400390 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400391 Value LabelList
392
Liz Kammer9abd62d2021-05-21 08:37:59 -0400393 // The configured attribute label list Values. Optional
394 // a map of independent configurability axes
395 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400396
397 // If true, differentiate between "nil" and "empty" list. nil means that
398 // this attribute should not be specified at all, and "empty" means that
399 // the attribute should be explicitly specified as an empty list.
400 // This mode facilitates use of attribute defaults: an empty list should
401 // override the default.
402 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000403
404 // If true, signal the intent to the code generator to emit all select keys,
405 // even if the Includes list for that key is empty. This mode facilitates
406 // specific select statements where an empty list for a non-default select
407 // key has a meaning.
408 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400409}
Jingwen Chen91220d72021-03-24 02:18:33 -0400410
Liz Kammer9abd62d2021-05-21 08:37:59 -0400411type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
412
413func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
414 if list.IsNil() {
415 if _, ok := cll[axis][config]; ok {
416 delete(cll[axis], config)
417 }
418 return
419 }
420 if cll[axis] == nil {
421 cll[axis] = make(labelListSelectValues)
422 }
423
424 cll[axis][config] = list
425}
426
427func (cll configurableLabelLists) Append(other configurableLabelLists) {
428 for axis, otherSelects := range other {
429 selects := cll[axis]
430 if selects == nil {
431 selects = make(labelListSelectValues, len(otherSelects))
432 }
433 selects.appendSelects(otherSelects)
434 cll[axis] = selects
435 }
Jingwen Chen07027912021-03-15 06:02:43 -0400436}
437
438// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
439func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400440 return LabelListAttribute{
441 Value: value,
442 ConfigurableValues: make(configurableLabelLists),
443 }
444}
445
446func (lla *LabelListAttribute) SetValue(list LabelList) {
447 lla.SetSelectValue(NoConfigAxis, "", list)
448}
449
450// SetSelectValue set a value for a bazel select for the given axis, config and value.
451func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
452 axis.validateConfig(config)
453 switch axis.configurationType {
454 case noConfig:
455 lla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400456 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400457 if lla.ConfigurableValues == nil {
458 lla.ConfigurableValues = make(configurableLabelLists)
459 }
460 lla.ConfigurableValues.setValueForAxis(axis, config, list)
461 default:
462 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
463 }
464}
465
466// SelectValue gets a value for a bazel select for the given axis and config.
467func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
468 axis.validateConfig(config)
469 switch axis.configurationType {
470 case noConfig:
471 return lla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400472 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400473 return lla.ConfigurableValues[axis][config]
474 default:
475 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
476 }
477}
478
479// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
480func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
481 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
482 for k := range lla.ConfigurableValues {
483 keys = append(keys, k)
484 }
485
486 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
487 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400488}
489
Jingwen Chened9c17d2021-04-13 07:14:55 +0000490// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400491// LabelListAttribute to this LabelListAttribute.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400492func (lla *LabelListAttribute) Append(other LabelListAttribute) {
Chris Parsons51f8c392021-08-03 21:01:05 -0400493 if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
494 lla.Value.Includes = []Label{}
495 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400496 lla.Value.Append(other.Value)
497 if lla.ConfigurableValues == nil {
498 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400499 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400500 lla.ConfigurableValues.Append(other.ConfigurableValues)
Jingwen Chen63930982021-03-24 10:04:33 -0400501}
502
Liz Kammer9abd62d2021-05-21 08:37:59 -0400503// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
504func (lla LabelListAttribute) HasConfigurableValues() bool {
505 return len(lla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400506}
507
Chris Parsons69fa9f92021-07-13 11:47:44 -0400508// IsEmpty returns true if the attribute has no values under any configuration.
509func (lla LabelListAttribute) IsEmpty() bool {
510 if len(lla.Value.Includes) > 0 {
511 return false
512 }
513 for axis, _ := range lla.ConfigurableValues {
514 if lla.ConfigurableValues[axis].HasConfigurableValues() {
515 return false
516 }
517 }
518 return true
519}
520
Liz Kammer74deed42021-06-02 13:02:03 -0400521// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
522// the base value and included in default values as appropriate.
523func (lla *LabelListAttribute) ResolveExcludes() {
524 for axis, configToLabels := range lla.ConfigurableValues {
525 baseLabels := lla.Value.deepCopy()
526 for config, val := range configToLabels {
527 // Exclude config-specific excludes from base value
528 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
529
530 // add base values to config specific to add labels excluded by others in this axis
531 // then remove all config-specific excludes
532 allLabels := baseLabels.deepCopy()
533 allLabels.Append(val)
534 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
535 }
536
537 // After going through all configs, delete the duplicates in the config
538 // values that are already in the base Value.
539 for config, val := range configToLabels {
540 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
541 }
542
Jingwen Chen9af49a42021-11-02 10:27:17 +0000543 // Now that the Value list is finalized for this axis, compare it with
544 // the original list, and union the difference with the default
545 // condition for the axis.
546 difference := SubtractBazelLabelList(baseLabels, lla.Value)
547 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
548 existingDefaults.Append(difference)
549 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400550
551 // if everything ends up without includes, just delete the axis
552 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
553 delete(lla.ConfigurableValues, axis)
554 }
555 }
556}
557
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400558// OtherModuleContext is a limited context that has methods with information about other modules.
559type OtherModuleContext interface {
560 ModuleFromName(name string) (blueprint.Module, bool)
561 OtherModuleType(m blueprint.Module) string
562 OtherModuleName(m blueprint.Module) string
563 OtherModuleDir(m blueprint.Module) string
564 ModuleErrorf(fmt string, args ...interface{})
565}
566
567// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
568// label and whether it was changed.
569type LabelMapper func(OtherModuleContext, string) (string, bool)
570
571// LabelPartition contains descriptions of a partition for labels
572type LabelPartition struct {
573 // Extensions to include in this partition
574 Extensions []string
575 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
576 // the mapped label in the partition
577 LabelMapper LabelMapper
578 // Whether to store files not included in any other partition in a group of LabelPartitions
579 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
580 Keep_remainder bool
581}
582
583// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
584// partition
585type LabelPartitions map[string]LabelPartition
586
587// filter returns a pointer to a label if the label should be included in the partition or nil if
588// not.
589func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
590 if lf.LabelMapper != nil {
591 if newLabel, changed := lf.LabelMapper(ctx, label.Label); changed {
592 return &Label{newLabel, label.OriginalModuleName}
593 }
594 }
595 for _, ext := range lf.Extensions {
596 if strings.HasSuffix(label.Label, ext) {
597 return &label
598 }
599 }
600
601 return nil
602}
603
604// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
605type PartitionToLabelListAttribute map[string]LabelListAttribute
606
607type partitionToLabelList map[string]*LabelList
608
609func (p partitionToLabelList) appendIncludes(partition string, label Label) {
610 if _, ok := p[partition]; !ok {
611 p[partition] = &LabelList{}
612 }
613 p[partition].Includes = append(p[partition].Includes, label)
614}
615
616func (p partitionToLabelList) excludes(partition string, excludes []Label) {
617 if _, ok := p[partition]; !ok {
618 p[partition] = &LabelList{}
619 }
620 p[partition].Excludes = excludes
621}
622
623// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
624func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
625 ret := PartitionToLabelListAttribute{}
626 var partitionNames []string
627 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
628 var remainderPartition *string
629 for p, f := range partitions {
630 partitionNames = append(partitionNames, p)
631 if f.Keep_remainder {
632 if remainderPartition != nil {
633 panic("only one partition can store the remainder")
634 }
635 // If we take the address of p in a loop, we'll end up with the last value of p in
636 // remainderPartition, we want the requested partition
637 capturePartition := p
638 remainderPartition = &capturePartition
639 }
640 }
641
642 partitionLabelList := func(axis ConfigurationAxis, config string) {
643 value := lla.SelectValue(axis, config)
644 partitionToLabels := partitionToLabelList{}
645 for _, item := range value.Includes {
646 wasFiltered := false
647 var inPartition *string
648 for partition, f := range partitions {
649 filtered := f.filter(ctx, item)
650 if filtered == nil {
651 // did not match this filter, keep looking
652 continue
653 }
654 wasFiltered = true
655 partitionToLabels.appendIncludes(partition, *filtered)
656 // don't need to check other partitions if this filter used the item,
657 // continue checking if mapped to another name
658 if *filtered == item {
659 if inPartition != nil {
660 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
661 }
662 capturePartition := partition
663 inPartition = &capturePartition
664 }
665 }
666
667 // if not specified in a partition, add to remainder partition if one exists
668 if !wasFiltered && remainderPartition != nil {
669 partitionToLabels.appendIncludes(*remainderPartition, item)
670 }
671 }
672
673 // ensure empty lists are maintained
674 if value.Excludes != nil {
675 for _, partition := range partitionNames {
676 partitionToLabels.excludes(partition, value.Excludes)
677 }
678 }
679
680 for partition, list := range partitionToLabels {
681 val := ret[partition]
682 (&val).SetSelectValue(axis, config, *list)
683 ret[partition] = val
684 }
685 }
686
687 partitionLabelList(NoConfigAxis, "")
688 for axis, configToList := range lla.ConfigurableValues {
689 for config, _ := range configToList {
690 partitionLabelList(axis, config)
691 }
692 }
693 return ret
694}
695
Jingwen Chen5d864492021-02-24 07:20:12 -0500696// StringListAttribute corresponds to the string_list Bazel attribute type with
697// support for additional metadata, like configurations.
698type StringListAttribute struct {
699 // The base value of the string list attribute.
700 Value []string
701
Liz Kammer9abd62d2021-05-21 08:37:59 -0400702 // The configured attribute label list Values. Optional
703 // a map of independent configurability axes
704 ConfigurableValues configurableStringLists
705}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000706
Liz Kammer9abd62d2021-05-21 08:37:59 -0400707type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400708
Liz Kammer9abd62d2021-05-21 08:37:59 -0400709func (csl configurableStringLists) Append(other configurableStringLists) {
710 for axis, otherSelects := range other {
711 selects := csl[axis]
712 if selects == nil {
713 selects = make(stringListSelectValues, len(otherSelects))
714 }
715 selects.appendSelects(otherSelects)
716 csl[axis] = selects
717 }
718}
719
720func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
721 if csl[axis] == nil {
722 csl[axis] = make(stringListSelectValues)
723 }
724 csl[axis][config] = list
725}
726
727type stringListSelectValues map[string][]string
728
729func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
730 for k, v := range other {
731 sl[k] = append(sl[k], v...)
732 }
733}
734
735func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
736 for _, val := range sl {
737 if len(val) > 0 {
738 return true
739 }
740 }
741 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500742}
743
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000744// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
745func MakeStringListAttribute(value []string) StringListAttribute {
746 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400747 return StringListAttribute{
748 Value: value,
749 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400750 }
751}
752
Liz Kammer9abd62d2021-05-21 08:37:59 -0400753// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
754func (sla StringListAttribute) HasConfigurableValues() bool {
755 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400756}
757
Jingwen Chened9c17d2021-04-13 07:14:55 +0000758// Append appends all values, including os and arch specific ones, from another
759// StringListAttribute to this StringListAttribute
Liz Kammer9abd62d2021-05-21 08:37:59 -0400760func (sla *StringListAttribute) Append(other StringListAttribute) {
761 sla.Value = append(sla.Value, other.Value...)
762 if sla.ConfigurableValues == nil {
763 sla.ConfigurableValues = make(configurableStringLists)
764 }
765 sla.ConfigurableValues.Append(other.ConfigurableValues)
766}
767
768// SetSelectValue set a value for a bazel select for the given axis, config and value.
769func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
770 axis.validateConfig(config)
771 switch axis.configurationType {
772 case noConfig:
773 sla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400774 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400775 if sla.ConfigurableValues == nil {
776 sla.ConfigurableValues = make(configurableStringLists)
777 }
778 sla.ConfigurableValues.setValueForAxis(axis, config, list)
779 default:
780 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
781 }
782}
783
784// SelectValue gets a value for a bazel select for the given axis and config.
785func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
786 axis.validateConfig(config)
787 switch axis.configurationType {
788 case noConfig:
789 return sla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400790 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400791 return sla.ConfigurableValues[axis][config]
792 default:
793 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
794 }
795}
796
797// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
798func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
799 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
800 for k := range sla.ConfigurableValues {
801 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000802 }
803
Liz Kammer9abd62d2021-05-21 08:37:59 -0400804 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
805 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000806}
807
Liz Kammer5fad5012021-09-09 14:08:21 -0400808// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
809// configuration-specific values. For example, if we would convert this StringListAttribute as:
810// ["a", "b", "c"] + select({
811// "//condition:one": ["a", "d"],
812// "//conditions:default": [],
813// })
814// after this function, we would convert this StringListAttribute as:
815// ["a", "b", "c"] + select({
816// "//condition:one": ["d"],
817// "//conditions:default": [],
818// })
819func (sla *StringListAttribute) DeduplicateAxesFromBase() {
820 base := sla.Value
821 for axis, configToList := range sla.ConfigurableValues {
822 for config, list := range configToList {
823 remaining := SubtractStrings(list, base)
824 if len(remaining) == 0 {
825 delete(sla.ConfigurableValues[axis], config)
826 } else {
827 sla.ConfigurableValues[axis][config] = remaining
828 }
829 }
830 }
831}
832
Liz Kammera060c452021-03-24 10:14:47 -0400833// TryVariableSubstitution, replace string substitution formatting within each string in slice with
834// Starlark string.format compatible tag for productVariable.
835func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
836 ret := make([]string, 0, len(slice))
837 changesMade := false
838 for _, s := range slice {
839 newS, changed := TryVariableSubstitution(s, productVariable)
840 ret = append(ret, newS)
841 changesMade = changesMade || changed
842 }
843 return ret, changesMade
844}
845
846// TryVariableSubstitution, replace string substitution formatting within s with Starlark
847// string.format compatible tag for productVariable.
848func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400849 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400850 return sub, s != sub
851}