58852a05f3
Also fix some bugs pertaining to configurable attribute handling of bool attributes and label sttributes, so that they may support values across multiple different axes at the same time. Test: unit tests for bp2build Test: mixed_droid Change-Id: I411efcfddf02d55dbc0775962068a11348a8bb2c
275 lines
8 KiB
Go
275 lines
8 KiB
Go
package bp2build
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"android/soong/android"
|
|
"android/soong/bazel"
|
|
)
|
|
|
|
// Configurability support for bp2build.
|
|
|
|
type selects map[string]reflect.Value
|
|
|
|
func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects) {
|
|
value := reflect.ValueOf(list.Value)
|
|
if !list.HasConfigurableValues() {
|
|
return value, []selects{}
|
|
}
|
|
|
|
var ret []selects
|
|
for _, axis := range list.SortedConfigurationAxes() {
|
|
configToLists := list.ConfigurableValues[axis]
|
|
archSelects := map[string]reflect.Value{}
|
|
for config, labels := range configToLists {
|
|
selectKey := axis.SelectKey(config)
|
|
archSelects[selectKey] = reflect.ValueOf(labels)
|
|
}
|
|
if len(archSelects) > 0 {
|
|
ret = append(ret, archSelects)
|
|
}
|
|
}
|
|
|
|
return value, ret
|
|
}
|
|
|
|
func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
|
|
value := reflect.ValueOf(label.Value)
|
|
if !label.HasConfigurableValues() {
|
|
return value, []selects{}
|
|
}
|
|
|
|
ret := selects{}
|
|
for _, axis := range label.SortedConfigurationAxes() {
|
|
configToLabels := label.ConfigurableValues[axis]
|
|
for config, labels := range configToLabels {
|
|
selectKey := axis.SelectKey(config)
|
|
ret[selectKey] = reflect.ValueOf(labels)
|
|
}
|
|
}
|
|
|
|
// if there is a select, use the base value as the conditions default value
|
|
if len(ret) > 0 {
|
|
ret[bazel.ConditionsDefaultSelectKey] = value
|
|
value = reflect.Zero(value.Type())
|
|
}
|
|
|
|
return value, []selects{ret}
|
|
}
|
|
|
|
func getBoolValue(boolAttr bazel.BoolAttribute) (reflect.Value, []selects) {
|
|
value := reflect.ValueOf(boolAttr.Value)
|
|
if !boolAttr.HasConfigurableValues() {
|
|
return value, []selects{}
|
|
}
|
|
|
|
ret := selects{}
|
|
for _, axis := range boolAttr.SortedConfigurationAxes() {
|
|
configToBools := boolAttr.ConfigurableValues[axis]
|
|
for config, bools := range configToBools {
|
|
selectKey := axis.SelectKey(config)
|
|
ret[selectKey] = reflect.ValueOf(bools)
|
|
}
|
|
}
|
|
// if there is a select, use the base value as the conditions default value
|
|
if len(ret) > 0 {
|
|
ret[bazel.ConditionsDefaultSelectKey] = value
|
|
value = reflect.Zero(value.Type())
|
|
}
|
|
|
|
return value, []selects{ret}
|
|
}
|
|
func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects) {
|
|
value := reflect.ValueOf(list.Value.Includes)
|
|
var ret []selects
|
|
for _, axis := range list.SortedConfigurationAxes() {
|
|
configToLabels := list.ConfigurableValues[axis]
|
|
if !configToLabels.HasConfigurableValues() {
|
|
continue
|
|
}
|
|
archSelects := map[string]reflect.Value{}
|
|
defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
|
|
// Skip empty list values unless ether EmitEmptyList is true, or these values differ from the default.
|
|
emitEmptyList := list.EmitEmptyList || len(defaultVal.Includes) > 0
|
|
for config, labels := range configToLabels {
|
|
// Omit any entries in the map which match the default value, for brevity.
|
|
if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
|
|
continue
|
|
}
|
|
selectKey := axis.SelectKey(config)
|
|
if use, value := labelListSelectValue(selectKey, labels, emitEmptyList); use {
|
|
archSelects[selectKey] = value
|
|
}
|
|
}
|
|
if len(archSelects) > 0 {
|
|
ret = append(ret, archSelects)
|
|
}
|
|
}
|
|
|
|
return value, ret
|
|
}
|
|
|
|
func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) {
|
|
if selectKey == bazel.ConditionsDefaultSelectKey || emitEmptyList || len(list.Includes) > 0 {
|
|
return true, reflect.ValueOf(list.Includes)
|
|
} else if len(list.Excludes) > 0 {
|
|
// if there is still an excludes -- we need to have an empty list for this select & use the
|
|
// value in conditions default Includes
|
|
return true, reflect.ValueOf([]string{})
|
|
}
|
|
return false, reflect.Zero(reflect.TypeOf([]string{}))
|
|
}
|
|
|
|
var (
|
|
emptyBazelList = "[]"
|
|
bazelNone = "None"
|
|
)
|
|
|
|
// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
|
|
// select statements.
|
|
func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
|
|
var value reflect.Value
|
|
var configurableAttrs []selects
|
|
var defaultSelectValue *string
|
|
var emitZeroValues bool
|
|
// If true, print the default attribute value, even if the attribute is zero.
|
|
shouldPrintDefault := false
|
|
switch list := v.(type) {
|
|
case bazel.StringListAttribute:
|
|
value, configurableAttrs = getStringListValues(list)
|
|
defaultSelectValue = &emptyBazelList
|
|
case bazel.LabelListAttribute:
|
|
value, configurableAttrs = getLabelListValues(list)
|
|
emitZeroValues = list.EmitEmptyList
|
|
defaultSelectValue = &emptyBazelList
|
|
if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
|
|
shouldPrintDefault = true
|
|
}
|
|
case bazel.LabelAttribute:
|
|
if err := list.Collapse(); err != nil {
|
|
return "", err
|
|
}
|
|
value, configurableAttrs = getLabelValue(list)
|
|
defaultSelectValue = &bazelNone
|
|
case bazel.BoolAttribute:
|
|
if err := list.Collapse(); err != nil {
|
|
return "", err
|
|
}
|
|
value, configurableAttrs = getBoolValue(list)
|
|
defaultSelectValue = &bazelNone
|
|
default:
|
|
return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
|
|
}
|
|
|
|
var err error
|
|
ret := ""
|
|
if value.Kind() != reflect.Invalid {
|
|
s, err := prettyPrint(value, indent, false) // never emit zero values for the base value
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
|
|
ret += s
|
|
}
|
|
// Convenience function to append selects components to an attribute value.
|
|
appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) {
|
|
selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if s != "" && selectMap != "" {
|
|
s += " + "
|
|
}
|
|
s += selectMap
|
|
|
|
return s, nil
|
|
}
|
|
|
|
for _, configurableAttr := range configurableAttrs {
|
|
ret, err = appendSelects(configurableAttr, defaultSelectValue, ret)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
if ret == "" && shouldPrintDefault {
|
|
return *defaultSelectValue, nil
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
|
|
// to construct a select map for any kind of attribute type.
|
|
func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int, emitZeroValues bool) (string, error) {
|
|
if selectMap == nil {
|
|
return "", nil
|
|
}
|
|
|
|
var selects string
|
|
for _, selectKey := range android.SortedStringKeys(selectMap) {
|
|
if selectKey == bazel.ConditionsDefaultSelectKey {
|
|
// Handle default condition later.
|
|
continue
|
|
}
|
|
value := selectMap[selectKey]
|
|
if isZero(value) && !emitZeroValues && isZero(selectMap[bazel.ConditionsDefaultSelectKey]) {
|
|
// Ignore zero values to not generate empty lists. However, always note zero values if
|
|
// the default value is non-zero.
|
|
continue
|
|
}
|
|
s, err := prettyPrintSelectEntry(value, selectKey, indent, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// s could still be an empty string, e.g. unset slices of structs with
|
|
// length of 0.
|
|
if s != "" {
|
|
selects += s + ",\n"
|
|
}
|
|
}
|
|
|
|
if len(selects) == 0 {
|
|
// No conditions (or all values are empty lists), so no need for a map.
|
|
return "", nil
|
|
}
|
|
|
|
// Create the map.
|
|
ret := "select({\n"
|
|
ret += selects
|
|
|
|
// Handle the default condition
|
|
s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent, emitZeroValues)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if s != "" {
|
|
// Print the custom default value.
|
|
ret += s
|
|
ret += ",\n"
|
|
} else if defaultValue != nil {
|
|
// Print an explicit empty list (the default value) even if the value is
|
|
// empty, to avoid errors about not finding a configuration that matches.
|
|
ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
|
|
}
|
|
|
|
ret += makeIndent(indent)
|
|
ret += "})"
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
|
|
// with a provided key.
|
|
func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
|
|
s := makeIndent(indent + 1)
|
|
v, err := prettyPrint(value, indent+1, emitZeroValues)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if v == "" {
|
|
return "", nil
|
|
}
|
|
s += fmt.Sprintf("\"%s\": %s", key, v)
|
|
return s, nil
|
|
}
|