blob: eb20eec4ab6c4964a49f349faf9831be8b880d1e [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 Parsons7b3289b2023-01-26 17:30:44 -050076func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
77 keys := make([]ConfigurationAxis, 0, len(m))
78 for k := range m {
79 keys = append(keys, k)
80 }
81
82 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
83 return keys
84}
85
Spandan Das4238c652022-09-09 01:38:47 +000086// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
87// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
88func MakeLabelListFromTargetNames(targetNames []string) LabelList {
89 labels := []Label{}
90 for _, name := range targetNames {
91 label := Label{Label: ":" + name}
92 labels = append(labels, label)
93 }
94 return MakeLabelList(labels)
95}
96
Chris Parsons51f8c392021-08-03 21:01:05 -040097func (ll *LabelList) Equals(other LabelList) bool {
98 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
99 return false
100 }
101 for i, _ := range ll.Includes {
102 if ll.Includes[i] != other.Includes[i] {
103 return false
104 }
105 }
106 for i, _ := range ll.Excludes {
107 if ll.Excludes[i] != other.Excludes[i] {
108 return false
109 }
110 }
111 return true
112}
113
Liz Kammer9abd62d2021-05-21 08:37:59 -0400114func (ll *LabelList) IsNil() bool {
115 return ll.Includes == nil && ll.Excludes == nil
116}
117
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000118func (ll *LabelList) IsEmpty() bool {
119 return len(ll.Includes) == 0 && len(ll.Excludes) == 0
120}
121
Liz Kammer74deed42021-06-02 13:02:03 -0400122func (ll *LabelList) deepCopy() LabelList {
123 return LabelList{
124 Includes: ll.Includes[:],
125 Excludes: ll.Excludes[:],
126 }
127}
128
Jingwen Chen63930982021-03-24 10:04:33 -0400129// uniqueParentDirectories returns a list of the unique parent directories for
130// all files in ll.Includes.
131func (ll *LabelList) uniqueParentDirectories() []string {
132 dirMap := map[string]bool{}
133 for _, label := range ll.Includes {
134 dirMap[filepath.Dir(label.Label)] = true
135 }
136 dirs := []string{}
137 for dir := range dirMap {
138 dirs = append(dirs, dir)
139 }
140 return dirs
141}
142
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400143// Add inserts the label Label at the end of the LabelList.Includes.
Liz Kammer12615db2021-09-28 09:19:17 -0400144func (ll *LabelList) Add(label *Label) {
145 if label == nil {
146 return
147 }
148 ll.Includes = append(ll.Includes, *label)
149}
150
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400151// AddExclude inserts the label Label at the end of the LabelList.Excludes.
152func (ll *LabelList) AddExclude(label *Label) {
153 if label == nil {
154 return
155 }
156 ll.Excludes = append(ll.Excludes, *label)
157}
158
Liz Kammer356f7d42021-01-26 09:18:53 -0500159// Append appends the fields of other labelList to the corresponding fields of ll.
160func (ll *LabelList) Append(other LabelList) {
161 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
162 ll.Includes = append(ll.Includes, other.Includes...)
163 }
164 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
165 ll.Excludes = append(other.Excludes, other.Excludes...)
166 }
167}
Jingwen Chen5d864492021-02-24 07:20:12 -0500168
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400169// Partition splits a LabelList into two LabelLists depending on the return value
170// of the predicate.
171// This function preserves the Includes and Excludes, but it does not provide
172// that information to the partition function.
173func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
174 predicated := LabelList{}
175 unpredicated := LabelList{}
176 for _, include := range ll.Includes {
177 if predicate(include) {
178 predicated.Add(&include)
179 } else {
180 unpredicated.Add(&include)
181 }
182 }
183 for _, exclude := range ll.Excludes {
184 if predicate(exclude) {
185 predicated.AddExclude(&exclude)
186 } else {
187 unpredicated.AddExclude(&exclude)
188 }
189 }
190 return predicated, unpredicated
191}
192
Jingwen Chened9c17d2021-04-13 07:14:55 +0000193// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
194// the slice in a sorted order.
195func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000196 uniqueLabelsSet := make(map[Label]bool)
197 for _, l := range originalLabels {
198 uniqueLabelsSet[l] = true
199 }
200 var uniqueLabels []Label
201 for l, _ := range uniqueLabelsSet {
202 uniqueLabels = append(uniqueLabels, l)
203 }
204 sort.SliceStable(uniqueLabels, func(i, j int) bool {
205 return uniqueLabels[i].Label < uniqueLabels[j].Label
206 })
207 return uniqueLabels
208}
209
Liz Kammer9abd62d2021-05-21 08:37:59 -0400210func FirstUniqueBazelLabels(originalLabels []Label) []Label {
211 var labels []Label
212 found := make(map[Label]bool, len(originalLabels))
213 for _, l := range originalLabels {
214 if _, ok := found[l]; ok {
215 continue
216 }
217 labels = append(labels, l)
218 found[l] = true
219 }
220 return labels
221}
222
223func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
224 var uniqueLabelList LabelList
225 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
226 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
227 return uniqueLabelList
228}
229
230func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000231 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000232 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
233 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000234 return uniqueLabelList
235}
236
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000237// Subtract needle from haystack
238func SubtractStrings(haystack []string, needle []string) []string {
239 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400240 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000241 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400242 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000243 }
244
245 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400246 for _, s := range haystack {
247 if exclude := needleMap[s]; !exclude {
248 strings = append(strings, s)
249 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000250 }
251
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000252 return strings
253}
254
255// Subtract needle from haystack
256func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
257 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400258 needleMap := make(map[Label]bool)
259 for _, s := range needle {
260 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000261 }
262
263 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400264 for _, label := range haystack {
265 if exclude := needleMap[label]; !exclude {
266 labels = append(labels, label)
267 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000268 }
269
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000270 return labels
271}
272
Chris Parsons484e50a2021-05-13 15:13:04 -0400273// Appends two LabelLists, returning the combined list.
274func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
275 var result LabelList
276 result.Includes = append(a.Includes, b.Includes...)
277 result.Excludes = append(a.Excludes, b.Excludes...)
278 return result
279}
280
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000281// Subtract needle from haystack
282func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
283 var result LabelList
284 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
285 // NOTE: Excludes are intentionally not subtracted
286 result.Excludes = haystack.Excludes
287 return result
288}
289
Jingwen Chenc1c26502021-04-05 10:35:13 +0000290type Attribute interface {
291 HasConfigurableValues() bool
292}
293
Liz Kammer9abd62d2021-05-21 08:37:59 -0400294type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400295
Liz Kammer9abd62d2021-05-21 08:37:59 -0400296type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400297
Liz Kammer9abd62d2021-05-21 08:37:59 -0400298func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
299 if cl[axis] == nil {
300 cl[axis] = make(labelSelectValues)
301 }
302 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400303}
304
305// Represents an attribute whose value is a single label
306type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400307 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400308
Liz Kammer9abd62d2021-05-21 08:37:59 -0400309 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200310}
311
Chris Parsons58852a02021-12-09 18:10:18 -0500312func (la *LabelAttribute) axisTypes() map[configurationType]bool {
313 types := map[configurationType]bool{}
314 for k := range la.ConfigurableValues {
315 if len(la.ConfigurableValues[k]) > 0 {
316 types[k.configurationType] = true
317 }
318 }
319 return types
320}
321
322// Collapse reduces the configurable axes of the label attribute to a single axis.
323// This is necessary for final writing to bp2build, as a configurable label
324// attribute can only be comprised by a single select.
325func (la *LabelAttribute) Collapse() error {
326 axisTypes := la.axisTypes()
327 _, containsOs := axisTypes[os]
328 _, containsArch := axisTypes[arch]
329 _, containsOsArch := axisTypes[osArch]
330 _, containsProductVariables := axisTypes[productVariables]
331 if containsProductVariables {
332 if containsOs || containsArch || containsOsArch {
Alixbbfd5382022-06-09 18:52:05 +0000333 if containsArch {
334 allProductVariablesAreArchVariant := true
335 for k := range la.ConfigurableValues {
336 if k.configurationType == productVariables && k.outerAxisType != arch {
337 allProductVariablesAreArchVariant = false
338 }
339 }
340 if !allProductVariablesAreArchVariant {
341 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
342 }
343 } else {
344 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
345 }
Chris Parsons58852a02021-12-09 18:10:18 -0500346 }
347 }
348 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
349 // If a bool attribute has both os and arch configuration axes, the only
350 // way to successfully union their values is to increase the granularity
351 // of the configuration criteria to os_arch.
352 for osType, supportedArchs := range osToArchMap {
353 for _, supportedArch := range supportedArchs {
354 osArch := osArchString(osType, supportedArch)
355 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
356 // Do nothing, as the arch_os is explicitly defined already.
357 } else {
358 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
359 osVal := la.SelectValue(OsConfigurationAxis, osType)
360 if osVal != nil && archVal != nil {
361 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
362 // runs after os mutator.
363 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
364 } else if osVal != nil && archVal == nil {
365 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
366 } else if osVal == nil && archVal != nil {
367 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
368 }
369 }
370 }
371 }
372 // All os_arch values are now set. Clear os and arch axes.
373 delete(la.ConfigurableValues, ArchConfigurationAxis)
374 delete(la.ConfigurableValues, OsConfigurationAxis)
375 }
376 return nil
377}
378
Liz Kammer9abd62d2021-05-21 08:37:59 -0400379// HasConfigurableValues returns whether there are configurable values set for this label.
380func (la LabelAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500381 for _, selectValues := range la.ConfigurableValues {
382 if len(selectValues) > 0 {
383 return true
384 }
385 }
386 return false
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200387}
388
Liz Kammer9abd62d2021-05-21 08:37:59 -0400389// SetValue sets the base, non-configured value for the Label
390func (la *LabelAttribute) SetValue(value Label) {
391 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400392}
393
Liz Kammer9abd62d2021-05-21 08:37:59 -0400394// SetSelectValue set a value for a bazel select for the given axis, config and value.
395func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
396 axis.validateConfig(config)
397 switch axis.configurationType {
398 case noConfig:
399 la.Value = &value
Wei Li81852ca2022-07-27 00:22:06 -0700400 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400401 if la.ConfigurableValues == nil {
402 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400403 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400404 la.ConfigurableValues.setValueForAxis(axis, config, &value)
405 default:
406 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
407 }
408}
409
410// SelectValue gets a value for a bazel select for the given axis and config.
Chris Parsons58852a02021-12-09 18:10:18 -0500411func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400412 axis.validateConfig(config)
413 switch axis.configurationType {
414 case noConfig:
Chris Parsons58852a02021-12-09 18:10:18 -0500415 return la.Value
Wei Li81852ca2022-07-27 00:22:06 -0700416 case arch, os, osArch, productVariables, osAndInApex:
Chris Parsons58852a02021-12-09 18:10:18 -0500417 return la.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400418 default:
419 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
420 }
421}
422
423// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
424func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -0500425 return SortedConfigurationAxes(la.ConfigurableValues)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400426}
427
Sam Delmericoc0161432022-02-25 21:34:51 +0000428// MakeLabelAttribute turns a string into a LabelAttribute
429func MakeLabelAttribute(label string) *LabelAttribute {
430 return &LabelAttribute{
431 Value: &Label{
432 Label: label,
433 },
434 }
435}
436
Liz Kammerd366c902021-06-03 13:43:01 -0400437type configToBools map[string]bool
438
439func (ctb configToBools) setValue(config string, value *bool) {
440 if value == nil {
441 if _, ok := ctb[config]; ok {
442 delete(ctb, config)
443 }
444 return
445 }
446 ctb[config] = *value
447}
448
449type configurableBools map[ConfigurationAxis]configToBools
450
451func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
452 if cb[axis] == nil {
453 cb[axis] = make(configToBools)
454 }
455 cb[axis].setValue(config, value)
456}
457
458// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
459type BoolAttribute struct {
460 Value *bool
461
462 ConfigurableValues configurableBools
463}
464
465// HasConfigurableValues returns whether there are configurable values for this attribute.
466func (ba BoolAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500467 for _, cfgToBools := range ba.ConfigurableValues {
468 if len(cfgToBools) > 0 {
469 return true
470 }
471 }
472 return false
Liz Kammerd366c902021-06-03 13:43:01 -0400473}
474
Liz Kammerdfeb1202022-05-13 17:20:20 -0400475// SetValue sets value for the no config axis
476func (ba *BoolAttribute) SetValue(value *bool) {
477 ba.SetSelectValue(NoConfigAxis, "", value)
478}
479
Liz Kammerd366c902021-06-03 13:43:01 -0400480// SetSelectValue sets value for the given axis/config.
481func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
482 axis.validateConfig(config)
483 switch axis.configurationType {
484 case noConfig:
485 ba.Value = value
Wei Li81852ca2022-07-27 00:22:06 -0700486 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammerd366c902021-06-03 13:43:01 -0400487 if ba.ConfigurableValues == nil {
488 ba.ConfigurableValues = make(configurableBools)
489 }
490 ba.ConfigurableValues.setValueForAxis(axis, config, value)
491 default:
492 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
493 }
494}
495
Chris Parsons58852a02021-12-09 18:10:18 -0500496// ToLabelListAttribute creates and returns a LabelListAttribute from this
497// bool attribute, where each bool in this attribute corresponds to a
498// label list value in the resultant attribute.
499func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
500 getLabelList := func(boolPtr *bool) LabelList {
501 if boolPtr == nil {
502 return LabelList{nil, nil}
503 } else if *boolPtr {
504 return trueVal
505 } else {
506 return falseVal
507 }
508 }
509
510 mainVal := getLabelList(ba.Value)
511 if !ba.HasConfigurableValues() {
512 return MakeLabelListAttribute(mainVal), nil
513 }
514
515 result := LabelListAttribute{}
516 if err := ba.Collapse(); err != nil {
517 return result, err
518 }
519
520 for axis, configToBools := range ba.ConfigurableValues {
521 if len(configToBools) < 1 {
522 continue
523 }
524 for config, boolPtr := range configToBools {
525 val := getLabelList(&boolPtr)
526 if !val.Equals(mainVal) {
527 result.SetSelectValue(axis, config, val)
528 }
529 }
530 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
531 }
532
533 return result, nil
534}
535
536// Collapse reduces the configurable axes of the boolean attribute to a single axis.
537// This is necessary for final writing to bp2build, as a configurable boolean
538// attribute can only be comprised by a single select.
539func (ba *BoolAttribute) Collapse() error {
540 axisTypes := ba.axisTypes()
541 _, containsOs := axisTypes[os]
542 _, containsArch := axisTypes[arch]
543 _, containsOsArch := axisTypes[osArch]
544 _, containsProductVariables := axisTypes[productVariables]
545 if containsProductVariables {
546 if containsOs || containsArch || containsOsArch {
547 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
548 }
549 }
550 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
551 // If a bool attribute has both os and arch configuration axes, the only
552 // way to successfully union their values is to increase the granularity
553 // of the configuration criteria to os_arch.
554 for osType, supportedArchs := range osToArchMap {
555 for _, supportedArch := range supportedArchs {
556 osArch := osArchString(osType, supportedArch)
557 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
558 // Do nothing, as the arch_os is explicitly defined already.
559 } else {
560 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
561 osVal := ba.SelectValue(OsConfigurationAxis, osType)
562 if osVal != nil && archVal != nil {
563 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
564 // runs after os mutator.
565 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
566 } else if osVal != nil && archVal == nil {
567 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
568 } else if osVal == nil && archVal != nil {
569 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
570 }
571 }
572 }
573 }
574 // All os_arch values are now set. Clear os and arch axes.
575 delete(ba.ConfigurableValues, ArchConfigurationAxis)
576 delete(ba.ConfigurableValues, OsConfigurationAxis)
577 // Verify post-condition; this should never fail, provided no additional
578 // axes are introduced.
579 if len(ba.ConfigurableValues) > 1 {
Liz Kammer07e106f2022-01-13 17:00:10 -0500580 panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
Chris Parsons58852a02021-12-09 18:10:18 -0500581 }
582 }
583 return nil
584}
585
586func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
587 types := map[configurationType]bool{}
588 for k := range ba.ConfigurableValues {
589 if len(ba.ConfigurableValues[k]) > 0 {
590 types[k.configurationType] = true
591 }
592 }
593 return types
594}
595
Liz Kammerd366c902021-06-03 13:43:01 -0400596// SelectValue gets the value for the given axis/config.
597func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
598 axis.validateConfig(config)
599 switch axis.configurationType {
600 case noConfig:
601 return ba.Value
Wei Li81852ca2022-07-27 00:22:06 -0700602 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammerd366c902021-06-03 13:43:01 -0400603 if v, ok := ba.ConfigurableValues[axis][config]; ok {
604 return &v
605 } else {
606 return nil
607 }
608 default:
609 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
610 }
611}
612
613// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
614func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -0500615 return SortedConfigurationAxes(ba.ConfigurableValues)
Liz Kammerd366c902021-06-03 13:43:01 -0400616}
617
Liz Kammer9abd62d2021-05-21 08:37:59 -0400618// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
619type labelListSelectValues map[string]LabelList
620
Liz Kammer12615db2021-09-28 09:19:17 -0400621func (ll labelListSelectValues) addSelects(label labelSelectValues) {
622 for k, v := range label {
623 if label == nil {
624 continue
625 }
626 l := ll[k]
627 (&l).Add(v)
628 ll[k] = l
629 }
630}
631
Chris Parsons77acf2e2021-12-03 17:27:16 -0500632func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400633 for k, v := range other {
634 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500635 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
636 l.Includes = []Label{}
637 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400638 (&l).Append(v)
639 ll[k] = l
640 }
641}
642
643// HasConfigurableValues returns whether there are configurable values within this set of selects.
644func (ll labelListSelectValues) HasConfigurableValues() bool {
645 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400646 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400647 return true
648 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400649 }
650 return false
651}
652
Jingwen Chen07027912021-03-15 06:02:43 -0400653// LabelListAttribute is used to represent a list of Bazel labels as an
654// attribute.
655type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400656 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400657 Value LabelList
658
Liz Kammer9abd62d2021-05-21 08:37:59 -0400659 // The configured attribute label list Values. Optional
660 // a map of independent configurability axes
661 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400662
663 // If true, differentiate between "nil" and "empty" list. nil means that
664 // this attribute should not be specified at all, and "empty" means that
665 // the attribute should be explicitly specified as an empty list.
666 // This mode facilitates use of attribute defaults: an empty list should
667 // override the default.
668 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000669
670 // If true, signal the intent to the code generator to emit all select keys,
671 // even if the Includes list for that key is empty. This mode facilitates
672 // specific select statements where an empty list for a non-default select
673 // key has a meaning.
674 EmitEmptyList bool
Zi Wang9f609db2023-01-04 11:06:54 -0800675
676 // If a property has struct tag "variant_prepend", this value should
677 // be set to True, so that when bp2build generates BUILD.bazel, variant
678 // properties(select ...) come before general properties.
679 Prepend bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400680}
Jingwen Chen91220d72021-03-24 02:18:33 -0400681
Liz Kammer9abd62d2021-05-21 08:37:59 -0400682type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
683
684func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
685 if list.IsNil() {
686 if _, ok := cll[axis][config]; ok {
687 delete(cll[axis], config)
688 }
689 return
690 }
691 if cll[axis] == nil {
692 cll[axis] = make(labelListSelectValues)
693 }
694
695 cll[axis][config] = list
696}
697
Chris Parsons77acf2e2021-12-03 17:27:16 -0500698func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400699 for axis, otherSelects := range other {
700 selects := cll[axis]
701 if selects == nil {
702 selects = make(labelListSelectValues, len(otherSelects))
703 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500704 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400705 cll[axis] = selects
706 }
Jingwen Chen07027912021-03-15 06:02:43 -0400707}
708
Chris Parsons77acf2e2021-12-03 17:27:16 -0500709func (lla *LabelListAttribute) Clone() *LabelListAttribute {
710 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
711 return result.Append(*lla)
712}
713
Jingwen Chen07027912021-03-15 06:02:43 -0400714// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
715func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400716 return LabelListAttribute{
717 Value: value,
718 ConfigurableValues: make(configurableLabelLists),
719 }
720}
721
Cole Faust53b62092022-05-12 15:37:02 -0700722// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
723func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
724 return MakeLabelListAttribute(MakeLabelList([]Label{value}))
725}
726
Liz Kammer9abd62d2021-05-21 08:37:59 -0400727func (lla *LabelListAttribute) SetValue(list LabelList) {
728 lla.SetSelectValue(NoConfigAxis, "", list)
729}
730
731// SetSelectValue set a value for a bazel select for the given axis, config and value.
732func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
733 axis.validateConfig(config)
734 switch axis.configurationType {
735 case noConfig:
736 lla.Value = list
Vinh Tran85fb07c2022-09-16 16:17:48 -0400737 case arch, os, osArch, productVariables, osAndInApex, inApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400738 if lla.ConfigurableValues == nil {
739 lla.ConfigurableValues = make(configurableLabelLists)
740 }
741 lla.ConfigurableValues.setValueForAxis(axis, config, list)
742 default:
743 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
744 }
745}
746
747// SelectValue gets a value for a bazel select for the given axis and config.
748func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
749 axis.validateConfig(config)
750 switch axis.configurationType {
751 case noConfig:
752 return lla.Value
Vinh Tran85fb07c2022-09-16 16:17:48 -0400753 case arch, os, osArch, productVariables, osAndInApex, inApex:
Cole Faustc843b992022-08-02 18:06:50 -0700754 return lla.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400755 default:
756 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
757 }
758}
759
760// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
761func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -0500762 return SortedConfigurationAxes(lla.ConfigurableValues)
Jingwen Chen07027912021-03-15 06:02:43 -0400763}
764
Jingwen Chened9c17d2021-04-13 07:14:55 +0000765// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500766// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
767func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
768 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
769 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400770 lla.Value.Includes = []Label{}
771 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400772 lla.Value.Append(other.Value)
773 if lla.ConfigurableValues == nil {
774 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400775 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500776 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
777 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400778}
779
Liz Kammer12615db2021-09-28 09:19:17 -0400780// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
781// LabelList within the LabelListAttribute
782func (lla *LabelListAttribute) Add(label *LabelAttribute) {
783 if label == nil {
784 return
785 }
786
787 lla.Value.Add(label.Value)
788 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
789 lla.ConfigurableValues = make(configurableLabelLists)
790 }
791 for axis, _ := range label.ConfigurableValues {
792 if _, exists := lla.ConfigurableValues[axis]; !exists {
793 lla.ConfigurableValues[axis] = make(labelListSelectValues)
794 }
795 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
796 }
797}
798
Liz Kammer9abd62d2021-05-21 08:37:59 -0400799// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
800func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500801 for _, selectValues := range lla.ConfigurableValues {
802 if len(selectValues) > 0 {
803 return true
804 }
805 }
806 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400807}
808
Trevor Radcliffe0d1b4022022-12-12 22:26:34 +0000809// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
810func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
811 for _, values := range lla.ConfigurableValues[axis] {
812 if !values.IsNil() {
813 return true
814 }
815 }
816 return false
817}
818
Chris Parsons69fa9f92021-07-13 11:47:44 -0400819// IsEmpty returns true if the attribute has no values under any configuration.
820func (lla LabelListAttribute) IsEmpty() bool {
821 if len(lla.Value.Includes) > 0 {
822 return false
823 }
824 for axis, _ := range lla.ConfigurableValues {
825 if lla.ConfigurableValues[axis].HasConfigurableValues() {
826 return false
827 }
828 }
829 return true
830}
831
Liz Kammer54309532021-12-14 12:21:22 -0500832// IsNil returns true if the attribute has not been set for any configuration.
833func (lla LabelListAttribute) IsNil() bool {
834 if lla.Value.Includes != nil {
835 return false
836 }
837 return !lla.HasConfigurableValues()
838}
839
840// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
841// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
842// be removed, e.g. if they could cause duplicate element failures.
843func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
844 val := lla.SelectValue(axis, config)
845 newList := SubtractBazelLabelList(val, labelList)
846 newList.Excludes = append(newList.Excludes, labelList.Includes...)
847 lla.SetSelectValue(axis, config, newList)
848}
849
Liz Kammer74deed42021-06-02 13:02:03 -0400850// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
851// the base value and included in default values as appropriate.
852func (lla *LabelListAttribute) ResolveExcludes() {
Liz Kammerffc17e42022-11-23 09:42:05 -0500853 // If there are OsAndInApexAxis, we need to use
854 // * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
855 // included in non-Android OSes
856 // * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
857 // be included in the non-Android OSes
858 if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
859 inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
860 for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
861 // OsAndroid has already handled its excludes.
862 // We only need to copy the excludes from other arches, so if there are none, skip it.
863 if config == OsAndroid || len(labels.Excludes) == 0 {
864 continue
865 }
866 lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
867 Includes: inApexLabels.Includes,
868 Excludes: labels.Excludes,
869 }
870 }
871 }
872
Liz Kammer74deed42021-06-02 13:02:03 -0400873 for axis, configToLabels := range lla.ConfigurableValues {
874 baseLabels := lla.Value.deepCopy()
875 for config, val := range configToLabels {
876 // Exclude config-specific excludes from base value
877 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
878
879 // add base values to config specific to add labels excluded by others in this axis
880 // then remove all config-specific excludes
881 allLabels := baseLabels.deepCopy()
882 allLabels.Append(val)
883 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
884 }
885
886 // After going through all configs, delete the duplicates in the config
887 // values that are already in the base Value.
888 for config, val := range configToLabels {
889 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
890 }
891
Jingwen Chen9af49a42021-11-02 10:27:17 +0000892 // Now that the Value list is finalized for this axis, compare it with
893 // the original list, and union the difference with the default
894 // condition for the axis.
895 difference := SubtractBazelLabelList(baseLabels, lla.Value)
896 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
897 existingDefaults.Append(difference)
898 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400899
900 // if everything ends up without includes, just delete the axis
901 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
902 delete(lla.ConfigurableValues, axis)
903 }
904 }
905}
906
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400907// Partition splits a LabelListAttribute into two LabelListAttributes depending
908// on the return value of the predicate.
909// This function preserves the Includes and Excludes, but it does not provide
910// that information to the partition function.
911func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
912 predicated := LabelListAttribute{}
913 unpredicated := LabelListAttribute{}
914
915 valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
916 predicated.SetValue(valuePartitionTrue)
917 unpredicated.SetValue(valuePartitionFalse)
918
919 for axis, selectValueLabelLists := range lla.ConfigurableValues {
920 for config, labelList := range selectValueLabelLists {
921 configPredicated, configUnpredicated := labelList.Partition(predicate)
922 predicated.SetSelectValue(axis, config, configPredicated)
923 unpredicated.SetSelectValue(axis, config, configUnpredicated)
924 }
925 }
926
927 return predicated, unpredicated
928}
929
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400930// OtherModuleContext is a limited context that has methods with information about other modules.
931type OtherModuleContext interface {
932 ModuleFromName(name string) (blueprint.Module, bool)
933 OtherModuleType(m blueprint.Module) string
934 OtherModuleName(m blueprint.Module) string
935 OtherModuleDir(m blueprint.Module) string
936 ModuleErrorf(fmt string, args ...interface{})
937}
938
939// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
940// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400941type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400942
943// LabelPartition contains descriptions of a partition for labels
944type LabelPartition struct {
945 // Extensions to include in this partition
946 Extensions []string
947 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
948 // the mapped label in the partition
949 LabelMapper LabelMapper
950 // Whether to store files not included in any other partition in a group of LabelPartitions
951 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
952 Keep_remainder bool
953}
954
955// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
956// partition
957type LabelPartitions map[string]LabelPartition
958
959// filter returns a pointer to a label if the label should be included in the partition or nil if
960// not.
961func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
962 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400963 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400964 return &Label{newLabel, label.OriginalModuleName}
965 }
966 }
967 for _, ext := range lf.Extensions {
968 if strings.HasSuffix(label.Label, ext) {
969 return &label
970 }
971 }
972
973 return nil
974}
975
976// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
977type PartitionToLabelListAttribute map[string]LabelListAttribute
978
979type partitionToLabelList map[string]*LabelList
980
981func (p partitionToLabelList) appendIncludes(partition string, label Label) {
982 if _, ok := p[partition]; !ok {
983 p[partition] = &LabelList{}
984 }
985 p[partition].Includes = append(p[partition].Includes, label)
986}
987
988func (p partitionToLabelList) excludes(partition string, excludes []Label) {
989 if _, ok := p[partition]; !ok {
990 p[partition] = &LabelList{}
991 }
992 p[partition].Excludes = excludes
993}
994
995// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
996func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
997 ret := PartitionToLabelListAttribute{}
998 var partitionNames []string
999 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
1000 var remainderPartition *string
1001 for p, f := range partitions {
1002 partitionNames = append(partitionNames, p)
1003 if f.Keep_remainder {
1004 if remainderPartition != nil {
1005 panic("only one partition can store the remainder")
1006 }
1007 // If we take the address of p in a loop, we'll end up with the last value of p in
1008 // remainderPartition, we want the requested partition
1009 capturePartition := p
1010 remainderPartition = &capturePartition
1011 }
1012 }
1013
1014 partitionLabelList := func(axis ConfigurationAxis, config string) {
1015 value := lla.SelectValue(axis, config)
1016 partitionToLabels := partitionToLabelList{}
1017 for _, item := range value.Includes {
1018 wasFiltered := false
1019 var inPartition *string
1020 for partition, f := range partitions {
1021 filtered := f.filter(ctx, item)
1022 if filtered == nil {
1023 // did not match this filter, keep looking
1024 continue
1025 }
1026 wasFiltered = true
1027 partitionToLabels.appendIncludes(partition, *filtered)
1028 // don't need to check other partitions if this filter used the item,
1029 // continue checking if mapped to another name
1030 if *filtered == item {
1031 if inPartition != nil {
1032 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
1033 }
1034 capturePartition := partition
1035 inPartition = &capturePartition
1036 }
1037 }
1038
1039 // if not specified in a partition, add to remainder partition if one exists
1040 if !wasFiltered && remainderPartition != nil {
1041 partitionToLabels.appendIncludes(*remainderPartition, item)
1042 }
1043 }
1044
1045 // ensure empty lists are maintained
1046 if value.Excludes != nil {
1047 for _, partition := range partitionNames {
1048 partitionToLabels.excludes(partition, value.Excludes)
1049 }
1050 }
1051
1052 for partition, list := range partitionToLabels {
1053 val := ret[partition]
1054 (&val).SetSelectValue(axis, config, *list)
1055 ret[partition] = val
1056 }
1057 }
1058
1059 partitionLabelList(NoConfigAxis, "")
1060 for axis, configToList := range lla.ConfigurableValues {
1061 for config, _ := range configToList {
1062 partitionLabelList(axis, config)
1063 }
1064 }
1065 return ret
1066}
1067
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001068// StringAttribute corresponds to the string Bazel attribute type with
1069// support for additional metadata, like configurations.
1070type StringAttribute struct {
1071 // The base value of the string attribute.
1072 Value *string
1073
1074 // The configured attribute label list Values. Optional
1075 // a map of independent configurability axes
1076 ConfigurableValues configurableStrings
1077}
1078
1079type configurableStrings map[ConfigurationAxis]stringSelectValues
1080
1081func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
1082 if cs[axis] == nil {
1083 cs[axis] = make(stringSelectValues)
1084 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001085 cs[axis][config] = str
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001086}
1087
Liz Kammer9d2d4102022-12-21 14:51:37 -05001088type stringSelectValues map[string]*string
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001089
1090// HasConfigurableValues returns true if the attribute contains axis-specific string values.
1091func (sa StringAttribute) HasConfigurableValues() bool {
1092 for _, selectValues := range sa.ConfigurableValues {
1093 if len(selectValues) > 0 {
1094 return true
1095 }
1096 }
1097 return false
1098}
1099
Liz Kammer9d2d4102022-12-21 14:51:37 -05001100// SetValue sets the base, non-configured value for the Label
1101func (sa *StringAttribute) SetValue(value string) {
1102 sa.SetSelectValue(NoConfigAxis, "", &value)
1103}
1104
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001105// SetSelectValue set a value for a bazel select for the given axis, config and value.
1106func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
1107 axis.validateConfig(config)
1108 switch axis.configurationType {
1109 case noConfig:
1110 sa.Value = str
1111 case arch, os, osArch, productVariables:
1112 if sa.ConfigurableValues == nil {
1113 sa.ConfigurableValues = make(configurableStrings)
1114 }
1115 sa.ConfigurableValues.setValueForAxis(axis, config, str)
1116 default:
1117 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1118 }
1119}
1120
1121// SelectValue gets a value for a bazel select for the given axis and config.
1122func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
1123 axis.validateConfig(config)
1124 switch axis.configurationType {
1125 case noConfig:
1126 return sa.Value
1127 case arch, os, osArch, productVariables:
1128 if v, ok := sa.ConfigurableValues[axis][config]; ok {
Liz Kammer9d2d4102022-12-21 14:51:37 -05001129 return v
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001130 } else {
1131 return nil
1132 }
1133 default:
1134 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1135 }
1136}
1137
1138// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1139func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -05001140 return SortedConfigurationAxes(sa.ConfigurableValues)
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001141}
1142
1143// Collapse reduces the configurable axes of the string attribute to a single axis.
1144// This is necessary for final writing to bp2build, as a configurable string
1145// attribute can only be comprised by a single select.
1146func (sa *StringAttribute) Collapse() error {
1147 axisTypes := sa.axisTypes()
1148 _, containsOs := axisTypes[os]
1149 _, containsArch := axisTypes[arch]
1150 _, containsOsArch := axisTypes[osArch]
1151 _, containsProductVariables := axisTypes[productVariables]
1152 if containsProductVariables {
1153 if containsOs || containsArch || containsOsArch {
Liz Kammer9d2d4102022-12-21 14:51:37 -05001154 return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001155 }
1156 }
1157 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
1158 // If a bool attribute has both os and arch configuration axes, the only
1159 // way to successfully union their values is to increase the granularity
1160 // of the configuration criteria to os_arch.
1161 for osType, supportedArchs := range osToArchMap {
1162 for _, supportedArch := range supportedArchs {
1163 osArch := osArchString(osType, supportedArch)
1164 if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
1165 // Do nothing, as the arch_os is explicitly defined already.
1166 } else {
1167 archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
1168 osVal := sa.SelectValue(OsConfigurationAxis, osType)
1169 if osVal != nil && archVal != nil {
1170 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
1171 // runs after os mutator.
1172 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1173 } else if osVal != nil && archVal == nil {
1174 sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
1175 } else if osVal == nil && archVal != nil {
1176 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1177 }
1178 }
1179 }
1180 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001181 /// All os_arch values are now set. Clear os and arch axes.
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001182 delete(sa.ConfigurableValues, ArchConfigurationAxis)
1183 delete(sa.ConfigurableValues, OsConfigurationAxis)
1184 // Verify post-condition; this should never fail, provided no additional
1185 // axes are introduced.
1186 if len(sa.ConfigurableValues) > 1 {
1187 panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
1188 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001189 } else if containsProductVariables {
1190 usedBaseValue := false
1191 for a, configToProp := range sa.ConfigurableValues {
1192 if a.configurationType == productVariables {
1193 for c, p := range configToProp {
1194 if p == nil {
1195 sa.SetSelectValue(a, c, sa.Value)
1196 usedBaseValue = true
1197 }
1198 }
1199 }
1200 }
1201 if usedBaseValue {
1202 sa.Value = nil
1203 }
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001204 }
1205 return nil
1206}
1207
1208func (sa *StringAttribute) axisTypes() map[configurationType]bool {
1209 types := map[configurationType]bool{}
1210 for k := range sa.ConfigurableValues {
1211 if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
1212 types[k.configurationType] = true
1213 }
1214 }
1215 return types
1216}
1217
Jingwen Chen5d864492021-02-24 07:20:12 -05001218// StringListAttribute corresponds to the string_list Bazel attribute type with
1219// support for additional metadata, like configurations.
1220type StringListAttribute struct {
1221 // The base value of the string list attribute.
1222 Value []string
1223
Liz Kammer9abd62d2021-05-21 08:37:59 -04001224 // The configured attribute label list Values. Optional
1225 // a map of independent configurability axes
1226 ConfigurableValues configurableStringLists
Zi Wang1cb11802022-12-09 16:08:54 -08001227
1228 // If a property has struct tag "variant_prepend", this value should
1229 // be set to True, so that when bp2build generates BUILD.bazel, variant
1230 // properties(select ...) come before general properties.
1231 Prepend bool
Liz Kammer9abd62d2021-05-21 08:37:59 -04001232}
Jingwen Chenc1c26502021-04-05 10:35:13 +00001233
Spandan Das4238c652022-09-09 01:38:47 +00001234// IsEmpty returns true if the attribute has no values under any configuration.
1235func (sla StringListAttribute) IsEmpty() bool {
1236 return len(sla.Value) == 0 && !sla.HasConfigurableValues()
1237}
1238
Liz Kammer9abd62d2021-05-21 08:37:59 -04001239type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -04001240
Liz Kammer9abd62d2021-05-21 08:37:59 -04001241func (csl configurableStringLists) Append(other configurableStringLists) {
1242 for axis, otherSelects := range other {
1243 selects := csl[axis]
1244 if selects == nil {
1245 selects = make(stringListSelectValues, len(otherSelects))
1246 }
1247 selects.appendSelects(otherSelects)
1248 csl[axis] = selects
1249 }
1250}
1251
1252func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
1253 if csl[axis] == nil {
1254 csl[axis] = make(stringListSelectValues)
1255 }
1256 csl[axis][config] = list
1257}
1258
1259type stringListSelectValues map[string][]string
1260
1261func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
1262 for k, v := range other {
1263 sl[k] = append(sl[k], v...)
1264 }
1265}
1266
1267func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
1268 for _, val := range sl {
1269 if len(val) > 0 {
1270 return true
1271 }
1272 }
1273 return false
Jingwen Chen5d864492021-02-24 07:20:12 -05001274}
1275
Rupert Shuttleworthb8151682021-04-06 20:06:21 +00001276// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1277func MakeStringListAttribute(value []string) StringListAttribute {
1278 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -04001279 return StringListAttribute{
1280 Value: value,
1281 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -04001282 }
1283}
1284
Liz Kammer9abd62d2021-05-21 08:37:59 -04001285// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1286func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -05001287 for _, selectValues := range sla.ConfigurableValues {
1288 if len(selectValues) > 0 {
1289 return true
1290 }
1291 }
1292 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -04001293}
1294
Jingwen Chened9c17d2021-04-13 07:14:55 +00001295// Append appends all values, including os and arch specific ones, from another
1296// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001297func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001298 sla.Value = append(sla.Value, other.Value...)
1299 if sla.ConfigurableValues == nil {
1300 sla.ConfigurableValues = make(configurableStringLists)
1301 }
1302 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001303 return sla
1304}
1305
1306func (sla *StringListAttribute) Clone() *StringListAttribute {
1307 result := &StringListAttribute{}
1308 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001309}
1310
1311// SetSelectValue set a value for a bazel select for the given axis, config and value.
1312func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1313 axis.validateConfig(config)
1314 switch axis.configurationType {
1315 case noConfig:
1316 sla.Value = list
Wei Li81852ca2022-07-27 00:22:06 -07001317 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001318 if sla.ConfigurableValues == nil {
1319 sla.ConfigurableValues = make(configurableStringLists)
1320 }
1321 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1322 default:
1323 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1324 }
1325}
1326
1327// SelectValue gets a value for a bazel select for the given axis and config.
1328func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1329 axis.validateConfig(config)
1330 switch axis.configurationType {
1331 case noConfig:
1332 return sla.Value
Wei Li81852ca2022-07-27 00:22:06 -07001333 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001334 return sla.ConfigurableValues[axis][config]
1335 default:
1336 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1337 }
1338}
1339
1340// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1341func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -05001342 return SortedConfigurationAxes(sla.ConfigurableValues)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001343}
1344
Liz Kammer5fad5012021-09-09 14:08:21 -04001345// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1346// configuration-specific values. For example, if we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001347//
1348// ["a", "b", "c"] + select({
1349// "//condition:one": ["a", "d"],
1350// "//conditions:default": [],
1351// })
1352//
Liz Kammer5fad5012021-09-09 14:08:21 -04001353// after this function, we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001354//
1355// ["a", "b", "c"] + select({
1356// "//condition:one": ["d"],
1357// "//conditions:default": [],
1358// })
Liz Kammer5fad5012021-09-09 14:08:21 -04001359func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1360 base := sla.Value
1361 for axis, configToList := range sla.ConfigurableValues {
1362 for config, list := range configToList {
1363 remaining := SubtractStrings(list, base)
1364 if len(remaining) == 0 {
1365 delete(sla.ConfigurableValues[axis], config)
1366 } else {
1367 sla.ConfigurableValues[axis][config] = remaining
1368 }
1369 }
1370 }
1371}
1372
Liz Kammera060c452021-03-24 10:14:47 -04001373// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1374// Starlark string.format compatible tag for productVariable.
1375func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
Liz Kammerf3963f82022-12-21 14:47:18 -05001376 if len(slice) == 0 {
1377 return slice, false
1378 }
Liz Kammera060c452021-03-24 10:14:47 -04001379 ret := make([]string, 0, len(slice))
1380 changesMade := false
1381 for _, s := range slice {
1382 newS, changed := TryVariableSubstitution(s, productVariable)
1383 ret = append(ret, newS)
1384 changesMade = changesMade || changed
1385 }
1386 return ret, changesMade
1387}
1388
1389// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1390// string.format compatible tag for productVariable.
1391func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001392 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001393 return sub, s != sub
1394}