1package bp2build 2 3import ( 4 "android/soong/android" 5 "android/soong/bazel" 6 "fmt" 7 "reflect" 8) 9 10// Configurability support for bp2build. 11 12type selects map[string]reflect.Value 13 14func getStringListValues(list bazel.StringListAttribute) (reflect.Value, selects, selects) { 15 value := reflect.ValueOf(list.Value) 16 if !list.HasConfigurableValues() { 17 return value, nil, nil 18 } 19 20 archSelects := map[string]reflect.Value{} 21 for arch, selectKey := range bazel.PlatformArchMap { 22 archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch)) 23 } 24 25 osSelects := map[string]reflect.Value{} 26 for os, selectKey := range bazel.PlatformOsMap { 27 osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os)) 28 } 29 30 return value, archSelects, osSelects 31} 32 33func getLabelValue(label bazel.LabelAttribute) (reflect.Value, selects, selects) { 34 var value reflect.Value 35 var archSelects selects 36 37 if label.HasConfigurableValues() { 38 archSelects = map[string]reflect.Value{} 39 for arch, selectKey := range bazel.PlatformArchMap { 40 archSelects[selectKey] = reflect.ValueOf(label.GetValueForArch(arch)) 41 } 42 } else { 43 value = reflect.ValueOf(label.Value) 44 } 45 46 return value, archSelects, nil 47} 48 49func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) { 50 value := reflect.ValueOf(list.Value.Includes) 51 if !list.HasConfigurableValues() { 52 return value, nil, nil 53 } 54 55 archSelects := map[string]reflect.Value{} 56 for arch, selectKey := range bazel.PlatformArchMap { 57 archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch).Includes) 58 } 59 60 osSelects := map[string]reflect.Value{} 61 for os, selectKey := range bazel.PlatformOsMap { 62 osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os).Includes) 63 } 64 65 return value, archSelects, osSelects 66} 67 68// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain 69// select statements. 70func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) { 71 var value reflect.Value 72 var archSelects, osSelects selects 73 var defaultSelectValue string 74 switch list := v.(type) { 75 case bazel.StringListAttribute: 76 value, archSelects, osSelects = getStringListValues(list) 77 defaultSelectValue = "[]" 78 case bazel.LabelListAttribute: 79 value, archSelects, osSelects = getLabelListValues(list) 80 defaultSelectValue = "[]" 81 case bazel.LabelAttribute: 82 value, archSelects, osSelects = getLabelValue(list) 83 defaultSelectValue = "None" 84 default: 85 return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v) 86 } 87 88 ret := "" 89 if value.Kind() != reflect.Invalid { 90 s, err := prettyPrint(value, indent) 91 if err != nil { 92 return ret, err 93 } 94 95 ret += s 96 } 97 // Convenience function to append selects components to an attribute value. 98 appendSelects := func(selectsData selects, defaultValue, s string) (string, error) { 99 selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent) 100 if err != nil { 101 return "", err 102 } 103 if s != "" && selectMap != "" { 104 s += " + " 105 } 106 s += selectMap 107 108 return s, nil 109 } 110 111 ret, err := appendSelects(archSelects, defaultSelectValue, ret) 112 if err != nil { 113 return "", err 114 } 115 116 ret, err = appendSelects(osSelects, defaultSelectValue, ret) 117 return ret, err 118} 119 120// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way 121// to construct a select map for any kind of attribute type. 122func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue string, indent int) (string, error) { 123 if selectMap == nil { 124 return "", nil 125 } 126 127 // addConditionsDefault := false 128 conditionsDefaultKey := bazel.PlatformArchMap[bazel.CONDITIONS_DEFAULT] 129 130 var selects string 131 for _, selectKey := range android.SortedStringKeys(selectMap) { 132 if selectKey == conditionsDefaultKey { 133 // Handle default condition later. 134 continue 135 } 136 value := selectMap[selectKey] 137 if isZero(value) { 138 // Ignore zero values to not generate empty lists. 139 continue 140 } 141 s, err := prettyPrintSelectEntry(value, selectKey, indent) 142 if err != nil { 143 return "", err 144 } 145 // s could still be an empty string, e.g. unset slices of structs with 146 // length of 0. 147 if s != "" { 148 selects += s + ",\n" 149 } 150 } 151 152 if len(selects) == 0 { 153 // No conditions (or all values are empty lists), so no need for a map. 154 return "", nil 155 } 156 157 // Create the map. 158 ret := "select({\n" 159 ret += selects 160 161 // Handle the default condition 162 s, err := prettyPrintSelectEntry(selectMap[conditionsDefaultKey], conditionsDefaultKey, indent) 163 if err != nil { 164 return "", err 165 } 166 if s == "" { 167 // Print an explicit empty list (the default value) even if the value is 168 // empty, to avoid errors about not finding a configuration that matches. 169 ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), "//conditions:default", defaultValue) 170 } else { 171 // Print the custom default value. 172 ret += s 173 ret += ",\n" 174 } 175 176 ret += makeIndent(indent) 177 ret += "})" 178 179 return ret, nil 180} 181 182// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map 183// with a provided key. 184func prettyPrintSelectEntry(value reflect.Value, key string, indent int) (string, error) { 185 s := makeIndent(indent + 1) 186 v, err := prettyPrint(value, indent+1) 187 if err != nil { 188 return "", err 189 } 190 if v == "" { 191 return "", nil 192 } 193 s += fmt.Sprintf("\"%s\": %s", key, v) 194 return s, nil 195} 196