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