blob: 9c63bc04b2701c03b88b5dbbd7a0c8f05c769b45 [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"
Trevor Radcliffe56b1a2b2023-02-06 21:58:30 +000020 "reflect"
Liz Kammera060c452021-03-24 10:14:47 -040021 "regexp"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000022 "sort"
Liz Kammer57e2e7a2021-09-20 12:55:02 -040023 "strings"
24
25 "github.com/google/blueprint"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000026)
Jingwen Chen5d864492021-02-24 07:20:12 -050027
Jingwen Chen73850672020-12-14 08:25:34 -050028// BazelTargetModuleProperties contain properties and metadata used for
29// Blueprint to BUILD file conversion.
30type BazelTargetModuleProperties struct {
31 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050032 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050033
34 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050035 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050036}
Liz Kammer356f7d42021-01-26 09:18:53 -050037
Liz Kammera060c452021-03-24 10:14:47 -040038var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
39
Jingwen Chen38e62642021-04-19 05:00:15 +000040// Label is used to represent a Bazel compatible Label. Also stores the original
41// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050042type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000043 // The string representation of a Bazel target label. This can be a relative
44 // or fully qualified label. These labels are used for generating BUILD
45 // files with bp2build.
46 Label string
47
48 // The original Soong/Blueprint module name that the label was derived from.
49 // This is used for replacing references to the original name with the new
50 // label, for example in genrule cmds.
51 //
52 // While there is a reversible 1:1 mapping from the module name to Bazel
53 // label with bp2build that could make computing the original module name
54 // from the label automatic, it is not the case for handcrafted targets,
55 // where modules can have a custom label mapping through the { bazel_module:
56 // { label: <label> } } property.
57 //
58 // With handcrafted labels, those modules don't go through bp2build
59 // conversion, but relies on handcrafted targets in the source tree.
60 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050061}
62
63// LabelList is used to represent a list of Bazel labels.
64type LabelList struct {
65 Includes []Label
66 Excludes []Label
67}
68
Sam Delmericoc0161432022-02-25 21:34:51 +000069// MakeLabelList creates a LabelList from a list Label
70func MakeLabelList(labels []Label) LabelList {
71 return LabelList{
72 Includes: labels,
73 Excludes: nil,
74 }
75}
76
Chris Parsons7b3289b2023-01-26 17:30:44 -050077func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
78 keys := make([]ConfigurationAxis, 0, len(m))
79 for k := range m {
80 keys = append(keys, k)
81 }
82
83 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
84 return keys
85}
86
Spandan Das4238c652022-09-09 01:38:47 +000087// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
88// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
89func MakeLabelListFromTargetNames(targetNames []string) LabelList {
90 labels := []Label{}
91 for _, name := range targetNames {
92 label := Label{Label: ":" + name}
93 labels = append(labels, label)
94 }
95 return MakeLabelList(labels)
96}
97
Chris Parsons51f8c392021-08-03 21:01:05 -040098func (ll *LabelList) Equals(other LabelList) bool {
99 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
100 return false
101 }
102 for i, _ := range ll.Includes {
103 if ll.Includes[i] != other.Includes[i] {
104 return false
105 }
106 }
107 for i, _ := range ll.Excludes {
108 if ll.Excludes[i] != other.Excludes[i] {
109 return false
110 }
111 }
112 return true
113}
114
Liz Kammer9abd62d2021-05-21 08:37:59 -0400115func (ll *LabelList) IsNil() bool {
116 return ll.Includes == nil && ll.Excludes == nil
117}
118
Sam Delmerico3177a6e2022-06-21 19:28:33 +0000119func (ll *LabelList) IsEmpty() bool {
120 return len(ll.Includes) == 0 && len(ll.Excludes) == 0
121}
122
Liz Kammer74deed42021-06-02 13:02:03 -0400123func (ll *LabelList) deepCopy() LabelList {
124 return LabelList{
125 Includes: ll.Includes[:],
126 Excludes: ll.Excludes[:],
127 }
128}
129
Jingwen Chen63930982021-03-24 10:04:33 -0400130// uniqueParentDirectories returns a list of the unique parent directories for
131// all files in ll.Includes.
132func (ll *LabelList) uniqueParentDirectories() []string {
133 dirMap := map[string]bool{}
134 for _, label := range ll.Includes {
135 dirMap[filepath.Dir(label.Label)] = true
136 }
137 dirs := []string{}
138 for dir := range dirMap {
139 dirs = append(dirs, dir)
140 }
141 return dirs
142}
143
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400144// Add inserts the label Label at the end of the LabelList.Includes.
Liz Kammer12615db2021-09-28 09:19:17 -0400145func (ll *LabelList) Add(label *Label) {
146 if label == nil {
147 return
148 }
149 ll.Includes = append(ll.Includes, *label)
150}
151
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400152// AddExclude inserts the label Label at the end of the LabelList.Excludes.
153func (ll *LabelList) AddExclude(label *Label) {
154 if label == nil {
155 return
156 }
157 ll.Excludes = append(ll.Excludes, *label)
158}
159
Liz Kammer356f7d42021-01-26 09:18:53 -0500160// Append appends the fields of other labelList to the corresponding fields of ll.
161func (ll *LabelList) Append(other LabelList) {
162 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
163 ll.Includes = append(ll.Includes, other.Includes...)
164 }
165 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
Liz Kammer748d7072023-01-25 12:07:43 -0500166 ll.Excludes = append(ll.Excludes, other.Excludes...)
Liz Kammer356f7d42021-01-26 09:18:53 -0500167 }
168}
Jingwen Chen5d864492021-02-24 07:20:12 -0500169
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400170// Partition splits a LabelList into two LabelLists depending on the return value
171// of the predicate.
172// This function preserves the Includes and Excludes, but it does not provide
173// that information to the partition function.
174func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
175 predicated := LabelList{}
176 unpredicated := LabelList{}
177 for _, include := range ll.Includes {
178 if predicate(include) {
179 predicated.Add(&include)
180 } else {
181 unpredicated.Add(&include)
182 }
183 }
184 for _, exclude := range ll.Excludes {
185 if predicate(exclude) {
186 predicated.AddExclude(&exclude)
187 } else {
188 unpredicated.AddExclude(&exclude)
189 }
190 }
191 return predicated, unpredicated
192}
193
Jingwen Chened9c17d2021-04-13 07:14:55 +0000194// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
195// the slice in a sorted order.
196func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Spandan Das5e04d482023-08-16 20:45:39 +0000197 uniqueLabels := FirstUniqueBazelLabels(originalLabels)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000198 sort.SliceStable(uniqueLabels, func(i, j int) bool {
199 return uniqueLabels[i].Label < uniqueLabels[j].Label
200 })
201 return uniqueLabels
202}
203
Liz Kammer9abd62d2021-05-21 08:37:59 -0400204func FirstUniqueBazelLabels(originalLabels []Label) []Label {
205 var labels []Label
Spandan Das5e04d482023-08-16 20:45:39 +0000206 found := make(map[string]bool, len(originalLabels))
Liz Kammer9abd62d2021-05-21 08:37:59 -0400207 for _, l := range originalLabels {
Spandan Das5e04d482023-08-16 20:45:39 +0000208 if _, ok := found[l.Label]; ok {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400209 continue
210 }
211 labels = append(labels, l)
Spandan Das5e04d482023-08-16 20:45:39 +0000212 found[l.Label] = true
Liz Kammer9abd62d2021-05-21 08:37:59 -0400213 }
214 return labels
215}
216
217func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
218 var uniqueLabelList LabelList
219 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
220 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
221 return uniqueLabelList
222}
223
224func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000225 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000226 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
227 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000228 return uniqueLabelList
229}
230
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000231// Subtract needle from haystack
232func SubtractStrings(haystack []string, needle []string) []string {
233 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400234 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000235 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400236 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000237 }
238
239 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400240 for _, s := range haystack {
241 if exclude := needleMap[s]; !exclude {
242 strings = append(strings, s)
243 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000244 }
245
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000246 return strings
247}
248
249// Subtract needle from haystack
250func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
251 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400252 needleMap := make(map[Label]bool)
253 for _, s := range needle {
254 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000255 }
256
257 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400258 for _, label := range haystack {
259 if exclude := needleMap[label]; !exclude {
260 labels = append(labels, label)
261 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000262 }
263
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000264 return labels
265}
266
Chris Parsons484e50a2021-05-13 15:13:04 -0400267// Appends two LabelLists, returning the combined list.
268func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
269 var result LabelList
270 result.Includes = append(a.Includes, b.Includes...)
271 result.Excludes = append(a.Excludes, b.Excludes...)
272 return result
273}
274
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000275// Subtract needle from haystack
276func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
277 var result LabelList
278 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
279 // NOTE: Excludes are intentionally not subtracted
280 result.Excludes = haystack.Excludes
281 return result
282}
283
Liz Kammer5f5dbaa2023-07-17 17:44:08 -0400284// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for
285// each axis/configuration by keeping the first instance of a Label and omitting all subsequent
286// repetitions.
287func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute {
288 var result LabelListAttribute
289 result.Value = FirstUniqueBazelLabelList(attr.Value)
290 if attr.HasConfigurableValues() {
291 result.ConfigurableValues = make(configurableLabelLists)
292 }
293 for axis, configToLabels := range attr.ConfigurableValues {
294 for c, l := range configToLabels {
295 result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l))
296 }
297 }
298
299 return result
300}
301
302// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each
303// axis/configuration.
304func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
305 var result LabelListAttribute
306 result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
307 if haystack.HasConfigurableValues() {
308 result.ConfigurableValues = make(configurableLabelLists)
309 }
310 for axis, configToLabels := range haystack.ConfigurableValues {
311 for haystackConfig, haystackLabels := range configToLabels {
312 result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig)))
313 }
314 }
315
316 return result
317}
318
Jingwen Chenc1c26502021-04-05 10:35:13 +0000319type Attribute interface {
320 HasConfigurableValues() bool
321}
322
Liz Kammer9abd62d2021-05-21 08:37:59 -0400323type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400324
Liz Kammer9abd62d2021-05-21 08:37:59 -0400325type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400326
Liz Kammer9abd62d2021-05-21 08:37:59 -0400327func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
328 if cl[axis] == nil {
329 cl[axis] = make(labelSelectValues)
330 }
331 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400332}
333
334// Represents an attribute whose value is a single label
335type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400336 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400337
Liz Kammer9abd62d2021-05-21 08:37:59 -0400338 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200339}
340
Chris Parsons58852a02021-12-09 18:10:18 -0500341func (la *LabelAttribute) axisTypes() map[configurationType]bool {
342 types := map[configurationType]bool{}
343 for k := range la.ConfigurableValues {
344 if len(la.ConfigurableValues[k]) > 0 {
345 types[k.configurationType] = true
346 }
347 }
348 return types
349}
350
351// Collapse reduces the configurable axes of the label attribute to a single axis.
352// This is necessary for final writing to bp2build, as a configurable label
353// attribute can only be comprised by a single select.
354func (la *LabelAttribute) Collapse() error {
355 axisTypes := la.axisTypes()
356 _, containsOs := axisTypes[os]
357 _, containsArch := axisTypes[arch]
358 _, containsOsArch := axisTypes[osArch]
359 _, containsProductVariables := axisTypes[productVariables]
360 if containsProductVariables {
361 if containsOs || containsArch || containsOsArch {
Alixbbfd5382022-06-09 18:52:05 +0000362 if containsArch {
363 allProductVariablesAreArchVariant := true
364 for k := range la.ConfigurableValues {
Cole Faust150f9a52023-04-26 10:52:24 -0700365 if k.configurationType == productVariables && !k.archVariant {
Alixbbfd5382022-06-09 18:52:05 +0000366 allProductVariablesAreArchVariant = false
367 }
368 }
369 if !allProductVariablesAreArchVariant {
370 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
371 }
372 } else {
373 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
374 }
Chris Parsons58852a02021-12-09 18:10:18 -0500375 }
376 }
377 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
378 // If a bool attribute has both os and arch configuration axes, the only
379 // way to successfully union their values is to increase the granularity
380 // of the configuration criteria to os_arch.
381 for osType, supportedArchs := range osToArchMap {
382 for _, supportedArch := range supportedArchs {
383 osArch := osArchString(osType, supportedArch)
384 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
385 // Do nothing, as the arch_os is explicitly defined already.
386 } else {
387 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
388 osVal := la.SelectValue(OsConfigurationAxis, osType)
389 if osVal != nil && archVal != nil {
390 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
391 // runs after os mutator.
392 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
393 } else if osVal != nil && archVal == nil {
394 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
395 } else if osVal == nil && archVal != nil {
396 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
397 }
398 }
399 }
400 }
401 // All os_arch values are now set. Clear os and arch axes.
402 delete(la.ConfigurableValues, ArchConfigurationAxis)
403 delete(la.ConfigurableValues, OsConfigurationAxis)
404 }
405 return nil
406}
407
Liz Kammer9abd62d2021-05-21 08:37:59 -0400408// HasConfigurableValues returns whether there are configurable values set for this label.
409func (la LabelAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500410 for _, selectValues := range la.ConfigurableValues {
411 if len(selectValues) > 0 {
412 return true
413 }
414 }
415 return false
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200416}
417
Liz Kammer9abd62d2021-05-21 08:37:59 -0400418// SetValue sets the base, non-configured value for the Label
419func (la *LabelAttribute) SetValue(value Label) {
420 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400421}
422
Liz Kammer9abd62d2021-05-21 08:37:59 -0400423// SetSelectValue set a value for a bazel select for the given axis, config and value.
424func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
425 axis.validateConfig(config)
426 switch axis.configurationType {
427 case noConfig:
428 la.Value = &value
Trevor Radcliffed9b7f172023-08-09 22:21:38 +0000429 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400430 if la.ConfigurableValues == nil {
431 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400432 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400433 la.ConfigurableValues.setValueForAxis(axis, config, &value)
434 default:
435 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
436 }
437}
438
439// SelectValue gets a value for a bazel select for the given axis and config.
Chris Parsons58852a02021-12-09 18:10:18 -0500440func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400441 axis.validateConfig(config)
442 switch axis.configurationType {
443 case noConfig:
Chris Parsons58852a02021-12-09 18:10:18 -0500444 return la.Value
Trevor Radcliffed9b7f172023-08-09 22:21:38 +0000445 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
Chris Parsons58852a02021-12-09 18:10:18 -0500446 return la.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400447 default:
448 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
449 }
450}
451
452// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
453func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -0500454 return SortedConfigurationAxes(la.ConfigurableValues)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400455}
456
Sam Delmericoc0161432022-02-25 21:34:51 +0000457// MakeLabelAttribute turns a string into a LabelAttribute
458func MakeLabelAttribute(label string) *LabelAttribute {
459 return &LabelAttribute{
460 Value: &Label{
461 Label: label,
462 },
463 }
464}
465
Liz Kammerd366c902021-06-03 13:43:01 -0400466type configToBools map[string]bool
467
468func (ctb configToBools) setValue(config string, value *bool) {
469 if value == nil {
470 if _, ok := ctb[config]; ok {
471 delete(ctb, config)
472 }
473 return
474 }
475 ctb[config] = *value
476}
477
478type configurableBools map[ConfigurationAxis]configToBools
479
480func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
481 if cb[axis] == nil {
482 cb[axis] = make(configToBools)
483 }
484 cb[axis].setValue(config, value)
485}
486
487// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
488type BoolAttribute struct {
489 Value *bool
490
491 ConfigurableValues configurableBools
492}
493
494// HasConfigurableValues returns whether there are configurable values for this attribute.
495func (ba BoolAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500496 for _, cfgToBools := range ba.ConfigurableValues {
497 if len(cfgToBools) > 0 {
498 return true
499 }
500 }
501 return false
Liz Kammerd366c902021-06-03 13:43:01 -0400502}
503
Liz Kammerdfeb1202022-05-13 17:20:20 -0400504// SetValue sets value for the no config axis
505func (ba *BoolAttribute) SetValue(value *bool) {
506 ba.SetSelectValue(NoConfigAxis, "", value)
507}
508
Liz Kammerd366c902021-06-03 13:43:01 -0400509// SetSelectValue sets value for the given axis/config.
510func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
511 axis.validateConfig(config)
512 switch axis.configurationType {
513 case noConfig:
514 ba.Value = value
Trevor Radcliffed9b7f172023-08-09 22:21:38 +0000515 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
Liz Kammerd366c902021-06-03 13:43:01 -0400516 if ba.ConfigurableValues == nil {
517 ba.ConfigurableValues = make(configurableBools)
518 }
519 ba.ConfigurableValues.setValueForAxis(axis, config, value)
520 default:
521 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
522 }
523}
524
Chris Parsons58852a02021-12-09 18:10:18 -0500525// ToLabelListAttribute creates and returns a LabelListAttribute from this
526// bool attribute, where each bool in this attribute corresponds to a
527// label list value in the resultant attribute.
528func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
529 getLabelList := func(boolPtr *bool) LabelList {
530 if boolPtr == nil {
531 return LabelList{nil, nil}
532 } else if *boolPtr {
533 return trueVal
534 } else {
535 return falseVal
536 }
537 }
538
539 mainVal := getLabelList(ba.Value)
540 if !ba.HasConfigurableValues() {
541 return MakeLabelListAttribute(mainVal), nil
542 }
543
544 result := LabelListAttribute{}
545 if err := ba.Collapse(); err != nil {
546 return result, err
547 }
548
549 for axis, configToBools := range ba.ConfigurableValues {
550 if len(configToBools) < 1 {
551 continue
552 }
553 for config, boolPtr := range configToBools {
554 val := getLabelList(&boolPtr)
555 if !val.Equals(mainVal) {
556 result.SetSelectValue(axis, config, val)
557 }
558 }
559 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
560 }
561
562 return result, nil
563}
564
Trevor Radcliffe56b1a2b2023-02-06 21:58:30 +0000565// ToStringListAttribute creates a StringListAttribute from this BoolAttribute,
566// where each bool corresponds to a string list value generated by the provided
567// function.
568// TODO(b/271425661): Generalize this
569func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) {
570 mainVal := valueFunc(ba.Value, NoConfigAxis, "")
571 if !ba.HasConfigurableValues() {
572 return MakeStringListAttribute(mainVal), nil
573 }
574
575 result := StringListAttribute{}
576 if err := ba.Collapse(); err != nil {
577 return result, err
578 }
579
580 for axis, configToBools := range ba.ConfigurableValues {
581 if len(configToBools) < 1 {
582 continue
583 }
584 for config, boolPtr := range configToBools {
585 val := valueFunc(&boolPtr, axis, config)
586 if !reflect.DeepEqual(val, mainVal) {
587 result.SetSelectValue(axis, config, val)
588 }
589 }
590 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
591 }
592
593 return result, nil
594}
595
Chris Parsons58852a02021-12-09 18:10:18 -0500596// Collapse reduces the configurable axes of the boolean attribute to a single axis.
597// This is necessary for final writing to bp2build, as a configurable boolean
598// attribute can only be comprised by a single select.
599func (ba *BoolAttribute) Collapse() error {
600 axisTypes := ba.axisTypes()
601 _, containsOs := axisTypes[os]
602 _, containsArch := axisTypes[arch]
603 _, containsOsArch := axisTypes[osArch]
604 _, containsProductVariables := axisTypes[productVariables]
605 if containsProductVariables {
606 if containsOs || containsArch || containsOsArch {
607 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
608 }
609 }
610 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
611 // If a bool attribute has both os and arch configuration axes, the only
612 // way to successfully union their values is to increase the granularity
613 // of the configuration criteria to os_arch.
614 for osType, supportedArchs := range osToArchMap {
615 for _, supportedArch := range supportedArchs {
616 osArch := osArchString(osType, supportedArch)
617 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
618 // Do nothing, as the arch_os is explicitly defined already.
619 } else {
620 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
621 osVal := ba.SelectValue(OsConfigurationAxis, osType)
622 if osVal != nil && archVal != nil {
623 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
624 // runs after os mutator.
625 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
626 } else if osVal != nil && archVal == nil {
627 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
628 } else if osVal == nil && archVal != nil {
629 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
630 }
631 }
632 }
633 }
634 // All os_arch values are now set. Clear os and arch axes.
635 delete(ba.ConfigurableValues, ArchConfigurationAxis)
636 delete(ba.ConfigurableValues, OsConfigurationAxis)
637 // Verify post-condition; this should never fail, provided no additional
638 // axes are introduced.
639 if len(ba.ConfigurableValues) > 1 {
Liz Kammer07e106f2022-01-13 17:00:10 -0500640 panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
Chris Parsons58852a02021-12-09 18:10:18 -0500641 }
642 }
643 return nil
644}
645
646func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
647 types := map[configurationType]bool{}
648 for k := range ba.ConfigurableValues {
649 if len(ba.ConfigurableValues[k]) > 0 {
650 types[k.configurationType] = true
651 }
652 }
653 return types
654}
655
Liz Kammerd366c902021-06-03 13:43:01 -0400656// SelectValue gets the value for the given axis/config.
657func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
658 axis.validateConfig(config)
659 switch axis.configurationType {
660 case noConfig:
661 return ba.Value
Trevor Radcliffed9b7f172023-08-09 22:21:38 +0000662 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
Liz Kammerd366c902021-06-03 13:43:01 -0400663 if v, ok := ba.ConfigurableValues[axis][config]; ok {
664 return &v
665 } else {
666 return nil
667 }
668 default:
669 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
670 }
671}
672
673// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
674func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -0500675 return SortedConfigurationAxes(ba.ConfigurableValues)
Liz Kammerd366c902021-06-03 13:43:01 -0400676}
677
Liz Kammer9abd62d2021-05-21 08:37:59 -0400678// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
679type labelListSelectValues map[string]LabelList
680
Liz Kammer12615db2021-09-28 09:19:17 -0400681func (ll labelListSelectValues) addSelects(label labelSelectValues) {
682 for k, v := range label {
683 if label == nil {
684 continue
685 }
686 l := ll[k]
687 (&l).Add(v)
688 ll[k] = l
689 }
690}
691
Chris Parsons77acf2e2021-12-03 17:27:16 -0500692func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400693 for k, v := range other {
694 l := ll[k]
Chris Parsons77acf2e2021-12-03 17:27:16 -0500695 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
696 l.Includes = []Label{}
697 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400698 (&l).Append(v)
699 ll[k] = l
700 }
701}
702
703// HasConfigurableValues returns whether there are configurable values within this set of selects.
704func (ll labelListSelectValues) HasConfigurableValues() bool {
705 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400706 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400707 return true
708 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400709 }
710 return false
711}
712
Jingwen Chen07027912021-03-15 06:02:43 -0400713// LabelListAttribute is used to represent a list of Bazel labels as an
714// attribute.
715type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400716 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400717 Value LabelList
718
Liz Kammer9abd62d2021-05-21 08:37:59 -0400719 // The configured attribute label list Values. Optional
720 // a map of independent configurability axes
721 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400722
723 // If true, differentiate between "nil" and "empty" list. nil means that
724 // this attribute should not be specified at all, and "empty" means that
725 // the attribute should be explicitly specified as an empty list.
726 // This mode facilitates use of attribute defaults: an empty list should
727 // override the default.
728 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000729
730 // If true, signal the intent to the code generator to emit all select keys,
731 // even if the Includes list for that key is empty. This mode facilitates
732 // specific select statements where an empty list for a non-default select
733 // key has a meaning.
734 EmitEmptyList bool
Zi Wang9f609db2023-01-04 11:06:54 -0800735
736 // If a property has struct tag "variant_prepend", this value should
737 // be set to True, so that when bp2build generates BUILD.bazel, variant
738 // properties(select ...) come before general properties.
739 Prepend bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400740}
Jingwen Chen91220d72021-03-24 02:18:33 -0400741
Liz Kammer9abd62d2021-05-21 08:37:59 -0400742type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
743
744func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
745 if list.IsNil() {
746 if _, ok := cll[axis][config]; ok {
747 delete(cll[axis], config)
748 }
749 return
750 }
751 if cll[axis] == nil {
752 cll[axis] = make(labelListSelectValues)
753 }
754
755 cll[axis][config] = list
756}
757
Chris Parsons77acf2e2021-12-03 17:27:16 -0500758func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400759 for axis, otherSelects := range other {
760 selects := cll[axis]
761 if selects == nil {
762 selects = make(labelListSelectValues, len(otherSelects))
763 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500764 selects.appendSelects(otherSelects, forceSpecifyEmptyList)
Liz Kammer9abd62d2021-05-21 08:37:59 -0400765 cll[axis] = selects
766 }
Jingwen Chen07027912021-03-15 06:02:43 -0400767}
768
Chris Parsons77acf2e2021-12-03 17:27:16 -0500769func (lla *LabelListAttribute) Clone() *LabelListAttribute {
770 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
771 return result.Append(*lla)
772}
773
Jingwen Chen07027912021-03-15 06:02:43 -0400774// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
775func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400776 return LabelListAttribute{
777 Value: value,
778 ConfigurableValues: make(configurableLabelLists),
779 }
780}
781
Cole Faust53b62092022-05-12 15:37:02 -0700782// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
783func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
784 return MakeLabelListAttribute(MakeLabelList([]Label{value}))
785}
786
Liz Kammer9abd62d2021-05-21 08:37:59 -0400787func (lla *LabelListAttribute) SetValue(list LabelList) {
788 lla.SetSelectValue(NoConfigAxis, "", list)
789}
790
791// SetSelectValue set a value for a bazel select for the given axis, config and value.
792func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
793 axis.validateConfig(config)
794 switch axis.configurationType {
795 case noConfig:
796 lla.Value = list
Trevor Radcliffed9b7f172023-08-09 22:21:38 +0000797 case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400798 if lla.ConfigurableValues == nil {
799 lla.ConfigurableValues = make(configurableLabelLists)
800 }
801 lla.ConfigurableValues.setValueForAxis(axis, config, list)
802 default:
803 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
804 }
805}
806
807// SelectValue gets a value for a bazel select for the given axis and config.
808func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
809 axis.validateConfig(config)
810 switch axis.configurationType {
811 case noConfig:
812 return lla.Value
Trevor Radcliffed9b7f172023-08-09 22:21:38 +0000813 case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
Cole Faustc843b992022-08-02 18:06:50 -0700814 return lla.ConfigurableValues[axis][config]
Liz Kammer9abd62d2021-05-21 08:37:59 -0400815 default:
816 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
817 }
818}
819
820// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
821func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -0500822 return SortedConfigurationAxes(lla.ConfigurableValues)
Jingwen Chen07027912021-03-15 06:02:43 -0400823}
824
Jingwen Chened9c17d2021-04-13 07:14:55 +0000825// Append all values, including os and arch specific ones, from another
Chris Parsons77acf2e2021-12-03 17:27:16 -0500826// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
827func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
828 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
829 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
Chris Parsons51f8c392021-08-03 21:01:05 -0400830 lla.Value.Includes = []Label{}
831 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400832 lla.Value.Append(other.Value)
833 if lla.ConfigurableValues == nil {
834 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400835 }
Chris Parsons77acf2e2021-12-03 17:27:16 -0500836 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
837 return lla
Jingwen Chen63930982021-03-24 10:04:33 -0400838}
839
Liz Kammer12615db2021-09-28 09:19:17 -0400840// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
841// LabelList within the LabelListAttribute
842func (lla *LabelListAttribute) Add(label *LabelAttribute) {
843 if label == nil {
844 return
845 }
846
847 lla.Value.Add(label.Value)
848 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
849 lla.ConfigurableValues = make(configurableLabelLists)
850 }
851 for axis, _ := range label.ConfigurableValues {
852 if _, exists := lla.ConfigurableValues[axis]; !exists {
853 lla.ConfigurableValues[axis] = make(labelListSelectValues)
854 }
855 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
856 }
857}
858
Liz Kammer9abd62d2021-05-21 08:37:59 -0400859// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
860func (lla LabelListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -0500861 for _, selectValues := range lla.ConfigurableValues {
862 if len(selectValues) > 0 {
863 return true
864 }
865 }
866 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400867}
868
Trevor Radcliffe0d1b4022022-12-12 22:26:34 +0000869// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
870func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
871 for _, values := range lla.ConfigurableValues[axis] {
872 if !values.IsNil() {
873 return true
874 }
875 }
876 return false
877}
878
Chris Parsons69fa9f92021-07-13 11:47:44 -0400879// IsEmpty returns true if the attribute has no values under any configuration.
880func (lla LabelListAttribute) IsEmpty() bool {
881 if len(lla.Value.Includes) > 0 {
882 return false
883 }
884 for axis, _ := range lla.ConfigurableValues {
885 if lla.ConfigurableValues[axis].HasConfigurableValues() {
886 return false
887 }
888 }
889 return true
890}
891
Liz Kammer54309532021-12-14 12:21:22 -0500892// IsNil returns true if the attribute has not been set for any configuration.
893func (lla LabelListAttribute) IsNil() bool {
894 if lla.Value.Includes != nil {
895 return false
896 }
897 return !lla.HasConfigurableValues()
898}
899
900// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
901// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
902// be removed, e.g. if they could cause duplicate element failures.
903func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
904 val := lla.SelectValue(axis, config)
905 newList := SubtractBazelLabelList(val, labelList)
906 newList.Excludes = append(newList.Excludes, labelList.Includes...)
907 lla.SetSelectValue(axis, config, newList)
908}
909
Liz Kammer74deed42021-06-02 13:02:03 -0400910// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
911// the base value and included in default values as appropriate.
912func (lla *LabelListAttribute) ResolveExcludes() {
Liz Kammerffc17e42022-11-23 09:42:05 -0500913 // If there are OsAndInApexAxis, we need to use
914 // * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
915 // included in non-Android OSes
916 // * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
917 // be included in the non-Android OSes
918 if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
919 inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
920 for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
921 // OsAndroid has already handled its excludes.
922 // We only need to copy the excludes from other arches, so if there are none, skip it.
923 if config == OsAndroid || len(labels.Excludes) == 0 {
924 continue
925 }
926 lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
927 Includes: inApexLabels.Includes,
928 Excludes: labels.Excludes,
929 }
930 }
931 }
932
Liz Kammer74deed42021-06-02 13:02:03 -0400933 for axis, configToLabels := range lla.ConfigurableValues {
934 baseLabels := lla.Value.deepCopy()
935 for config, val := range configToLabels {
936 // Exclude config-specific excludes from base value
937 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
938
939 // add base values to config specific to add labels excluded by others in this axis
940 // then remove all config-specific excludes
941 allLabels := baseLabels.deepCopy()
942 allLabels.Append(val)
Liz Kammer748d7072023-01-25 12:07:43 -0500943 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
Liz Kammer74deed42021-06-02 13:02:03 -0400944 }
945
946 // After going through all configs, delete the duplicates in the config
947 // values that are already in the base Value.
948 for config, val := range configToLabels {
949 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
950 }
951
Jingwen Chen9af49a42021-11-02 10:27:17 +0000952 // Now that the Value list is finalized for this axis, compare it with
953 // the original list, and union the difference with the default
954 // condition for the axis.
955 difference := SubtractBazelLabelList(baseLabels, lla.Value)
956 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
957 existingDefaults.Append(difference)
958 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400959
960 // if everything ends up without includes, just delete the axis
961 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
962 delete(lla.ConfigurableValues, axis)
963 }
964 }
965}
966
Sam Delmericoc1130dc2022-08-25 14:43:54 -0400967// Partition splits a LabelListAttribute into two LabelListAttributes depending
968// on the return value of the predicate.
969// This function preserves the Includes and Excludes, but it does not provide
970// that information to the partition function.
971func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
972 predicated := LabelListAttribute{}
973 unpredicated := LabelListAttribute{}
974
975 valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
976 predicated.SetValue(valuePartitionTrue)
977 unpredicated.SetValue(valuePartitionFalse)
978
979 for axis, selectValueLabelLists := range lla.ConfigurableValues {
980 for config, labelList := range selectValueLabelLists {
981 configPredicated, configUnpredicated := labelList.Partition(predicate)
982 predicated.SetSelectValue(axis, config, configPredicated)
983 unpredicated.SetSelectValue(axis, config, configUnpredicated)
984 }
985 }
986
987 return predicated, unpredicated
988}
989
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400990// OtherModuleContext is a limited context that has methods with information about other modules.
991type OtherModuleContext interface {
992 ModuleFromName(name string) (blueprint.Module, bool)
993 OtherModuleType(m blueprint.Module) string
994 OtherModuleName(m blueprint.Module) string
995 OtherModuleDir(m blueprint.Module) string
996 ModuleErrorf(fmt string, args ...interface{})
997}
998
999// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
1000// label and whether it was changed.
Liz Kammer12615db2021-09-28 09:19:17 -04001001type LabelMapper func(OtherModuleContext, Label) (string, bool)
Liz Kammer57e2e7a2021-09-20 12:55:02 -04001002
1003// LabelPartition contains descriptions of a partition for labels
1004type LabelPartition struct {
1005 // Extensions to include in this partition
1006 Extensions []string
1007 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
1008 // the mapped label in the partition
1009 LabelMapper LabelMapper
1010 // Whether to store files not included in any other partition in a group of LabelPartitions
1011 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
1012 Keep_remainder bool
1013}
1014
1015// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
1016// partition
1017type LabelPartitions map[string]LabelPartition
1018
1019// filter returns a pointer to a label if the label should be included in the partition or nil if
1020// not.
1021func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
1022 if lf.LabelMapper != nil {
Liz Kammer12615db2021-09-28 09:19:17 -04001023 if newLabel, changed := lf.LabelMapper(ctx, label); changed {
Liz Kammer57e2e7a2021-09-20 12:55:02 -04001024 return &Label{newLabel, label.OriginalModuleName}
1025 }
1026 }
1027 for _, ext := range lf.Extensions {
1028 if strings.HasSuffix(label.Label, ext) {
1029 return &label
1030 }
1031 }
1032
1033 return nil
1034}
1035
1036// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
1037type PartitionToLabelListAttribute map[string]LabelListAttribute
1038
1039type partitionToLabelList map[string]*LabelList
1040
1041func (p partitionToLabelList) appendIncludes(partition string, label Label) {
1042 if _, ok := p[partition]; !ok {
1043 p[partition] = &LabelList{}
1044 }
1045 p[partition].Includes = append(p[partition].Includes, label)
1046}
1047
1048func (p partitionToLabelList) excludes(partition string, excludes []Label) {
1049 if _, ok := p[partition]; !ok {
1050 p[partition] = &LabelList{}
1051 }
1052 p[partition].Excludes = excludes
1053}
1054
1055// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
1056func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
1057 ret := PartitionToLabelListAttribute{}
1058 var partitionNames []string
1059 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
1060 var remainderPartition *string
1061 for p, f := range partitions {
1062 partitionNames = append(partitionNames, p)
1063 if f.Keep_remainder {
1064 if remainderPartition != nil {
1065 panic("only one partition can store the remainder")
1066 }
1067 // If we take the address of p in a loop, we'll end up with the last value of p in
1068 // remainderPartition, we want the requested partition
1069 capturePartition := p
1070 remainderPartition = &capturePartition
1071 }
1072 }
1073
1074 partitionLabelList := func(axis ConfigurationAxis, config string) {
1075 value := lla.SelectValue(axis, config)
1076 partitionToLabels := partitionToLabelList{}
1077 for _, item := range value.Includes {
1078 wasFiltered := false
1079 var inPartition *string
1080 for partition, f := range partitions {
1081 filtered := f.filter(ctx, item)
1082 if filtered == nil {
1083 // did not match this filter, keep looking
1084 continue
1085 }
1086 wasFiltered = true
1087 partitionToLabels.appendIncludes(partition, *filtered)
1088 // don't need to check other partitions if this filter used the item,
1089 // continue checking if mapped to another name
1090 if *filtered == item {
1091 if inPartition != nil {
1092 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
1093 }
1094 capturePartition := partition
1095 inPartition = &capturePartition
1096 }
1097 }
1098
1099 // if not specified in a partition, add to remainder partition if one exists
1100 if !wasFiltered && remainderPartition != nil {
1101 partitionToLabels.appendIncludes(*remainderPartition, item)
1102 }
1103 }
1104
1105 // ensure empty lists are maintained
1106 if value.Excludes != nil {
1107 for _, partition := range partitionNames {
1108 partitionToLabels.excludes(partition, value.Excludes)
1109 }
1110 }
1111
1112 for partition, list := range partitionToLabels {
1113 val := ret[partition]
1114 (&val).SetSelectValue(axis, config, *list)
1115 ret[partition] = val
1116 }
1117 }
1118
1119 partitionLabelList(NoConfigAxis, "")
1120 for axis, configToList := range lla.ConfigurableValues {
1121 for config, _ := range configToList {
1122 partitionLabelList(axis, config)
1123 }
1124 }
1125 return ret
1126}
1127
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001128// StringAttribute corresponds to the string Bazel attribute type with
1129// support for additional metadata, like configurations.
1130type StringAttribute struct {
1131 // The base value of the string attribute.
1132 Value *string
1133
1134 // The configured attribute label list Values. Optional
1135 // a map of independent configurability axes
1136 ConfigurableValues configurableStrings
1137}
1138
1139type configurableStrings map[ConfigurationAxis]stringSelectValues
1140
1141func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
1142 if cs[axis] == nil {
1143 cs[axis] = make(stringSelectValues)
1144 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001145 cs[axis][config] = str
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001146}
1147
Liz Kammer9d2d4102022-12-21 14:51:37 -05001148type stringSelectValues map[string]*string
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001149
1150// HasConfigurableValues returns true if the attribute contains axis-specific string values.
1151func (sa StringAttribute) HasConfigurableValues() bool {
1152 for _, selectValues := range sa.ConfigurableValues {
1153 if len(selectValues) > 0 {
1154 return true
1155 }
1156 }
1157 return false
1158}
1159
Liz Kammer9d2d4102022-12-21 14:51:37 -05001160// SetValue sets the base, non-configured value for the Label
1161func (sa *StringAttribute) SetValue(value string) {
1162 sa.SetSelectValue(NoConfigAxis, "", &value)
1163}
1164
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001165// SetSelectValue set a value for a bazel select for the given axis, config and value.
1166func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
1167 axis.validateConfig(config)
1168 switch axis.configurationType {
1169 case noConfig:
1170 sa.Value = str
Trevor Radcliffed9b7f172023-08-09 22:21:38 +00001171 case arch, os, osArch, productVariables, sanitizersEnabled:
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001172 if sa.ConfigurableValues == nil {
1173 sa.ConfigurableValues = make(configurableStrings)
1174 }
1175 sa.ConfigurableValues.setValueForAxis(axis, config, str)
1176 default:
1177 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1178 }
1179}
1180
1181// SelectValue gets a value for a bazel select for the given axis and config.
1182func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
1183 axis.validateConfig(config)
1184 switch axis.configurationType {
1185 case noConfig:
1186 return sa.Value
Trevor Radcliffed9b7f172023-08-09 22:21:38 +00001187 case arch, os, osArch, productVariables, sanitizersEnabled:
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001188 if v, ok := sa.ConfigurableValues[axis][config]; ok {
Liz Kammer9d2d4102022-12-21 14:51:37 -05001189 return v
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001190 } else {
1191 return nil
1192 }
1193 default:
1194 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1195 }
1196}
1197
1198// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1199func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -05001200 return SortedConfigurationAxes(sa.ConfigurableValues)
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001201}
1202
1203// Collapse reduces the configurable axes of the string attribute to a single axis.
1204// This is necessary for final writing to bp2build, as a configurable string
1205// attribute can only be comprised by a single select.
1206func (sa *StringAttribute) Collapse() error {
1207 axisTypes := sa.axisTypes()
1208 _, containsOs := axisTypes[os]
1209 _, containsArch := axisTypes[arch]
1210 _, containsOsArch := axisTypes[osArch]
1211 _, containsProductVariables := axisTypes[productVariables]
1212 if containsProductVariables {
1213 if containsOs || containsArch || containsOsArch {
Liz Kammer9d2d4102022-12-21 14:51:37 -05001214 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 +00001215 }
1216 }
1217 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
1218 // If a bool attribute has both os and arch configuration axes, the only
1219 // way to successfully union their values is to increase the granularity
1220 // of the configuration criteria to os_arch.
1221 for osType, supportedArchs := range osToArchMap {
1222 for _, supportedArch := range supportedArchs {
1223 osArch := osArchString(osType, supportedArch)
1224 if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
1225 // Do nothing, as the arch_os is explicitly defined already.
1226 } else {
1227 archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
1228 osVal := sa.SelectValue(OsConfigurationAxis, osType)
1229 if osVal != nil && archVal != nil {
1230 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
1231 // runs after os mutator.
1232 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1233 } else if osVal != nil && archVal == nil {
1234 sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
1235 } else if osVal == nil && archVal != nil {
1236 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1237 }
1238 }
1239 }
1240 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001241 /// 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 +00001242 delete(sa.ConfigurableValues, ArchConfigurationAxis)
1243 delete(sa.ConfigurableValues, OsConfigurationAxis)
1244 // Verify post-condition; this should never fail, provided no additional
1245 // axes are introduced.
1246 if len(sa.ConfigurableValues) > 1 {
1247 panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
1248 }
Liz Kammer9d2d4102022-12-21 14:51:37 -05001249 } else if containsProductVariables {
1250 usedBaseValue := false
1251 for a, configToProp := range sa.ConfigurableValues {
1252 if a.configurationType == productVariables {
1253 for c, p := range configToProp {
1254 if p == nil {
1255 sa.SetSelectValue(a, c, sa.Value)
1256 usedBaseValue = true
1257 }
1258 }
1259 }
1260 }
1261 if usedBaseValue {
1262 sa.Value = nil
1263 }
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux3a019a62022-06-23 16:02:44 +00001264 }
1265 return nil
1266}
1267
1268func (sa *StringAttribute) axisTypes() map[configurationType]bool {
1269 types := map[configurationType]bool{}
1270 for k := range sa.ConfigurableValues {
1271 if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
1272 types[k.configurationType] = true
1273 }
1274 }
1275 return types
1276}
1277
Jingwen Chen5d864492021-02-24 07:20:12 -05001278// StringListAttribute corresponds to the string_list Bazel attribute type with
1279// support for additional metadata, like configurations.
1280type StringListAttribute struct {
1281 // The base value of the string list attribute.
1282 Value []string
1283
Liz Kammer9abd62d2021-05-21 08:37:59 -04001284 // The configured attribute label list Values. Optional
1285 // a map of independent configurability axes
1286 ConfigurableValues configurableStringLists
Zi Wang1cb11802022-12-09 16:08:54 -08001287
1288 // If a property has struct tag "variant_prepend", this value should
1289 // be set to True, so that when bp2build generates BUILD.bazel, variant
1290 // properties(select ...) come before general properties.
1291 Prepend bool
Liz Kammer9abd62d2021-05-21 08:37:59 -04001292}
Jingwen Chenc1c26502021-04-05 10:35:13 +00001293
Spandan Das4238c652022-09-09 01:38:47 +00001294// IsEmpty returns true if the attribute has no values under any configuration.
1295func (sla StringListAttribute) IsEmpty() bool {
1296 return len(sla.Value) == 0 && !sla.HasConfigurableValues()
1297}
1298
Liz Kammer9abd62d2021-05-21 08:37:59 -04001299type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -04001300
Liz Kammer9abd62d2021-05-21 08:37:59 -04001301func (csl configurableStringLists) Append(other configurableStringLists) {
1302 for axis, otherSelects := range other {
1303 selects := csl[axis]
1304 if selects == nil {
1305 selects = make(stringListSelectValues, len(otherSelects))
1306 }
1307 selects.appendSelects(otherSelects)
1308 csl[axis] = selects
1309 }
1310}
1311
1312func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
1313 if csl[axis] == nil {
1314 csl[axis] = make(stringListSelectValues)
1315 }
1316 csl[axis][config] = list
1317}
1318
1319type stringListSelectValues map[string][]string
1320
1321func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
1322 for k, v := range other {
1323 sl[k] = append(sl[k], v...)
1324 }
1325}
1326
1327func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
1328 for _, val := range sl {
1329 if len(val) > 0 {
1330 return true
1331 }
1332 }
1333 return false
Jingwen Chen5d864492021-02-24 07:20:12 -05001334}
1335
Rupert Shuttleworthb8151682021-04-06 20:06:21 +00001336// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1337func MakeStringListAttribute(value []string) StringListAttribute {
1338 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -04001339 return StringListAttribute{
1340 Value: value,
1341 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -04001342 }
1343}
1344
Liz Kammer9abd62d2021-05-21 08:37:59 -04001345// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1346func (sla StringListAttribute) HasConfigurableValues() bool {
Chris Parsons58852a02021-12-09 18:10:18 -05001347 for _, selectValues := range sla.ConfigurableValues {
1348 if len(selectValues) > 0 {
1349 return true
1350 }
1351 }
1352 return false
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -04001353}
1354
Jingwen Chened9c17d2021-04-13 07:14:55 +00001355// Append appends all values, including os and arch specific ones, from another
1356// StringListAttribute to this StringListAttribute
Chris Parsons77acf2e2021-12-03 17:27:16 -05001357func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -04001358 sla.Value = append(sla.Value, other.Value...)
1359 if sla.ConfigurableValues == nil {
1360 sla.ConfigurableValues = make(configurableStringLists)
1361 }
1362 sla.ConfigurableValues.Append(other.ConfigurableValues)
Chris Parsons77acf2e2021-12-03 17:27:16 -05001363 return sla
1364}
1365
1366func (sla *StringListAttribute) Clone() *StringListAttribute {
1367 result := &StringListAttribute{}
1368 return result.Append(*sla)
Liz Kammer9abd62d2021-05-21 08:37:59 -04001369}
1370
1371// SetSelectValue set a value for a bazel select for the given axis, config and value.
1372func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1373 axis.validateConfig(config)
1374 switch axis.configurationType {
1375 case noConfig:
1376 sla.Value = list
Trevor Radcliffed9b7f172023-08-09 22:21:38 +00001377 case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001378 if sla.ConfigurableValues == nil {
1379 sla.ConfigurableValues = make(configurableStringLists)
1380 }
1381 sla.ConfigurableValues.setValueForAxis(axis, config, list)
1382 default:
1383 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1384 }
1385}
1386
1387// SelectValue gets a value for a bazel select for the given axis and config.
1388func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1389 axis.validateConfig(config)
1390 switch axis.configurationType {
1391 case noConfig:
1392 return sla.Value
Trevor Radcliffed9b7f172023-08-09 22:21:38 +00001393 case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
Liz Kammer9abd62d2021-05-21 08:37:59 -04001394 return sla.ConfigurableValues[axis][config]
1395 default:
1396 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1397 }
1398}
1399
1400// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1401func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
Chris Parsons7b3289b2023-01-26 17:30:44 -05001402 return SortedConfigurationAxes(sla.ConfigurableValues)
Jingwen Chened9c17d2021-04-13 07:14:55 +00001403}
1404
Liz Kammer5fad5012021-09-09 14:08:21 -04001405// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1406// configuration-specific values. For example, if we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001407//
1408// ["a", "b", "c"] + select({
1409// "//condition:one": ["a", "d"],
1410// "//conditions:default": [],
1411// })
1412//
Liz Kammer5fad5012021-09-09 14:08:21 -04001413// after this function, we would convert this StringListAttribute as:
Colin Crossd079e0b2022-08-16 10:27:33 -07001414//
1415// ["a", "b", "c"] + select({
1416// "//condition:one": ["d"],
1417// "//conditions:default": [],
1418// })
Liz Kammer5fad5012021-09-09 14:08:21 -04001419func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1420 base := sla.Value
1421 for axis, configToList := range sla.ConfigurableValues {
1422 for config, list := range configToList {
1423 remaining := SubtractStrings(list, base)
1424 if len(remaining) == 0 {
1425 delete(sla.ConfigurableValues[axis], config)
1426 } else {
1427 sla.ConfigurableValues[axis][config] = remaining
1428 }
1429 }
1430 }
1431}
1432
Liz Kammera060c452021-03-24 10:14:47 -04001433// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1434// Starlark string.format compatible tag for productVariable.
1435func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
Liz Kammerf3963f82022-12-21 14:47:18 -05001436 if len(slice) == 0 {
1437 return slice, false
1438 }
Liz Kammera060c452021-03-24 10:14:47 -04001439 ret := make([]string, 0, len(slice))
1440 changesMade := false
1441 for _, s := range slice {
1442 newS, changed := TryVariableSubstitution(s, productVariable)
1443 ret = append(ret, newS)
1444 changesMade = changesMade || changed
1445 }
1446 return ret, changesMade
1447}
1448
1449// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1450// string.format compatible tag for productVariable.
1451func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -04001452 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -04001453 return sub, s != sub
1454}
Spandan Das6a448ec2023-04-19 17:36:12 +00001455
1456// StringMapAttribute is a map of strings.
1457// The use case for this is storing the flag_values in a config_setting object.
1458// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules.
1459type StringMapAttribute map[string]string
1460
1461// ConfigSettingAttributes stores the keys of a config_setting object.
1462type ConfigSettingAttributes struct {
1463 // Each key in Flag_values is a label to a custom string_setting
1464 Flag_values StringMapAttribute
Spandan Das9cad90f2023-05-04 17:15:44 +00001465 // Each element in Constraint_values is a label to a constraint_value
1466 Constraint_values LabelListAttribute
Spandan Das6a448ec2023-04-19 17:36:12 +00001467}