blob: 9be21ebc742a832797edc32d970394a1217e80cf [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 {
Liz Kammer748d7072023-01-25 12:07:43 -0500155 ll.Excludes = append(ll.Excludes, other.Excludes...)
Liz Kammer356f7d42021-01-26 09:18:53 -0500156 }
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
Zi Wang9f609db2023-01-04 11:06:54 -0800677
678 // If a property has struct tag "variant_prepend", this value should
679 // be set to True, so that when bp2build generates BUILD.bazel, variant
680 // properties(select ...) come before general properties.
681 Prepend bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400682}
Jingwen Chen91220d72021-03-24 02:18:33 -0400683
Liz Kammer9abd62d2021-05-21 08:37:59 -0400684type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
685
686func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
687 if list.IsNil() {
688 if _, ok := cll[axis][config]; ok {
689 delete(cll[axis], config)
690 }
691 return
692 }
693 if cll[axis] == nil {
694 cll[axis] = make(labelListSelectValues)
695 }
696
697 cll[axis][config] = list
698}
699
Chris Parsons77acf2e2021-12-03 17:27:16 -0500700func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400701 for axis, otherSelects := range other {
702 selects := cll[axis]
703 if selects == nil {
704 selects = make(labelListSelectValues, len(otherSelects))
705 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500706 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400707 cll[axis] = selects
708 }
Jingwen Chen07027912021-03-15 06:02:43 -0400709}
710
Chris Parsons77acf2e2021-12-03 17:27:16 -0500711func (lla *LabelListAttribute) Clone() *LabelListAttribute {
712 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
713 return result.Append(*lla)
714}
715
Jingwen Chen07027912021-03-15 06:02:43 -0400716// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
717func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400718 return LabelListAttribute{
719 Value: value,
720 ConfigurableValues: make(configurableLabelLists),
721 }
722}
723
Cole Faust53b62092022-05-12 15:37:02 -0700724// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
725func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
726 return MakeLabelListAttribute(MakeLabelList([]Label{value}))
727}
728
Liz Kammer9abd62d2021-05-21 08:37:59 -0400729func (lla *LabelListAttribute) SetValue(list LabelList) {
730 lla.SetSelectValue(NoConfigAxis, "", list)
731}
732
733// SetSelectValue set a value for a bazel select for the given axis, config and value.
734func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
735 axis.validateConfig(config)
736 switch axis.configurationType {
737 case noConfig:
738 lla.Value = list
Vinh Tran85fb07c2022-09-16 16:17:48 -0400739 case arch, os, osArch, productVariables, osAndInApex, inApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400740 if lla.ConfigurableValues == nil {
741 lla.ConfigurableValues = make(configurableLabelLists)
742 }
743 lla.ConfigurableValues.setValueForAxis(axis, config, list)
744 default:
745 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
746 }
747}
748
749// SelectValue gets a value for a bazel select for the given axis and config.
750func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
751 axis.validateConfig(config)
752 switch axis.configurationType {
753 case noConfig:
754 return lla.Value
Vinh Tran85fb07c2022-09-16 16:17:48 -0400755 case arch, os, osArch, productVariables, osAndInApex, inApex:
Cole Faustc843b992022-08-02 18:06:50 -0700756 return lla.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400757 default:
758 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
759 }
760}
761
762// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
763func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
764 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
765 for k := range lla.ConfigurableValues {
766 keys = append(keys, k)
767 }
768
769 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
770 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400771}
772
Jingwen Chened9c17d2021-04-13 07:14:55 +0000773// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500774// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
775func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
776 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
777 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400778 lla.Value.Includes = []Label{}
779 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400780 lla.Value.Append(other.Value)
781 if lla.ConfigurableValues == nil {
782 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400783 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500784 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
785 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400786}
787
Liz Kammer12615db2021-09-28 09:19:17 -0400788// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
789// LabelList within the LabelListAttribute
790func (lla *LabelListAttribute) Add(label *LabelAttribute) {
791 if label == nil {
792 return
793 }
794
795 lla.Value.Add(label.Value)
796 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
797 lla.ConfigurableValues = make(configurableLabelLists)
798 }
799 for axis, _ := range label.ConfigurableValues {
800 if _, exists := lla.ConfigurableValues[axis]; !exists {
801 lla.ConfigurableValues[axis] = make(labelListSelectValues)
802 }
803 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
804 }
805}
806
Liz Kammer9abd62d2021-05-21 08:37:59 -0400807// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
808func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500809 for _, selectValues := range lla.ConfigurableValues {
810 if len(selectValues) > 0 {
811 return true
812 }
813 }
814 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400815}
816
Trevor Radcliffe0d1b4022022-12-12 22:26:34 +0000817// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
818func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
819 for _, values := range lla.ConfigurableValues[axis] {
820 if !values.IsNil() {
821 return true
822 }
823 }
824 return false
825}
826
Chris Parsons69fa9f92021-07-13 11:47:44 -0400827// IsEmpty returns true if the attribute has no values under any configuration.
828func (lla LabelListAttribute) IsEmpty() bool {
829 if len(lla.Value.Includes) > 0 {
830 return false
831 }
832 for axis, _ := range lla.ConfigurableValues {
833 if lla.ConfigurableValues[axis].HasConfigurableValues() {
834 return false
835 }
836 }
837 return true
838}
839
Liz Kammer54309532021-12-14 12:21:22 -0500840// IsNil returns true if the attribute has not been set for any configuration.
841func (lla LabelListAttribute) IsNil() bool {
842 if lla.Value.Includes != nil {
843 return false
844 }
845 return !lla.HasConfigurableValues()
846}
847
848// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
849// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
850// be removed, e.g. if they could cause duplicate element failures.
851func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
852 val := lla.SelectValue(axis, config)
853 newList := SubtractBazelLabelList(val, labelList)
854 newList.Excludes = append(newList.Excludes, labelList.Includes...)
855 lla.SetSelectValue(axis, config, newList)
856}
857
Liz Kammer74deed42021-06-02 13:02:03 -0400858// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
859// the base value and included in default values as appropriate.
860func (lla *LabelListAttribute) ResolveExcludes() {
Liz Kammerffc17e42022-11-23 09:42:05 -0500861 // If there are OsAndInApexAxis, we need to use
862 // * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
863 // included in non-Android OSes
864 // * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
865 // be included in the non-Android OSes
866 if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
867 inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
868 for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
869 // OsAndroid has already handled its excludes.
870 // We only need to copy the excludes from other arches, so if there are none, skip it.
871 if config == OsAndroid || len(labels.Excludes) == 0 {
872 continue
873 }
874 lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
875 Includes: inApexLabels.Includes,
876 Excludes: labels.Excludes,
877 }
878 }
879 }
880
Liz Kammer74deed42021-06-02 13:02:03 -0400881 for axis, configToLabels := range lla.ConfigurableValues {
882 baseLabels := lla.Value.deepCopy()
883 for config, val := range configToLabels {
884 // Exclude config-specific excludes from base value
885 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
886
887 // add base values to config specific to add labels excluded by others in this axis
888 // then remove all config-specific excludes
889 allLabels := baseLabels.deepCopy()
890 allLabels.Append(val)
Liz Kammer748d7072023-01-25 12:07:43 -0500891 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
Liz Kammer74deed42021-06-02 13:02:03 -0400892 }
893
894 // After going through all configs, delete the duplicates in the config
895 // values that are already in the base Value.
896 for config, val := range configToLabels {
897 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
898 }
899
Jingwen Chen9af49a42021-11-02 10:27:17 +0000900 // Now that the Value list is finalized for this axis, compare it with
901 // the original list, and union the difference with the default
902 // condition for the axis.
903 difference := SubtractBazelLabelList(baseLabels, lla.Value)
904 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
905 existingDefaults.Append(difference)
906 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400907
908 // if everything ends up without includes, just delete the axis
909 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
910 delete(lla.ConfigurableValues, axis)
911 }
912 }
913}
914
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400915// Partition splits a LabelListAttribute into two LabelListAttributes depending
916// on the return value of the predicate.
917// This function preserves the Includes and Excludes, but it does not provide
918// that information to the partition function.
919func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
920 predicated := LabelListAttribute{}
921 unpredicated := LabelListAttribute{}
922
923 valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
924 predicated.SetValue(valuePartitionTrue)
925 unpredicated.SetValue(valuePartitionFalse)
926
927 for axis, selectValueLabelLists := range lla.ConfigurableValues {
928 for config, labelList := range selectValueLabelLists {
929 configPredicated, configUnpredicated := labelList.Partition(predicate)
930 predicated.SetSelectValue(axis, config, configPredicated)
931 unpredicated.SetSelectValue(axis, config, configUnpredicated)
932 }
933 }
934
935 return predicated, unpredicated
936}
937
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400938// OtherModuleContext is a limited context that has methods with information about other modules.
939type OtherModuleContext interface {
940 ModuleFromName(name string) (blueprint.Module, bool)
941 OtherModuleType(m blueprint.Module) string
942 OtherModuleName(m blueprint.Module) string
943 OtherModuleDir(m blueprint.Module) string
944 ModuleErrorf(fmt string, args ...interface{})
945}
946
947// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
948// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -0400949type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400950
951// LabelPartition contains descriptions of a partition for labels
952type LabelPartition struct {
953 // Extensions to include in this partition
954 Extensions []string
955 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
956 // the mapped label in the partition
957 LabelMapper LabelMapper
958 // Whether to store files not included in any other partition in a group of LabelPartitions
959 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
960 Keep_remainder bool
961}
962
963// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
964// partition
965type LabelPartitions map[string]LabelPartition
966
967// filter returns a pointer to a label if the label should be included in the partition or nil if
968// not.
969func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
970 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -0400971 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400972 return &Label{newLabel, label.OriginalModuleName}
973 }
974 }
975 for _, ext := range lf.Extensions {
976 if strings.HasSuffix(label.Label, ext) {
977 return &label
978 }
979 }
980
981 return nil
982}
983
984// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
985type PartitionToLabelListAttribute map[string]LabelListAttribute
986
987type partitionToLabelList map[string]*LabelList
988
989func (p partitionToLabelList) appendIncludes(partition string, label Label) {
990 if _, ok := p[partition]; !ok {
991 p[partition] = &LabelList{}
992 }
993 p[partition].Includes = append(p[partition].Includes, label)
994}
995
996func (p partitionToLabelList) excludes(partition string, excludes []Label) {
997 if _, ok := p[partition]; !ok {
998 p[partition] = &LabelList{}
999 }
1000 p[partition].Excludes = excludes
1001}
1002
1003// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
1004func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
1005 ret := PartitionToLabelListAttribute{}
1006 var partitionNames []string
1007 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
1008 var remainderPartition *string
1009 for p, f := range partitions {
1010 partitionNames = append(partitionNames, p)
1011 if f.Keep_remainder {
1012 if remainderPartition != nil {
1013 panic("only one partition can store the remainder")
1014 }
1015 // If we take the address of p in a loop, we'll end up with the last value of p in
1016 // remainderPartition, we want the requested partition
1017 capturePartition := p
1018 remainderPartition = &capturePartition
1019 }
1020 }
1021
1022 partitionLabelList := func(axis ConfigurationAxis, config string) {
1023 value := lla.SelectValue(axis, config)
1024 partitionToLabels := partitionToLabelList{}
1025 for _, item := range value.Includes {
1026 wasFiltered := false
1027 var inPartition *string
1028 for partition, f := range partitions {
1029 filtered := f.filter(ctx, item)
1030 if filtered == nil {
1031 // did not match this filter, keep looking
1032 continue
1033 }
1034 wasFiltered = true
1035 partitionToLabels.appendIncludes(partition, *filtered)
1036 // don't need to check other partitions if this filter used the item,
1037 // continue checking if mapped to another name
1038 if *filtered == item {
1039 if inPartition != nil {
1040 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
1041 }
1042 capturePartition := partition
1043 inPartition = &capturePartition
1044 }
1045 }
1046
1047 // if not specified in a partition, add to remainder partition if one exists
1048 if !wasFiltered && remainderPartition != nil {
1049 partitionToLabels.appendIncludes(*remainderPartition, item)
1050 }
1051 }
1052
1053 // ensure empty lists are maintained
1054 if value.Excludes != nil {
1055 for _, partition := range partitionNames {
1056 partitionToLabels.excludes(partition, value.Excludes)
1057 }
1058 }
1059
1060 for partition, list := range partitionToLabels {
1061 val := ret[partition]
1062 (&val).SetSelectValue(axis, config, *list)
1063 ret[partition] = val
1064 }
1065 }
1066
1067 partitionLabelList(NoConfigAxis, "")
1068 for axis, configToList := range lla.ConfigurableValues {
1069 for config, _ := range configToList {
1070 partitionLabelList(axis, config)
1071 }
1072 }
1073 return ret
1074}
1075
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001076// StringAttribute corresponds to the string Bazel attribute type with
1077// support for additional metadata, like configurations.
1078type StringAttribute struct {
1079 // The base value of the string attribute.
1080 Value *string
1081
1082 // The configured attribute label list Values. Optional
1083 // a map of independent configurability axes
1084 ConfigurableValues configurableStrings
1085}
1086
1087type configurableStrings map[ConfigurationAxis]stringSelectValues
1088
1089func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
1090 if cs[axis] == nil {
1091 cs[axis] = make(stringSelectValues)
1092 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001093 cs[axis][config] = str
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001094}
1095
Liz Kammer9d2d4102022-12-21 14:51:37 -05001096type stringSelectValues map[string]*string
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001097
1098// HasConfigurableValues returns true if the attribute contains axis-specific string values.
1099func (sa StringAttribute) HasConfigurableValues() bool {
1100 for _, selectValues := range sa.ConfigurableValues {
1101 if len(selectValues) > 0 {
1102 return true
1103 }
1104 }
1105 return false
1106}
1107
Liz Kammer9d2d4102022-12-21 14:51:37 -05001108// SetValue sets the base, non-configured value for the Label
1109func (sa *StringAttribute) SetValue(value string) {
1110 sa.SetSelectValue(NoConfigAxis, "", &value)
1111}
1112
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001113// SetSelectValue set a value for a bazel select for the given axis, config and value.
1114func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
1115 axis.validateConfig(config)
1116 switch axis.configurationType {
1117 case noConfig:
1118 sa.Value = str
1119 case arch, os, osArch, productVariables:
1120 if sa.ConfigurableValues == nil {
1121 sa.ConfigurableValues = make(configurableStrings)
1122 }
1123 sa.ConfigurableValues.setValueForAxis(axis, config, str)
1124 default:
1125 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1126 }
1127}
1128
1129// SelectValue gets a value for a bazel select for the given axis and config.
1130func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
1131 axis.validateConfig(config)
1132 switch axis.configurationType {
1133 case noConfig:
1134 return sa.Value
1135 case arch, os, osArch, productVariables:
1136 if v, ok := sa.ConfigurableValues[axis][config]; ok {
Liz Kammer9d2d4102022-12-21 14:51:37 -05001137 return v
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001138 } else {
1139 return nil
1140 }
1141 default:
1142 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1143 }
1144}
1145
1146// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1147func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1148 keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
1149 for k := range sa.ConfigurableValues {
1150 keys = append(keys, k)
1151 }
1152
1153 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1154 return keys
1155}
1156
1157// Collapse reduces the configurable axes of the string attribute to a single axis.
1158// This is necessary for final writing to bp2build, as a configurable string
1159// attribute can only be comprised by a single select.
1160func (sa *StringAttribute) Collapse() error {
1161 axisTypes := sa.axisTypes()
1162 _, containsOs := axisTypes[os]
1163 _, containsArch := axisTypes[arch]
1164 _, containsOsArch := axisTypes[osArch]
1165 _, containsProductVariables := axisTypes[productVariables]
1166 if containsProductVariables {
1167 if containsOs || containsArch || containsOsArch {
Liz Kammer9d2d4102022-12-21 14:51:37 -05001168 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 +00001169 }
1170 }
1171 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
1172 // If a bool attribute has both os and arch configuration axes, the only
1173 // way to successfully union their values is to increase the granularity
1174 // of the configuration criteria to os_arch.
1175 for osType, supportedArchs := range osToArchMap {
1176 for _, supportedArch := range supportedArchs {
1177 osArch := osArchString(osType, supportedArch)
1178 if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
1179 // Do nothing, as the arch_os is explicitly defined already.
1180 } else {
1181 archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
1182 osVal := sa.SelectValue(OsConfigurationAxis, osType)
1183 if osVal != nil && archVal != nil {
1184 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
1185 // runs after os mutator.
1186 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1187 } else if osVal != nil && archVal == nil {
1188 sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
1189 } else if osVal == nil && archVal != nil {
1190 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1191 }
1192 }
1193 }
1194 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001195 /// 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 +00001196 delete(sa.ConfigurableValues, ArchConfigurationAxis)
1197 delete(sa.ConfigurableValues, OsConfigurationAxis)
1198 // Verify post-condition; this should never fail, provided no additional
1199 // axes are introduced.
1200 if len(sa.ConfigurableValues) > 1 {
1201 panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
1202 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001203 } else if containsProductVariables {
1204 usedBaseValue := false
1205 for a, configToProp := range sa.ConfigurableValues {
1206 if a.configurationType == productVariables {
1207 for c, p := range configToProp {
1208 if p == nil {
1209 sa.SetSelectValue(a, c, sa.Value)
1210 usedBaseValue = true
1211 }
1212 }
1213 }
1214 }
1215 if usedBaseValue {
1216 sa.Value = nil
1217 }
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001218 }
1219 return nil
1220}
1221
1222func (sa *StringAttribute) axisTypes() map[configurationType]bool {
1223 types := map[configurationType]bool{}
1224 for k := range sa.ConfigurableValues {
1225 if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
1226 types[k.configurationType] = true
1227 }
1228 }
1229 return types
1230}
1231
Jingwen Chen5d864492021-02-24 07:20:12 -05001232// StringListAttribute corresponds to the string_list Bazel attribute type with
1233// support for additional metadata, like configurations.
1234type StringListAttribute struct {
1235 // The base value of the string list attribute.
1236 Value []string
1237
Liz Kammer9abd62d2021-05-21 08:37:59 -04001238 // The configured attribute label list Values. Optional
1239 // a map of independent configurability axes
1240 ConfigurableValues configurableStringLists
Zi Wang1cb11802022-12-09 16:08:54 -08001241
1242 // If a property has struct tag "variant_prepend", this value should
1243 // be set to True, so that when bp2build generates BUILD.bazel, variant
1244 // properties(select ...) come before general properties.
1245 Prepend bool
Liz Kammer9abd62d2021-05-21 08:37:59 -04001246}
Jingwen Chenc1c26502021-04-05 10:35:13 +00001247
Spandan Das4238c652022-09-09 01:38:47 +00001248// IsEmpty returns true if the attribute has no values under any configuration.
1249func (sla StringListAttribute) IsEmpty() bool {
1250 return len(sla.Value) == 0 && !sla.HasConfigurableValues()
1251}
1252
Liz Kammer9abd62d2021-05-21 08:37:59 -04001253type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -04001254
Liz Kammer9abd62d2021-05-21 08:37:59 -04001255func (csl configurableStringLists) Append(other configurableStringLists) {
1256 for axis, otherSelects := range other {
1257 selects := csl[axis]
1258 if selects == nil {
1259 selects = make(stringListSelectValues, len(otherSelects))
1260 }
1261 selects.appendSelects(otherSelects)
1262 csl[axis] = selects
1263 }
1264}
1265
1266func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
1267 if csl[axis] == nil {
1268 csl[axis] = make(stringListSelectValues)
1269 }
1270 csl[axis][config] = list
1271}
1272
1273type stringListSelectValues map[string][]string
1274
1275func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
1276 for k, v := range other {
1277 sl[k] = append(sl[k], v...)
1278 }
1279}
1280
1281func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
1282 for _, val := range sl {
1283 if len(val) > 0 {
1284 return true
1285 }
1286 }
1287 return false
Jingwen Chen5d864492021-02-24 07:20:12 -05001288}
1289
Rupert Shuttleworthb8151682021-04-06 20:06:21 +00001290// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1291func MakeStringListAttribute(value []string) StringListAttribute {
1292 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -04001293 return StringListAttribute{
1294 Value: value,
1295 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -04001296 }
1297}
1298
Liz Kammer9abd62d2021-05-21 08:37:59 -04001299// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1300func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -05001301 for _, selectValues := range sla.ConfigurableValues {
1302 if len(selectValues) > 0 {
1303 return true
1304 }
1305 }
1306 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -04001307}
1308
Jingwen Chened9c17d2021-04-13 07:14:55 +00001309// Append appends all values, including os and arch specific ones, from another
1310// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001311func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001312 sla.Value = append(sla.Value, other.Value...)
1313 if sla.ConfigurableValues == nil {
1314 sla.ConfigurableValues = make(configurableStringLists)
1315 }
1316 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001317 return sla
1318}
1319
1320func (sla *StringListAttribute) Clone() *StringListAttribute {
1321 result := &StringListAttribute{}
1322 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001323}
1324
1325// SetSelectValue set a value for a bazel select for the given axis, config and value.
1326func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1327 axis.validateConfig(config)
1328 switch axis.configurationType {
1329 case noConfig:
1330 sla.Value = list
Wei Li81852ca2022-07-27 00:22:06 -07001331 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001332 if sla.ConfigurableValues == nil {
1333 sla.ConfigurableValues = make(configurableStringLists)
1334 }
1335 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1336 default:
1337 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1338 }
1339}
1340
1341// SelectValue gets a value for a bazel select for the given axis and config.
1342func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1343 axis.validateConfig(config)
1344 switch axis.configurationType {
1345 case noConfig:
1346 return sla.Value
Wei Li81852ca2022-07-27 00:22:06 -07001347 case arch, os, osArch, productVariables, osAndInApex:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001348 return sla.ConfigurableValues[axis][config]
1349 default:
1350 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1351 }
1352}
1353
1354// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1355func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1356 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
1357 for k := range sla.ConfigurableValues {
1358 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001359 }
1360
Liz Kammer9abd62d2021-05-21 08:37:59 -04001361 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1362 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +00001363}
1364
Liz Kammer5fad5012021-09-09 14:08:21 -04001365// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1366// configuration-specific values. For example, if we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001367//
1368// ["a", "b", "c"] + select({
1369// "//condition:one": ["a", "d"],
1370// "//conditions:default": [],
1371// })
1372//
Liz Kammer5fad5012021-09-09 14:08:21 -04001373// after this function, we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001374//
1375// ["a", "b", "c"] + select({
1376// "//condition:one": ["d"],
1377// "//conditions:default": [],
1378// })
Liz Kammer5fad5012021-09-09 14:08:21 -04001379func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1380 base := sla.Value
1381 for axis, configToList := range sla.ConfigurableValues {
1382 for config, list := range configToList {
1383 remaining := SubtractStrings(list, base)
1384 if len(remaining) == 0 {
1385 delete(sla.ConfigurableValues[axis], config)
1386 } else {
1387 sla.ConfigurableValues[axis][config] = remaining
1388 }
1389 }
1390 }
1391}
1392
Liz Kammera060c452021-03-24 10:14:47 -04001393// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1394// Starlark string.format compatible tag for productVariable.
1395func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
Liz Kammerf3963f82022-12-21 14:47:18 -05001396 if len(slice) == 0 {
1397 return slice, false
1398 }
Liz Kammera060c452021-03-24 10:14:47 -04001399 ret := make([]string, 0, len(slice))
1400 changesMade := false
1401 for _, s := range slice {
1402 newS, changed := TryVariableSubstitution(s, productVariable)
1403 ret = append(ret, newS)
1404 changesMade = changesMade || changed
1405 }
1406 return ret, changesMade
1407}
1408
1409// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1410// string.format compatible tag for productVariable.
1411func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001412 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001413 return sub, s != sub
1414}