blob: 823cda8b2f00f36bb4a18de0765d9c388bcbc6aa [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
Spandan Das4238c652022-09-09 01:38:47 +000076// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
77// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
78func MakeLabelListFromTargetNames(targetNames []string) LabelList {
79 labels := []Label{}
80 for _, name := range targetNames {
81 label := Label{Label: ":" + name}
82 labels = append(labels, label)
83 }
84 return MakeLabelList(labels)
85}
86
Chris Parsons51f8c392021-08-03 21:01:05 -040087func (ll *LabelList) Equals(other LabelList) bool {
88 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
89 return false
90 }
91 for i, _ := range ll.Includes {
92 if ll.Includes[i] != other.Includes[i] {
93 return false
94 }
95 }
96 for i, _ := range ll.Excludes {
97 if ll.Excludes[i] != other.Excludes[i] {
98 return false
99 }
100 }
101 return true
102}
103
Liz Kammer9abd62d2021-05-21 08:37:59 -0400104func (ll *LabelList) IsNil() bool {
105 return ll.Includes == nil && ll.Excludes == nil
106}
107
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000108func (ll *LabelList) IsEmpty() bool {
109 return len(ll.Includes) == 0 && len(ll.Excludes) == 0
110}
111
Liz Kammer74deed42021-06-02 13:02:03 -0400112func (ll *LabelList) deepCopy() LabelList {
113 return LabelList{
114 Includes: ll.Includes[:],
115 Excludes: ll.Excludes[:],
116 }
117}
118
Jingwen Chen63930982021-03-24 10:04:33 -0400119// uniqueParentDirectories returns a list of the unique parent directories for
120// all files in ll.Includes.
121func (ll *LabelList) uniqueParentDirectories() []string {
122 dirMap := map[string]bool{}
123 for _, label := range ll.Includes {
124 dirMap[filepath.Dir(label.Label)] = true
125 }
126 dirs := []string{}
127 for dir := range dirMap {
128 dirs = append(dirs, dir)
129 }
130 return dirs
131}
132
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400133// Add inserts the label Label at the end of the LabelList.Includes.
Liz Kammer12615db2021-09-28 09:19:17 -0400134func (ll *LabelList) Add(label *Label) {
135 if label == nil {
136 return
137 }
138 ll.Includes = append(ll.Includes, *label)
139}
140
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400141// AddExclude inserts the label Label at the end of the LabelList.Excludes.
142func (ll *LabelList) AddExclude(label *Label) {
143 if label == nil {
144 return
145 }
146 ll.Excludes = append(ll.Excludes, *label)
147}
148
Liz Kammer356f7d42021-01-26 09:18:53 -0500149// Append appends the fields of other labelList to the corresponding fields of ll.
150func (ll *LabelList) Append(other LabelList) {
151 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
152 ll.Includes = append(ll.Includes, other.Includes...)
153 }
154 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
155 ll.Excludes = append(other.Excludes, other.Excludes...)
156 }
157}
Jingwen Chen5d864492021-02-24 07:20:12 -0500158
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400159// Partition splits a LabelList into two LabelLists depending on the return value
160// of the predicate.
161// This function preserves the Includes and Excludes, but it does not provide
162// that information to the partition function.
163func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
164 predicated := LabelList{}
165 unpredicated := LabelList{}
166 for _, include := range ll.Includes {
167 if predicate(include) {
168 predicated.Add(&include)
169 } else {
170 unpredicated.Add(&include)
171 }
172 }
173 for _, exclude := range ll.Excludes {
174 if predicate(exclude) {
175 predicated.AddExclude(&exclude)
176 } else {
177 unpredicated.AddExclude(&exclude)
178 }
179 }
180 return predicated, unpredicated
181}
182
Jingwen Chened9c17d2021-04-13 07:14:55 +0000183// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
184// the slice in a sorted order.
185func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000186 uniqueLabelsSet := make(map[Label]bool)
187 for _, l := range originalLabels {
188 uniqueLabelsSet[l] = true
189 }
190 var uniqueLabels []Label
191 for l, _ := range uniqueLabelsSet {
192 uniqueLabels = append(uniqueLabels, l)
193 }
194 sort.SliceStable(uniqueLabels, func(i, j int) bool {
195 return uniqueLabels[i].Label < uniqueLabels[j].Label
196 })
197 return uniqueLabels
198}
199
Liz Kammer9abd62d2021-05-21 08:37:59 -0400200func FirstUniqueBazelLabels(originalLabels []Label) []Label {
201 var labels []Label
202 found := make(map[Label]bool, len(originalLabels))
203 for _, l := range originalLabels {
204 if _, ok := found[l]; ok {
205 continue
206 }
207 labels = append(labels, l)
208 found[l] = true
209 }
210 return labels
211}
212
213func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
214 var uniqueLabelList LabelList
215 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
216 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
217 return uniqueLabelList
218}
219
220func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000221 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000222 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
223 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000224 return uniqueLabelList
225}
226
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000227// Subtract needle from haystack
228func SubtractStrings(haystack []string, needle []string) []string {
229 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400230 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000231 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400232 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000233 }
234
235 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400236 for _, s := range haystack {
237 if exclude := needleMap[s]; !exclude {
238 strings = append(strings, s)
239 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000240 }
241
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000242 return strings
243}
244
245// Subtract needle from haystack
246func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
247 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400248 needleMap := make(map[Label]bool)
249 for _, s := range needle {
250 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000251 }
252
253 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400254 for _, label := range haystack {
255 if exclude := needleMap[label]; !exclude {
256 labels = append(labels, label)
257 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000258 }
259
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000260 return labels
261}
262
Chris Parsons484e50a2021-05-13 15:13:04 -0400263// Appends two LabelLists, returning the combined list.
264func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
265 var result LabelList
266 result.Includes = append(a.Includes, b.Includes...)
267 result.Excludes = append(a.Excludes, b.Excludes...)
268 return result
269}
270
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000271// Subtract needle from haystack
272func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
273 var result LabelList
274 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
275 // NOTE: Excludes are intentionally not subtracted
276 result.Excludes = haystack.Excludes
277 return result
278}
279
Jingwen Chenc1c26502021-04-05 10:35:13 +0000280type Attribute interface {
281 HasConfigurableValues() bool
282}
283
Liz Kammer9abd62d2021-05-21 08:37:59 -0400284type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400285
Liz Kammer9abd62d2021-05-21 08:37:59 -0400286type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400287
Liz Kammer9abd62d2021-05-21 08:37:59 -0400288func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
289 if cl[axis] == nil {
290 cl[axis] = make(labelSelectValues)
291 }
292 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400293}
294
295// Represents an attribute whose value is a single label
296type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400297 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400298
Liz Kammer9abd62d2021-05-21 08:37:59 -0400299 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200300}
301
Chris Parsons58852a02021-12-09 18:10:18 -0500302func (la *LabelAttribute) axisTypes() map[configurationType]bool {
303 types := map[configurationType]bool{}
304 for k := range la.ConfigurableValues {
305 if len(la.ConfigurableValues[k]) > 0 {
306 types[k.configurationType] = true
307 }
308 }
309 return types
310}
311
312// Collapse reduces the configurable axes of the label attribute to a single axis.
313// This is necessary for final writing to bp2build, as a configurable label
314// attribute can only be comprised by a single select.
315func (la *LabelAttribute) Collapse() error {
316 axisTypes := la.axisTypes()
317 _, containsOs := axisTypes[os]
318 _, containsArch := axisTypes[arch]
319 _, containsOsArch := axisTypes[osArch]
320 _, containsProductVariables := axisTypes[productVariables]
321 if containsProductVariables {
322 if containsOs || containsArch || containsOsArch {
Alixbbfd5382022-06-09 18:52:05 +0000323 if containsArch {
324 allProductVariablesAreArchVariant := true
325 for k := range la.ConfigurableValues {
326 if k.configurationType == productVariables && k.outerAxisType != arch {
327 allProductVariablesAreArchVariant = false
328 }
329 }
330 if !allProductVariablesAreArchVariant {
331 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
332 }
333 } else {
334 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
335 }
Chris Parsons58852a02021-12-09 18:10:18 -0500336 }
337 }
338 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
339 // If a bool attribute has both os and arch configuration axes, the only
340 // way to successfully union their values is to increase the granularity
341 // of the configuration criteria to os_arch.
342 for osType, supportedArchs := range osToArchMap {
343 for _, supportedArch := range supportedArchs {
344 osArch := osArchString(osType, supportedArch)
345 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
346 // Do nothing, as the arch_os is explicitly defined already.
347 } else {
348 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
349 osVal := la.SelectValue(OsConfigurationAxis, osType)
350 if osVal != nil && archVal != nil {
351 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
352 // runs after os mutator.
353 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
354 } else if osVal != nil && archVal == nil {
355 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
356 } else if osVal == nil && archVal != nil {
357 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
358 }
359 }
360 }
361 }
362 // All os_arch values are now set. Clear os and arch axes.
363 delete(la.ConfigurableValues, ArchConfigurationAxis)
364 delete(la.ConfigurableValues, OsConfigurationAxis)
365 }
366 return nil
367}
368
Liz Kammer9abd62d2021-05-21 08:37:59 -0400369// HasConfigurableValues returns whether there are configurable values set for this label.
370func (la LabelAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500371 for _, selectValues := range la.ConfigurableValues {
372 if len(selectValues) > 0 {
373 return true
374 }
375 }
376 return false
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200377}
378
Liz Kammer9abd62d2021-05-21 08:37:59 -0400379// SetValue sets the base, non-configured value for the Label
380func (la *LabelAttribute) SetValue(value Label) {
381 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400382}
383
Liz Kammer9abd62d2021-05-21 08:37:59 -0400384// SetSelectValue set a value for a bazel select for the given axis, config and value.
385func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
386 axis.validateConfig(config)
387 switch axis.configurationType {
388 case noConfig:
389 la.Value = &value
Wei Li81852ca2022-07-27 00:22:06 -0700390 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400391 if la.ConfigurableValues == nil {
392 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400393 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400394 la.ConfigurableValues.setValueForAxis(axis, config, &value)
395 default:
396 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
397 }
398}
399
400// SelectValue gets a value for a bazel select for the given axis and config.
Chris Parsons58852a02021-12-09 18:10:18 -0500401func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400402 axis.validateConfig(config)
403 switch axis.configurationType {
404 case noConfig:
Chris Parsons58852a02021-12-09 18:10:18 -0500405 return la.Value
Wei Li81852ca2022-07-27 00:22:06 -0700406 case arch, os, osArch, productVariables, osAndInApex:
Chris Parsons58852a02021-12-09 18:10:18 -0500407 return la.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400408 default:
409 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
410 }
411}
412
413// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
414func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
415 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
416 for k := range la.ConfigurableValues {
417 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400418 }
419
Liz Kammer9abd62d2021-05-21 08:37:59 -0400420 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
421 return keys
422}
423
Sam Delmericoc0161432022-02-25 21:34:51 +0000424// MakeLabelAttribute turns a string into a LabelAttribute
425func MakeLabelAttribute(label string) *LabelAttribute {
426 return &LabelAttribute{
427 Value: &Label{
428 Label: label,
429 },
430 }
431}
432
Liz Kammerd366c902021-06-03 13:43:01 -0400433type configToBools map[string]bool
434
435func (ctb configToBools) setValue(config string, value *bool) {
436 if value == nil {
437 if _, ok := ctb[config]; ok {
438 delete(ctb, config)
439 }
440 return
441 }
442 ctb[config] = *value
443}
444
445type configurableBools map[ConfigurationAxis]configToBools
446
447func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
448 if cb[axis] == nil {
449 cb[axis] = make(configToBools)
450 }
451 cb[axis].setValue(config, value)
452}
453
454// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
455type BoolAttribute struct {
456 Value *bool
457
458 ConfigurableValues configurableBools
459}
460
461// HasConfigurableValues returns whether there are configurable values for this attribute.
462func (ba BoolAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500463 for _, cfgToBools := range ba.ConfigurableValues {
464 if len(cfgToBools) > 0 {
465 return true
466 }
467 }
468 return false
Liz Kammerd366c902021-06-03 13:43:01 -0400469}
470
Liz Kammerdfeb1202022-05-13 17:20:20 -0400471// SetValue sets value for the no config axis
472func (ba *BoolAttribute) SetValue(value *bool) {
473 ba.SetSelectValue(NoConfigAxis, "", value)
474}
475
Liz Kammerd366c902021-06-03 13:43:01 -0400476// SetSelectValue sets value for the given axis/config.
477func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
478 axis.validateConfig(config)
479 switch axis.configurationType {
480 case noConfig:
481 ba.Value = value
Wei Li81852ca2022-07-27 00:22:06 -0700482 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammerd366c902021-06-03 13:43:01 -0400483 if ba.ConfigurableValues == nil {
484 ba.ConfigurableValues = make(configurableBools)
485 }
486 ba.ConfigurableValues.setValueForAxis(axis, config, value)
487 default:
488 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
489 }
490}
491
Chris Parsons58852a02021-12-09 18:10:18 -0500492// ToLabelListAttribute creates and returns a LabelListAttribute from this
493// bool attribute, where each bool in this attribute corresponds to a
494// label list value in the resultant attribute.
495func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
496 getLabelList := func(boolPtr *bool) LabelList {
497 if boolPtr == nil {
498 return LabelList{nil, nil}
499 } else if *boolPtr {
500 return trueVal
501 } else {
502 return falseVal
503 }
504 }
505
506 mainVal := getLabelList(ba.Value)
507 if !ba.HasConfigurableValues() {
508 return MakeLabelListAttribute(mainVal), nil
509 }
510
511 result := LabelListAttribute{}
512 if err := ba.Collapse(); err != nil {
513 return result, err
514 }
515
516 for axis, configToBools := range ba.ConfigurableValues {
517 if len(configToBools) < 1 {
518 continue
519 }
520 for config, boolPtr := range configToBools {
521 val := getLabelList(&boolPtr)
522 if !val.Equals(mainVal) {
523 result.SetSelectValue(axis, config, val)
524 }
525 }
526 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
527 }
528
529 return result, nil
530}
531
532// Collapse reduces the configurable axes of the boolean attribute to a single axis.
533// This is necessary for final writing to bp2build, as a configurable boolean
534// attribute can only be comprised by a single select.
535func (ba *BoolAttribute) Collapse() error {
536 axisTypes := ba.axisTypes()
537 _, containsOs := axisTypes[os]
538 _, containsArch := axisTypes[arch]
539 _, containsOsArch := axisTypes[osArch]
540 _, containsProductVariables := axisTypes[productVariables]
541 if containsProductVariables {
542 if containsOs || containsArch || containsOsArch {
543 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
544 }
545 }
546 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
547 // If a bool attribute has both os and arch configuration axes, the only
548 // way to successfully union their values is to increase the granularity
549 // of the configuration criteria to os_arch.
550 for osType, supportedArchs := range osToArchMap {
551 for _, supportedArch := range supportedArchs {
552 osArch := osArchString(osType, supportedArch)
553 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
554 // Do nothing, as the arch_os is explicitly defined already.
555 } else {
556 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
557 osVal := ba.SelectValue(OsConfigurationAxis, osType)
558 if osVal != nil && archVal != nil {
559 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
560 // runs after os mutator.
561 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
562 } else if osVal != nil && archVal == nil {
563 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
564 } else if osVal == nil && archVal != nil {
565 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
566 }
567 }
568 }
569 }
570 // All os_arch values are now set. Clear os and arch axes.
571 delete(ba.ConfigurableValues, ArchConfigurationAxis)
572 delete(ba.ConfigurableValues, OsConfigurationAxis)
573 // Verify post-condition; this should never fail, provided no additional
574 // axes are introduced.
575 if len(ba.ConfigurableValues) > 1 {
Liz Kammer07e106f2022-01-13 17:00:10 -0500576 panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
Chris Parsons58852a02021-12-09 18:10:18 -0500577 }
578 }
579 return nil
580}
581
582func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
583 types := map[configurationType]bool{}
584 for k := range ba.ConfigurableValues {
585 if len(ba.ConfigurableValues[k]) > 0 {
586 types[k.configurationType] = true
587 }
588 }
589 return types
590}
591
Liz Kammerd366c902021-06-03 13:43:01 -0400592// SelectValue gets the value for the given axis/config.
593func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
594 axis.validateConfig(config)
595 switch axis.configurationType {
596 case noConfig:
597 return ba.Value
Wei Li81852ca2022-07-27 00:22:06 -0700598 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammerd366c902021-06-03 13:43:01 -0400599 if v, ok := ba.ConfigurableValues[axis][config]; ok {
600 return &v
601 } else {
602 return nil
603 }
604 default:
605 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
606 }
607}
608
609// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
610func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
611 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
612 for k := range ba.ConfigurableValues {
613 keys = append(keys, k)
614 }
615
616 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
617 return keys
618}
619
Liz Kammer9abd62d2021-05-21 08:37:59 -0400620// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
621type labelListSelectValues map[string]LabelList
622
Liz Kammer12615db2021-09-28 09:19:17 -0400623func (ll labelListSelectValues) addSelects(label labelSelectValues) {
624 for k, v := range label {
625 if label == nil {
626 continue
627 }
628 l := ll[k]
629 (&l).Add(v)
630 ll[k] = l
631 }
632}
633
Chris Parsons77acf2e2021-12-03 17:27:16 -0500634func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400635 for k, v := range other {
636 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500637 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
638 l.Includes = []Label{}
639 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400640 (&l).Append(v)
641 ll[k] = l
642 }
643}
644
645// HasConfigurableValues returns whether there are configurable values within this set of selects.
646func (ll labelListSelectValues) HasConfigurableValues() bool {
647 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400648 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400649 return true
650 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400651 }
652 return false
653}
654
Jingwen Chen07027912021-03-15 06:02:43 -0400655// LabelListAttribute is used to represent a list of Bazel labels as an
656// attribute.
657type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400658 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400659 Value LabelList
660
Liz Kammer9abd62d2021-05-21 08:37:59 -0400661 // The configured attribute label list Values. Optional
662 // a map of independent configurability axes
663 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400664
665 // If true, differentiate between "nil" and "empty" list. nil means that
666 // this attribute should not be specified at all, and "empty" means that
667 // the attribute should be explicitly specified as an empty list.
668 // This mode facilitates use of attribute defaults: an empty list should
669 // override the default.
670 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000671
672 // If true, signal the intent to the code generator to emit all select keys,
673 // even if the Includes list for that key is empty. This mode facilitates
674 // specific select statements where an empty list for a non-default select
675 // key has a meaning.
676 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400677}
Jingwen Chen91220d72021-03-24 02:18:33 -0400678
Liz Kammer9abd62d2021-05-21 08:37:59 -0400679type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
680
681func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
682 if list.IsNil() {
683 if _, ok := cll[axis][config]; ok {
684 delete(cll[axis], config)
685 }
686 return
687 }
688 if cll[axis] == nil {
689 cll[axis] = make(labelListSelectValues)
690 }
691
692 cll[axis][config] = list
693}
694
Chris Parsons77acf2e2021-12-03 17:27:16 -0500695func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400696 for axis, otherSelects := range other {
697 selects := cll[axis]
698 if selects == nil {
699 selects = make(labelListSelectValues, len(otherSelects))
700 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500701 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400702 cll[axis] = selects
703 }
Jingwen Chen07027912021-03-15 06:02:43 -0400704}
705
Chris Parsons77acf2e2021-12-03 17:27:16 -0500706func (lla *LabelListAttribute) Clone() *LabelListAttribute {
707 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
708 return result.Append(*lla)
709}
710
Jingwen Chen07027912021-03-15 06:02:43 -0400711// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
712func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400713 return LabelListAttribute{
714 Value: value,
715 ConfigurableValues: make(configurableLabelLists),
716 }
717}
718
Cole Faust53b62092022-05-12 15:37:02 -0700719// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
720func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
721 return MakeLabelListAttribute(MakeLabelList([]Label{value}))
722}
723
Liz Kammer9abd62d2021-05-21 08:37:59 -0400724func (lla *LabelListAttribute) SetValue(list LabelList) {
725 lla.SetSelectValue(NoConfigAxis, "", list)
726}
727
728// SetSelectValue set a value for a bazel select for the given axis, config and value.
729func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
730 axis.validateConfig(config)
731 switch axis.configurationType {
732 case noConfig:
733 lla.Value = list
Vinh Tran85fb07c2022-09-16 16:17:48 -0400734 case arch, os, osArch, productVariables, osAndInApex, inApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400735 if lla.ConfigurableValues == nil {
736 lla.ConfigurableValues = make(configurableLabelLists)
737 }
738 lla.ConfigurableValues.setValueForAxis(axis, config, list)
739 default:
740 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
741 }
742}
743
744// SelectValue gets a value for a bazel select for the given axis and config.
745func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
746 axis.validateConfig(config)
747 switch axis.configurationType {
748 case noConfig:
749 return lla.Value
Vinh Tran85fb07c2022-09-16 16:17:48 -0400750 case arch, os, osArch, productVariables, osAndInApex, inApex:
Cole Faustc843b992022-08-02 18:06:50 -0700751 return lla.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400752 default:
753 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
754 }
755}
756
757// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
758func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
759 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
760 for k := range lla.ConfigurableValues {
761 keys = append(keys, k)
762 }
763
764 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
765 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400766}
767
Jingwen Chened9c17d2021-04-13 07:14:55 +0000768// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500769// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
770func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
771 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
772 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400773 lla.Value.Includes = []Label{}
774 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400775 lla.Value.Append(other.Value)
776 if lla.ConfigurableValues == nil {
777 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400778 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500779 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
780 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400781}
782
Liz Kammer12615db2021-09-28 09:19:17 -0400783// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
784// LabelList within the LabelListAttribute
785func (lla *LabelListAttribute) Add(label *LabelAttribute) {
786 if label == nil {
787 return
788 }
789
790 lla.Value.Add(label.Value)
791 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
792 lla.ConfigurableValues = make(configurableLabelLists)
793 }
794 for axis, _ := range label.ConfigurableValues {
795 if _, exists := lla.ConfigurableValues[axis]; !exists {
796 lla.ConfigurableValues[axis] = make(labelListSelectValues)
797 }
798 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
799 }
800}
801
Liz Kammer9abd62d2021-05-21 08:37:59 -0400802// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
803func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500804 for _, selectValues := range lla.ConfigurableValues {
805 if len(selectValues) > 0 {
806 return true
807 }
808 }
809 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400810}
811
Chris Parsons69fa9f92021-07-13 11:47:44 -0400812// IsEmpty returns true if the attribute has no values under any configuration.
813func (lla LabelListAttribute) IsEmpty() bool {
814 if len(lla.Value.Includes) > 0 {
815 return false
816 }
817 for axis, _ := range lla.ConfigurableValues {
818 if lla.ConfigurableValues[axis].HasConfigurableValues() {
819 return false
820 }
821 }
822 return true
823}
824
Liz Kammer54309532021-12-14 12:21:22 -0500825// IsNil returns true if the attribute has not been set for any configuration.
826func (lla LabelListAttribute) IsNil() bool {
827 if lla.Value.Includes != nil {
828 return false
829 }
830 return !lla.HasConfigurableValues()
831}
832
833// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
834// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
835// be removed, e.g. if they could cause duplicate element failures.
836func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
837 val := lla.SelectValue(axis, config)
838 newList := SubtractBazelLabelList(val, labelList)
839 newList.Excludes = append(newList.Excludes, labelList.Includes...)
840 lla.SetSelectValue(axis, config, newList)
841}
842
Liz Kammer74deed42021-06-02 13:02:03 -0400843// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
844// the base value and included in default values as appropriate.
845func (lla *LabelListAttribute) ResolveExcludes() {
846 for axis, configToLabels := range lla.ConfigurableValues {
847 baseLabels := lla.Value.deepCopy()
848 for config, val := range configToLabels {
849 // Exclude config-specific excludes from base value
850 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
851
852 // add base values to config specific to add labels excluded by others in this axis
853 // then remove all config-specific excludes
854 allLabels := baseLabels.deepCopy()
855 allLabels.Append(val)
856 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
857 }
858
859 // After going through all configs, delete the duplicates in the config
860 // values that are already in the base Value.
861 for config, val := range configToLabels {
862 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
863 }
864
Jingwen Chen9af49a42021-11-02 10:27:17 +0000865 // Now that the Value list is finalized for this axis, compare it with
866 // the original list, and union the difference with the default
867 // condition for the axis.
868 difference := SubtractBazelLabelList(baseLabels, lla.Value)
869 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
870 existingDefaults.Append(difference)
871 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400872
873 // if everything ends up without includes, just delete the axis
874 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
875 delete(lla.ConfigurableValues, axis)
876 }
877 }
878}
879
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400880// Partition splits a LabelListAttribute into two LabelListAttributes depending
881// on the return value of the predicate.
882// This function preserves the Includes and Excludes, but it does not provide
883// that information to the partition function.
884func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
885 predicated := LabelListAttribute{}
886 unpredicated := LabelListAttribute{}
887
888 valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
889 predicated.SetValue(valuePartitionTrue)
890 unpredicated.SetValue(valuePartitionFalse)
891
892 for axis, selectValueLabelLists := range lla.ConfigurableValues {
893 for config, labelList := range selectValueLabelLists {
894 configPredicated, configUnpredicated := labelList.Partition(predicate)
895 predicated.SetSelectValue(axis, config, configPredicated)
896 unpredicated.SetSelectValue(axis, config, configUnpredicated)
897 }
898 }
899
900 return predicated, unpredicated
901}
902
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400903// OtherModuleContext is a limited context that has methods with information about other modules.
904type OtherModuleContext interface {
905 ModuleFromName(name string) (blueprint.Module, bool)
906 OtherModuleType(m blueprint.Module) string
907 OtherModuleName(m blueprint.Module) string
908 OtherModuleDir(m blueprint.Module) string
909 ModuleErrorf(fmt string, args ...interface{})
910}
911
912// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
913// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400914type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400915
916// LabelPartition contains descriptions of a partition for labels
917type LabelPartition struct {
918 // Extensions to include in this partition
919 Extensions []string
920 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
921 // the mapped label in the partition
922 LabelMapper LabelMapper
923 // Whether to store files not included in any other partition in a group of LabelPartitions
924 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
925 Keep_remainder bool
926}
927
928// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
929// partition
930type LabelPartitions map[string]LabelPartition
931
932// filter returns a pointer to a label if the label should be included in the partition or nil if
933// not.
934func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
935 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400936 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400937 return &Label{newLabel, label.OriginalModuleName}
938 }
939 }
940 for _, ext := range lf.Extensions {
941 if strings.HasSuffix(label.Label, ext) {
942 return &label
943 }
944 }
945
946 return nil
947}
948
949// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
950type PartitionToLabelListAttribute map[string]LabelListAttribute
951
952type partitionToLabelList map[string]*LabelList
953
954func (p partitionToLabelList) appendIncludes(partition string, label Label) {
955 if _, ok := p[partition]; !ok {
956 p[partition] = &LabelList{}
957 }
958 p[partition].Includes = append(p[partition].Includes, label)
959}
960
961func (p partitionToLabelList) excludes(partition string, excludes []Label) {
962 if _, ok := p[partition]; !ok {
963 p[partition] = &LabelList{}
964 }
965 p[partition].Excludes = excludes
966}
967
968// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
969func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
970 ret := PartitionToLabelListAttribute{}
971 var partitionNames []string
972 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
973 var remainderPartition *string
974 for p, f := range partitions {
975 partitionNames = append(partitionNames, p)
976 if f.Keep_remainder {
977 if remainderPartition != nil {
978 panic("only one partition can store the remainder")
979 }
980 // If we take the address of p in a loop, we'll end up with the last value of p in
981 // remainderPartition, we want the requested partition
982 capturePartition := p
983 remainderPartition = &capturePartition
984 }
985 }
986
987 partitionLabelList := func(axis ConfigurationAxis, config string) {
988 value := lla.SelectValue(axis, config)
989 partitionToLabels := partitionToLabelList{}
990 for _, item := range value.Includes {
991 wasFiltered := false
992 var inPartition *string
993 for partition, f := range partitions {
994 filtered := f.filter(ctx, item)
995 if filtered == nil {
996 // did not match this filter, keep looking
997 continue
998 }
999 wasFiltered = true
1000 partitionToLabels.appendIncludes(partition, *filtered)
1001 // don't need to check other partitions if this filter used the item,
1002 // continue checking if mapped to another name
1003 if *filtered == item {
1004 if inPartition != nil {
1005 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
1006 }
1007 capturePartition := partition
1008 inPartition = &capturePartition
1009 }
1010 }
1011
1012 // if not specified in a partition, add to remainder partition if one exists
1013 if !wasFiltered && remainderPartition != nil {
1014 partitionToLabels.appendIncludes(*remainderPartition, item)
1015 }
1016 }
1017
1018 // ensure empty lists are maintained
1019 if value.Excludes != nil {
1020 for _, partition := range partitionNames {
1021 partitionToLabels.excludes(partition, value.Excludes)
1022 }
1023 }
1024
1025 for partition, list := range partitionToLabels {
1026 val := ret[partition]
1027 (&val).SetSelectValue(axis, config, *list)
1028 ret[partition] = val
1029 }
1030 }
1031
1032 partitionLabelList(NoConfigAxis, "")
1033 for axis, configToList := range lla.ConfigurableValues {
1034 for config, _ := range configToList {
1035 partitionLabelList(axis, config)
1036 }
1037 }
1038 return ret
1039}
1040
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001041// StringAttribute corresponds to the string Bazel attribute type with
1042// support for additional metadata, like configurations.
1043type StringAttribute struct {
1044 // The base value of the string attribute.
1045 Value *string
1046
1047 // The configured attribute label list Values. Optional
1048 // a map of independent configurability axes
1049 ConfigurableValues configurableStrings
1050}
1051
1052type configurableStrings map[ConfigurationAxis]stringSelectValues
1053
1054func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
1055 if cs[axis] == nil {
1056 cs[axis] = make(stringSelectValues)
1057 }
1058 var v = ""
1059 if str != nil {
1060 v = *str
1061 }
1062 cs[axis][config] = v
1063}
1064
1065type stringSelectValues map[string]string
1066
1067// HasConfigurableValues returns true if the attribute contains axis-specific string values.
1068func (sa StringAttribute) HasConfigurableValues() bool {
1069 for _, selectValues := range sa.ConfigurableValues {
1070 if len(selectValues) > 0 {
1071 return true
1072 }
1073 }
1074 return false
1075}
1076
1077// SetSelectValue set a value for a bazel select for the given axis, config and value.
1078func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
1079 axis.validateConfig(config)
1080 switch axis.configurationType {
1081 case noConfig:
1082 sa.Value = str
1083 case arch, os, osArch, productVariables:
1084 if sa.ConfigurableValues == nil {
1085 sa.ConfigurableValues = make(configurableStrings)
1086 }
1087 sa.ConfigurableValues.setValueForAxis(axis, config, str)
1088 default:
1089 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1090 }
1091}
1092
1093// SelectValue gets a value for a bazel select for the given axis and config.
1094func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
1095 axis.validateConfig(config)
1096 switch axis.configurationType {
1097 case noConfig:
1098 return sa.Value
1099 case arch, os, osArch, productVariables:
1100 if v, ok := sa.ConfigurableValues[axis][config]; ok {
1101 return &v
1102 } else {
1103 return nil
1104 }
1105 default:
1106 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1107 }
1108}
1109
1110// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1111func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1112 keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
1113 for k := range sa.ConfigurableValues {
1114 keys = append(keys, k)
1115 }
1116
1117 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1118 return keys
1119}
1120
1121// Collapse reduces the configurable axes of the string attribute to a single axis.
1122// This is necessary for final writing to bp2build, as a configurable string
1123// attribute can only be comprised by a single select.
1124func (sa *StringAttribute) Collapse() error {
1125 axisTypes := sa.axisTypes()
1126 _, containsOs := axisTypes[os]
1127 _, containsArch := axisTypes[arch]
1128 _, containsOsArch := axisTypes[osArch]
1129 _, containsProductVariables := axisTypes[productVariables]
1130 if containsProductVariables {
1131 if containsOs || containsArch || containsOsArch {
1132 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
1133 }
1134 }
1135 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
1136 // If a bool attribute has both os and arch configuration axes, the only
1137 // way to successfully union their values is to increase the granularity
1138 // of the configuration criteria to os_arch.
1139 for osType, supportedArchs := range osToArchMap {
1140 for _, supportedArch := range supportedArchs {
1141 osArch := osArchString(osType, supportedArch)
1142 if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
1143 // Do nothing, as the arch_os is explicitly defined already.
1144 } else {
1145 archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
1146 osVal := sa.SelectValue(OsConfigurationAxis, osType)
1147 if osVal != nil && archVal != nil {
1148 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
1149 // runs after os mutator.
1150 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1151 } else if osVal != nil && archVal == nil {
1152 sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
1153 } else if osVal == nil && archVal != nil {
1154 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1155 }
1156 }
1157 }
1158 }
1159 // All os_arch values are now set. Clear os and arch axes.
1160 delete(sa.ConfigurableValues, ArchConfigurationAxis)
1161 delete(sa.ConfigurableValues, OsConfigurationAxis)
1162 // Verify post-condition; this should never fail, provided no additional
1163 // axes are introduced.
1164 if len(sa.ConfigurableValues) > 1 {
1165 panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
1166 }
1167 }
1168 return nil
1169}
1170
1171func (sa *StringAttribute) axisTypes() map[configurationType]bool {
1172 types := map[configurationType]bool{}
1173 for k := range sa.ConfigurableValues {
1174 if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
1175 types[k.configurationType] = true
1176 }
1177 }
1178 return types
1179}
1180
Jingwen Chen5d864492021-02-24 07:20:12 -05001181// StringListAttribute corresponds to the string_list Bazel attribute type with
1182// support for additional metadata, like configurations.
1183type StringListAttribute struct {
1184 // The base value of the string list attribute.
1185 Value []string
1186
Liz Kammer9abd62d2021-05-21 08:37:59 -04001187 // The configured attribute label list Values. Optional
1188 // a map of independent configurability axes
1189 ConfigurableValues configurableStringLists
1190}
Jingwen Chenc1c26502021-04-05 10:35:13 +00001191
Spandan Das4238c652022-09-09 01:38:47 +00001192// IsEmpty returns true if the attribute has no values under any configuration.
1193func (sla StringListAttribute) IsEmpty() bool {
1194 return len(sla.Value) == 0 && !sla.HasConfigurableValues()
1195}
1196
Liz Kammer9abd62d2021-05-21 08:37:59 -04001197type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -04001198
Liz Kammer9abd62d2021-05-21 08:37:59 -04001199func (csl configurableStringLists) Append(other configurableStringLists) {
1200 for axis, otherSelects := range other {
1201 selects := csl[axis]
1202 if selects == nil {
1203 selects = make(stringListSelectValues, len(otherSelects))
1204 }
1205 selects.appendSelects(otherSelects)
1206 csl[axis] = selects
1207 }
1208}
1209
1210func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
1211 if csl[axis] == nil {
1212 csl[axis] = make(stringListSelectValues)
1213 }
1214 csl[axis][config] = list
1215}
1216
1217type stringListSelectValues map[string][]string
1218
1219func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
1220 for k, v := range other {
1221 sl[k] = append(sl[k], v...)
1222 }
1223}
1224
1225func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
1226 for _, val := range sl {
1227 if len(val) > 0 {
1228 return true
1229 }
1230 }
1231 return false
Jingwen Chen5d864492021-02-24 07:20:12 -05001232}
1233
Rupert Shuttleworthb8151682021-04-06 20:06:21 +00001234// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1235func MakeStringListAttribute(value []string) StringListAttribute {
1236 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -04001237 return StringListAttribute{
1238 Value: value,
1239 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -04001240 }
1241}
1242
Liz Kammer9abd62d2021-05-21 08:37:59 -04001243// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1244func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -05001245 for _, selectValues := range sla.ConfigurableValues {
1246 if len(selectValues) > 0 {
1247 return true
1248 }
1249 }
1250 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -04001251}
1252
Jingwen Chened9c17d2021-04-13 07:14:55 +00001253// Append appends all values, including os and arch specific ones, from another
1254// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001255func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001256 sla.Value = append(sla.Value, other.Value...)
1257 if sla.ConfigurableValues == nil {
1258 sla.ConfigurableValues = make(configurableStringLists)
1259 }
1260 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001261 return sla
1262}
1263
1264func (sla *StringListAttribute) Clone() *StringListAttribute {
1265 result := &StringListAttribute{}
1266 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001267}
1268
1269// SetSelectValue set a value for a bazel select for the given axis, config and value.
1270func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1271 axis.validateConfig(config)
1272 switch axis.configurationType {
1273 case noConfig:
1274 sla.Value = list
Wei Li81852ca2022-07-27 00:22:06 -07001275 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001276 if sla.ConfigurableValues == nil {
1277 sla.ConfigurableValues = make(configurableStringLists)
1278 }
1279 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1280 default:
1281 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1282 }
1283}
1284
1285// SelectValue gets a value for a bazel select for the given axis and config.
1286func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1287 axis.validateConfig(config)
1288 switch axis.configurationType {
1289 case noConfig:
1290 return sla.Value
Wei Li81852ca2022-07-27 00:22:06 -07001291 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001292 return sla.ConfigurableValues[axis][config]
1293 default:
1294 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1295 }
1296}
1297
1298// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1299func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1300 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
1301 for k := range sla.ConfigurableValues {
1302 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001303 }
1304
Liz Kammer9abd62d2021-05-21 08:37:59 -04001305 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1306 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +00001307}
1308
Liz Kammer5fad5012021-09-09 14:08:21 -04001309// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1310// configuration-specific values. For example, if we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001311//
1312// ["a", "b", "c"] + select({
1313// "//condition:one": ["a", "d"],
1314// "//conditions:default": [],
1315// })
1316//
Liz Kammer5fad5012021-09-09 14:08:21 -04001317// after this function, we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001318//
1319// ["a", "b", "c"] + select({
1320// "//condition:one": ["d"],
1321// "//conditions:default": [],
1322// })
Liz Kammer5fad5012021-09-09 14:08:21 -04001323func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1324 base := sla.Value
1325 for axis, configToList := range sla.ConfigurableValues {
1326 for config, list := range configToList {
1327 remaining := SubtractStrings(list, base)
1328 if len(remaining) == 0 {
1329 delete(sla.ConfigurableValues[axis], config)
1330 } else {
1331 sla.ConfigurableValues[axis][config] = remaining
1332 }
1333 }
1334 }
1335}
1336
Liz Kammera060c452021-03-24 10:14:47 -04001337// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1338// Starlark string.format compatible tag for productVariable.
1339func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
1340 ret := make([]string, 0, len(slice))
1341 changesMade := false
1342 for _, s := range slice {
1343 newS, changed := TryVariableSubstitution(s, productVariable)
1344 ret = append(ret, newS)
1345 changesMade = changesMade || changed
1346 }
1347 return ret, changesMade
1348}
1349
1350// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1351// string.format compatible tag for productVariable.
1352func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001353 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001354 return sub, s != sub
1355}