• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package bp2build
2
3import (
4	"fmt"
5	"reflect"
6
7	"android/soong/android"
8	"android/soong/bazel"
9	"android/soong/starlark_fmt"
10)
11
12// Configurability support for bp2build.
13
14type selects map[string]reflect.Value
15
16func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects) {
17	value := reflect.ValueOf(list.Value)
18	if !list.HasConfigurableValues() {
19		return value, []selects{}
20	}
21
22	var ret []selects
23	for _, axis := range list.SortedConfigurationAxes() {
24		configToLists := list.ConfigurableValues[axis]
25		archSelects := map[string]reflect.Value{}
26		for config, labels := range configToLists {
27			selectKey := axis.SelectKey(config)
28			archSelects[selectKey] = reflect.ValueOf(labels)
29		}
30		if len(archSelects) > 0 {
31			ret = append(ret, archSelects)
32		}
33	}
34
35	return value, ret
36}
37
38func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
39	value := reflect.ValueOf(label.Value)
40	if !label.HasConfigurableValues() {
41		return value, []selects{}
42	}
43
44	ret := selects{}
45	for _, axis := range label.SortedConfigurationAxes() {
46		configToLabels := label.ConfigurableValues[axis]
47		for config, labels := range configToLabels {
48			selectKey := axis.SelectKey(config)
49			ret[selectKey] = reflect.ValueOf(labels)
50		}
51	}
52
53	// if there is a select, use the base value as the conditions default value
54	if len(ret) > 0 {
55		ret[bazel.ConditionsDefaultSelectKey] = value
56		value = reflect.Zero(value.Type())
57	}
58
59	return value, []selects{ret}
60}
61
62func getBoolValue(boolAttr bazel.BoolAttribute) (reflect.Value, []selects) {
63	value := reflect.ValueOf(boolAttr.Value)
64	if !boolAttr.HasConfigurableValues() {
65		return value, []selects{}
66	}
67
68	ret := selects{}
69	for _, axis := range boolAttr.SortedConfigurationAxes() {
70		configToBools := boolAttr.ConfigurableValues[axis]
71		for config, bools := range configToBools {
72			selectKey := axis.SelectKey(config)
73			ret[selectKey] = reflect.ValueOf(bools)
74		}
75	}
76	// if there is a select, use the base value as the conditions default value
77	if len(ret) > 0 {
78		ret[bazel.ConditionsDefaultSelectKey] = value
79		value = reflect.Zero(value.Type())
80	}
81
82	return value, []selects{ret}
83}
84func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects) {
85	value := reflect.ValueOf(list.Value.Includes)
86	var ret []selects
87	for _, axis := range list.SortedConfigurationAxes() {
88		configToLabels := list.ConfigurableValues[axis]
89		if !configToLabels.HasConfigurableValues() {
90			continue
91		}
92		archSelects := map[string]reflect.Value{}
93		defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
94		// Skip empty list values unless ether EmitEmptyList is true, or these values differ from the default.
95		emitEmptyList := list.EmitEmptyList || len(defaultVal.Includes) > 0
96		for config, labels := range configToLabels {
97			// Omit any entries in the map which match the default value, for brevity.
98			if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
99				continue
100			}
101			selectKey := axis.SelectKey(config)
102			if use, value := labelListSelectValue(selectKey, labels, emitEmptyList); use {
103				archSelects[selectKey] = value
104			}
105		}
106		if len(archSelects) > 0 {
107			ret = append(ret, archSelects)
108		}
109	}
110
111	return value, ret
112}
113
114func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) {
115	if selectKey == bazel.ConditionsDefaultSelectKey || emitEmptyList || len(list.Includes) > 0 {
116		return true, reflect.ValueOf(list.Includes)
117	} else if len(list.Excludes) > 0 {
118		// if there is still an excludes -- we need to have an empty list for this select & use the
119		// value in conditions default Includes
120		return true, reflect.ValueOf([]string{})
121	}
122	return false, reflect.Zero(reflect.TypeOf([]string{}))
123}
124
125var (
126	emptyBazelList = "[]"
127	bazelNone      = "None"
128)
129
130// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
131// select statements.
132func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
133	var value reflect.Value
134	var configurableAttrs []selects
135	var defaultSelectValue *string
136	var emitZeroValues bool
137	// If true, print the default attribute value, even if the attribute is zero.
138	shouldPrintDefault := false
139	switch list := v.(type) {
140	case bazel.StringListAttribute:
141		value, configurableAttrs = getStringListValues(list)
142		defaultSelectValue = &emptyBazelList
143	case bazel.LabelListAttribute:
144		value, configurableAttrs = getLabelListValues(list)
145		emitZeroValues = list.EmitEmptyList
146		defaultSelectValue = &emptyBazelList
147		if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
148			shouldPrintDefault = true
149		}
150	case bazel.LabelAttribute:
151		if err := list.Collapse(); err != nil {
152			return "", err
153		}
154		value, configurableAttrs = getLabelValue(list)
155		defaultSelectValue = &bazelNone
156	case bazel.BoolAttribute:
157		if err := list.Collapse(); err != nil {
158			return "", err
159		}
160		value, configurableAttrs = getBoolValue(list)
161		defaultSelectValue = &bazelNone
162	default:
163		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
164	}
165
166	var err error
167	ret := ""
168	if value.Kind() != reflect.Invalid {
169		s, err := prettyPrint(value, indent, false) // never emit zero values for the base value
170		if err != nil {
171			return ret, err
172		}
173
174		ret += s
175	}
176	// Convenience function to append selects components to an attribute value.
177	appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) {
178		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues)
179		if err != nil {
180			return "", err
181		}
182		if s != "" && selectMap != "" {
183			s += " + "
184		}
185		s += selectMap
186
187		return s, nil
188	}
189
190	for _, configurableAttr := range configurableAttrs {
191		ret, err = appendSelects(configurableAttr, defaultSelectValue, ret)
192		if err != nil {
193			return "", err
194		}
195	}
196
197	if ret == "" && shouldPrintDefault {
198		return *defaultSelectValue, nil
199	}
200	return ret, nil
201}
202
203// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
204// to construct a select map for any kind of attribute type.
205func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int, emitZeroValues bool) (string, error) {
206	if selectMap == nil {
207		return "", nil
208	}
209
210	var selects string
211	for _, selectKey := range android.SortedStringKeys(selectMap) {
212		if selectKey == bazel.ConditionsDefaultSelectKey {
213			// Handle default condition later.
214			continue
215		}
216		value := selectMap[selectKey]
217		if isZero(value) && !emitZeroValues && isZero(selectMap[bazel.ConditionsDefaultSelectKey]) {
218			// Ignore zero values to not generate empty lists. However, always note zero values if
219			// the default value is non-zero.
220			continue
221		}
222		s, err := prettyPrintSelectEntry(value, selectKey, indent, true)
223		if err != nil {
224			return "", err
225		}
226		// s could still be an empty string, e.g. unset slices of structs with
227		// length of 0.
228		if s != "" {
229			selects += s + ",\n"
230		}
231	}
232
233	if len(selects) == 0 {
234		// No conditions (or all values are empty lists), so no need for a map.
235		return "", nil
236	}
237
238	// Create the map.
239	ret := "select({\n"
240	ret += selects
241
242	// Handle the default condition
243	s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent, emitZeroValues)
244	if err != nil {
245		return "", err
246	}
247	if s != "" {
248		// Print the custom default value.
249		ret += s
250		ret += ",\n"
251	} else if defaultValue != nil {
252		// Print an explicit empty list (the default value) even if the value is
253		// empty, to avoid errors about not finding a configuration that matches.
254		ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
255	}
256
257	ret += starlark_fmt.Indention(indent)
258	ret += "})"
259
260	return ret, nil
261}
262
263// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
264// with a provided key.
265func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
266	s := starlark_fmt.Indention(indent + 1)
267	v, err := prettyPrint(value, indent+1, emitZeroValues)
268	if err != nil {
269		return "", err
270	}
271	if v == "" {
272		return "", nil
273	}
274	s += fmt.Sprintf("\"%s\": %s", key, v)
275	return s, nil
276}
277