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