package bp2build import ( "android/soong/android" "android/soong/bazel" "fmt" "reflect" ) // Configurability support for bp2build. type selects map[string]reflect.Value func getStringListValues(list bazel.StringListAttribute) (reflect.Value, selects, selects) { value := reflect.ValueOf(list.Value) if !list.HasConfigurableValues() { return value, nil, nil } archSelects := map[string]reflect.Value{} for arch, selectKey := range bazel.PlatformArchMap { archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch)) } osSelects := map[string]reflect.Value{} for os, selectKey := range bazel.PlatformOsMap { osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os)) } return value, archSelects, osSelects } func getLabelValue(label bazel.LabelAttribute) (reflect.Value, selects, selects) { var value reflect.Value var archSelects selects if label.HasConfigurableValues() { archSelects = map[string]reflect.Value{} for arch, selectKey := range bazel.PlatformArchMap { archSelects[selectKey] = reflect.ValueOf(label.GetValueForArch(arch)) } } else { value = reflect.ValueOf(label.Value) } return value, archSelects, nil } func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) { value := reflect.ValueOf(list.Value.Includes) if !list.HasConfigurableValues() { return value, nil, nil } archSelects := map[string]reflect.Value{} for arch, selectKey := range bazel.PlatformArchMap { archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch).Includes) } osSelects := map[string]reflect.Value{} for os, selectKey := range bazel.PlatformOsMap { osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os).Includes) } return value, archSelects, osSelects } // 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 archSelects, osSelects selects var defaultSelectValue string switch list := v.(type) { case bazel.StringListAttribute: value, archSelects, osSelects = getStringListValues(list) defaultSelectValue = "[]" case bazel.LabelListAttribute: value, archSelects, osSelects = getLabelListValues(list) defaultSelectValue = "[]" case bazel.LabelAttribute: value, archSelects, osSelects = getLabelValue(list) defaultSelectValue = "None" default: return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v) } ret := "" if value.Kind() != reflect.Invalid { s, err := prettyPrint(value, indent) if err != nil { return ret, err } ret += s } // Convenience function to append selects components to an attribute value. appendSelects := func(selectsData selects, defaultValue, s string) (string, error) { selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent) if err != nil { return "", err } if s != "" && selectMap != "" { s += " + " } s += selectMap return s, nil } ret, err := appendSelects(archSelects, defaultSelectValue, ret) if err != nil { return "", err } ret, err = appendSelects(osSelects, defaultSelectValue, ret) return ret, err } // 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) (string, error) { if selectMap == nil { return "", nil } // addConditionsDefault := false conditionsDefaultKey := bazel.PlatformArchMap[bazel.CONDITIONS_DEFAULT] var selects string for _, selectKey := range android.SortedStringKeys(selectMap) { if selectKey == conditionsDefaultKey { // Handle default condition later. continue } value := selectMap[selectKey] if isZero(value) { // Ignore zero values to not generate empty lists. continue } s, err := prettyPrintSelectEntry(value, selectKey, indent) 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[conditionsDefaultKey], conditionsDefaultKey, indent) if err != nil { return "", err } if s == "" { // 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), "//conditions:default", defaultValue) } else { // Print the custom default value. ret += s ret += ",\n" } 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) (string, error) { s := makeIndent(indent + 1) v, err := prettyPrint(value, indent+1) if err != nil { return "", err } if v == "" { return "", nil } s += fmt.Sprintf("\"%s\": %s", key, v) return s, nil }