blob: 11f62477d69e7adcf6c8216bae50275b9196cf6a [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
Sam Delmericoc0161432022-02-25 21:34:51 +000068// MakeLabelList creates a LabelList from a list Label
69func MakeLabelList(labels []Label) LabelList {
70 return LabelList{
71 Includes: labels,
72 Excludes: nil,
73 }
74}
75
Chris Parsons51f8c392021-08-03 21:01:05 -040076func (ll *LabelList) Equals(other LabelList) bool {
77 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
78 return false
79 }
80 for i, _ := range ll.Includes {
81 if ll.Includes[i] != other.Includes[i] {
82 return false
83 }
84 }
85 for i, _ := range ll.Excludes {
86 if ll.Excludes[i] != other.Excludes[i] {
87 return false
88 }
89 }
90 return true
91}
92
Liz Kammer9abd62d2021-05-21 08:37:59 -040093func (ll *LabelList) IsNil() bool {
94 return ll.Includes == nil && ll.Excludes == nil
95}
96
Sam Delmerico3177a6e2022-06-21 19:28:33 +000097func (ll *LabelList) IsEmpty() bool {
98 return len(ll.Includes) == 0 && len(ll.Excludes) == 0
99}
100
Liz Kammer74deed42021-06-02 13:02:03 -0400101func (ll *LabelList) deepCopy() LabelList {
102 return LabelList{
103 Includes: ll.Includes[:],
104 Excludes: ll.Excludes[:],
105 }
106}
107
Jingwen Chen63930982021-03-24 10:04:33 -0400108// uniqueParentDirectories returns a list of the unique parent directories for
109// all files in ll.Includes.
110func (ll *LabelList) uniqueParentDirectories() []string {
111 dirMap := map[string]bool{}
112 for _, label := range ll.Includes {
113 dirMap[filepath.Dir(label.Label)] = true
114 }
115 dirs := []string{}
116 for dir := range dirMap {
117 dirs = append(dirs, dir)
118 }
119 return dirs
120}
121
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400122// Add inserts the label Label at the end of the LabelList.Includes.
Liz Kammer12615db2021-09-28 09:19:17 -0400123func (ll *LabelList) Add(label *Label) {
124 if label == nil {
125 return
126 }
127 ll.Includes = append(ll.Includes, *label)
128}
129
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400130// AddExclude inserts the label Label at the end of the LabelList.Excludes.
131func (ll *LabelList) AddExclude(label *Label) {
132 if label == nil {
133 return
134 }
135 ll.Excludes = append(ll.Excludes, *label)
136}
137
Liz Kammer356f7d42021-01-26 09:18:53 -0500138// Append appends the fields of other labelList to the corresponding fields of ll.
139func (ll *LabelList) Append(other LabelList) {
140 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
141 ll.Includes = append(ll.Includes, other.Includes...)
142 }
143 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
144 ll.Excludes = append(other.Excludes, other.Excludes...)
145 }
146}
Jingwen Chen5d864492021-02-24 07:20:12 -0500147
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400148// Partition splits a LabelList into two LabelLists depending on the return value
149// of the predicate.
150// This function preserves the Includes and Excludes, but it does not provide
151// that information to the partition function.
152func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
153 predicated := LabelList{}
154 unpredicated := LabelList{}
155 for _, include := range ll.Includes {
156 if predicate(include) {
157 predicated.Add(&include)
158 } else {
159 unpredicated.Add(&include)
160 }
161 }
162 for _, exclude := range ll.Excludes {
163 if predicate(exclude) {
164 predicated.AddExclude(&exclude)
165 } else {
166 unpredicated.AddExclude(&exclude)
167 }
168 }
169 return predicated, unpredicated
170}
171
Jingwen Chened9c17d2021-04-13 07:14:55 +0000172// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
173// the slice in a sorted order.
174func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000175 uniqueLabelsSet := make(map[Label]bool)
176 for _, l := range originalLabels {
177 uniqueLabelsSet[l] = true
178 }
179 var uniqueLabels []Label
180 for l, _ := range uniqueLabelsSet {
181 uniqueLabels = append(uniqueLabels, l)
182 }
183 sort.SliceStable(uniqueLabels, func(i, j int) bool {
184 return uniqueLabels[i].Label < uniqueLabels[j].Label
185 })
186 return uniqueLabels
187}
188
Liz Kammer9abd62d2021-05-21 08:37:59 -0400189func FirstUniqueBazelLabels(originalLabels []Label) []Label {
190 var labels []Label
191 found := make(map[Label]bool, len(originalLabels))
192 for _, l := range originalLabels {
193 if _, ok := found[l]; ok {
194 continue
195 }
196 labels = append(labels, l)
197 found[l] = true
198 }
199 return labels
200}
201
202func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
203 var uniqueLabelList LabelList
204 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
205 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
206 return uniqueLabelList
207}
208
209func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000210 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000211 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
212 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000213 return uniqueLabelList
214}
215
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000216// Subtract needle from haystack
217func SubtractStrings(haystack []string, needle []string) []string {
218 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400219 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000220 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400221 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000222 }
223
224 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400225 for _, s := range haystack {
226 if exclude := needleMap[s]; !exclude {
227 strings = append(strings, s)
228 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000229 }
230
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000231 return strings
232}
233
234// Subtract needle from haystack
235func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
236 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400237 needleMap := make(map[Label]bool)
238 for _, s := range needle {
239 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000240 }
241
242 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400243 for _, label := range haystack {
244 if exclude := needleMap[label]; !exclude {
245 labels = append(labels, label)
246 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000247 }
248
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000249 return labels
250}
251
Chris Parsons484e50a2021-05-13 15:13:04 -0400252// Appends two LabelLists, returning the combined list.
253func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
254 var result LabelList
255 result.Includes = append(a.Includes, b.Includes...)
256 result.Excludes = append(a.Excludes, b.Excludes...)
257 return result
258}
259
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000260// Subtract needle from haystack
261func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
262 var result LabelList
263 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
264 // NOTE: Excludes are intentionally not subtracted
265 result.Excludes = haystack.Excludes
266 return result
267}
268
Jingwen Chenc1c26502021-04-05 10:35:13 +0000269type Attribute interface {
270 HasConfigurableValues() bool
271}
272
Liz Kammer9abd62d2021-05-21 08:37:59 -0400273type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400274
Liz Kammer9abd62d2021-05-21 08:37:59 -0400275type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400276
Liz Kammer9abd62d2021-05-21 08:37:59 -0400277func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
278 if cl[axis] == nil {
279 cl[axis] = make(labelSelectValues)
280 }
281 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400282}
283
284// Represents an attribute whose value is a single label
285type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400286 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400287
Liz Kammer9abd62d2021-05-21 08:37:59 -0400288 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200289}
290
Chris Parsons58852a02021-12-09 18:10:18 -0500291func (la *LabelAttribute) axisTypes() map[configurationType]bool {
292 types := map[configurationType]bool{}
293 for k := range la.ConfigurableValues {
294 if len(la.ConfigurableValues[k]) > 0 {
295 types[k.configurationType] = true
296 }
297 }
298 return types
299}
300
301// Collapse reduces the configurable axes of the label attribute to a single axis.
302// This is necessary for final writing to bp2build, as a configurable label
303// attribute can only be comprised by a single select.
304func (la *LabelAttribute) Collapse() error {
305 axisTypes := la.axisTypes()
306 _, containsOs := axisTypes[os]
307 _, containsArch := axisTypes[arch]
308 _, containsOsArch := axisTypes[osArch]
309 _, containsProductVariables := axisTypes[productVariables]
310 if containsProductVariables {
311 if containsOs || containsArch || containsOsArch {
Alixbbfd5382022-06-09 18:52:05 +0000312 if containsArch {
313 allProductVariablesAreArchVariant := true
314 for k := range la.ConfigurableValues {
315 if k.configurationType == productVariables && k.outerAxisType != arch {
316 allProductVariablesAreArchVariant = false
317 }
318 }
319 if !allProductVariablesAreArchVariant {
320 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
321 }
322 } else {
323 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
324 }
Chris Parsons58852a02021-12-09 18:10:18 -0500325 }
326 }
327 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
328 // If a bool attribute has both os and arch configuration axes, the only
329 // way to successfully union their values is to increase the granularity
330 // of the configuration criteria to os_arch.
331 for osType, supportedArchs := range osToArchMap {
332 for _, supportedArch := range supportedArchs {
333 osArch := osArchString(osType, supportedArch)
334 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
335 // Do nothing, as the arch_os is explicitly defined already.
336 } else {
337 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
338 osVal := la.SelectValue(OsConfigurationAxis, osType)
339 if osVal != nil && archVal != nil {
340 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
341 // runs after os mutator.
342 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
343 } else if osVal != nil && archVal == nil {
344 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
345 } else if osVal == nil && archVal != nil {
346 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
347 }
348 }
349 }
350 }
351 // All os_arch values are now set. Clear os and arch axes.
352 delete(la.ConfigurableValues, ArchConfigurationAxis)
353 delete(la.ConfigurableValues, OsConfigurationAxis)
354 }
355 return nil
356}
357
Liz Kammer9abd62d2021-05-21 08:37:59 -0400358// HasConfigurableValues returns whether there are configurable values set for this label.
359func (la LabelAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500360 for _, selectValues := range la.ConfigurableValues {
361 if len(selectValues) > 0 {
362 return true
363 }
364 }
365 return false
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200366}
367
Liz Kammer9abd62d2021-05-21 08:37:59 -0400368// SetValue sets the base, non-configured value for the Label
369func (la *LabelAttribute) SetValue(value Label) {
370 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400371}
372
Liz Kammer9abd62d2021-05-21 08:37:59 -0400373// SetSelectValue set a value for a bazel select for the given axis, config and value.
374func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
375 axis.validateConfig(config)
376 switch axis.configurationType {
377 case noConfig:
378 la.Value = &value
Wei Li81852ca2022-07-27 00:22:06 -0700379 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400380 if la.ConfigurableValues == nil {
381 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400382 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400383 la.ConfigurableValues.setValueForAxis(axis, config, &value)
384 default:
385 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
386 }
387}
388
389// SelectValue gets a value for a bazel select for the given axis and config.
Chris Parsons58852a02021-12-09 18:10:18 -0500390func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400391 axis.validateConfig(config)
392 switch axis.configurationType {
393 case noConfig:
Chris Parsons58852a02021-12-09 18:10:18 -0500394 return la.Value
Wei Li81852ca2022-07-27 00:22:06 -0700395 case arch, os, osArch, productVariables, osAndInApex:
Chris Parsons58852a02021-12-09 18:10:18 -0500396 return la.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400397 default:
398 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
399 }
400}
401
402// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
403func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
404 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
405 for k := range la.ConfigurableValues {
406 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400407 }
408
Liz Kammer9abd62d2021-05-21 08:37:59 -0400409 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
410 return keys
411}
412
Sam Delmericoc0161432022-02-25 21:34:51 +0000413// MakeLabelAttribute turns a string into a LabelAttribute
414func MakeLabelAttribute(label string) *LabelAttribute {
415 return &LabelAttribute{
416 Value: &Label{
417 Label: label,
418 },
419 }
420}
421
Liz Kammerd366c902021-06-03 13:43:01 -0400422type configToBools map[string]bool
423
424func (ctb configToBools) setValue(config string, value *bool) {
425 if value == nil {
426 if _, ok := ctb[config]; ok {
427 delete(ctb, config)
428 }
429 return
430 }
431 ctb[config] = *value
432}
433
434type configurableBools map[ConfigurationAxis]configToBools
435
436func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
437 if cb[axis] == nil {
438 cb[axis] = make(configToBools)
439 }
440 cb[axis].setValue(config, value)
441}
442
443// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
444type BoolAttribute struct {
445 Value *bool
446
447 ConfigurableValues configurableBools
448}
449
450// HasConfigurableValues returns whether there are configurable values for this attribute.
451func (ba BoolAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500452 for _, cfgToBools := range ba.ConfigurableValues {
453 if len(cfgToBools) > 0 {
454 return true
455 }
456 }
457 return false
Liz Kammerd366c902021-06-03 13:43:01 -0400458}
459
Liz Kammerdfeb1202022-05-13 17:20:20 -0400460// SetValue sets value for the no config axis
461func (ba *BoolAttribute) SetValue(value *bool) {
462 ba.SetSelectValue(NoConfigAxis, "", value)
463}
464
Liz Kammerd366c902021-06-03 13:43:01 -0400465// SetSelectValue sets value for the given axis/config.
466func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
467 axis.validateConfig(config)
468 switch axis.configurationType {
469 case noConfig:
470 ba.Value = value
Wei Li81852ca2022-07-27 00:22:06 -0700471 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammerd366c902021-06-03 13:43:01 -0400472 if ba.ConfigurableValues == nil {
473 ba.ConfigurableValues = make(configurableBools)
474 }
475 ba.ConfigurableValues.setValueForAxis(axis, config, value)
476 default:
477 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
478 }
479}
480
Chris Parsons58852a02021-12-09 18:10:18 -0500481// ToLabelListAttribute creates and returns a LabelListAttribute from this
482// bool attribute, where each bool in this attribute corresponds to a
483// label list value in the resultant attribute.
484func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
485 getLabelList := func(boolPtr *bool) LabelList {
486 if boolPtr == nil {
487 return LabelList{nil, nil}
488 } else if *boolPtr {
489 return trueVal
490 } else {
491 return falseVal
492 }
493 }
494
495 mainVal := getLabelList(ba.Value)
496 if !ba.HasConfigurableValues() {
497 return MakeLabelListAttribute(mainVal), nil
498 }
499
500 result := LabelListAttribute{}
501 if err := ba.Collapse(); err != nil {
502 return result, err
503 }
504
505 for axis, configToBools := range ba.ConfigurableValues {
506 if len(configToBools) < 1 {
507 continue
508 }
509 for config, boolPtr := range configToBools {
510 val := getLabelList(&boolPtr)
511 if !val.Equals(mainVal) {
512 result.SetSelectValue(axis, config, val)
513 }
514 }
515 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
516 }
517
518 return result, nil
519}
520
521// Collapse reduces the configurable axes of the boolean attribute to a single axis.
522// This is necessary for final writing to bp2build, as a configurable boolean
523// attribute can only be comprised by a single select.
524func (ba *BoolAttribute) Collapse() error {
525 axisTypes := ba.axisTypes()
526 _, containsOs := axisTypes[os]
527 _, containsArch := axisTypes[arch]
528 _, containsOsArch := axisTypes[osArch]
529 _, containsProductVariables := axisTypes[productVariables]
530 if containsProductVariables {
531 if containsOs || containsArch || containsOsArch {
532 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
533 }
534 }
535 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
536 // If a bool attribute has both os and arch configuration axes, the only
537 // way to successfully union their values is to increase the granularity
538 // of the configuration criteria to os_arch.
539 for osType, supportedArchs := range osToArchMap {
540 for _, supportedArch := range supportedArchs {
541 osArch := osArchString(osType, supportedArch)
542 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
543 // Do nothing, as the arch_os is explicitly defined already.
544 } else {
545 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
546 osVal := ba.SelectValue(OsConfigurationAxis, osType)
547 if osVal != nil && archVal != nil {
548 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
549 // runs after os mutator.
550 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
551 } else if osVal != nil && archVal == nil {
552 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
553 } else if osVal == nil && archVal != nil {
554 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
555 }
556 }
557 }
558 }
559 // All os_arch values are now set. Clear os and arch axes.
560 delete(ba.ConfigurableValues, ArchConfigurationAxis)
561 delete(ba.ConfigurableValues, OsConfigurationAxis)
562 // Verify post-condition; this should never fail, provided no additional
563 // axes are introduced.
564 if len(ba.ConfigurableValues) > 1 {
Liz Kammer07e106f2022-01-13 17:00:10 -0500565 panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
Chris Parsons58852a02021-12-09 18:10:18 -0500566 }
567 }
568 return nil
569}
570
571func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
572 types := map[configurationType]bool{}
573 for k := range ba.ConfigurableValues {
574 if len(ba.ConfigurableValues[k]) > 0 {
575 types[k.configurationType] = true
576 }
577 }
578 return types
579}
580
Liz Kammerd366c902021-06-03 13:43:01 -0400581// SelectValue gets the value for the given axis/config.
582func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
583 axis.validateConfig(config)
584 switch axis.configurationType {
585 case noConfig:
586 return ba.Value
Wei Li81852ca2022-07-27 00:22:06 -0700587 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammerd366c902021-06-03 13:43:01 -0400588 if v, ok := ba.ConfigurableValues[axis][config]; ok {
589 return &v
590 } else {
591 return nil
592 }
593 default:
594 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
595 }
596}
597
598// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
599func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
600 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
601 for k := range ba.ConfigurableValues {
602 keys = append(keys, k)
603 }
604
605 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
606 return keys
607}
608
Liz Kammer9abd62d2021-05-21 08:37:59 -0400609// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
610type labelListSelectValues map[string]LabelList
611
Liz Kammer12615db2021-09-28 09:19:17 -0400612func (ll labelListSelectValues) addSelects(label labelSelectValues) {
613 for k, v := range label {
614 if label == nil {
615 continue
616 }
617 l := ll[k]
618 (&l).Add(v)
619 ll[k] = l
620 }
621}
622
Chris Parsons77acf2e2021-12-03 17:27:16 -0500623func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400624 for k, v := range other {
625 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500626 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
627 l.Includes = []Label{}
628 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400629 (&l).Append(v)
630 ll[k] = l
631 }
632}
633
634// HasConfigurableValues returns whether there are configurable values within this set of selects.
635func (ll labelListSelectValues) HasConfigurableValues() bool {
636 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400637 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400638 return true
639 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400640 }
641 return false
642}
643
Jingwen Chen07027912021-03-15 06:02:43 -0400644// LabelListAttribute is used to represent a list of Bazel labels as an
645// attribute.
646type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400647 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400648 Value LabelList
649
Liz Kammer9abd62d2021-05-21 08:37:59 -0400650 // The configured attribute label list Values. Optional
651 // a map of independent configurability axes
652 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400653
654 // If true, differentiate between "nil" and "empty" list. nil means that
655 // this attribute should not be specified at all, and "empty" means that
656 // the attribute should be explicitly specified as an empty list.
657 // This mode facilitates use of attribute defaults: an empty list should
658 // override the default.
659 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000660
661 // If true, signal the intent to the code generator to emit all select keys,
662 // even if the Includes list for that key is empty. This mode facilitates
663 // specific select statements where an empty list for a non-default select
664 // key has a meaning.
665 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400666}
Jingwen Chen91220d72021-03-24 02:18:33 -0400667
Liz Kammer9abd62d2021-05-21 08:37:59 -0400668type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
669
670func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
671 if list.IsNil() {
672 if _, ok := cll[axis][config]; ok {
673 delete(cll[axis], config)
674 }
675 return
676 }
677 if cll[axis] == nil {
678 cll[axis] = make(labelListSelectValues)
679 }
680
681 cll[axis][config] = list
682}
683
Chris Parsons77acf2e2021-12-03 17:27:16 -0500684func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400685 for axis, otherSelects := range other {
686 selects := cll[axis]
687 if selects == nil {
688 selects = make(labelListSelectValues, len(otherSelects))
689 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500690 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400691 cll[axis] = selects
692 }
Jingwen Chen07027912021-03-15 06:02:43 -0400693}
694
Chris Parsons77acf2e2021-12-03 17:27:16 -0500695func (lla *LabelListAttribute) Clone() *LabelListAttribute {
696 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
697 return result.Append(*lla)
698}
699
Jingwen Chen07027912021-03-15 06:02:43 -0400700// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
701func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400702 return LabelListAttribute{
703 Value: value,
704 ConfigurableValues: make(configurableLabelLists),
705 }
706}
707
Cole Faust53b62092022-05-12 15:37:02 -0700708// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
709func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
710 return MakeLabelListAttribute(MakeLabelList([]Label{value}))
711}
712
Liz Kammer9abd62d2021-05-21 08:37:59 -0400713func (lla *LabelListAttribute) SetValue(list LabelList) {
714 lla.SetSelectValue(NoConfigAxis, "", list)
715}
716
717// SetSelectValue set a value for a bazel select for the given axis, config and value.
718func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
719 axis.validateConfig(config)
720 switch axis.configurationType {
721 case noConfig:
722 lla.Value = list
Wei Li81852ca2022-07-27 00:22:06 -0700723 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400724 if lla.ConfigurableValues == nil {
725 lla.ConfigurableValues = make(configurableLabelLists)
726 }
727 lla.ConfigurableValues.setValueForAxis(axis, config, list)
728 default:
729 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
730 }
731}
732
733// SelectValue gets a value for a bazel select for the given axis and config.
734func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
735 axis.validateConfig(config)
736 switch axis.configurationType {
737 case noConfig:
738 return lla.Value
Wei Li81852ca2022-07-27 00:22:06 -0700739 case arch, os, osArch, productVariables, osAndInApex:
Cole Faustc843b992022-08-02 18:06:50 -0700740 return lla.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400741 default:
742 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
743 }
744}
745
746// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
747func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
748 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
749 for k := range lla.ConfigurableValues {
750 keys = append(keys, k)
751 }
752
753 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
754 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400755}
756
Jingwen Chened9c17d2021-04-13 07:14:55 +0000757// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500758// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
759func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
760 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
761 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400762 lla.Value.Includes = []Label{}
763 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400764 lla.Value.Append(other.Value)
765 if lla.ConfigurableValues == nil {
766 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400767 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500768 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
769 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400770}
771
Liz Kammer12615db2021-09-28 09:19:17 -0400772// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
773// LabelList within the LabelListAttribute
774func (lla *LabelListAttribute) Add(label *LabelAttribute) {
775 if label == nil {
776 return
777 }
778
779 lla.Value.Add(label.Value)
780 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
781 lla.ConfigurableValues = make(configurableLabelLists)
782 }
783 for axis, _ := range label.ConfigurableValues {
784 if _, exists := lla.ConfigurableValues[axis]; !exists {
785 lla.ConfigurableValues[axis] = make(labelListSelectValues)
786 }
787 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
788 }
789}
790
Liz Kammer9abd62d2021-05-21 08:37:59 -0400791// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
792func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500793 for _, selectValues := range lla.ConfigurableValues {
794 if len(selectValues) > 0 {
795 return true
796 }
797 }
798 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400799}
800
Chris Parsons69fa9f92021-07-13 11:47:44 -0400801// IsEmpty returns true if the attribute has no values under any configuration.
802func (lla LabelListAttribute) IsEmpty() bool {
803 if len(lla.Value.Includes) > 0 {
804 return false
805 }
806 for axis, _ := range lla.ConfigurableValues {
807 if lla.ConfigurableValues[axis].HasConfigurableValues() {
808 return false
809 }
810 }
811 return true
812}
813
Liz Kammer54309532021-12-14 12:21:22 -0500814// IsNil returns true if the attribute has not been set for any configuration.
815func (lla LabelListAttribute) IsNil() bool {
816 if lla.Value.Includes != nil {
817 return false
818 }
819 return !lla.HasConfigurableValues()
820}
821
822// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
823// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
824// be removed, e.g. if they could cause duplicate element failures.
825func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
826 val := lla.SelectValue(axis, config)
827 newList := SubtractBazelLabelList(val, labelList)
828 newList.Excludes = append(newList.Excludes, labelList.Includes...)
829 lla.SetSelectValue(axis, config, newList)
830}
831
Liz Kammer74deed42021-06-02 13:02:03 -0400832// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
833// the base value and included in default values as appropriate.
834func (lla *LabelListAttribute) ResolveExcludes() {
835 for axis, configToLabels := range lla.ConfigurableValues {
836 baseLabels := lla.Value.deepCopy()
837 for config, val := range configToLabels {
838 // Exclude config-specific excludes from base value
839 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
840
841 // add base values to config specific to add labels excluded by others in this axis
842 // then remove all config-specific excludes
843 allLabels := baseLabels.deepCopy()
844 allLabels.Append(val)
845 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
846 }
847
848 // After going through all configs, delete the duplicates in the config
849 // values that are already in the base Value.
850 for config, val := range configToLabels {
851 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
852 }
853
Jingwen Chen9af49a42021-11-02 10:27:17 +0000854 // Now that the Value list is finalized for this axis, compare it with
855 // the original list, and union the difference with the default
856 // condition for the axis.
857 difference := SubtractBazelLabelList(baseLabels, lla.Value)
858 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
859 existingDefaults.Append(difference)
860 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400861
862 // if everything ends up without includes, just delete the axis
863 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
864 delete(lla.ConfigurableValues, axis)
865 }
866 }
867}
868
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400869// Partition splits a LabelListAttribute into two LabelListAttributes depending
870// on the return value of the predicate.
871// This function preserves the Includes and Excludes, but it does not provide
872// that information to the partition function.
873func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
874 predicated := LabelListAttribute{}
875 unpredicated := LabelListAttribute{}
876
877 valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
878 predicated.SetValue(valuePartitionTrue)
879 unpredicated.SetValue(valuePartitionFalse)
880
881 for axis, selectValueLabelLists := range lla.ConfigurableValues {
882 for config, labelList := range selectValueLabelLists {
883 configPredicated, configUnpredicated := labelList.Partition(predicate)
884 predicated.SetSelectValue(axis, config, configPredicated)
885 unpredicated.SetSelectValue(axis, config, configUnpredicated)
886 }
887 }
888
889 return predicated, unpredicated
890}
891
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400892// OtherModuleContext is a limited context that has methods with information about other modules.
893type OtherModuleContext interface {
894 ModuleFromName(name string) (blueprint.Module, bool)
895 OtherModuleType(m blueprint.Module) string
896 OtherModuleName(m blueprint.Module) string
897 OtherModuleDir(m blueprint.Module) string
898 ModuleErrorf(fmt string, args ...interface{})
899}
900
901// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
902// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400903type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400904
905// LabelPartition contains descriptions of a partition for labels
906type LabelPartition struct {
907 // Extensions to include in this partition
908 Extensions []string
909 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
910 // the mapped label in the partition
911 LabelMapper LabelMapper
912 // Whether to store files not included in any other partition in a group of LabelPartitions
913 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
914 Keep_remainder bool
915}
916
917// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
918// partition
919type LabelPartitions map[string]LabelPartition
920
921// filter returns a pointer to a label if the label should be included in the partition or nil if
922// not.
923func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
924 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400925 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400926 return &Label{newLabel, label.OriginalModuleName}
927 }
928 }
929 for _, ext := range lf.Extensions {
930 if strings.HasSuffix(label.Label, ext) {
931 return &label
932 }
933 }
934
935 return nil
936}
937
938// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
939type PartitionToLabelListAttribute map[string]LabelListAttribute
940
941type partitionToLabelList map[string]*LabelList
942
943func (p partitionToLabelList) appendIncludes(partition string, label Label) {
944 if _, ok := p[partition]; !ok {
945 p[partition] = &LabelList{}
946 }
947 p[partition].Includes = append(p[partition].Includes, label)
948}
949
950func (p partitionToLabelList) excludes(partition string, excludes []Label) {
951 if _, ok := p[partition]; !ok {
952 p[partition] = &LabelList{}
953 }
954 p[partition].Excludes = excludes
955}
956
957// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
958func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
959 ret := PartitionToLabelListAttribute{}
960 var partitionNames []string
961 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
962 var remainderPartition *string
963 for p, f := range partitions {
964 partitionNames = append(partitionNames, p)
965 if f.Keep_remainder {
966 if remainderPartition != nil {
967 panic("only one partition can store the remainder")
968 }
969 // If we take the address of p in a loop, we'll end up with the last value of p in
970 // remainderPartition, we want the requested partition
971 capturePartition := p
972 remainderPartition = &capturePartition
973 }
974 }
975
976 partitionLabelList := func(axis ConfigurationAxis, config string) {
977 value := lla.SelectValue(axis, config)
978 partitionToLabels := partitionToLabelList{}
979 for _, item := range value.Includes {
980 wasFiltered := false
981 var inPartition *string
982 for partition, f := range partitions {
983 filtered := f.filter(ctx, item)
984 if filtered == nil {
985 // did not match this filter, keep looking
986 continue
987 }
988 wasFiltered = true
989 partitionToLabels.appendIncludes(partition, *filtered)
990 // don't need to check other partitions if this filter used the item,
991 // continue checking if mapped to another name
992 if *filtered == item {
993 if inPartition != nil {
994 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
995 }
996 capturePartition := partition
997 inPartition = &capturePartition
998 }
999 }
1000
1001 // if not specified in a partition, add to remainder partition if one exists
1002 if !wasFiltered && remainderPartition != nil {
1003 partitionToLabels.appendIncludes(*remainderPartition, item)
1004 }
1005 }
1006
1007 // ensure empty lists are maintained
1008 if value.Excludes != nil {
1009 for _, partition := range partitionNames {
1010 partitionToLabels.excludes(partition, value.Excludes)
1011 }
1012 }
1013
1014 for partition, list := range partitionToLabels {
1015 val := ret[partition]
1016 (&val).SetSelectValue(axis, config, *list)
1017 ret[partition] = val
1018 }
1019 }
1020
1021 partitionLabelList(NoConfigAxis, "")
1022 for axis, configToList := range lla.ConfigurableValues {
1023 for config, _ := range configToList {
1024 partitionLabelList(axis, config)
1025 }
1026 }
1027 return ret
1028}
1029
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001030// StringAttribute corresponds to the string Bazel attribute type with
1031// support for additional metadata, like configurations.
1032type StringAttribute struct {
1033 // The base value of the string attribute.
1034 Value *string
1035
1036 // The configured attribute label list Values. Optional
1037 // a map of independent configurability axes
1038 ConfigurableValues configurableStrings
1039}
1040
1041type configurableStrings map[ConfigurationAxis]stringSelectValues
1042
1043func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
1044 if cs[axis] == nil {
1045 cs[axis] = make(stringSelectValues)
1046 }
1047 var v = ""
1048 if str != nil {
1049 v = *str
1050 }
1051 cs[axis][config] = v
1052}
1053
1054type stringSelectValues map[string]string
1055
1056// HasConfigurableValues returns true if the attribute contains axis-specific string values.
1057func (sa StringAttribute) HasConfigurableValues() bool {
1058 for _, selectValues := range sa.ConfigurableValues {
1059 if len(selectValues) > 0 {
1060 return true
1061 }
1062 }
1063 return false
1064}
1065
1066// SetSelectValue set a value for a bazel select for the given axis, config and value.
1067func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
1068 axis.validateConfig(config)
1069 switch axis.configurationType {
1070 case noConfig:
1071 sa.Value = str
1072 case arch, os, osArch, productVariables:
1073 if sa.ConfigurableValues == nil {
1074 sa.ConfigurableValues = make(configurableStrings)
1075 }
1076 sa.ConfigurableValues.setValueForAxis(axis, config, str)
1077 default:
1078 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1079 }
1080}
1081
1082// SelectValue gets a value for a bazel select for the given axis and config.
1083func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
1084 axis.validateConfig(config)
1085 switch axis.configurationType {
1086 case noConfig:
1087 return sa.Value
1088 case arch, os, osArch, productVariables:
1089 if v, ok := sa.ConfigurableValues[axis][config]; ok {
1090 return &v
1091 } else {
1092 return nil
1093 }
1094 default:
1095 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1096 }
1097}
1098
1099// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1100func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1101 keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
1102 for k := range sa.ConfigurableValues {
1103 keys = append(keys, k)
1104 }
1105
1106 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1107 return keys
1108}
1109
1110// Collapse reduces the configurable axes of the string attribute to a single axis.
1111// This is necessary for final writing to bp2build, as a configurable string
1112// attribute can only be comprised by a single select.
1113func (sa *StringAttribute) Collapse() error {
1114 axisTypes := sa.axisTypes()
1115 _, containsOs := axisTypes[os]
1116 _, containsArch := axisTypes[arch]
1117 _, containsOsArch := axisTypes[osArch]
1118 _, containsProductVariables := axisTypes[productVariables]
1119 if containsProductVariables {
1120 if containsOs || containsArch || containsOsArch {
1121 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
1122 }
1123 }
1124 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
1125 // If a bool attribute has both os and arch configuration axes, the only
1126 // way to successfully union their values is to increase the granularity
1127 // of the configuration criteria to os_arch.
1128 for osType, supportedArchs := range osToArchMap {
1129 for _, supportedArch := range supportedArchs {
1130 osArch := osArchString(osType, supportedArch)
1131 if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
1132 // Do nothing, as the arch_os is explicitly defined already.
1133 } else {
1134 archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
1135 osVal := sa.SelectValue(OsConfigurationAxis, osType)
1136 if osVal != nil && archVal != nil {
1137 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
1138 // runs after os mutator.
1139 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1140 } else if osVal != nil && archVal == nil {
1141 sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
1142 } else if osVal == nil && archVal != nil {
1143 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1144 }
1145 }
1146 }
1147 }
1148 // All os_arch values are now set. Clear os and arch axes.
1149 delete(sa.ConfigurableValues, ArchConfigurationAxis)
1150 delete(sa.ConfigurableValues, OsConfigurationAxis)
1151 // Verify post-condition; this should never fail, provided no additional
1152 // axes are introduced.
1153 if len(sa.ConfigurableValues) > 1 {
1154 panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
1155 }
1156 }
1157 return nil
1158}
1159
1160func (sa *StringAttribute) axisTypes() map[configurationType]bool {
1161 types := map[configurationType]bool{}
1162 for k := range sa.ConfigurableValues {
1163 if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
1164 types[k.configurationType] = true
1165 }
1166 }
1167 return types
1168}
1169
Jingwen Chen5d864492021-02-24 07:20:12 -05001170// StringListAttribute corresponds to the string_list Bazel attribute type with
1171// support for additional metadata, like configurations.
1172type StringListAttribute struct {
1173 // The base value of the string list attribute.
1174 Value []string
1175
Liz Kammer9abd62d2021-05-21 08:37:59 -04001176 // The configured attribute label list Values. Optional
1177 // a map of independent configurability axes
1178 ConfigurableValues configurableStringLists
1179}
Jingwen Chenc1c26502021-04-05 10:35:13 +00001180
Liz Kammer9abd62d2021-05-21 08:37:59 -04001181type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -04001182
Liz Kammer9abd62d2021-05-21 08:37:59 -04001183func (csl configurableStringLists) Append(other configurableStringLists) {
1184 for axis, otherSelects := range other {
1185 selects := csl[axis]
1186 if selects == nil {
1187 selects = make(stringListSelectValues, len(otherSelects))
1188 }
1189 selects.appendSelects(otherSelects)
1190 csl[axis] = selects
1191 }
1192}
1193
1194func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
1195 if csl[axis] == nil {
1196 csl[axis] = make(stringListSelectValues)
1197 }
1198 csl[axis][config] = list
1199}
1200
1201type stringListSelectValues map[string][]string
1202
1203func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
1204 for k, v := range other {
1205 sl[k] = append(sl[k], v...)
1206 }
1207}
1208
1209func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
1210 for _, val := range sl {
1211 if len(val) > 0 {
1212 return true
1213 }
1214 }
1215 return false
Jingwen Chen5d864492021-02-24 07:20:12 -05001216}
1217
Rupert Shuttleworthb8151682021-04-06 20:06:21 +00001218// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1219func MakeStringListAttribute(value []string) StringListAttribute {
1220 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -04001221 return StringListAttribute{
1222 Value: value,
1223 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -04001224 }
1225}
1226
Liz Kammer9abd62d2021-05-21 08:37:59 -04001227// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1228func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -05001229 for _, selectValues := range sla.ConfigurableValues {
1230 if len(selectValues) > 0 {
1231 return true
1232 }
1233 }
1234 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -04001235}
1236
Jingwen Chened9c17d2021-04-13 07:14:55 +00001237// Append appends all values, including os and arch specific ones, from another
1238// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001239func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001240 sla.Value = append(sla.Value, other.Value...)
1241 if sla.ConfigurableValues == nil {
1242 sla.ConfigurableValues = make(configurableStringLists)
1243 }
1244 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001245 return sla
1246}
1247
1248func (sla *StringListAttribute) Clone() *StringListAttribute {
1249 result := &StringListAttribute{}
1250 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001251}
1252
1253// SetSelectValue set a value for a bazel select for the given axis, config and value.
1254func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1255 axis.validateConfig(config)
1256 switch axis.configurationType {
1257 case noConfig:
1258 sla.Value = list
Wei Li81852ca2022-07-27 00:22:06 -07001259 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001260 if sla.ConfigurableValues == nil {
1261 sla.ConfigurableValues = make(configurableStringLists)
1262 }
1263 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1264 default:
1265 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1266 }
1267}
1268
1269// SelectValue gets a value for a bazel select for the given axis and config.
1270func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1271 axis.validateConfig(config)
1272 switch axis.configurationType {
1273 case noConfig:
1274 return sla.Value
Wei Li81852ca2022-07-27 00:22:06 -07001275 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001276 return sla.ConfigurableValues[axis][config]
1277 default:
1278 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1279 }
1280}
1281
1282// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1283func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1284 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
1285 for k := range sla.ConfigurableValues {
1286 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001287 }
1288
Liz Kammer9abd62d2021-05-21 08:37:59 -04001289 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1290 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +00001291}
1292
Liz Kammer5fad5012021-09-09 14:08:21 -04001293// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1294// configuration-specific values. For example, if we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001295//
1296// ["a", "b", "c"] + select({
1297// "//condition:one": ["a", "d"],
1298// "//conditions:default": [],
1299// })
1300//
Liz Kammer5fad5012021-09-09 14:08:21 -04001301// after this function, we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001302//
1303// ["a", "b", "c"] + select({
1304// "//condition:one": ["d"],
1305// "//conditions:default": [],
1306// })
Liz Kammer5fad5012021-09-09 14:08:21 -04001307func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1308 base := sla.Value
1309 for axis, configToList := range sla.ConfigurableValues {
1310 for config, list := range configToList {
1311 remaining := SubtractStrings(list, base)
1312 if len(remaining) == 0 {
1313 delete(sla.ConfigurableValues[axis], config)
1314 } else {
1315 sla.ConfigurableValues[axis][config] = remaining
1316 }
1317 }
1318 }
1319}
1320
Liz Kammera060c452021-03-24 10:14:47 -04001321// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1322// Starlark string.format compatible tag for productVariable.
1323func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
1324 ret := make([]string, 0, len(slice))
1325 changesMade := false
1326 for _, s := range slice {
1327 newS, changed := TryVariableSubstitution(s, productVariable)
1328 ret = append(ret, newS)
1329 changesMade = changesMade || changed
1330 }
1331 return ret, changesMade
1332}
1333
1334// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1335// string.format compatible tag for productVariable.
1336func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001337 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001338 return sub, s != sub
1339}