• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
17import (
18	"fmt"
19	"path/filepath"
20	"reflect"
21	"regexp"
22	"sort"
23	"strings"
24
25	"github.com/google/blueprint"
26)
27
28// BazelTargetModuleProperties contain properties and metadata used for
29// Blueprint to BUILD file conversion.
30type BazelTargetModuleProperties struct {
31	// The Bazel rule class for this target.
32	Rule_class string `blueprint:"mutated"`
33
34	// The target label for the bzl file containing the definition of the rule class.
35	Bzl_load_location string `blueprint:"mutated"`
36}
37
38var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
39
40// Label is used to represent a Bazel compatible Label. Also stores the original
41// bp text to support string replacement.
42type Label struct {
43	// 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
61}
62
63// LabelList is used to represent a list of Bazel labels.
64type LabelList struct {
65	Includes []Label
66	Excludes []Label
67}
68
69// 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
77func 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
87// 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
98func (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
115func (ll *LabelList) IsNil() bool {
116	return ll.Includes == nil && ll.Excludes == nil
117}
118
119func (ll *LabelList) IsEmpty() bool {
120	return len(ll.Includes) == 0 && len(ll.Excludes) == 0
121}
122
123func (ll *LabelList) deepCopy() LabelList {
124	return LabelList{
125		Includes: ll.Includes[:],
126		Excludes: ll.Excludes[:],
127	}
128}
129
130// 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
144// Add inserts the label Label at the end of the LabelList.Includes.
145func (ll *LabelList) Add(label *Label) {
146	if label == nil {
147		return
148	}
149	ll.Includes = append(ll.Includes, *label)
150}
151
152// 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
160// 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 {
166		ll.Excludes = append(ll.Excludes, other.Excludes...)
167	}
168}
169
170// 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
194// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
195// the slice in a sorted order.
196func UniqueSortedBazelLabels(originalLabels []Label) []Label {
197	uniqueLabelsSet := make(map[Label]bool)
198	for _, l := range originalLabels {
199		uniqueLabelsSet[l] = true
200	}
201	var uniqueLabels []Label
202	for l, _ := range uniqueLabelsSet {
203		uniqueLabels = append(uniqueLabels, l)
204	}
205	sort.SliceStable(uniqueLabels, func(i, j int) bool {
206		return uniqueLabels[i].Label < uniqueLabels[j].Label
207	})
208	return uniqueLabels
209}
210
211func FirstUniqueBazelLabels(originalLabels []Label) []Label {
212	var labels []Label
213	found := make(map[Label]bool, len(originalLabels))
214	for _, l := range originalLabels {
215		if _, ok := found[l]; ok {
216			continue
217		}
218		labels = append(labels, l)
219		found[l] = true
220	}
221	return labels
222}
223
224func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
225	var uniqueLabelList LabelList
226	uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
227	uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
228	return uniqueLabelList
229}
230
231func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
232	var uniqueLabelList LabelList
233	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
234	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
235	return uniqueLabelList
236}
237
238// Subtract needle from haystack
239func SubtractStrings(haystack []string, needle []string) []string {
240	// This is really a set
241	needleMap := make(map[string]bool)
242	for _, s := range needle {
243		needleMap[s] = true
244	}
245
246	var strings []string
247	for _, s := range haystack {
248		if exclude := needleMap[s]; !exclude {
249			strings = append(strings, s)
250		}
251	}
252
253	return strings
254}
255
256// Subtract needle from haystack
257func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
258	// This is really a set
259	needleMap := make(map[Label]bool)
260	for _, s := range needle {
261		needleMap[s] = true
262	}
263
264	var labels []Label
265	for _, label := range haystack {
266		if exclude := needleMap[label]; !exclude {
267			labels = append(labels, label)
268		}
269	}
270
271	return labels
272}
273
274// Appends two LabelLists, returning the combined list.
275func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
276	var result LabelList
277	result.Includes = append(a.Includes, b.Includes...)
278	result.Excludes = append(a.Excludes, b.Excludes...)
279	return result
280}
281
282// Subtract needle from haystack
283func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
284	var result LabelList
285	result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
286	// NOTE: Excludes are intentionally not subtracted
287	result.Excludes = haystack.Excludes
288	return result
289}
290
291type Attribute interface {
292	HasConfigurableValues() bool
293}
294
295type labelSelectValues map[string]*Label
296
297type configurableLabels map[ConfigurationAxis]labelSelectValues
298
299func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
300	if cl[axis] == nil {
301		cl[axis] = make(labelSelectValues)
302	}
303	cl[axis][config] = value
304}
305
306// Represents an attribute whose value is a single label
307type LabelAttribute struct {
308	Value *Label
309
310	ConfigurableValues configurableLabels
311}
312
313func (la *LabelAttribute) axisTypes() map[configurationType]bool {
314	types := map[configurationType]bool{}
315	for k := range la.ConfigurableValues {
316		if len(la.ConfigurableValues[k]) > 0 {
317			types[k.configurationType] = true
318		}
319	}
320	return types
321}
322
323// Collapse reduces the configurable axes of the label attribute to a single axis.
324// This is necessary for final writing to bp2build, as a configurable label
325// attribute can only be comprised by a single select.
326func (la *LabelAttribute) Collapse() error {
327	axisTypes := la.axisTypes()
328	_, containsOs := axisTypes[os]
329	_, containsArch := axisTypes[arch]
330	_, containsOsArch := axisTypes[osArch]
331	_, containsProductVariables := axisTypes[productVariables]
332	if containsProductVariables {
333		if containsOs || containsArch || containsOsArch {
334			if containsArch {
335				allProductVariablesAreArchVariant := true
336				for k := range la.ConfigurableValues {
337					if k.configurationType == productVariables && k.outerAxisType != arch {
338						allProductVariablesAreArchVariant = false
339					}
340				}
341				if !allProductVariablesAreArchVariant {
342					return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
343				}
344			} else {
345				return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
346			}
347		}
348	}
349	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
350		// If a bool attribute has both os and arch configuration axes, the only
351		// way to successfully union their values is to increase the granularity
352		// of the configuration criteria to os_arch.
353		for osType, supportedArchs := range osToArchMap {
354			for _, supportedArch := range supportedArchs {
355				osArch := osArchString(osType, supportedArch)
356				if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
357					// Do nothing, as the arch_os is explicitly defined already.
358				} else {
359					archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
360					osVal := la.SelectValue(OsConfigurationAxis, osType)
361					if osVal != nil && archVal != nil {
362						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
363						// runs after os mutator.
364						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
365					} else if osVal != nil && archVal == nil {
366						la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
367					} else if osVal == nil && archVal != nil {
368						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
369					}
370				}
371			}
372		}
373		// All os_arch values are now set. Clear os and arch axes.
374		delete(la.ConfigurableValues, ArchConfigurationAxis)
375		delete(la.ConfigurableValues, OsConfigurationAxis)
376	}
377	return nil
378}
379
380// HasConfigurableValues returns whether there are configurable values set for this label.
381func (la LabelAttribute) HasConfigurableValues() bool {
382	for _, selectValues := range la.ConfigurableValues {
383		if len(selectValues) > 0 {
384			return true
385		}
386	}
387	return false
388}
389
390// SetValue sets the base, non-configured value for the Label
391func (la *LabelAttribute) SetValue(value Label) {
392	la.SetSelectValue(NoConfigAxis, "", value)
393}
394
395// SetSelectValue set a value for a bazel select for the given axis, config and value.
396func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
397	axis.validateConfig(config)
398	switch axis.configurationType {
399	case noConfig:
400		la.Value = &value
401	case arch, os, osArch, productVariables, osAndInApex:
402		if la.ConfigurableValues == nil {
403			la.ConfigurableValues = make(configurableLabels)
404		}
405		la.ConfigurableValues.setValueForAxis(axis, config, &value)
406	default:
407		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
408	}
409}
410
411// SelectValue gets a value for a bazel select for the given axis and config.
412func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
413	axis.validateConfig(config)
414	switch axis.configurationType {
415	case noConfig:
416		return la.Value
417	case arch, os, osArch, productVariables, osAndInApex:
418		return la.ConfigurableValues[axis][config]
419	default:
420		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
421	}
422}
423
424// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
425func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
426	return SortedConfigurationAxes(la.ConfigurableValues)
427}
428
429// MakeLabelAttribute turns a string into a LabelAttribute
430func MakeLabelAttribute(label string) *LabelAttribute {
431	return &LabelAttribute{
432		Value: &Label{
433			Label: label,
434		},
435	}
436}
437
438type configToBools map[string]bool
439
440func (ctb configToBools) setValue(config string, value *bool) {
441	if value == nil {
442		if _, ok := ctb[config]; ok {
443			delete(ctb, config)
444		}
445		return
446	}
447	ctb[config] = *value
448}
449
450type configurableBools map[ConfigurationAxis]configToBools
451
452func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
453	if cb[axis] == nil {
454		cb[axis] = make(configToBools)
455	}
456	cb[axis].setValue(config, value)
457}
458
459// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
460type BoolAttribute struct {
461	Value *bool
462
463	ConfigurableValues configurableBools
464}
465
466// HasConfigurableValues returns whether there are configurable values for this attribute.
467func (ba BoolAttribute) HasConfigurableValues() bool {
468	for _, cfgToBools := range ba.ConfigurableValues {
469		if len(cfgToBools) > 0 {
470			return true
471		}
472	}
473	return false
474}
475
476// SetValue sets value for the no config axis
477func (ba *BoolAttribute) SetValue(value *bool) {
478	ba.SetSelectValue(NoConfigAxis, "", value)
479}
480
481// SetSelectValue sets value for the given axis/config.
482func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
483	axis.validateConfig(config)
484	switch axis.configurationType {
485	case noConfig:
486		ba.Value = value
487	case arch, os, osArch, productVariables, osAndInApex:
488		if ba.ConfigurableValues == nil {
489			ba.ConfigurableValues = make(configurableBools)
490		}
491		ba.ConfigurableValues.setValueForAxis(axis, config, value)
492	default:
493		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
494	}
495}
496
497// ToLabelListAttribute creates and returns a LabelListAttribute from this
498// bool attribute, where each bool in this attribute corresponds to a
499// label list value in the resultant attribute.
500func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
501	getLabelList := func(boolPtr *bool) LabelList {
502		if boolPtr == nil {
503			return LabelList{nil, nil}
504		} else if *boolPtr {
505			return trueVal
506		} else {
507			return falseVal
508		}
509	}
510
511	mainVal := getLabelList(ba.Value)
512	if !ba.HasConfigurableValues() {
513		return MakeLabelListAttribute(mainVal), nil
514	}
515
516	result := LabelListAttribute{}
517	if err := ba.Collapse(); err != nil {
518		return result, err
519	}
520
521	for axis, configToBools := range ba.ConfigurableValues {
522		if len(configToBools) < 1 {
523			continue
524		}
525		for config, boolPtr := range configToBools {
526			val := getLabelList(&boolPtr)
527			if !val.Equals(mainVal) {
528				result.SetSelectValue(axis, config, val)
529			}
530		}
531		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
532	}
533
534	return result, nil
535}
536
537// ToStringListAttribute creates a StringListAttribute from this BoolAttribute,
538// where each bool corresponds to a string list value generated by the provided
539// function.
540// TODO(b/271425661): Generalize this
541func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) {
542	mainVal := valueFunc(ba.Value, NoConfigAxis, "")
543	if !ba.HasConfigurableValues() {
544		return MakeStringListAttribute(mainVal), nil
545	}
546
547	result := StringListAttribute{}
548	if err := ba.Collapse(); err != nil {
549		return result, err
550	}
551
552	for axis, configToBools := range ba.ConfigurableValues {
553		if len(configToBools) < 1 {
554			continue
555		}
556		for config, boolPtr := range configToBools {
557			val := valueFunc(&boolPtr, axis, config)
558			if !reflect.DeepEqual(val, mainVal) {
559				result.SetSelectValue(axis, config, val)
560			}
561		}
562		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
563	}
564
565	return result, nil
566}
567
568// Collapse reduces the configurable axes of the boolean attribute to a single axis.
569// This is necessary for final writing to bp2build, as a configurable boolean
570// attribute can only be comprised by a single select.
571func (ba *BoolAttribute) Collapse() error {
572	axisTypes := ba.axisTypes()
573	_, containsOs := axisTypes[os]
574	_, containsArch := axisTypes[arch]
575	_, containsOsArch := axisTypes[osArch]
576	_, containsProductVariables := axisTypes[productVariables]
577	if containsProductVariables {
578		if containsOs || containsArch || containsOsArch {
579			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
580		}
581	}
582	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
583		// If a bool attribute has both os and arch configuration axes, the only
584		// way to successfully union their values is to increase the granularity
585		// of the configuration criteria to os_arch.
586		for osType, supportedArchs := range osToArchMap {
587			for _, supportedArch := range supportedArchs {
588				osArch := osArchString(osType, supportedArch)
589				if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
590					// Do nothing, as the arch_os is explicitly defined already.
591				} else {
592					archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
593					osVal := ba.SelectValue(OsConfigurationAxis, osType)
594					if osVal != nil && archVal != nil {
595						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
596						// runs after os mutator.
597						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
598					} else if osVal != nil && archVal == nil {
599						ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
600					} else if osVal == nil && archVal != nil {
601						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
602					}
603				}
604			}
605		}
606		// All os_arch values are now set. Clear os and arch axes.
607		delete(ba.ConfigurableValues, ArchConfigurationAxis)
608		delete(ba.ConfigurableValues, OsConfigurationAxis)
609		// Verify post-condition; this should never fail, provided no additional
610		// axes are introduced.
611		if len(ba.ConfigurableValues) > 1 {
612			panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
613		}
614	}
615	return nil
616}
617
618func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
619	types := map[configurationType]bool{}
620	for k := range ba.ConfigurableValues {
621		if len(ba.ConfigurableValues[k]) > 0 {
622			types[k.configurationType] = true
623		}
624	}
625	return types
626}
627
628// SelectValue gets the value for the given axis/config.
629func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
630	axis.validateConfig(config)
631	switch axis.configurationType {
632	case noConfig:
633		return ba.Value
634	case arch, os, osArch, productVariables, osAndInApex:
635		if v, ok := ba.ConfigurableValues[axis][config]; ok {
636			return &v
637		} else {
638			return nil
639		}
640	default:
641		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
642	}
643}
644
645// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
646func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
647	return SortedConfigurationAxes(ba.ConfigurableValues)
648}
649
650// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
651type labelListSelectValues map[string]LabelList
652
653func (ll labelListSelectValues) addSelects(label labelSelectValues) {
654	for k, v := range label {
655		if label == nil {
656			continue
657		}
658		l := ll[k]
659		(&l).Add(v)
660		ll[k] = l
661	}
662}
663
664func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
665	for k, v := range other {
666		l := ll[k]
667		if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
668			l.Includes = []Label{}
669		}
670		(&l).Append(v)
671		ll[k] = l
672	}
673}
674
675// HasConfigurableValues returns whether there are configurable values within this set of selects.
676func (ll labelListSelectValues) HasConfigurableValues() bool {
677	for _, v := range ll {
678		if v.Includes != nil {
679			return true
680		}
681	}
682	return false
683}
684
685// LabelListAttribute is used to represent a list of Bazel labels as an
686// attribute.
687type LabelListAttribute struct {
688	// The non-configured attribute label list Value. Required.
689	Value LabelList
690
691	// The configured attribute label list Values. Optional
692	// a map of independent configurability axes
693	ConfigurableValues configurableLabelLists
694
695	// If true, differentiate between "nil" and "empty" list. nil means that
696	// this attribute should not be specified at all, and "empty" means that
697	// the attribute should be explicitly specified as an empty list.
698	// This mode facilitates use of attribute defaults: an empty list should
699	// override the default.
700	ForceSpecifyEmptyList bool
701
702	// If true, signal the intent to the code generator to emit all select keys,
703	// even if the Includes list for that key is empty. This mode facilitates
704	// specific select statements where an empty list for a non-default select
705	// key has a meaning.
706	EmitEmptyList bool
707
708	// If a property has struct tag "variant_prepend", this value should
709	// be set to True, so that when bp2build generates BUILD.bazel, variant
710	// properties(select ...) come before general properties.
711	Prepend bool
712}
713
714type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
715
716func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
717	if list.IsNil() {
718		if _, ok := cll[axis][config]; ok {
719			delete(cll[axis], config)
720		}
721		return
722	}
723	if cll[axis] == nil {
724		cll[axis] = make(labelListSelectValues)
725	}
726
727	cll[axis][config] = list
728}
729
730func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
731	for axis, otherSelects := range other {
732		selects := cll[axis]
733		if selects == nil {
734			selects = make(labelListSelectValues, len(otherSelects))
735		}
736		selects.appendSelects(otherSelects, forceSpecifyEmptyList)
737		cll[axis] = selects
738	}
739}
740
741func (lla *LabelListAttribute) Clone() *LabelListAttribute {
742	result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
743	return result.Append(*lla)
744}
745
746// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
747func MakeLabelListAttribute(value LabelList) LabelListAttribute {
748	return LabelListAttribute{
749		Value:              value,
750		ConfigurableValues: make(configurableLabelLists),
751	}
752}
753
754// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
755func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
756	return MakeLabelListAttribute(MakeLabelList([]Label{value}))
757}
758
759func (lla *LabelListAttribute) SetValue(list LabelList) {
760	lla.SetSelectValue(NoConfigAxis, "", list)
761}
762
763// SetSelectValue set a value for a bazel select for the given axis, config and value.
764func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
765	axis.validateConfig(config)
766	switch axis.configurationType {
767	case noConfig:
768		lla.Value = list
769	case arch, os, osArch, productVariables, osAndInApex, inApex:
770		if lla.ConfigurableValues == nil {
771			lla.ConfigurableValues = make(configurableLabelLists)
772		}
773		lla.ConfigurableValues.setValueForAxis(axis, config, list)
774	default:
775		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
776	}
777}
778
779// SelectValue gets a value for a bazel select for the given axis and config.
780func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
781	axis.validateConfig(config)
782	switch axis.configurationType {
783	case noConfig:
784		return lla.Value
785	case arch, os, osArch, productVariables, osAndInApex, inApex:
786		return lla.ConfigurableValues[axis][config]
787	default:
788		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
789	}
790}
791
792// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
793func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
794	return SortedConfigurationAxes(lla.ConfigurableValues)
795}
796
797// Append all values, including os and arch specific ones, from another
798// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
799func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
800	forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
801	if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
802		lla.Value.Includes = []Label{}
803	}
804	lla.Value.Append(other.Value)
805	if lla.ConfigurableValues == nil {
806		lla.ConfigurableValues = make(configurableLabelLists)
807	}
808	lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
809	return lla
810}
811
812// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
813// LabelList within the LabelListAttribute
814func (lla *LabelListAttribute) Add(label *LabelAttribute) {
815	if label == nil {
816		return
817	}
818
819	lla.Value.Add(label.Value)
820	if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
821		lla.ConfigurableValues = make(configurableLabelLists)
822	}
823	for axis, _ := range label.ConfigurableValues {
824		if _, exists := lla.ConfigurableValues[axis]; !exists {
825			lla.ConfigurableValues[axis] = make(labelListSelectValues)
826		}
827		lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
828	}
829}
830
831// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
832func (lla LabelListAttribute) HasConfigurableValues() bool {
833	for _, selectValues := range lla.ConfigurableValues {
834		if len(selectValues) > 0 {
835			return true
836		}
837	}
838	return false
839}
840
841// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
842func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
843	for _, values := range lla.ConfigurableValues[axis] {
844		if !values.IsNil() {
845			return true
846		}
847	}
848	return false
849}
850
851// IsEmpty returns true if the attribute has no values under any configuration.
852func (lla LabelListAttribute) IsEmpty() bool {
853	if len(lla.Value.Includes) > 0 {
854		return false
855	}
856	for axis, _ := range lla.ConfigurableValues {
857		if lla.ConfigurableValues[axis].HasConfigurableValues() {
858			return false
859		}
860	}
861	return true
862}
863
864// IsNil returns true if the attribute has not been set for any configuration.
865func (lla LabelListAttribute) IsNil() bool {
866	if lla.Value.Includes != nil {
867		return false
868	}
869	return !lla.HasConfigurableValues()
870}
871
872// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
873// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
874// be removed, e.g. if they could cause duplicate element failures.
875func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
876	val := lla.SelectValue(axis, config)
877	newList := SubtractBazelLabelList(val, labelList)
878	newList.Excludes = append(newList.Excludes, labelList.Includes...)
879	lla.SetSelectValue(axis, config, newList)
880}
881
882// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
883// the base value and included in default values as appropriate.
884func (lla *LabelListAttribute) ResolveExcludes() {
885	// If there are OsAndInApexAxis, we need to use
886	//   * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
887	//     included in non-Android OSes
888	//   * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
889	//     be included in the non-Android OSes
890	if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
891		inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
892		for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
893			// OsAndroid has already handled its excludes.
894			// We only need to copy the excludes from other arches, so if there are none, skip it.
895			if config == OsAndroid || len(labels.Excludes) == 0 {
896				continue
897			}
898			lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
899				Includes: inApexLabels.Includes,
900				Excludes: labels.Excludes,
901			}
902		}
903	}
904
905	for axis, configToLabels := range lla.ConfigurableValues {
906		baseLabels := lla.Value.deepCopy()
907		for config, val := range configToLabels {
908			// Exclude config-specific excludes from base value
909			lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
910
911			// add base values to config specific to add labels excluded by others in this axis
912			// then remove all config-specific excludes
913			allLabels := baseLabels.deepCopy()
914			allLabels.Append(val)
915			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
916		}
917
918		// After going through all configs, delete the duplicates in the config
919		// values that are already in the base Value.
920		for config, val := range configToLabels {
921			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
922		}
923
924		// Now that the Value list is finalized for this axis, compare it with
925		// the original list, and union the difference with the default
926		// condition for the axis.
927		difference := SubtractBazelLabelList(baseLabels, lla.Value)
928		existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
929		existingDefaults.Append(difference)
930		lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
931
932		// if everything ends up without includes, just delete the axis
933		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
934			delete(lla.ConfigurableValues, axis)
935		}
936	}
937}
938
939// Partition splits a LabelListAttribute into two LabelListAttributes depending
940// on the return value of the predicate.
941// This function preserves the Includes and Excludes, but it does not provide
942// that information to the partition function.
943func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
944	predicated := LabelListAttribute{}
945	unpredicated := LabelListAttribute{}
946
947	valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
948	predicated.SetValue(valuePartitionTrue)
949	unpredicated.SetValue(valuePartitionFalse)
950
951	for axis, selectValueLabelLists := range lla.ConfigurableValues {
952		for config, labelList := range selectValueLabelLists {
953			configPredicated, configUnpredicated := labelList.Partition(predicate)
954			predicated.SetSelectValue(axis, config, configPredicated)
955			unpredicated.SetSelectValue(axis, config, configUnpredicated)
956		}
957	}
958
959	return predicated, unpredicated
960}
961
962// OtherModuleContext is a limited context that has methods with information about other modules.
963type OtherModuleContext interface {
964	ModuleFromName(name string) (blueprint.Module, bool)
965	OtherModuleType(m blueprint.Module) string
966	OtherModuleName(m blueprint.Module) string
967	OtherModuleDir(m blueprint.Module) string
968	ModuleErrorf(fmt string, args ...interface{})
969}
970
971// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
972// label and whether it was changed.
973type LabelMapper func(OtherModuleContext, Label) (string, bool)
974
975// LabelPartition contains descriptions of a partition for labels
976type LabelPartition struct {
977	// Extensions to include in this partition
978	Extensions []string
979	// LabelMapper is a function that can map a label to a new label, and indicate whether to include
980	// the mapped label in the partition
981	LabelMapper LabelMapper
982	// Whether to store files not included in any other partition in a group of LabelPartitions
983	// Only one partition in a group of LabelPartitions can enabled Keep_remainder
984	Keep_remainder bool
985}
986
987// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
988// partition
989type LabelPartitions map[string]LabelPartition
990
991// filter returns a pointer to a label if the label should be included in the partition or nil if
992// not.
993func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
994	if lf.LabelMapper != nil {
995		if newLabel, changed := lf.LabelMapper(ctx, label); changed {
996			return &Label{newLabel, label.OriginalModuleName}
997		}
998	}
999	for _, ext := range lf.Extensions {
1000		if strings.HasSuffix(label.Label, ext) {
1001			return &label
1002		}
1003	}
1004
1005	return nil
1006}
1007
1008// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
1009type PartitionToLabelListAttribute map[string]LabelListAttribute
1010
1011type partitionToLabelList map[string]*LabelList
1012
1013func (p partitionToLabelList) appendIncludes(partition string, label Label) {
1014	if _, ok := p[partition]; !ok {
1015		p[partition] = &LabelList{}
1016	}
1017	p[partition].Includes = append(p[partition].Includes, label)
1018}
1019
1020func (p partitionToLabelList) excludes(partition string, excludes []Label) {
1021	if _, ok := p[partition]; !ok {
1022		p[partition] = &LabelList{}
1023	}
1024	p[partition].Excludes = excludes
1025}
1026
1027// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
1028func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
1029	ret := PartitionToLabelListAttribute{}
1030	var partitionNames []string
1031	// Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
1032	var remainderPartition *string
1033	for p, f := range partitions {
1034		partitionNames = append(partitionNames, p)
1035		if f.Keep_remainder {
1036			if remainderPartition != nil {
1037				panic("only one partition can store the remainder")
1038			}
1039			// If we take the address of p in a loop, we'll end up with the last value of p in
1040			// remainderPartition, we want the requested partition
1041			capturePartition := p
1042			remainderPartition = &capturePartition
1043		}
1044	}
1045
1046	partitionLabelList := func(axis ConfigurationAxis, config string) {
1047		value := lla.SelectValue(axis, config)
1048		partitionToLabels := partitionToLabelList{}
1049		for _, item := range value.Includes {
1050			wasFiltered := false
1051			var inPartition *string
1052			for partition, f := range partitions {
1053				filtered := f.filter(ctx, item)
1054				if filtered == nil {
1055					// did not match this filter, keep looking
1056					continue
1057				}
1058				wasFiltered = true
1059				partitionToLabels.appendIncludes(partition, *filtered)
1060				// don't need to check other partitions if this filter used the item,
1061				// continue checking if mapped to another name
1062				if *filtered == item {
1063					if inPartition != nil {
1064						ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
1065					}
1066					capturePartition := partition
1067					inPartition = &capturePartition
1068				}
1069			}
1070
1071			// if not specified in a partition, add to remainder partition if one exists
1072			if !wasFiltered && remainderPartition != nil {
1073				partitionToLabels.appendIncludes(*remainderPartition, item)
1074			}
1075		}
1076
1077		// ensure empty lists are maintained
1078		if value.Excludes != nil {
1079			for _, partition := range partitionNames {
1080				partitionToLabels.excludes(partition, value.Excludes)
1081			}
1082		}
1083
1084		for partition, list := range partitionToLabels {
1085			val := ret[partition]
1086			(&val).SetSelectValue(axis, config, *list)
1087			ret[partition] = val
1088		}
1089	}
1090
1091	partitionLabelList(NoConfigAxis, "")
1092	for axis, configToList := range lla.ConfigurableValues {
1093		for config, _ := range configToList {
1094			partitionLabelList(axis, config)
1095		}
1096	}
1097	return ret
1098}
1099
1100// StringAttribute corresponds to the string Bazel attribute type with
1101// support for additional metadata, like configurations.
1102type StringAttribute struct {
1103	// The base value of the string attribute.
1104	Value *string
1105
1106	// The configured attribute label list Values. Optional
1107	// a map of independent configurability axes
1108	ConfigurableValues configurableStrings
1109}
1110
1111type configurableStrings map[ConfigurationAxis]stringSelectValues
1112
1113func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
1114	if cs[axis] == nil {
1115		cs[axis] = make(stringSelectValues)
1116	}
1117	cs[axis][config] = str
1118}
1119
1120type stringSelectValues map[string]*string
1121
1122// HasConfigurableValues returns true if the attribute contains axis-specific string values.
1123func (sa StringAttribute) HasConfigurableValues() bool {
1124	for _, selectValues := range sa.ConfigurableValues {
1125		if len(selectValues) > 0 {
1126			return true
1127		}
1128	}
1129	return false
1130}
1131
1132// SetValue sets the base, non-configured value for the Label
1133func (sa *StringAttribute) SetValue(value string) {
1134	sa.SetSelectValue(NoConfigAxis, "", &value)
1135}
1136
1137// SetSelectValue set a value for a bazel select for the given axis, config and value.
1138func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
1139	axis.validateConfig(config)
1140	switch axis.configurationType {
1141	case noConfig:
1142		sa.Value = str
1143	case arch, os, osArch, productVariables:
1144		if sa.ConfigurableValues == nil {
1145			sa.ConfigurableValues = make(configurableStrings)
1146		}
1147		sa.ConfigurableValues.setValueForAxis(axis, config, str)
1148	default:
1149		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1150	}
1151}
1152
1153// SelectValue gets a value for a bazel select for the given axis and config.
1154func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
1155	axis.validateConfig(config)
1156	switch axis.configurationType {
1157	case noConfig:
1158		return sa.Value
1159	case arch, os, osArch, productVariables:
1160		if v, ok := sa.ConfigurableValues[axis][config]; ok {
1161			return v
1162		} else {
1163			return nil
1164		}
1165	default:
1166		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1167	}
1168}
1169
1170// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1171func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1172	return SortedConfigurationAxes(sa.ConfigurableValues)
1173}
1174
1175// Collapse reduces the configurable axes of the string attribute to a single axis.
1176// This is necessary for final writing to bp2build, as a configurable string
1177// attribute can only be comprised by a single select.
1178func (sa *StringAttribute) Collapse() error {
1179	axisTypes := sa.axisTypes()
1180	_, containsOs := axisTypes[os]
1181	_, containsArch := axisTypes[arch]
1182	_, containsOsArch := axisTypes[osArch]
1183	_, containsProductVariables := axisTypes[productVariables]
1184	if containsProductVariables {
1185		if containsOs || containsArch || containsOsArch {
1186			return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
1187		}
1188	}
1189	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
1190		// If a bool attribute has both os and arch configuration axes, the only
1191		// way to successfully union their values is to increase the granularity
1192		// of the configuration criteria to os_arch.
1193		for osType, supportedArchs := range osToArchMap {
1194			for _, supportedArch := range supportedArchs {
1195				osArch := osArchString(osType, supportedArch)
1196				if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
1197					// Do nothing, as the arch_os is explicitly defined already.
1198				} else {
1199					archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
1200					osVal := sa.SelectValue(OsConfigurationAxis, osType)
1201					if osVal != nil && archVal != nil {
1202						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
1203						// runs after os mutator.
1204						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1205					} else if osVal != nil && archVal == nil {
1206						sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
1207					} else if osVal == nil && archVal != nil {
1208						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
1209					}
1210				}
1211			}
1212		}
1213		/// All os_arch values are now set. Clear os and arch axes.
1214		delete(sa.ConfigurableValues, ArchConfigurationAxis)
1215		delete(sa.ConfigurableValues, OsConfigurationAxis)
1216		// Verify post-condition; this should never fail, provided no additional
1217		// axes are introduced.
1218		if len(sa.ConfigurableValues) > 1 {
1219			panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
1220		}
1221	} else if containsProductVariables {
1222		usedBaseValue := false
1223		for a, configToProp := range sa.ConfigurableValues {
1224			if a.configurationType == productVariables {
1225				for c, p := range configToProp {
1226					if p == nil {
1227						sa.SetSelectValue(a, c, sa.Value)
1228						usedBaseValue = true
1229					}
1230				}
1231			}
1232		}
1233		if usedBaseValue {
1234			sa.Value = nil
1235		}
1236	}
1237	return nil
1238}
1239
1240func (sa *StringAttribute) axisTypes() map[configurationType]bool {
1241	types := map[configurationType]bool{}
1242	for k := range sa.ConfigurableValues {
1243		if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
1244			types[k.configurationType] = true
1245		}
1246	}
1247	return types
1248}
1249
1250// StringListAttribute corresponds to the string_list Bazel attribute type with
1251// support for additional metadata, like configurations.
1252type StringListAttribute struct {
1253	// The base value of the string list attribute.
1254	Value []string
1255
1256	// The configured attribute label list Values. Optional
1257	// a map of independent configurability axes
1258	ConfigurableValues configurableStringLists
1259
1260	// If a property has struct tag "variant_prepend", this value should
1261	// be set to True, so that when bp2build generates BUILD.bazel, variant
1262	// properties(select ...) come before general properties.
1263	Prepend bool
1264}
1265
1266// IsEmpty returns true if the attribute has no values under any configuration.
1267func (sla StringListAttribute) IsEmpty() bool {
1268	return len(sla.Value) == 0 && !sla.HasConfigurableValues()
1269}
1270
1271type configurableStringLists map[ConfigurationAxis]stringListSelectValues
1272
1273func (csl configurableStringLists) Append(other configurableStringLists) {
1274	for axis, otherSelects := range other {
1275		selects := csl[axis]
1276		if selects == nil {
1277			selects = make(stringListSelectValues, len(otherSelects))
1278		}
1279		selects.appendSelects(otherSelects)
1280		csl[axis] = selects
1281	}
1282}
1283
1284func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
1285	if csl[axis] == nil {
1286		csl[axis] = make(stringListSelectValues)
1287	}
1288	csl[axis][config] = list
1289}
1290
1291type stringListSelectValues map[string][]string
1292
1293func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
1294	for k, v := range other {
1295		sl[k] = append(sl[k], v...)
1296	}
1297}
1298
1299func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
1300	for _, val := range sl {
1301		if len(val) > 0 {
1302			return true
1303		}
1304	}
1305	return false
1306}
1307
1308// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
1309func MakeStringListAttribute(value []string) StringListAttribute {
1310	// NOTE: These strings are not necessarily unique or sorted.
1311	return StringListAttribute{
1312		Value:              value,
1313		ConfigurableValues: make(configurableStringLists),
1314	}
1315}
1316
1317// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1318func (sla StringListAttribute) HasConfigurableValues() bool {
1319	for _, selectValues := range sla.ConfigurableValues {
1320		if len(selectValues) > 0 {
1321			return true
1322		}
1323	}
1324	return false
1325}
1326
1327// Append appends all values, including os and arch specific ones, from another
1328// StringListAttribute to this StringListAttribute
1329func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
1330	sla.Value = append(sla.Value, other.Value...)
1331	if sla.ConfigurableValues == nil {
1332		sla.ConfigurableValues = make(configurableStringLists)
1333	}
1334	sla.ConfigurableValues.Append(other.ConfigurableValues)
1335	return sla
1336}
1337
1338func (sla *StringListAttribute) Clone() *StringListAttribute {
1339	result := &StringListAttribute{}
1340	return result.Append(*sla)
1341}
1342
1343// SetSelectValue set a value for a bazel select for the given axis, config and value.
1344func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1345	axis.validateConfig(config)
1346	switch axis.configurationType {
1347	case noConfig:
1348		sla.Value = list
1349	case arch, os, osArch, productVariables, osAndInApex:
1350		if sla.ConfigurableValues == nil {
1351			sla.ConfigurableValues = make(configurableStringLists)
1352		}
1353		sla.ConfigurableValues.setValueForAxis(axis, config, list)
1354	default:
1355		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1356	}
1357}
1358
1359// SelectValue gets a value for a bazel select for the given axis and config.
1360func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1361	axis.validateConfig(config)
1362	switch axis.configurationType {
1363	case noConfig:
1364		return sla.Value
1365	case arch, os, osArch, productVariables, osAndInApex:
1366		return sla.ConfigurableValues[axis][config]
1367	default:
1368		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1369	}
1370}
1371
1372// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1373func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1374	return SortedConfigurationAxes(sla.ConfigurableValues)
1375}
1376
1377// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1378// configuration-specific values. For example, if we would convert this StringListAttribute as:
1379//
1380//	["a", "b", "c"] + select({
1381//	   "//condition:one": ["a", "d"],
1382//	   "//conditions:default": [],
1383//	})
1384//
1385// after this function, we would convert this StringListAttribute as:
1386//
1387//	["a", "b", "c"] + select({
1388//	   "//condition:one": ["d"],
1389//	   "//conditions:default": [],
1390//	})
1391func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1392	base := sla.Value
1393	for axis, configToList := range sla.ConfigurableValues {
1394		for config, list := range configToList {
1395			remaining := SubtractStrings(list, base)
1396			if len(remaining) == 0 {
1397				delete(sla.ConfigurableValues[axis], config)
1398			} else {
1399				sla.ConfigurableValues[axis][config] = remaining
1400			}
1401		}
1402	}
1403}
1404
1405// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1406// Starlark string.format compatible tag for productVariable.
1407func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
1408	if len(slice) == 0 {
1409		return slice, false
1410	}
1411	ret := make([]string, 0, len(slice))
1412	changesMade := false
1413	for _, s := range slice {
1414		newS, changed := TryVariableSubstitution(s, productVariable)
1415		ret = append(ret, newS)
1416		changesMade = changesMade || changed
1417	}
1418	return ret, changesMade
1419}
1420
1421// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1422// string.format compatible tag for productVariable.
1423func TryVariableSubstitution(s string, productVariable string) (string, bool) {
1424	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
1425	return sub, s != sub
1426}
1427