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