• 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	"regexp"
21	"sort"
22	"strings"
23
24	"github.com/google/blueprint"
25)
26
27// BazelTargetModuleProperties contain properties and metadata used for
28// Blueprint to BUILD file conversion.
29type BazelTargetModuleProperties struct {
30	// The Bazel rule class for this target.
31	Rule_class string `blueprint:"mutated"`
32
33	// The target label for the bzl file containing the definition of the rule class.
34	Bzl_load_location string `blueprint:"mutated"`
35}
36
37var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
38
39// Label is used to represent a Bazel compatible Label. Also stores the original
40// bp text to support string replacement.
41type Label struct {
42	// The string representation of a Bazel target label. This can be a relative
43	// or fully qualified label. These labels are used for generating BUILD
44	// files with bp2build.
45	Label string
46
47	// The original Soong/Blueprint module name that the label was derived from.
48	// This is used for replacing references to the original name with the new
49	// label, for example in genrule cmds.
50	//
51	// While there is a reversible 1:1 mapping from the module name to Bazel
52	// label with bp2build that could make computing the original module name
53	// from the label automatic, it is not the case for handcrafted targets,
54	// where modules can have a custom label mapping through the { bazel_module:
55	// { label: <label> } } property.
56	//
57	// With handcrafted labels, those modules don't go through bp2build
58	// conversion, but relies on handcrafted targets in the source tree.
59	OriginalModuleName string
60}
61
62// LabelList is used to represent a list of Bazel labels.
63type LabelList struct {
64	Includes []Label
65	Excludes []Label
66}
67
68// MakeLabelList creates a LabelList from a list Label
69func MakeLabelList(labels []Label) LabelList {
70	return LabelList{
71		Includes: labels,
72		Excludes: nil,
73	}
74}
75
76func (ll *LabelList) Equals(other LabelList) bool {
77	if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
78		return false
79	}
80	for i, _ := range ll.Includes {
81		if ll.Includes[i] != other.Includes[i] {
82			return false
83		}
84	}
85	for i, _ := range ll.Excludes {
86		if ll.Excludes[i] != other.Excludes[i] {
87			return false
88		}
89	}
90	return true
91}
92
93func (ll *LabelList) IsNil() bool {
94	return ll.Includes == nil && ll.Excludes == nil
95}
96
97func (ll *LabelList) deepCopy() LabelList {
98	return LabelList{
99		Includes: ll.Includes[:],
100		Excludes: ll.Excludes[:],
101	}
102}
103
104// uniqueParentDirectories returns a list of the unique parent directories for
105// all files in ll.Includes.
106func (ll *LabelList) uniqueParentDirectories() []string {
107	dirMap := map[string]bool{}
108	for _, label := range ll.Includes {
109		dirMap[filepath.Dir(label.Label)] = true
110	}
111	dirs := []string{}
112	for dir := range dirMap {
113		dirs = append(dirs, dir)
114	}
115	return dirs
116}
117
118// Add inserts the label Label at the end of the LabelList.
119func (ll *LabelList) Add(label *Label) {
120	if label == nil {
121		return
122	}
123	ll.Includes = append(ll.Includes, *label)
124}
125
126// Append appends the fields of other labelList to the corresponding fields of ll.
127func (ll *LabelList) Append(other LabelList) {
128	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
129		ll.Includes = append(ll.Includes, other.Includes...)
130	}
131	if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
132		ll.Excludes = append(other.Excludes, other.Excludes...)
133	}
134}
135
136// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
137// the slice in a sorted order.
138func UniqueSortedBazelLabels(originalLabels []Label) []Label {
139	uniqueLabelsSet := make(map[Label]bool)
140	for _, l := range originalLabels {
141		uniqueLabelsSet[l] = true
142	}
143	var uniqueLabels []Label
144	for l, _ := range uniqueLabelsSet {
145		uniqueLabels = append(uniqueLabels, l)
146	}
147	sort.SliceStable(uniqueLabels, func(i, j int) bool {
148		return uniqueLabels[i].Label < uniqueLabels[j].Label
149	})
150	return uniqueLabels
151}
152
153func FirstUniqueBazelLabels(originalLabels []Label) []Label {
154	var labels []Label
155	found := make(map[Label]bool, len(originalLabels))
156	for _, l := range originalLabels {
157		if _, ok := found[l]; ok {
158			continue
159		}
160		labels = append(labels, l)
161		found[l] = true
162	}
163	return labels
164}
165
166func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
167	var uniqueLabelList LabelList
168	uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
169	uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
170	return uniqueLabelList
171}
172
173func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
174	var uniqueLabelList LabelList
175	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
176	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
177	return uniqueLabelList
178}
179
180// Subtract needle from haystack
181func SubtractStrings(haystack []string, needle []string) []string {
182	// This is really a set
183	needleMap := make(map[string]bool)
184	for _, s := range needle {
185		needleMap[s] = true
186	}
187
188	var strings []string
189	for _, s := range haystack {
190		if exclude := needleMap[s]; !exclude {
191			strings = append(strings, s)
192		}
193	}
194
195	return strings
196}
197
198// Subtract needle from haystack
199func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
200	// This is really a set
201	needleMap := make(map[Label]bool)
202	for _, s := range needle {
203		needleMap[s] = true
204	}
205
206	var labels []Label
207	for _, label := range haystack {
208		if exclude := needleMap[label]; !exclude {
209			labels = append(labels, label)
210		}
211	}
212
213	return labels
214}
215
216// Appends two LabelLists, returning the combined list.
217func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
218	var result LabelList
219	result.Includes = append(a.Includes, b.Includes...)
220	result.Excludes = append(a.Excludes, b.Excludes...)
221	return result
222}
223
224// Subtract needle from haystack
225func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
226	var result LabelList
227	result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
228	// NOTE: Excludes are intentionally not subtracted
229	result.Excludes = haystack.Excludes
230	return result
231}
232
233type Attribute interface {
234	HasConfigurableValues() bool
235}
236
237type labelSelectValues map[string]*Label
238
239type configurableLabels map[ConfigurationAxis]labelSelectValues
240
241func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
242	if cl[axis] == nil {
243		cl[axis] = make(labelSelectValues)
244	}
245	cl[axis][config] = value
246}
247
248// Represents an attribute whose value is a single label
249type LabelAttribute struct {
250	Value *Label
251
252	ConfigurableValues configurableLabels
253}
254
255func (la *LabelAttribute) axisTypes() map[configurationType]bool {
256	types := map[configurationType]bool{}
257	for k := range la.ConfigurableValues {
258		if len(la.ConfigurableValues[k]) > 0 {
259			types[k.configurationType] = true
260		}
261	}
262	return types
263}
264
265// Collapse reduces the configurable axes of the label attribute to a single axis.
266// This is necessary for final writing to bp2build, as a configurable label
267// attribute can only be comprised by a single select.
268func (la *LabelAttribute) Collapse() error {
269	axisTypes := la.axisTypes()
270	_, containsOs := axisTypes[os]
271	_, containsArch := axisTypes[arch]
272	_, containsOsArch := axisTypes[osArch]
273	_, containsProductVariables := axisTypes[productVariables]
274	if containsProductVariables {
275		if containsOs || containsArch || containsOsArch {
276			return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
277		}
278	}
279	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
280		// If a bool attribute has both os and arch configuration axes, the only
281		// way to successfully union their values is to increase the granularity
282		// of the configuration criteria to os_arch.
283		for osType, supportedArchs := range osToArchMap {
284			for _, supportedArch := range supportedArchs {
285				osArch := osArchString(osType, supportedArch)
286				if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
287					// Do nothing, as the arch_os is explicitly defined already.
288				} else {
289					archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
290					osVal := la.SelectValue(OsConfigurationAxis, osType)
291					if osVal != nil && archVal != nil {
292						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
293						// runs after os mutator.
294						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
295					} else if osVal != nil && archVal == nil {
296						la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
297					} else if osVal == nil && archVal != nil {
298						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
299					}
300				}
301			}
302		}
303		// All os_arch values are now set. Clear os and arch axes.
304		delete(la.ConfigurableValues, ArchConfigurationAxis)
305		delete(la.ConfigurableValues, OsConfigurationAxis)
306	}
307	return nil
308}
309
310// HasConfigurableValues returns whether there are configurable values set for this label.
311func (la LabelAttribute) HasConfigurableValues() bool {
312	for _, selectValues := range la.ConfigurableValues {
313		if len(selectValues) > 0 {
314			return true
315		}
316	}
317	return false
318}
319
320// SetValue sets the base, non-configured value for the Label
321func (la *LabelAttribute) SetValue(value Label) {
322	la.SetSelectValue(NoConfigAxis, "", value)
323}
324
325// SetSelectValue set a value for a bazel select for the given axis, config and value.
326func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
327	axis.validateConfig(config)
328	switch axis.configurationType {
329	case noConfig:
330		la.Value = &value
331	case arch, os, osArch, productVariables:
332		if la.ConfigurableValues == nil {
333			la.ConfigurableValues = make(configurableLabels)
334		}
335		la.ConfigurableValues.setValueForAxis(axis, config, &value)
336	default:
337		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
338	}
339}
340
341// SelectValue gets a value for a bazel select for the given axis and config.
342func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
343	axis.validateConfig(config)
344	switch axis.configurationType {
345	case noConfig:
346		return la.Value
347	case arch, os, osArch, productVariables:
348		return la.ConfigurableValues[axis][config]
349	default:
350		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
351	}
352}
353
354// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
355func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
356	keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
357	for k := range la.ConfigurableValues {
358		keys = append(keys, k)
359	}
360
361	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
362	return keys
363}
364
365// MakeLabelAttribute turns a string into a LabelAttribute
366func MakeLabelAttribute(label string) *LabelAttribute {
367	return &LabelAttribute{
368		Value: &Label{
369			Label: label,
370		},
371	}
372}
373
374type configToBools map[string]bool
375
376func (ctb configToBools) setValue(config string, value *bool) {
377	if value == nil {
378		if _, ok := ctb[config]; ok {
379			delete(ctb, config)
380		}
381		return
382	}
383	ctb[config] = *value
384}
385
386type configurableBools map[ConfigurationAxis]configToBools
387
388func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
389	if cb[axis] == nil {
390		cb[axis] = make(configToBools)
391	}
392	cb[axis].setValue(config, value)
393}
394
395// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
396type BoolAttribute struct {
397	Value *bool
398
399	ConfigurableValues configurableBools
400}
401
402// HasConfigurableValues returns whether there are configurable values for this attribute.
403func (ba BoolAttribute) HasConfigurableValues() bool {
404	for _, cfgToBools := range ba.ConfigurableValues {
405		if len(cfgToBools) > 0 {
406			return true
407		}
408	}
409	return false
410}
411
412// SetSelectValue sets value for the given axis/config.
413func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
414	axis.validateConfig(config)
415	switch axis.configurationType {
416	case noConfig:
417		ba.Value = value
418	case arch, os, osArch, productVariables:
419		if ba.ConfigurableValues == nil {
420			ba.ConfigurableValues = make(configurableBools)
421		}
422		ba.ConfigurableValues.setValueForAxis(axis, config, value)
423	default:
424		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
425	}
426}
427
428// ToLabelListAttribute creates and returns a LabelListAttribute from this
429// bool attribute, where each bool in this attribute corresponds to a
430// label list value in the resultant attribute.
431func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
432	getLabelList := func(boolPtr *bool) LabelList {
433		if boolPtr == nil {
434			return LabelList{nil, nil}
435		} else if *boolPtr {
436			return trueVal
437		} else {
438			return falseVal
439		}
440	}
441
442	mainVal := getLabelList(ba.Value)
443	if !ba.HasConfigurableValues() {
444		return MakeLabelListAttribute(mainVal), nil
445	}
446
447	result := LabelListAttribute{}
448	if err := ba.Collapse(); err != nil {
449		return result, err
450	}
451
452	for axis, configToBools := range ba.ConfigurableValues {
453		if len(configToBools) < 1 {
454			continue
455		}
456		for config, boolPtr := range configToBools {
457			val := getLabelList(&boolPtr)
458			if !val.Equals(mainVal) {
459				result.SetSelectValue(axis, config, val)
460			}
461		}
462		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
463	}
464
465	return result, nil
466}
467
468// Collapse reduces the configurable axes of the boolean attribute to a single axis.
469// This is necessary for final writing to bp2build, as a configurable boolean
470// attribute can only be comprised by a single select.
471func (ba *BoolAttribute) Collapse() error {
472	axisTypes := ba.axisTypes()
473	_, containsOs := axisTypes[os]
474	_, containsArch := axisTypes[arch]
475	_, containsOsArch := axisTypes[osArch]
476	_, containsProductVariables := axisTypes[productVariables]
477	if containsProductVariables {
478		if containsOs || containsArch || containsOsArch {
479			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
480		}
481	}
482	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
483		// If a bool attribute has both os and arch configuration axes, the only
484		// way to successfully union their values is to increase the granularity
485		// of the configuration criteria to os_arch.
486		for osType, supportedArchs := range osToArchMap {
487			for _, supportedArch := range supportedArchs {
488				osArch := osArchString(osType, supportedArch)
489				if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
490					// Do nothing, as the arch_os is explicitly defined already.
491				} else {
492					archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
493					osVal := ba.SelectValue(OsConfigurationAxis, osType)
494					if osVal != nil && archVal != nil {
495						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
496						// runs after os mutator.
497						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
498					} else if osVal != nil && archVal == nil {
499						ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
500					} else if osVal == nil && archVal != nil {
501						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
502					}
503				}
504			}
505		}
506		// All os_arch values are now set. Clear os and arch axes.
507		delete(ba.ConfigurableValues, ArchConfigurationAxis)
508		delete(ba.ConfigurableValues, OsConfigurationAxis)
509		// Verify post-condition; this should never fail, provided no additional
510		// axes are introduced.
511		if len(ba.ConfigurableValues) > 1 {
512			panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
513		}
514	}
515	return nil
516}
517
518func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
519	types := map[configurationType]bool{}
520	for k := range ba.ConfigurableValues {
521		if len(ba.ConfigurableValues[k]) > 0 {
522			types[k.configurationType] = true
523		}
524	}
525	return types
526}
527
528// SelectValue gets the value for the given axis/config.
529func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
530	axis.validateConfig(config)
531	switch axis.configurationType {
532	case noConfig:
533		return ba.Value
534	case arch, os, osArch, productVariables:
535		if v, ok := ba.ConfigurableValues[axis][config]; ok {
536			return &v
537		} else {
538			return nil
539		}
540	default:
541		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
542	}
543}
544
545// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
546func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
547	keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
548	for k := range ba.ConfigurableValues {
549		keys = append(keys, k)
550	}
551
552	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
553	return keys
554}
555
556// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
557type labelListSelectValues map[string]LabelList
558
559func (ll labelListSelectValues) addSelects(label labelSelectValues) {
560	for k, v := range label {
561		if label == nil {
562			continue
563		}
564		l := ll[k]
565		(&l).Add(v)
566		ll[k] = l
567	}
568}
569
570func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
571	for k, v := range other {
572		l := ll[k]
573		if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
574			l.Includes = []Label{}
575		}
576		(&l).Append(v)
577		ll[k] = l
578	}
579}
580
581// HasConfigurableValues returns whether there are configurable values within this set of selects.
582func (ll labelListSelectValues) HasConfigurableValues() bool {
583	for _, v := range ll {
584		if v.Includes != nil {
585			return true
586		}
587	}
588	return false
589}
590
591// LabelListAttribute is used to represent a list of Bazel labels as an
592// attribute.
593type LabelListAttribute struct {
594	// The non-configured attribute label list Value. Required.
595	Value LabelList
596
597	// The configured attribute label list Values. Optional
598	// a map of independent configurability axes
599	ConfigurableValues configurableLabelLists
600
601	// If true, differentiate between "nil" and "empty" list. nil means that
602	// this attribute should not be specified at all, and "empty" means that
603	// the attribute should be explicitly specified as an empty list.
604	// This mode facilitates use of attribute defaults: an empty list should
605	// override the default.
606	ForceSpecifyEmptyList bool
607
608	// If true, signal the intent to the code generator to emit all select keys,
609	// even if the Includes list for that key is empty. This mode facilitates
610	// specific select statements where an empty list for a non-default select
611	// key has a meaning.
612	EmitEmptyList bool
613}
614
615type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
616
617func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
618	if list.IsNil() {
619		if _, ok := cll[axis][config]; ok {
620			delete(cll[axis], config)
621		}
622		return
623	}
624	if cll[axis] == nil {
625		cll[axis] = make(labelListSelectValues)
626	}
627
628	cll[axis][config] = list
629}
630
631func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
632	for axis, otherSelects := range other {
633		selects := cll[axis]
634		if selects == nil {
635			selects = make(labelListSelectValues, len(otherSelects))
636		}
637		selects.appendSelects(otherSelects, forceSpecifyEmptyList)
638		cll[axis] = selects
639	}
640}
641
642func (lla *LabelListAttribute) Clone() *LabelListAttribute {
643	result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
644	return result.Append(*lla)
645}
646
647// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
648func MakeLabelListAttribute(value LabelList) LabelListAttribute {
649	return LabelListAttribute{
650		Value:              value,
651		ConfigurableValues: make(configurableLabelLists),
652	}
653}
654
655func (lla *LabelListAttribute) SetValue(list LabelList) {
656	lla.SetSelectValue(NoConfigAxis, "", list)
657}
658
659// SetSelectValue set a value for a bazel select for the given axis, config and value.
660func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
661	axis.validateConfig(config)
662	switch axis.configurationType {
663	case noConfig:
664		lla.Value = list
665	case arch, os, osArch, productVariables:
666		if lla.ConfigurableValues == nil {
667			lla.ConfigurableValues = make(configurableLabelLists)
668		}
669		lla.ConfigurableValues.setValueForAxis(axis, config, list)
670	default:
671		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
672	}
673}
674
675// SelectValue gets a value for a bazel select for the given axis and config.
676func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
677	axis.validateConfig(config)
678	switch axis.configurationType {
679	case noConfig:
680		return lla.Value
681	case arch, os, osArch, productVariables:
682		return lla.ConfigurableValues[axis][config]
683	default:
684		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
685	}
686}
687
688// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
689func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
690	keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
691	for k := range lla.ConfigurableValues {
692		keys = append(keys, k)
693	}
694
695	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
696	return keys
697}
698
699// Append all values, including os and arch specific ones, from another
700// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
701func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
702	forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
703	if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
704		lla.Value.Includes = []Label{}
705	}
706	lla.Value.Append(other.Value)
707	if lla.ConfigurableValues == nil {
708		lla.ConfigurableValues = make(configurableLabelLists)
709	}
710	lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
711	return lla
712}
713
714// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
715// LabelList within the LabelListAttribute
716func (lla *LabelListAttribute) Add(label *LabelAttribute) {
717	if label == nil {
718		return
719	}
720
721	lla.Value.Add(label.Value)
722	if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
723		lla.ConfigurableValues = make(configurableLabelLists)
724	}
725	for axis, _ := range label.ConfigurableValues {
726		if _, exists := lla.ConfigurableValues[axis]; !exists {
727			lla.ConfigurableValues[axis] = make(labelListSelectValues)
728		}
729		lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
730	}
731}
732
733// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
734func (lla LabelListAttribute) HasConfigurableValues() bool {
735	for _, selectValues := range lla.ConfigurableValues {
736		if len(selectValues) > 0 {
737			return true
738		}
739	}
740	return false
741}
742
743// IsEmpty returns true if the attribute has no values under any configuration.
744func (lla LabelListAttribute) IsEmpty() bool {
745	if len(lla.Value.Includes) > 0 {
746		return false
747	}
748	for axis, _ := range lla.ConfigurableValues {
749		if lla.ConfigurableValues[axis].HasConfigurableValues() {
750			return false
751		}
752	}
753	return true
754}
755
756// IsNil returns true if the attribute has not been set for any configuration.
757func (lla LabelListAttribute) IsNil() bool {
758	if lla.Value.Includes != nil {
759		return false
760	}
761	return !lla.HasConfigurableValues()
762}
763
764// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
765// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
766// be removed, e.g. if they could cause duplicate element failures.
767func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
768	val := lla.SelectValue(axis, config)
769	newList := SubtractBazelLabelList(val, labelList)
770	newList.Excludes = append(newList.Excludes, labelList.Includes...)
771	lla.SetSelectValue(axis, config, newList)
772}
773
774// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
775// the base value and included in default values as appropriate.
776func (lla *LabelListAttribute) ResolveExcludes() {
777	for axis, configToLabels := range lla.ConfigurableValues {
778		baseLabels := lla.Value.deepCopy()
779		for config, val := range configToLabels {
780			// Exclude config-specific excludes from base value
781			lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
782
783			// add base values to config specific to add labels excluded by others in this axis
784			// then remove all config-specific excludes
785			allLabels := baseLabels.deepCopy()
786			allLabels.Append(val)
787			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
788		}
789
790		// After going through all configs, delete the duplicates in the config
791		// values that are already in the base Value.
792		for config, val := range configToLabels {
793			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
794		}
795
796		// Now that the Value list is finalized for this axis, compare it with
797		// the original list, and union the difference with the default
798		// condition for the axis.
799		difference := SubtractBazelLabelList(baseLabels, lla.Value)
800		existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
801		existingDefaults.Append(difference)
802		lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
803
804		// if everything ends up without includes, just delete the axis
805		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
806			delete(lla.ConfigurableValues, axis)
807		}
808	}
809}
810
811// OtherModuleContext is a limited context that has methods with information about other modules.
812type OtherModuleContext interface {
813	ModuleFromName(name string) (blueprint.Module, bool)
814	OtherModuleType(m blueprint.Module) string
815	OtherModuleName(m blueprint.Module) string
816	OtherModuleDir(m blueprint.Module) string
817	ModuleErrorf(fmt string, args ...interface{})
818}
819
820// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
821// label and whether it was changed.
822type LabelMapper func(OtherModuleContext, Label) (string, bool)
823
824// LabelPartition contains descriptions of a partition for labels
825type LabelPartition struct {
826	// Extensions to include in this partition
827	Extensions []string
828	// LabelMapper is a function that can map a label to a new label, and indicate whether to include
829	// the mapped label in the partition
830	LabelMapper LabelMapper
831	// Whether to store files not included in any other partition in a group of LabelPartitions
832	// Only one partition in a group of LabelPartitions can enabled Keep_remainder
833	Keep_remainder bool
834}
835
836// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
837// partition
838type LabelPartitions map[string]LabelPartition
839
840// filter returns a pointer to a label if the label should be included in the partition or nil if
841// not.
842func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
843	if lf.LabelMapper != nil {
844		if newLabel, changed := lf.LabelMapper(ctx, label); changed {
845			return &Label{newLabel, label.OriginalModuleName}
846		}
847	}
848	for _, ext := range lf.Extensions {
849		if strings.HasSuffix(label.Label, ext) {
850			return &label
851		}
852	}
853
854	return nil
855}
856
857// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
858type PartitionToLabelListAttribute map[string]LabelListAttribute
859
860type partitionToLabelList map[string]*LabelList
861
862func (p partitionToLabelList) appendIncludes(partition string, label Label) {
863	if _, ok := p[partition]; !ok {
864		p[partition] = &LabelList{}
865	}
866	p[partition].Includes = append(p[partition].Includes, label)
867}
868
869func (p partitionToLabelList) excludes(partition string, excludes []Label) {
870	if _, ok := p[partition]; !ok {
871		p[partition] = &LabelList{}
872	}
873	p[partition].Excludes = excludes
874}
875
876// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
877func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
878	ret := PartitionToLabelListAttribute{}
879	var partitionNames []string
880	// Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
881	var remainderPartition *string
882	for p, f := range partitions {
883		partitionNames = append(partitionNames, p)
884		if f.Keep_remainder {
885			if remainderPartition != nil {
886				panic("only one partition can store the remainder")
887			}
888			// If we take the address of p in a loop, we'll end up with the last value of p in
889			// remainderPartition, we want the requested partition
890			capturePartition := p
891			remainderPartition = &capturePartition
892		}
893	}
894
895	partitionLabelList := func(axis ConfigurationAxis, config string) {
896		value := lla.SelectValue(axis, config)
897		partitionToLabels := partitionToLabelList{}
898		for _, item := range value.Includes {
899			wasFiltered := false
900			var inPartition *string
901			for partition, f := range partitions {
902				filtered := f.filter(ctx, item)
903				if filtered == nil {
904					// did not match this filter, keep looking
905					continue
906				}
907				wasFiltered = true
908				partitionToLabels.appendIncludes(partition, *filtered)
909				// don't need to check other partitions if this filter used the item,
910				// continue checking if mapped to another name
911				if *filtered == item {
912					if inPartition != nil {
913						ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
914					}
915					capturePartition := partition
916					inPartition = &capturePartition
917				}
918			}
919
920			// if not specified in a partition, add to remainder partition if one exists
921			if !wasFiltered && remainderPartition != nil {
922				partitionToLabels.appendIncludes(*remainderPartition, item)
923			}
924		}
925
926		// ensure empty lists are maintained
927		if value.Excludes != nil {
928			for _, partition := range partitionNames {
929				partitionToLabels.excludes(partition, value.Excludes)
930			}
931		}
932
933		for partition, list := range partitionToLabels {
934			val := ret[partition]
935			(&val).SetSelectValue(axis, config, *list)
936			ret[partition] = val
937		}
938	}
939
940	partitionLabelList(NoConfigAxis, "")
941	for axis, configToList := range lla.ConfigurableValues {
942		for config, _ := range configToList {
943			partitionLabelList(axis, config)
944		}
945	}
946	return ret
947}
948
949// StringListAttribute corresponds to the string_list Bazel attribute type with
950// support for additional metadata, like configurations.
951type StringListAttribute struct {
952	// The base value of the string list attribute.
953	Value []string
954
955	// The configured attribute label list Values. Optional
956	// a map of independent configurability axes
957	ConfigurableValues configurableStringLists
958}
959
960type configurableStringLists map[ConfigurationAxis]stringListSelectValues
961
962func (csl configurableStringLists) Append(other configurableStringLists) {
963	for axis, otherSelects := range other {
964		selects := csl[axis]
965		if selects == nil {
966			selects = make(stringListSelectValues, len(otherSelects))
967		}
968		selects.appendSelects(otherSelects)
969		csl[axis] = selects
970	}
971}
972
973func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
974	if csl[axis] == nil {
975		csl[axis] = make(stringListSelectValues)
976	}
977	csl[axis][config] = list
978}
979
980type stringListSelectValues map[string][]string
981
982func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
983	for k, v := range other {
984		sl[k] = append(sl[k], v...)
985	}
986}
987
988func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
989	for _, val := range sl {
990		if len(val) > 0 {
991			return true
992		}
993	}
994	return false
995}
996
997// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
998func MakeStringListAttribute(value []string) StringListAttribute {
999	// NOTE: These strings are not necessarily unique or sorted.
1000	return StringListAttribute{
1001		Value:              value,
1002		ConfigurableValues: make(configurableStringLists),
1003	}
1004}
1005
1006// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
1007func (sla StringListAttribute) HasConfigurableValues() bool {
1008	for _, selectValues := range sla.ConfigurableValues {
1009		if len(selectValues) > 0 {
1010			return true
1011		}
1012	}
1013	return false
1014}
1015
1016// Append appends all values, including os and arch specific ones, from another
1017// StringListAttribute to this StringListAttribute
1018func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
1019	sla.Value = append(sla.Value, other.Value...)
1020	if sla.ConfigurableValues == nil {
1021		sla.ConfigurableValues = make(configurableStringLists)
1022	}
1023	sla.ConfigurableValues.Append(other.ConfigurableValues)
1024	return sla
1025}
1026
1027func (sla *StringListAttribute) Clone() *StringListAttribute {
1028	result := &StringListAttribute{}
1029	return result.Append(*sla)
1030}
1031
1032// SetSelectValue set a value for a bazel select for the given axis, config and value.
1033func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
1034	axis.validateConfig(config)
1035	switch axis.configurationType {
1036	case noConfig:
1037		sla.Value = list
1038	case arch, os, osArch, productVariables:
1039		if sla.ConfigurableValues == nil {
1040			sla.ConfigurableValues = make(configurableStringLists)
1041		}
1042		sla.ConfigurableValues.setValueForAxis(axis, config, list)
1043	default:
1044		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1045	}
1046}
1047
1048// SelectValue gets a value for a bazel select for the given axis and config.
1049func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
1050	axis.validateConfig(config)
1051	switch axis.configurationType {
1052	case noConfig:
1053		return sla.Value
1054	case arch, os, osArch, productVariables:
1055		return sla.ConfigurableValues[axis][config]
1056	default:
1057		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
1058	}
1059}
1060
1061// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
1062func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
1063	keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
1064	for k := range sla.ConfigurableValues {
1065		keys = append(keys, k)
1066	}
1067
1068	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
1069	return keys
1070}
1071
1072// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
1073// configuration-specific values. For example, if we would convert this StringListAttribute as:
1074// ["a", "b", "c"] + select({
1075//    "//condition:one": ["a", "d"],
1076//    "//conditions:default": [],
1077// })
1078// after this function, we would convert this StringListAttribute as:
1079// ["a", "b", "c"] + select({
1080//    "//condition:one": ["d"],
1081//    "//conditions:default": [],
1082// })
1083func (sla *StringListAttribute) DeduplicateAxesFromBase() {
1084	base := sla.Value
1085	for axis, configToList := range sla.ConfigurableValues {
1086		for config, list := range configToList {
1087			remaining := SubtractStrings(list, base)
1088			if len(remaining) == 0 {
1089				delete(sla.ConfigurableValues[axis], config)
1090			} else {
1091				sla.ConfigurableValues[axis][config] = remaining
1092			}
1093		}
1094	}
1095}
1096
1097// TryVariableSubstitution, replace string substitution formatting within each string in slice with
1098// Starlark string.format compatible tag for productVariable.
1099func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
1100	ret := make([]string, 0, len(slice))
1101	changesMade := false
1102	for _, s := range slice {
1103		newS, changed := TryVariableSubstitution(s, productVariable)
1104		ret = append(ret, newS)
1105		changesMade = changesMade || changed
1106	}
1107	return ret, changesMade
1108}
1109
1110// TryVariableSubstitution, replace string substitution formatting within s with Starlark
1111// string.format compatible tag for productVariable.
1112func TryVariableSubstitution(s string, productVariable string) (string, bool) {
1113	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
1114	return sub, s != sub
1115}
1116