blob: 76e73c588a570eabc97daad9dd89ad081381b5fd [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 Chena47f28d2021-11-02 16:43:57 +000027type BazelModuleProperties struct {
28 // The label of the Bazel target replacing this Soong module. When run in conversion mode, this
29 // will import the handcrafted build target into the autogenerated file. Note: this may result in
30 // a conflict due to duplicate targets if bp2build_available is also set.
31 Label *string
32
33 // If true, bp2build will generate the converted Bazel target for this module. Note: this may
34 // cause a conflict due to the duplicate targets if label is also set.
35 //
36 // This is a bool pointer to support tristates: true, false, not set.
37 //
38 // To opt-in a module, set bazel_module: { bp2build_available: true }
39 // To opt-out a module, set bazel_module: { bp2build_available: false }
40 // To defer the default setting for the directory, do not set the value.
41 Bp2build_available *bool
42}
43
Jingwen Chen73850672020-12-14 08:25:34 -050044// BazelTargetModuleProperties contain properties and metadata used for
45// Blueprint to BUILD file conversion.
46type BazelTargetModuleProperties struct {
47 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050048 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050049
50 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050051 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050052}
Liz Kammer356f7d42021-01-26 09:18:53 -050053
Liz Kammera060c452021-03-24 10:14:47 -040054var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
55
Jingwen Chen38e62642021-04-19 05:00:15 +000056// Label is used to represent a Bazel compatible Label. Also stores the original
57// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050058type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000059 // The string representation of a Bazel target label. This can be a relative
60 // or fully qualified label. These labels are used for generating BUILD
61 // files with bp2build.
62 Label string
63
64 // The original Soong/Blueprint module name that the label was derived from.
65 // This is used for replacing references to the original name with the new
66 // label, for example in genrule cmds.
67 //
68 // While there is a reversible 1:1 mapping from the module name to Bazel
69 // label with bp2build that could make computing the original module name
70 // from the label automatic, it is not the case for handcrafted targets,
71 // where modules can have a custom label mapping through the { bazel_module:
72 // { label: <label> } } property.
73 //
74 // With handcrafted labels, those modules don't go through bp2build
75 // conversion, but relies on handcrafted targets in the source tree.
76 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050077}
78
79// LabelList is used to represent a list of Bazel labels.
80type LabelList struct {
81 Includes []Label
82 Excludes []Label
83}
84
Chris Parsons51f8c392021-08-03 21:01:05 -040085func (ll *LabelList) Equals(other LabelList) bool {
86 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
87 return false
88 }
89 for i, _ := range ll.Includes {
90 if ll.Includes[i] != other.Includes[i] {
91 return false
92 }
93 }
94 for i, _ := range ll.Excludes {
95 if ll.Excludes[i] != other.Excludes[i] {
96 return false
97 }
98 }
99 return true
100}
101
Liz Kammer9abd62d2021-05-21 08:37:59 -0400102func (ll *LabelList) IsNil() bool {
103 return ll.Includes == nil && ll.Excludes == nil
104}
105
Liz Kammer74deed42021-06-02 13:02:03 -0400106func (ll *LabelList) deepCopy() LabelList {
107 return LabelList{
108 Includes: ll.Includes[:],
109 Excludes: ll.Excludes[:],
110 }
111}
112
Jingwen Chen63930982021-03-24 10:04:33 -0400113// uniqueParentDirectories returns a list of the unique parent directories for
114// all files in ll.Includes.
115func (ll *LabelList) uniqueParentDirectories() []string {
116 dirMap := map[string]bool{}
117 for _, label := range ll.Includes {
118 dirMap[filepath.Dir(label.Label)] = true
119 }
120 dirs := []string{}
121 for dir := range dirMap {
122 dirs = append(dirs, dir)
123 }
124 return dirs
125}
126
Liz Kammer356f7d42021-01-26 09:18:53 -0500127// Append appends the fields of other labelList to the corresponding fields of ll.
128func (ll *LabelList) Append(other LabelList) {
129 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
130 ll.Includes = append(ll.Includes, other.Includes...)
131 }
132 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
133 ll.Excludes = append(other.Excludes, other.Excludes...)
134 }
135}
Jingwen Chen5d864492021-02-24 07:20:12 -0500136
Jingwen Chened9c17d2021-04-13 07:14:55 +0000137// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
138// the slice in a sorted order.
139func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000140 uniqueLabelsSet := make(map[Label]bool)
141 for _, l := range originalLabels {
142 uniqueLabelsSet[l] = true
143 }
144 var uniqueLabels []Label
145 for l, _ := range uniqueLabelsSet {
146 uniqueLabels = append(uniqueLabels, l)
147 }
148 sort.SliceStable(uniqueLabels, func(i, j int) bool {
149 return uniqueLabels[i].Label < uniqueLabels[j].Label
150 })
151 return uniqueLabels
152}
153
Liz Kammer9abd62d2021-05-21 08:37:59 -0400154func FirstUniqueBazelLabels(originalLabels []Label) []Label {
155 var labels []Label
156 found := make(map[Label]bool, len(originalLabels))
157 for _, l := range originalLabels {
158 if _, ok := found[l]; ok {
159 continue
160 }
161 labels = append(labels, l)
162 found[l] = true
163 }
164 return labels
165}
166
167func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
168 var uniqueLabelList LabelList
169 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
170 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
171 return uniqueLabelList
172}
173
174func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000175 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000176 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
177 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000178 return uniqueLabelList
179}
180
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000181// Subtract needle from haystack
182func SubtractStrings(haystack []string, needle []string) []string {
183 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400184 needleMap := make(map[string]bool)
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000185 for _, s := range needle {
Liz Kammer9bad9d62021-10-11 15:40:35 -0400186 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000187 }
188
189 var strings []string
Liz Kammer9bad9d62021-10-11 15:40:35 -0400190 for _, s := range haystack {
191 if exclude := needleMap[s]; !exclude {
192 strings = append(strings, s)
193 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000194 }
195
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000196 return strings
197}
198
199// Subtract needle from haystack
200func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
201 // This is really a set
Liz Kammer9bad9d62021-10-11 15:40:35 -0400202 needleMap := make(map[Label]bool)
203 for _, s := range needle {
204 needleMap[s] = true
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000205 }
206
207 var labels []Label
Liz Kammer9bad9d62021-10-11 15:40:35 -0400208 for _, label := range haystack {
209 if exclude := needleMap[label]; !exclude {
210 labels = append(labels, label)
211 }
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000212 }
213
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000214 return labels
215}
216
Chris Parsons484e50a2021-05-13 15:13:04 -0400217// Appends two LabelLists, returning the combined list.
218func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
219 var result LabelList
220 result.Includes = append(a.Includes, b.Includes...)
221 result.Excludes = append(a.Excludes, b.Excludes...)
222 return result
223}
224
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000225// Subtract needle from haystack
226func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
227 var result LabelList
228 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
229 // NOTE: Excludes are intentionally not subtracted
230 result.Excludes = haystack.Excludes
231 return result
232}
233
Jingwen Chenc1c26502021-04-05 10:35:13 +0000234type Attribute interface {
235 HasConfigurableValues() bool
236}
237
Liz Kammer9abd62d2021-05-21 08:37:59 -0400238type labelSelectValues map[string]*Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400239
Liz Kammer9abd62d2021-05-21 08:37:59 -0400240type configurableLabels map[ConfigurationAxis]labelSelectValues
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400241
Liz Kammer9abd62d2021-05-21 08:37:59 -0400242func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
243 if cl[axis] == nil {
244 cl[axis] = make(labelSelectValues)
245 }
246 cl[axis][config] = value
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400247}
248
249// Represents an attribute whose value is a single label
250type LabelAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400251 Value *Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400252
Liz Kammer9abd62d2021-05-21 08:37:59 -0400253 ConfigurableValues configurableLabels
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200254}
255
Liz Kammer9abd62d2021-05-21 08:37:59 -0400256// HasConfigurableValues returns whether there are configurable values set for this label.
257func (la LabelAttribute) HasConfigurableValues() bool {
258 return len(la.ConfigurableValues) > 0
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200259}
260
Liz Kammer9abd62d2021-05-21 08:37:59 -0400261// SetValue sets the base, non-configured value for the Label
262func (la *LabelAttribute) SetValue(value Label) {
263 la.SetSelectValue(NoConfigAxis, "", value)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400264}
265
Liz Kammer9abd62d2021-05-21 08:37:59 -0400266// SetSelectValue set a value for a bazel select for the given axis, config and value.
267func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
268 axis.validateConfig(config)
269 switch axis.configurationType {
270 case noConfig:
271 la.Value = &value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400272 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400273 if la.ConfigurableValues == nil {
274 la.ConfigurableValues = make(configurableLabels)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400275 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400276 la.ConfigurableValues.setValueForAxis(axis, config, &value)
277 default:
278 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
279 }
280}
281
282// SelectValue gets a value for a bazel select for the given axis and config.
283func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
284 axis.validateConfig(config)
285 switch axis.configurationType {
286 case noConfig:
287 return *la.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400288 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400289 return *la.ConfigurableValues[axis][config]
290 default:
291 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
292 }
293}
294
295// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
296func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
297 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
298 for k := range la.ConfigurableValues {
299 keys = append(keys, k)
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400300 }
301
Liz Kammer9abd62d2021-05-21 08:37:59 -0400302 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
303 return keys
304}
305
Liz Kammerd366c902021-06-03 13:43:01 -0400306type configToBools map[string]bool
307
308func (ctb configToBools) setValue(config string, value *bool) {
309 if value == nil {
310 if _, ok := ctb[config]; ok {
311 delete(ctb, config)
312 }
313 return
314 }
315 ctb[config] = *value
316}
317
318type configurableBools map[ConfigurationAxis]configToBools
319
320func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
321 if cb[axis] == nil {
322 cb[axis] = make(configToBools)
323 }
324 cb[axis].setValue(config, value)
325}
326
327// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
328type BoolAttribute struct {
329 Value *bool
330
331 ConfigurableValues configurableBools
332}
333
334// HasConfigurableValues returns whether there are configurable values for this attribute.
335func (ba BoolAttribute) HasConfigurableValues() bool {
336 return len(ba.ConfigurableValues) > 0
337}
338
339// SetSelectValue sets value for the given axis/config.
340func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
341 axis.validateConfig(config)
342 switch axis.configurationType {
343 case noConfig:
344 ba.Value = value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400345 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400346 if ba.ConfigurableValues == nil {
347 ba.ConfigurableValues = make(configurableBools)
348 }
349 ba.ConfigurableValues.setValueForAxis(axis, config, value)
350 default:
351 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
352 }
353}
354
355// SelectValue gets the value for the given axis/config.
356func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
357 axis.validateConfig(config)
358 switch axis.configurationType {
359 case noConfig:
360 return ba.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400361 case arch, os, osArch, productVariables:
Liz Kammerd366c902021-06-03 13:43:01 -0400362 if v, ok := ba.ConfigurableValues[axis][config]; ok {
363 return &v
364 } else {
365 return nil
366 }
367 default:
368 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
369 }
370}
371
372// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
373func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
374 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
375 for k := range ba.ConfigurableValues {
376 keys = append(keys, k)
377 }
378
379 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
380 return keys
381}
382
Liz Kammer9abd62d2021-05-21 08:37:59 -0400383// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
384type labelListSelectValues map[string]LabelList
385
386func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
387 for k, v := range other {
388 l := ll[k]
389 (&l).Append(v)
390 ll[k] = l
391 }
392}
393
394// HasConfigurableValues returns whether there are configurable values within this set of selects.
395func (ll labelListSelectValues) HasConfigurableValues() bool {
396 for _, v := range ll {
Chris Parsons51f8c392021-08-03 21:01:05 -0400397 if v.Includes != nil {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400398 return true
399 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400400 }
401 return false
402}
403
Jingwen Chen07027912021-03-15 06:02:43 -0400404// LabelListAttribute is used to represent a list of Bazel labels as an
405// attribute.
406type LabelListAttribute struct {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400407 // The non-configured attribute label list Value. Required.
Jingwen Chen07027912021-03-15 06:02:43 -0400408 Value LabelList
409
Liz Kammer9abd62d2021-05-21 08:37:59 -0400410 // The configured attribute label list Values. Optional
411 // a map of independent configurability axes
412 ConfigurableValues configurableLabelLists
Chris Parsons51f8c392021-08-03 21:01:05 -0400413
414 // If true, differentiate between "nil" and "empty" list. nil means that
415 // this attribute should not be specified at all, and "empty" means that
416 // the attribute should be explicitly specified as an empty list.
417 // This mode facilitates use of attribute defaults: an empty list should
418 // override the default.
419 ForceSpecifyEmptyList bool
Jingwen Chen58ff6802021-11-17 12:14:41 +0000420
421 // If true, signal the intent to the code generator to emit all select keys,
422 // even if the Includes list for that key is empty. This mode facilitates
423 // specific select statements where an empty list for a non-default select
424 // key has a meaning.
425 EmitEmptyList bool
Liz Kammer9abd62d2021-05-21 08:37:59 -0400426}
Jingwen Chen91220d72021-03-24 02:18:33 -0400427
Liz Kammer9abd62d2021-05-21 08:37:59 -0400428type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
429
430func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
431 if list.IsNil() {
432 if _, ok := cll[axis][config]; ok {
433 delete(cll[axis], config)
434 }
435 return
436 }
437 if cll[axis] == nil {
438 cll[axis] = make(labelListSelectValues)
439 }
440
441 cll[axis][config] = list
442}
443
444func (cll configurableLabelLists) Append(other configurableLabelLists) {
445 for axis, otherSelects := range other {
446 selects := cll[axis]
447 if selects == nil {
448 selects = make(labelListSelectValues, len(otherSelects))
449 }
450 selects.appendSelects(otherSelects)
451 cll[axis] = selects
452 }
Jingwen Chen07027912021-03-15 06:02:43 -0400453}
454
455// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
456func MakeLabelListAttribute(value LabelList) LabelListAttribute {
Liz Kammer9abd62d2021-05-21 08:37:59 -0400457 return LabelListAttribute{
458 Value: value,
459 ConfigurableValues: make(configurableLabelLists),
460 }
461}
462
463func (lla *LabelListAttribute) SetValue(list LabelList) {
464 lla.SetSelectValue(NoConfigAxis, "", list)
465}
466
467// SetSelectValue set a value for a bazel select for the given axis, config and value.
468func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
469 axis.validateConfig(config)
470 switch axis.configurationType {
471 case noConfig:
472 lla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400473 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400474 if lla.ConfigurableValues == nil {
475 lla.ConfigurableValues = make(configurableLabelLists)
476 }
477 lla.ConfigurableValues.setValueForAxis(axis, config, list)
478 default:
479 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
480 }
481}
482
483// SelectValue gets a value for a bazel select for the given axis and config.
484func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
485 axis.validateConfig(config)
486 switch axis.configurationType {
487 case noConfig:
488 return lla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400489 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400490 return lla.ConfigurableValues[axis][config]
491 default:
492 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
493 }
494}
495
496// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
497func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
498 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
499 for k := range lla.ConfigurableValues {
500 keys = append(keys, k)
501 }
502
503 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
504 return keys
Jingwen Chen07027912021-03-15 06:02:43 -0400505}
506
Jingwen Chened9c17d2021-04-13 07:14:55 +0000507// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400508// LabelListAttribute to this LabelListAttribute.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400509func (lla *LabelListAttribute) Append(other LabelListAttribute) {
Chris Parsons51f8c392021-08-03 21:01:05 -0400510 if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
511 lla.Value.Includes = []Label{}
512 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400513 lla.Value.Append(other.Value)
514 if lla.ConfigurableValues == nil {
515 lla.ConfigurableValues = make(configurableLabelLists)
Jingwen Chen63930982021-03-24 10:04:33 -0400516 }
Liz Kammer9abd62d2021-05-21 08:37:59 -0400517 lla.ConfigurableValues.Append(other.ConfigurableValues)
Jingwen Chen63930982021-03-24 10:04:33 -0400518}
519
Liz Kammer9abd62d2021-05-21 08:37:59 -0400520// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
521func (lla LabelListAttribute) HasConfigurableValues() bool {
522 return len(lla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400523}
524
Chris Parsons69fa9f92021-07-13 11:47:44 -0400525// IsEmpty returns true if the attribute has no values under any configuration.
526func (lla LabelListAttribute) IsEmpty() bool {
527 if len(lla.Value.Includes) > 0 {
528 return false
529 }
530 for axis, _ := range lla.ConfigurableValues {
531 if lla.ConfigurableValues[axis].HasConfigurableValues() {
532 return false
533 }
534 }
535 return true
536}
537
Liz Kammer74deed42021-06-02 13:02:03 -0400538// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
539// the base value and included in default values as appropriate.
540func (lla *LabelListAttribute) ResolveExcludes() {
541 for axis, configToLabels := range lla.ConfigurableValues {
542 baseLabels := lla.Value.deepCopy()
543 for config, val := range configToLabels {
544 // Exclude config-specific excludes from base value
545 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
546
547 // add base values to config specific to add labels excluded by others in this axis
548 // then remove all config-specific excludes
549 allLabels := baseLabels.deepCopy()
550 allLabels.Append(val)
551 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
552 }
553
554 // After going through all configs, delete the duplicates in the config
555 // values that are already in the base Value.
556 for config, val := range configToLabels {
557 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
558 }
559
Jingwen Chen9af49a42021-11-02 10:27:17 +0000560 // Now that the Value list is finalized for this axis, compare it with
561 // the original list, and union the difference with the default
562 // condition for the axis.
563 difference := SubtractBazelLabelList(baseLabels, lla.Value)
564 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
565 existingDefaults.Append(difference)
566 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
Liz Kammer74deed42021-06-02 13:02:03 -0400567
568 // if everything ends up without includes, just delete the axis
569 if !lla.ConfigurableValues[axis].HasConfigurableValues() {
570 delete(lla.ConfigurableValues, axis)
571 }
572 }
573}
574
Liz Kammer57e2e7a2021-09-20 12:55:02 -0400575// OtherModuleContext is a limited context that has methods with information about other modules.
576type OtherModuleContext interface {
577 ModuleFromName(name string) (blueprint.Module, bool)
578 OtherModuleType(m blueprint.Module) string
579 OtherModuleName(m blueprint.Module) string
580 OtherModuleDir(m blueprint.Module) string
581 ModuleErrorf(fmt string, args ...interface{})
582}
583
584// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
585// label and whether it was changed.
586type LabelMapper func(OtherModuleContext, string) (string, bool)
587
588// LabelPartition contains descriptions of a partition for labels
589type LabelPartition struct {
590 // Extensions to include in this partition
591 Extensions []string
592 // LabelMapper is a function that can map a label to a new label, and indicate whether to include
593 // the mapped label in the partition
594 LabelMapper LabelMapper
595 // Whether to store files not included in any other partition in a group of LabelPartitions
596 // Only one partition in a group of LabelPartitions can enabled Keep_remainder
597 Keep_remainder bool
598}
599
600// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
601// partition
602type LabelPartitions map[string]LabelPartition
603
604// filter returns a pointer to a label if the label should be included in the partition or nil if
605// not.
606func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
607 if lf.LabelMapper != nil {
608 if newLabel, changed := lf.LabelMapper(ctx, label.Label); changed {
609 return &Label{newLabel, label.OriginalModuleName}
610 }
611 }
612 for _, ext := range lf.Extensions {
613 if strings.HasSuffix(label.Label, ext) {
614 return &label
615 }
616 }
617
618 return nil
619}
620
621// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
622type PartitionToLabelListAttribute map[string]LabelListAttribute
623
624type partitionToLabelList map[string]*LabelList
625
626func (p partitionToLabelList) appendIncludes(partition string, label Label) {
627 if _, ok := p[partition]; !ok {
628 p[partition] = &LabelList{}
629 }
630 p[partition].Includes = append(p[partition].Includes, label)
631}
632
633func (p partitionToLabelList) excludes(partition string, excludes []Label) {
634 if _, ok := p[partition]; !ok {
635 p[partition] = &LabelList{}
636 }
637 p[partition].Excludes = excludes
638}
639
640// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
641func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
642 ret := PartitionToLabelListAttribute{}
643 var partitionNames []string
644 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
645 var remainderPartition *string
646 for p, f := range partitions {
647 partitionNames = append(partitionNames, p)
648 if f.Keep_remainder {
649 if remainderPartition != nil {
650 panic("only one partition can store the remainder")
651 }
652 // If we take the address of p in a loop, we'll end up with the last value of p in
653 // remainderPartition, we want the requested partition
654 capturePartition := p
655 remainderPartition = &capturePartition
656 }
657 }
658
659 partitionLabelList := func(axis ConfigurationAxis, config string) {
660 value := lla.SelectValue(axis, config)
661 partitionToLabels := partitionToLabelList{}
662 for _, item := range value.Includes {
663 wasFiltered := false
664 var inPartition *string
665 for partition, f := range partitions {
666 filtered := f.filter(ctx, item)
667 if filtered == nil {
668 // did not match this filter, keep looking
669 continue
670 }
671 wasFiltered = true
672 partitionToLabels.appendIncludes(partition, *filtered)
673 // don't need to check other partitions if this filter used the item,
674 // continue checking if mapped to another name
675 if *filtered == item {
676 if inPartition != nil {
677 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
678 }
679 capturePartition := partition
680 inPartition = &capturePartition
681 }
682 }
683
684 // if not specified in a partition, add to remainder partition if one exists
685 if !wasFiltered && remainderPartition != nil {
686 partitionToLabels.appendIncludes(*remainderPartition, item)
687 }
688 }
689
690 // ensure empty lists are maintained
691 if value.Excludes != nil {
692 for _, partition := range partitionNames {
693 partitionToLabels.excludes(partition, value.Excludes)
694 }
695 }
696
697 for partition, list := range partitionToLabels {
698 val := ret[partition]
699 (&val).SetSelectValue(axis, config, *list)
700 ret[partition] = val
701 }
702 }
703
704 partitionLabelList(NoConfigAxis, "")
705 for axis, configToList := range lla.ConfigurableValues {
706 for config, _ := range configToList {
707 partitionLabelList(axis, config)
708 }
709 }
710 return ret
711}
712
Jingwen Chen5d864492021-02-24 07:20:12 -0500713// StringListAttribute corresponds to the string_list Bazel attribute type with
714// support for additional metadata, like configurations.
715type StringListAttribute struct {
716 // The base value of the string list attribute.
717 Value []string
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 configurableStringLists
722}
Jingwen Chenc1c26502021-04-05 10:35:13 +0000723
Liz Kammer9abd62d2021-05-21 08:37:59 -0400724type configurableStringLists map[ConfigurationAxis]stringListSelectValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400725
Liz Kammer9abd62d2021-05-21 08:37:59 -0400726func (csl configurableStringLists) Append(other configurableStringLists) {
727 for axis, otherSelects := range other {
728 selects := csl[axis]
729 if selects == nil {
730 selects = make(stringListSelectValues, len(otherSelects))
731 }
732 selects.appendSelects(otherSelects)
733 csl[axis] = selects
734 }
735}
736
737func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
738 if csl[axis] == nil {
739 csl[axis] = make(stringListSelectValues)
740 }
741 csl[axis][config] = list
742}
743
744type stringListSelectValues map[string][]string
745
746func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
747 for k, v := range other {
748 sl[k] = append(sl[k], v...)
749 }
750}
751
752func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
753 for _, val := range sl {
754 if len(val) > 0 {
755 return true
756 }
757 }
758 return false
Jingwen Chen5d864492021-02-24 07:20:12 -0500759}
760
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000761// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
762func MakeStringListAttribute(value []string) StringListAttribute {
763 // NOTE: These strings are not necessarily unique or sorted.
Liz Kammer9abd62d2021-05-21 08:37:59 -0400764 return StringListAttribute{
765 Value: value,
766 ConfigurableValues: make(configurableStringLists),
Jingwen Chen91220d72021-03-24 02:18:33 -0400767 }
768}
769
Liz Kammer9abd62d2021-05-21 08:37:59 -0400770// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
771func (sla StringListAttribute) HasConfigurableValues() bool {
772 return len(sla.ConfigurableValues) > 0
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400773}
774
Jingwen Chened9c17d2021-04-13 07:14:55 +0000775// Append appends all values, including os and arch specific ones, from another
776// StringListAttribute to this StringListAttribute
Liz Kammer9abd62d2021-05-21 08:37:59 -0400777func (sla *StringListAttribute) Append(other StringListAttribute) {
778 sla.Value = append(sla.Value, other.Value...)
779 if sla.ConfigurableValues == nil {
780 sla.ConfigurableValues = make(configurableStringLists)
781 }
782 sla.ConfigurableValues.Append(other.ConfigurableValues)
783}
784
785// SetSelectValue set a value for a bazel select for the given axis, config and value.
786func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
787 axis.validateConfig(config)
788 switch axis.configurationType {
789 case noConfig:
790 sla.Value = list
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400791 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400792 if sla.ConfigurableValues == nil {
793 sla.ConfigurableValues = make(configurableStringLists)
794 }
795 sla.ConfigurableValues.setValueForAxis(axis, config, list)
796 default:
797 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
798 }
799}
800
801// SelectValue gets a value for a bazel select for the given axis and config.
802func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
803 axis.validateConfig(config)
804 switch axis.configurationType {
805 case noConfig:
806 return sla.Value
Chris Parsons2dde0cb2021-10-01 14:45:30 -0400807 case arch, os, osArch, productVariables:
Liz Kammer9abd62d2021-05-21 08:37:59 -0400808 return sla.ConfigurableValues[axis][config]
809 default:
810 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
811 }
812}
813
814// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
815func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
816 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
817 for k := range sla.ConfigurableValues {
818 keys = append(keys, k)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000819 }
820
Liz Kammer9abd62d2021-05-21 08:37:59 -0400821 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
822 return keys
Jingwen Chened9c17d2021-04-13 07:14:55 +0000823}
824
Liz Kammer5fad5012021-09-09 14:08:21 -0400825// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
826// configuration-specific values. For example, if we would convert this StringListAttribute as:
827// ["a", "b", "c"] + select({
828// "//condition:one": ["a", "d"],
829// "//conditions:default": [],
830// })
831// after this function, we would convert this StringListAttribute as:
832// ["a", "b", "c"] + select({
833// "//condition:one": ["d"],
834// "//conditions:default": [],
835// })
836func (sla *StringListAttribute) DeduplicateAxesFromBase() {
837 base := sla.Value
838 for axis, configToList := range sla.ConfigurableValues {
839 for config, list := range configToList {
840 remaining := SubtractStrings(list, base)
841 if len(remaining) == 0 {
842 delete(sla.ConfigurableValues[axis], config)
843 } else {
844 sla.ConfigurableValues[axis][config] = remaining
845 }
846 }
847 }
848}
849
Liz Kammera060c452021-03-24 10:14:47 -0400850// TryVariableSubstitution, replace string substitution formatting within each string in slice with
851// Starlark string.format compatible tag for productVariable.
852func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
853 ret := make([]string, 0, len(slice))
854 changesMade := false
855 for _, s := range slice {
856 newS, changed := TryVariableSubstitution(s, productVariable)
857 ret = append(ret, newS)
858 changesMade = changesMade || changed
859 }
860 return ret, changesMade
861}
862
863// TryVariableSubstitution, replace string substitution formatting within s with Starlark
864// string.format compatible tag for productVariable.
865func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400866 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400867 return sub, s != sub
868}