• 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	uniqueLabels := FirstUniqueBazelLabels(originalLabels)
198	sort.SliceStable(uniqueLabels, func(i, j int) bool {
199		return uniqueLabels[i].Label < uniqueLabels[j].Label
200	})
201	return uniqueLabels
202}
203
204func FirstUniqueBazelLabels(originalLabels []Label) []Label {
205	var labels []Label
206	found := make(map[string]bool, len(originalLabels))
207	for _, l := range originalLabels {
208		if _, ok := found[l.Label]; ok {
209			continue
210		}
211		labels = append(labels, l)
212		found[l.Label] = true
213	}
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 {
225	var uniqueLabelList LabelList
226	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
227	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
228	return uniqueLabelList
229}
230
231// Subtract needle from haystack
232func SubtractStrings(haystack []string, needle []string) []string {
233	// This is really a set
234	needleMap := make(map[string]bool)
235	for _, s := range needle {
236		needleMap[s] = true
237	}
238
239	var strings []string
240	for _, s := range haystack {
241		if exclude := needleMap[s]; !exclude {
242			strings = append(strings, s)
243		}
244	}
245
246	return strings
247}
248
249// Subtract needle from haystack
250func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
251	// This is really a set
252	needleMap := make(map[Label]bool)
253	for _, s := range needle {
254		needleMap[s] = true
255	}
256
257	var labels []Label
258	for _, label := range haystack {
259		if exclude := needleMap[label]; !exclude {
260			labels = append(labels, label)
261		}
262	}
263
264	return labels
265}
266
267// 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
275// 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
284// 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
319type Attribute interface {
320	HasConfigurableValues() bool
321}
322
323type labelSelectValues map[string]*Label
324
325type configurableLabels map[ConfigurationAxis]labelSelectValues
326
327func (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
332}
333
334// Represents an attribute whose value is a single label
335type LabelAttribute struct {
336	Value *Label
337
338	ConfigurableValues configurableLabels
339}
340
341func (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 {
362			if containsArch {
363				allProductVariablesAreArchVariant := true
364				for k := range la.ConfigurableValues {
365					if k.configurationType == productVariables && !k.archVariant {
366						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			}
375		}
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
408// HasConfigurableValues returns whether there are configurable values set for this label.
409func (la LabelAttribute) HasConfigurableValues() bool {
410	for _, selectValues := range la.ConfigurableValues {
411		if len(selectValues) > 0 {
412			return true
413		}
414	}
415	return false
416}
417
418// SetValue sets the base, non-configured value for the Label
419func (la *LabelAttribute) SetValue(value Label) {
420	la.SetSelectValue(NoConfigAxis, "", value)
421}
422
423// 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
429	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
430		if la.ConfigurableValues == nil {
431			la.ConfigurableValues = make(configurableLabels)
432		}
433		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.
440func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
441	axis.validateConfig(config)
442	switch axis.configurationType {
443	case noConfig:
444		return la.Value
445	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
446		return la.ConfigurableValues[axis][config]
447	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 {
454	return SortedConfigurationAxes(la.ConfigurableValues)
455}
456
457// 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
466type 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 {
496	for _, cfgToBools := range ba.ConfigurableValues {
497		if len(cfgToBools) > 0 {
498			return true
499		}
500	}
501	return false
502}
503
504// SetValue sets value for the no config axis
505func (ba *BoolAttribute) SetValue(value *bool) {
506	ba.SetSelectValue(NoConfigAxis, "", value)
507}
508
509// 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
515	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
516		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
525// 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
565// 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
596// 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 {
640			panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
641		}
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
656// 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
662	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
663		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 {
675	return SortedConfigurationAxes(ba.ConfigurableValues)
676}
677
678// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
679type labelListSelectValues map[string]LabelList
680
681func (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
692func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
693	for k, v := range other {
694		l := ll[k]
695		if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
696			l.Includes = []Label{}
697		}
698		(&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 {
706		if v.Includes != nil {
707			return true
708		}
709	}
710	return false
711}
712
713// LabelListAttribute is used to represent a list of Bazel labels as an
714// attribute.
715type LabelListAttribute struct {
716	// The non-configured attribute label list Value. Required.
717	Value LabelList
718
719	// The configured attribute label list Values. Optional
720	// a map of independent configurability axes
721	ConfigurableValues configurableLabelLists
722
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
729
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
735
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
740}
741
742type 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
758func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
759	for axis, otherSelects := range other {
760		selects := cll[axis]
761		if selects == nil {
762			selects = make(labelListSelectValues, len(otherSelects))
763		}
764		selects.appendSelects(otherSelects, forceSpecifyEmptyList)
765		cll[axis] = selects
766	}
767}
768
769func (lla *LabelListAttribute) Clone() *LabelListAttribute {
770	result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
771	return result.Append(*lla)
772}
773
774// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
775func MakeLabelListAttribute(value LabelList) LabelListAttribute {
776	return LabelListAttribute{
777		Value:              value,
778		ConfigurableValues: make(configurableLabelLists),
779	}
780}
781
782// 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
787func (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
797	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
798		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
813	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
814		return lla.ConfigurableValues[axis][config]
815	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 {
822	return SortedConfigurationAxes(lla.ConfigurableValues)
823}
824
825// Append all values, including os and arch specific ones, from another
826// 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() {
830		lla.Value.Includes = []Label{}
831	}
832	lla.Value.Append(other.Value)
833	if lla.ConfigurableValues == nil {
834		lla.ConfigurableValues = make(configurableLabelLists)
835	}
836	lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
837	return lla
838}
839
840// 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
859// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
860func (lla LabelListAttribute) HasConfigurableValues() bool {
861	for _, selectValues := range lla.ConfigurableValues {
862		if len(selectValues) > 0 {
863			return true
864		}
865	}
866	return false
867}
868
869// 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
879// 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
892// 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
910// 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() {
913	// 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
933	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)
943			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
944		}
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
952		// 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)
959
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
967// 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
990// 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.
1001type LabelMapper func(OtherModuleContext, Label) (string, bool)
1002
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 {
1023		if newLabel, changed := lf.LabelMapper(ctx, label); changed {
1024			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
1128// 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	}
1145	cs[axis][config] = str
1146}
1147
1148type stringSelectValues map[string]*string
1149
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
1160// SetValue sets the base, non-configured value for the Label
1161func (sa *StringAttribute) SetValue(value string) {
1162	sa.SetSelectValue(NoConfigAxis, "", &value)
1163}
1164
1165// 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
1171	case arch, os, osArch, productVariables, sanitizersEnabled:
1172		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
1187	case arch, os, osArch, productVariables, sanitizersEnabled:
1188		if v, ok := sa.ConfigurableValues[axis][config]; ok {
1189			return v
1190		} 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 {
1200	return SortedConfigurationAxes(sa.ConfigurableValues)
1201}
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 {
1214			return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
1215		}
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		}
1241		/// All os_arch values are now set. Clear os and arch axes.
1242		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		}
1249	} 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		}
1264	}
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
1278// 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
1284	// The configured attribute label list Values. Optional
1285	// a map of independent configurability axes
1286	ConfigurableValues configurableStringLists
1287
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
1292}
1293
1294// 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
1299type configurableStringLists map[ConfigurationAxis]stringListSelectValues
1300
1301func (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
1334}
1335
1336// 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.
1339	return StringListAttribute{
1340		Value:              value,
1341		ConfigurableValues: make(configurableStringLists),
1342	}
1343}
1344
1345// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1346func (sla StringListAttribute) HasConfigurableValues() bool {
1347	for _, selectValues := range sla.ConfigurableValues {
1348		if len(selectValues) > 0 {
1349			return true
1350		}
1351	}
1352	return false
1353}
1354
1355// Append appends all values, including os and arch specific ones, from another
1356// StringListAttribute to this StringListAttribute
1357func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
1358	sla.Value = append(sla.Value, other.Value...)
1359	if sla.ConfigurableValues == nil {
1360		sla.ConfigurableValues = make(configurableStringLists)
1361	}
1362	sla.ConfigurableValues.Append(other.ConfigurableValues)
1363	return sla
1364}
1365
1366func (sla *StringListAttribute) Clone() *StringListAttribute {
1367	result := &StringListAttribute{}
1368	return result.Append(*sla)
1369}
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
1377	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
1378		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
1393	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
1394		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 {
1402	return SortedConfigurationAxes(sla.ConfigurableValues)
1403}
1404
1405// 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:
1407//
1408//	["a", "b", "c"] + select({
1409//	   "//condition:one": ["a", "d"],
1410//	   "//conditions:default": [],
1411//	})
1412//
1413// after this function, we would convert this StringListAttribute as:
1414//
1415//	["a", "b", "c"] + select({
1416//	   "//condition:one": ["d"],
1417//	   "//conditions:default": [],
1418//	})
1419func (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
1433// 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) {
1436	if len(slice) == 0 {
1437		return slice, false
1438	}
1439	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) {
1452	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
1453	return sub, s != sub
1454}
1455
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
1465	// Each element in Constraint_values is a label to a constraint_value
1466	Constraint_values LabelListAttribute
1467}
1468