blob: a438481be6170292ece9c294973151c955ee4894 [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 Chena47f28d2021-11-02 16:43:57 +000027type BazelModuleProperties struct {
28 // The label of the Bazel target replacing this Soong module. When run in conversion mode, this
29 // will import the handcrafted build target into the autogenerated file. Note: this may result in
30 // a conflict due to duplicate targets if bp2build_available is also set.
31 Label *string
32
33 // If true, bp2build will generate the converted Bazel target for this module. Note: this may
34 // cause a conflict due to the duplicate targets if label is also set.
35 //
36 // This is a bool pointer to support tristates: true, false, not set.
37 //
38 // To opt-in a module, set bazel_module: { bp2build_available: true }
39 // To opt-out a module, set bazel_module: { bp2build_available: false }
40 // To defer the default setting for the directory, do not set the value.
41 Bp2build_available *bool
42}
43
Jingwen Chen73850672020-12-14 08:25:34 -050044// BazelTargetModuleProperties contain properties and metadata used for
45// Blueprint to BUILD file conversion.
46type BazelTargetModuleProperties struct {
47 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050048 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050049
50 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050051 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050052}
Liz Kammer356f7d42021-01-26 09:18:53 -050053
Liz Kammera060c452021-03-24 10:14:47 -040054var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
55
Jingwen Chen38e62642021-04-19 05:00:15 +000056// Label is used to represent a Bazel compatible Label. Also stores the original
57// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050058type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000059 // The string representation of a Bazel target label. This can be a relative
60 // or fully qualified label. These labels are used for generating BUILD
61 // files with bp2build.
62 Label string
63
64 // The original Soong/Blueprint module name that the label was derived from.
65 // This is used for replacing references to the original name with the new
66 // label, for example in genrule cmds.
67 //
68 // While there is a reversible 1:1 mapping from the module name to Bazel
69 // label with bp2build that could make computing the original module name
70 // from the label automatic, it is not the case for handcrafted targets,
71 // where modules can have a custom label mapping through the { bazel_module:
72 // { label: <label> } } property.
73 //
74 // With handcrafted labels, those modules don't go through bp2build
75 // conversion, but relies on handcrafted targets in the source tree.
76 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050077}
78
79// LabelList is used to represent a list of Bazel labels.
80type LabelList struct {
81 Includes []Label
82 Excludes []Label
83}
84
Chris Parsons51f8c392021-08-03 21:01:05 -040085func (ll *LabelList) Equals(other LabelList) bool {
86 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
87 return false
88 }
89 for i, _ := range ll.Includes {
90 if ll.Includes[i] != other.Includes[i] {
91 return false
92 }
93 }
94 for i, _ := range ll.Excludes {
95 if ll.Excludes[i] != other.Excludes[i] {
96 return false
97 }
98 }
99 return true
100}
101
Liz Kammer9abd62d2021-05-21 08:37:59 -0400102func (ll *LabelList) IsNil() bool {
103 return ll.Includes == nil && ll.Excludes == nil
104}
105
Liz Kammer74deed42021-06-02 13:02:03 -0400106func (ll *LabelList) deepCopy() LabelList {
107 return LabelList{
108 Includes: ll.Includes[:],
109 Excludes: ll.Excludes[:],
110 }
111}
112
Jingwen Chen63930982021-03-24 10:04:33 -0400113// uniqueParentDirectories returns a list of the unique parent directories for
114// all files in ll.Includes.
115func (ll *LabelList) uniqueParentDirectories() []string {
116 dirMap := map[string]bool{}
117 for _, label := range ll.Includes {
118 dirMap[filepath.Dir(label.Label)] = true
119 }
120 dirs := []string{}
121 for dir := range dirMap {
122 dirs = append(dirs, dir)
123 }
124 return dirs
125}
126
Liz Kammer356f7d42021-01-26 09:18:53 -0500127// Append appends the fields of other labelList to the corresponding fields of ll.
128func (ll *LabelList) Append(other LabelList) {
129 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
130 ll.Includes = append(ll.Includes, other.Includes...)
131 }
132 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
133 ll.Excludes = append(other.Excludes, other.Excludes...)
134 }
135}
Jingwen Chen5d864492021-02-24 07:20:12 -0500136
Jingwen Chened9c17d2021-04-13 07:14:55 +0000137// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
138// the slice in a sorted order.
139func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000140 uniqueLabelsSet := make(map[Label]bool)
141 for _, l := range originalLabels {
142 uniqueLabelsSet[l] = true
143 }
144 var uniqueLabels []Label
145 for l, _ := range uniqueLabelsSet {
146 uniqueLabels = append(uniqueLabels, l)
147 }
148 sort.SliceStable(uniqueLabels, func(i, j int) bool {
149 return uniqueLabels[i].Label < uniqueLabels[j].Label
150 })
151 return uniqueLabels
152}
153
Liz Kammer9abd62d2021-05-21 08:37:59 -0400154func FirstUniqueBazelLabels(originalLabels []Label) []Label {
155 var labels []Label
156 found := make(map[Label]bool, len(originalLabels))
157 for _, l := range originalLabels {
158 if _, ok := found[l]; ok {
159 continue
160 }
161 labels = append(labels, l)
162 found[l] = true
163 }
164 return labels
165}
166
167func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
168 var uniqueLabelList LabelList
169 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
170 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
171 return uniqueLabelList
172}
173
174func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000175 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000176 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
177 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000178 return uniqueLabelList
179}
180
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000181// Subtract needle from haystack
182func SubtractStrings(haystack []string, needle []string) []string {
183 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400184 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000185 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400186 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000187 }
188
189 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400190 for _, s := range haystack {
191 if exclude := needleMap[s]; !exclude {
192 strings = append(strings, s)
193 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000194 }
195
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000196 return strings
197}
198
199// Subtract needle from haystack
200func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
201 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400202 needleMap := make(map[Label]bool)
203 for _, s := range needle {
204 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000205 }
206
207 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400208 for _, label := range haystack {
209 if exclude := needleMap[label]; !exclude {
210 labels = append(labels, label)
211 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000212 }
213
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000214 return labels
215}
216
Chris Parsons484e50a2021-05-13 15:13:04 -0400217// Appends two LabelLists, returning the combined list.
218func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
219 var result LabelList
220 result.Includes = append(a.Includes, b.Includes...)
221 result.Excludes = append(a.Excludes, b.Excludes...)
222 return result
223}
224
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000225// Subtract needle from haystack
226func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
227 var result LabelList
228 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
229 // NOTE: Excludes are intentionally not subtracted
230 result.Excludes = haystack.Excludes
231 return result
232}
233
Jingwen Chenc1c26502021-04-05 10:35:13 +0000234type Attribute interface {
235 HasConfigurableValues() bool
236}
237
Liz Kammer9abd62d2021-05-21 08:37:59 -0400238type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400239
Liz Kammer9abd62d2021-05-21 08:37:59 -0400240type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400241
Liz Kammer9abd62d2021-05-21 08:37:59 -0400242func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
243 if cl[axis] == nil {
244 cl[axis] = make(labelSelectValues)
245 }
246 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400247}
248
249// Represents an attribute whose value is a single label
250type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400251 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400252
Liz Kammer9abd62d2021-05-21 08:37:59 -0400253 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200254}
255
Liz Kammer9abd62d2021-05-21 08:37:59 -0400256// HasConfigurableValues returns whether there are configurable values set for this label.
257func (la LabelAttribute) HasConfigurableValues() bool {
258 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200259}
260
Liz Kammer9abd62d2021-05-21 08:37:59 -0400261// SetValue sets the base, non-configured value for the Label
262func (la *LabelAttribute) SetValue(value Label) {
263 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400264}
265
Liz Kammer9abd62d2021-05-21 08:37:59 -0400266// SetSelectValue set a value for a bazel select for the given axis, config and value.
267func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
268 axis.validateConfig(config)
269 switch axis.configurationType {
270 case noConfig:
271 la.Value = &value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400272 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400273 if la.ConfigurableValues == nil {
274 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400275 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400276 la.ConfigurableValues.setValueForAxis(axis, config, &value)
277 default:
278 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
279 }
280}
281
282// SelectValue gets a value for a bazel select for the given axis and config.
283func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
284 axis.validateConfig(config)
285 switch axis.configurationType {
286 case noConfig:
287 return *la.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400288 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400289 return *la.ConfigurableValues[axis][config]
290 default:
291 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
292 }
293}
294
295// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
296func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
297 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
298 for k := range la.ConfigurableValues {
299 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400300 }
301
Liz Kammer9abd62d2021-05-21 08:37:59 -0400302 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
303 return keys
304}
305
Liz Kammerd366c902021-06-03 13:43:01 -0400306type configToBools map[string]bool
307
308func (ctb configToBools) setValue(config string, value *bool) {
309 if value == nil {
310 if _, ok := ctb[config]; ok {
311 delete(ctb, config)
312 }
313 return
314 }
315 ctb[config] = *value
316}
317
318type configurableBools map[ConfigurationAxis]configToBools
319
320func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
321 if cb[axis] == nil {
322 cb[axis] = make(configToBools)
323 }
324 cb[axis].setValue(config, value)
325}
326
327// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
328type BoolAttribute struct {
329 Value *bool
330
331 ConfigurableValues configurableBools
332}
333
334// HasConfigurableValues returns whether there are configurable values for this attribute.
335func (ba BoolAttribute) HasConfigurableValues() bool {
336 return len(ba.ConfigurableValues) > 0
337}
338
339// SetSelectValue sets value for the given axis/config.
340func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
341 axis.validateConfig(config)
342 switch axis.configurationType {
343 case noConfig:
344 ba.Value = value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400345 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400346 if ba.ConfigurableValues == nil {
347 ba.ConfigurableValues = make(configurableBools)
348 }
349 ba.ConfigurableValues.setValueForAxis(axis, config, value)
350 default:
351 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
352 }
353}
354
355// SelectValue gets the value for the given axis/config.
356func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
357 axis.validateConfig(config)
358 switch axis.configurationType {
359 case noConfig:
360 return ba.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400361 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400362 if v, ok := ba.ConfigurableValues[axis][config]; ok {
363 return &v
364 } else {
365 return nil
366 }
367 default:
368 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
369 }
370}
371
372// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
373func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
374 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
375 for k := range ba.ConfigurableValues {
376 keys = append(keys, k)
377 }
378
379 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
380 return keys
381}
382
Liz Kammer9abd62d2021-05-21 08:37:59 -0400383// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
384type labelListSelectValues map[string]LabelList
385
386func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
387 for k, v := range other {
388 l := ll[k]
389 (&l).Append(v)
390 ll[k] = l
391 }
392}
393
394// HasConfigurableValues returns whether there are configurable values within this set of selects.
395func (ll labelListSelectValues) HasConfigurableValues() bool {
396 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400397 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400398 return true
399 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400400 }
401 return false
402}
403
Jingwen Chen07027912021-03-15 06:02:43 -0400404// LabelListAttribute is used to represent a list of Bazel labels as an
405// attribute.
406type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400407 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400408 Value LabelList
409
Liz Kammer9abd62d2021-05-21 08:37:59 -0400410 // The configured attribute label list Values. Optional
411 // a map of independent configurability axes
412 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400413
414 // If true, differentiate between "nil" and "empty" list. nil means that
415 // this attribute should not be specified at all, and "empty" means that
416 // the attribute should be explicitly specified as an empty list.
417 // This mode facilitates use of attribute defaults: an empty list should
418 // override the default.
419 ForceSpecifyEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400420}
Jingwen Chen91220d72021-03-24 02:18:33 -0400421
Liz Kammer9abd62d2021-05-21 08:37:59 -0400422type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
423
424func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
425 if list.IsNil() {
426 if _, ok := cll[axis][config]; ok {
427 delete(cll[axis], config)
428 }
429 return
430 }
431 if cll[axis] == nil {
432 cll[axis] = make(labelListSelectValues)
433 }
434
435 cll[axis][config] = list
436}
437
438func (cll configurableLabelLists) Append(other configurableLabelLists) {
439 for axis, otherSelects := range other {
440 selects := cll[axis]
441 if selects == nil {
442 selects = make(labelListSelectValues, len(otherSelects))
443 }
444 selects.appendSelects(otherSelects)
445 cll[axis] = selects
446 }
Jingwen Chen07027912021-03-15 06:02:43 -0400447}
448
449// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
450func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400451 return LabelListAttribute{
452 Value: value,
453 ConfigurableValues: make(configurableLabelLists),
454 }
455}
456
457func (lla *LabelListAttribute) SetValue(list LabelList) {
458 lla.SetSelectValue(NoConfigAxis, "", list)
459}
460
461// SetSelectValue set a value for a bazel select for the given axis, config and value.
462func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
463 axis.validateConfig(config)
464 switch axis.configurationType {
465 case noConfig:
466 lla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400467 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400468 if lla.ConfigurableValues == nil {
469 lla.ConfigurableValues = make(configurableLabelLists)
470 }
471 lla.ConfigurableValues.setValueForAxis(axis, config, list)
472 default:
473 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
474 }
475}
476
477// SelectValue gets a value for a bazel select for the given axis and config.
478func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
479 axis.validateConfig(config)
480 switch axis.configurationType {
481 case noConfig:
482 return lla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400483 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400484 return lla.ConfigurableValues[axis][config]
485 default:
486 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
487 }
488}
489
490// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
491func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
492 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
493 for k := range lla.ConfigurableValues {
494 keys = append(keys, k)
495 }
496
497 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
498 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400499}
500
Jingwen Chened9c17d2021-04-13 07:14:55 +0000501// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400502// LabelListAttribute to this LabelListAttribute.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400503func (lla *LabelListAttribute) Append(other LabelListAttribute) {
Chris Parsons51f8c392021-08-03 21:01:05 -0400504 if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
505 lla.Value.Includes = []Label{}
506 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400507 lla.Value.Append(other.Value)
508 if lla.ConfigurableValues == nil {
509 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400510 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400511 lla.ConfigurableValues.Append(other.ConfigurableValues)
Jingwen Chen63930982021-03-24 10:04:33 -0400512}
513
Liz Kammer9abd62d2021-05-21 08:37:59 -0400514// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
515func (lla LabelListAttribute) HasConfigurableValues() bool {
516 return len(lla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400517}
518
Chris Parsons69fa9f92021-07-13 11:47:44 -0400519// IsEmpty returns true if the attribute has no values under any configuration.
520func (lla LabelListAttribute) IsEmpty() bool {
521 if len(lla.Value.Includes) > 0 {
522 return false
523 }
524 for axis, _ := range lla.ConfigurableValues {
525 if lla.ConfigurableValues[axis].HasConfigurableValues() {
526 return false
527 }
528 }
529 return true
530}
531
Liz Kammer74deed42021-06-02 13:02:03 -0400532// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
533// the base value and included in default values as appropriate.
534func (lla *LabelListAttribute) ResolveExcludes() {
535 for axis, configToLabels := range lla.ConfigurableValues {
536 baseLabels := lla.Value.deepCopy()
537 for config, val := range configToLabels {
538 // Exclude config-specific excludes from base value
539 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
540
541 // add base values to config specific to add labels excluded by others in this axis
542 // then remove all config-specific excludes
543 allLabels := baseLabels.deepCopy()
544 allLabels.Append(val)
545 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
546 }
547
548 // After going through all configs, delete the duplicates in the config
549 // values that are already in the base Value.
550 for config, val := range configToLabels {
551 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
552 }
553
Jingwen Chen9af49a42021-11-02 10:27:17 +0000554 // Now that the Value list is finalized for this axis, compare it with
555 // the original list, and union the difference with the default
556 // condition for the axis.
557 difference := SubtractBazelLabelList(baseLabels, lla.Value)
558 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
559 existingDefaults.Append(difference)
560 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400561
562 // if everything ends up without includes, just delete the axis
563 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
564 delete(lla.ConfigurableValues, axis)
565 }
566 }
567}
568
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400569// OtherModuleContext is a limited context that has methods with information about other modules.
570type OtherModuleContext interface {
571 ModuleFromName(name string) (blueprint.Module, bool)
572 OtherModuleType(m blueprint.Module) string
573 OtherModuleName(m blueprint.Module) string
574 OtherModuleDir(m blueprint.Module) string
575 ModuleErrorf(fmt string, args ...interface{})
576}
577
578// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
579// label and whether it was changed.
580type LabelMapper func(OtherModuleContext, string) (string, bool)
581
582// LabelPartition contains descriptions of a partition for labels
583type LabelPartition struct {
584 // Extensions to include in this partition
585 Extensions []string
586 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
587 // the mapped label in the partition
588 LabelMapper LabelMapper
589 // Whether to store files not included in any other partition in a group of LabelPartitions
590 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
591 Keep_remainder bool
592}
593
594// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
595// partition
596type LabelPartitions map[string]LabelPartition
597
598// filter returns a pointer to a label if the label should be included in the partition or nil if
599// not.
600func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
601 if lf.LabelMapper != nil {
602 if newLabel, changed := lf.LabelMapper(ctx, label.Label); changed {
603 return &Label{newLabel, label.OriginalModuleName}
604 }
605 }
606 for _, ext := range lf.Extensions {
607 if strings.HasSuffix(label.Label, ext) {
608 return &label
609 }
610 }
611
612 return nil
613}
614
615// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
616type PartitionToLabelListAttribute map[string]LabelListAttribute
617
618type partitionToLabelList map[string]*LabelList
619
620func (p partitionToLabelList) appendIncludes(partition string, label Label) {
621 if _, ok := p[partition]; !ok {
622 p[partition] = &LabelList{}
623 }
624 p[partition].Includes = append(p[partition].Includes, label)
625}
626
627func (p partitionToLabelList) excludes(partition string, excludes []Label) {
628 if _, ok := p[partition]; !ok {
629 p[partition] = &LabelList{}
630 }
631 p[partition].Excludes = excludes
632}
633
634// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
635func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
636 ret := PartitionToLabelListAttribute{}
637 var partitionNames []string
638 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
639 var remainderPartition *string
640 for p, f := range partitions {
641 partitionNames = append(partitionNames, p)
642 if f.Keep_remainder {
643 if remainderPartition != nil {
644 panic("only one partition can store the remainder")
645 }
646 // If we take the address of p in a loop, we'll end up with the last value of p in
647 // remainderPartition, we want the requested partition
648 capturePartition := p
649 remainderPartition = &capturePartition
650 }
651 }
652
653 partitionLabelList := func(axis ConfigurationAxis, config string) {
654 value := lla.SelectValue(axis, config)
655 partitionToLabels := partitionToLabelList{}
656 for _, item := range value.Includes {
657 wasFiltered := false
658 var inPartition *string
659 for partition, f := range partitions {
660 filtered := f.filter(ctx, item)
661 if filtered == nil {
662 // did not match this filter, keep looking
663 continue
664 }
665 wasFiltered = true
666 partitionToLabels.appendIncludes(partition, *filtered)
667 // don't need to check other partitions if this filter used the item,
668 // continue checking if mapped to another name
669 if *filtered == item {
670 if inPartition != nil {
671 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
672 }
673 capturePartition := partition
674 inPartition = &capturePartition
675 }
676 }
677
678 // if not specified in a partition, add to remainder partition if one exists
679 if !wasFiltered && remainderPartition != nil {
680 partitionToLabels.appendIncludes(*remainderPartition, item)
681 }
682 }
683
684 // ensure empty lists are maintained
685 if value.Excludes != nil {
686 for _, partition := range partitionNames {
687 partitionToLabels.excludes(partition, value.Excludes)
688 }
689 }
690
691 for partition, list := range partitionToLabels {
692 val := ret[partition]
693 (&val).SetSelectValue(axis, config, *list)
694 ret[partition] = val
695 }
696 }
697
698 partitionLabelList(NoConfigAxis, "")
699 for axis, configToList := range lla.ConfigurableValues {
700 for config, _ := range configToList {
701 partitionLabelList(axis, config)
702 }
703 }
704 return ret
705}
706
Jingwen Chen5d864492021-02-24 07:20:12 -0500707// StringListAttribute corresponds to the string_list Bazel attribute type with
708// support for additional metadata, like configurations.
709type StringListAttribute struct {
710 // The base value of the string list attribute.
711 Value []string
712
Liz Kammer9abd62d2021-05-21 08:37:59 -0400713 // The configured attribute label list Values. Optional
714 // a map of independent configurability axes
715 ConfigurableValues configurableStringLists
716}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000717
Liz Kammer9abd62d2021-05-21 08:37:59 -0400718type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400719
Liz Kammer9abd62d2021-05-21 08:37:59 -0400720func (csl configurableStringLists) Append(other configurableStringLists) {
721 for axis, otherSelects := range other {
722 selects := csl[axis]
723 if selects == nil {
724 selects = make(stringListSelectValues, len(otherSelects))
725 }
726 selects.appendSelects(otherSelects)
727 csl[axis] = selects
728 }
729}
730
731func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
732 if csl[axis] == nil {
733 csl[axis] = make(stringListSelectValues)
734 }
735 csl[axis][config] = list
736}
737
738type stringListSelectValues map[string][]string
739
740func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
741 for k, v := range other {
742 sl[k] = append(sl[k], v...)
743 }
744}
745
746func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
747 for _, val := range sl {
748 if len(val) > 0 {
749 return true
750 }
751 }
752 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500753}
754
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000755// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
756func MakeStringListAttribute(value []string) StringListAttribute {
757 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400758 return StringListAttribute{
759 Value: value,
760 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400761 }
762}
763
Liz Kammer9abd62d2021-05-21 08:37:59 -0400764// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
765func (sla StringListAttribute) HasConfigurableValues() bool {
766 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400767}
768
Jingwen Chened9c17d2021-04-13 07:14:55 +0000769// Append appends all values, including os and arch specific ones, from another
770// StringListAttribute to this StringListAttribute
Liz Kammer9abd62d2021-05-21 08:37:59 -0400771func (sla *StringListAttribute) Append(other StringListAttribute) {
772 sla.Value = append(sla.Value, other.Value...)
773 if sla.ConfigurableValues == nil {
774 sla.ConfigurableValues = make(configurableStringLists)
775 }
776 sla.ConfigurableValues.Append(other.ConfigurableValues)
777}
778
779// SetSelectValue set a value for a bazel select for the given axis, config and value.
780func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
781 axis.validateConfig(config)
782 switch axis.configurationType {
783 case noConfig:
784 sla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400785 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400786 if sla.ConfigurableValues == nil {
787 sla.ConfigurableValues = make(configurableStringLists)
788 }
789 sla.ConfigurableValues.setValueForAxis(axis, config, list)
790 default:
791 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
792 }
793}
794
795// SelectValue gets a value for a bazel select for the given axis and config.
796func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
797 axis.validateConfig(config)
798 switch axis.configurationType {
799 case noConfig:
800 return sla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400801 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400802 return sla.ConfigurableValues[axis][config]
803 default:
804 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
805 }
806}
807
808// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
809func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
810 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
811 for k := range sla.ConfigurableValues {
812 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000813 }
814
Liz Kammer9abd62d2021-05-21 08:37:59 -0400815 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
816 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000817}
818
Liz Kammer5fad5012021-09-09 14:08:21 -0400819// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
820// configuration-specific values. For example, if we would convert this StringListAttribute as:
821// ["a", "b", "c"] + select({
822// "//condition:one": ["a", "d"],
823// "//conditions:default": [],
824// })
825// after this function, we would convert this StringListAttribute as:
826// ["a", "b", "c"] + select({
827// "//condition:one": ["d"],
828// "//conditions:default": [],
829// })
830func (sla *StringListAttribute) DeduplicateAxesFromBase() {
831 base := sla.Value
832 for axis, configToList := range sla.ConfigurableValues {
833 for config, list := range configToList {
834 remaining := SubtractStrings(list, base)
835 if len(remaining) == 0 {
836 delete(sla.ConfigurableValues[axis], config)
837 } else {
838 sla.ConfigurableValues[axis][config] = remaining
839 }
840 }
841 }
842}
843
Liz Kammera060c452021-03-24 10:14:47 -0400844// TryVariableSubstitution, replace string substitution formatting within each string in slice with
845// Starlark string.format compatible tag for productVariable.
846func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
847 ret := make([]string, 0, len(slice))
848 changesMade := false
849 for _, s := range slice {
850 newS, changed := TryVariableSubstitution(s, productVariable)
851 ret = append(ret, newS)
852 changesMade = changesMade || changed
853 }
854 return ret, changesMade
855}
856
857// TryVariableSubstitution, replace string substitution formatting within s with Starlark
858// string.format compatible tag for productVariable.
859func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400860 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400861 return sub, s != sub
862}