1
15
16 package rule
17
18 import (
19 "fmt"
20 "log"
21 "strings"
22
23 "github.com/bazelbuild/bazel-gazelle/label"
24 bzl "github.com/bazelbuild/buildtools/build"
25 )
26
27
28
29
30 func MapExprStrings(e bzl.Expr, f func(string) string) bzl.Expr {
31 if e == nil {
32 return nil
33 }
34 switch expr := e.(type) {
35 case *bzl.StringExpr:
36 s := f(expr.Value)
37 if s == "" {
38 return nil
39 }
40 ret := *expr
41 ret.Value = s
42 return &ret
43
44 case *bzl.ListExpr:
45 var list []bzl.Expr
46 for _, elem := range expr.List {
47 elem = MapExprStrings(elem, f)
48 if elem != nil {
49 list = append(list, elem)
50 }
51 }
52 if len(list) == 0 && len(expr.List) > 0 {
53 return nil
54 }
55 ret := *expr
56 ret.List = list
57 return &ret
58
59 case *bzl.DictExpr:
60 var cases []*bzl.KeyValueExpr
61 isEmpty := true
62 for _, kv := range expr.List {
63 value := MapExprStrings(kv.Value, f)
64 if value != nil {
65 cases = append(cases, &bzl.KeyValueExpr{Key: kv.Key, Value: value})
66 if key, ok := kv.Key.(*bzl.StringExpr); !ok || key.Value != "//conditions:default" {
67 isEmpty = false
68 }
69 }
70 }
71 if isEmpty {
72 return nil
73 }
74 ret := *expr
75 ret.List = cases
76 return &ret
77
78 case *bzl.CallExpr:
79 if x, ok := expr.X.(*bzl.Ident); !ok || x.Name != "select" || len(expr.List) != 1 {
80 log.Panicf("unexpected call expression in generated imports: %#v", e)
81 }
82 arg := MapExprStrings(expr.List[0], f)
83 if arg == nil {
84 return nil
85 }
86 call := *expr
87 call.List[0] = arg
88 return &call
89
90 case *bzl.BinaryExpr:
91 x := MapExprStrings(expr.X, f)
92 y := MapExprStrings(expr.Y, f)
93 if x == nil {
94 return y
95 }
96 if y == nil {
97 return x
98 }
99 binop := *expr
100 binop.X = x
101 binop.Y = y
102 return &binop
103
104 default:
105 return nil
106 }
107 }
108
109
110
111
112
113
114 func FlattenExpr(e bzl.Expr) bzl.Expr {
115 ps, err := extractPlatformStringsExprs(e)
116 if err != nil {
117 return e
118 }
119
120 ls := makeListSquasher()
121 addElem := func(e bzl.Expr) bool {
122 s, ok := e.(*bzl.StringExpr)
123 if !ok {
124 return false
125 }
126 ls.add(s)
127 return true
128 }
129 addList := func(e bzl.Expr) bool {
130 l, ok := e.(*bzl.ListExpr)
131 if !ok {
132 return false
133 }
134 for _, elem := range l.List {
135 if !addElem(elem) {
136 return false
137 }
138 }
139 return true
140 }
141 addDict := func(d *bzl.DictExpr) bool {
142 for _, kv := range d.List {
143 if !addList(kv.Value) {
144 return false
145 }
146 }
147 return true
148 }
149
150 if ps.generic != nil {
151 if !addList(ps.generic) {
152 return e
153 }
154 }
155 for _, d := range []*bzl.DictExpr{ps.os, ps.arch, ps.platform} {
156 if d == nil {
157 continue
158 }
159 if !addDict(d) {
160 return e
161 }
162 }
163
164 return ls.list()
165 }
166
167 func isScalar(e bzl.Expr) bool {
168 switch e.(type) {
169 case *bzl.StringExpr, *bzl.LiteralExpr, *bzl.Ident:
170 return true
171 default:
172 return false
173 }
174 }
175
176 func dictEntryKeyValue(e bzl.Expr) (string, *bzl.ListExpr, error) {
177 kv, ok := e.(*bzl.KeyValueExpr)
178 if !ok {
179 return "", nil, fmt.Errorf("dict entry was not a key-value pair: %#v", e)
180 }
181 k, ok := kv.Key.(*bzl.StringExpr)
182 if !ok {
183 return "", nil, fmt.Errorf("dict key was not string: %#v", kv.Key)
184 }
185 v, ok := kv.Value.(*bzl.ListExpr)
186 if !ok {
187 return "", nil, fmt.Errorf("dict value was not list: %#v", kv.Value)
188 }
189 return k.Value, v, nil
190 }
191
192 func stringValue(e bzl.Expr) string {
193 s, ok := e.(*bzl.StringExpr)
194 if !ok {
195 return ""
196 }
197 return s.Value
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211 type platformStringsExprs struct {
212 generic *bzl.ListExpr
213 os, arch, platform *bzl.DictExpr
214 }
215
216
217
218
219
220
221 func extractPlatformStringsExprs(expr bzl.Expr) (platformStringsExprs, error) {
222 var ps platformStringsExprs
223 if expr == nil {
224 return ps, nil
225 }
226
227
228 var parts []bzl.Expr
229 for {
230 binop, ok := expr.(*bzl.BinaryExpr)
231 if !ok {
232 parts = append(parts, expr)
233 break
234 }
235 parts = append(parts, binop.Y)
236 expr = binop.X
237 }
238
239
240 for _, part := range parts {
241 switch part := part.(type) {
242 case *bzl.ListExpr:
243 if ps.generic != nil {
244 return platformStringsExprs{}, fmt.Errorf("expression could not be matched: multiple list expressions")
245 }
246 ps.generic = part
247
248 case *bzl.CallExpr:
249 x, ok := part.X.(*bzl.Ident)
250 if !ok || x.Name != "select" || len(part.List) != 1 {
251 return platformStringsExprs{}, fmt.Errorf("expression could not be matched: callee other than select or wrong number of args")
252 }
253 arg, ok := part.List[0].(*bzl.DictExpr)
254 if !ok {
255 return platformStringsExprs{}, fmt.Errorf("expression could not be matched: select argument not dict")
256 }
257 var dict **bzl.DictExpr
258 for _, kv := range arg.List {
259 k, ok := kv.Key.(*bzl.StringExpr)
260 if !ok {
261 return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict keys are not all strings")
262 }
263 if k.Value == "//conditions:default" {
264 continue
265 }
266 key, err := label.Parse(k.Value)
267 if err != nil {
268 return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict key is not label: %q", k.Value)
269 }
270 if KnownOSSet[key.Name] {
271 dict = &ps.os
272 break
273 }
274 if KnownArchSet[key.Name] {
275 dict = &ps.arch
276 break
277 }
278 osArch := strings.Split(key.Name, "_")
279 if len(osArch) != 2 || !KnownOSSet[osArch[0]] || !KnownArchSet[osArch[1]] {
280 return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict key contains unknown platform: %q", k.Value)
281 }
282 dict = &ps.platform
283 break
284 }
285 if dict == nil {
286
287
288
289 dict = &ps.platform
290 }
291 if *dict != nil {
292 return platformStringsExprs{}, fmt.Errorf("expression could not be matched: multiple selects that are either os-specific, arch-specific, or platform-specific")
293 }
294 *dict = arg
295 }
296 }
297 return ps, nil
298 }
299
300
301
302 func makePlatformStringsExpr(ps platformStringsExprs) bzl.Expr {
303 makeSelect := func(dict *bzl.DictExpr) bzl.Expr {
304 return &bzl.CallExpr{
305 X: &bzl.Ident{Name: "select"},
306 List: []bzl.Expr{dict},
307 }
308 }
309 forceMultiline := func(e bzl.Expr) {
310 switch e := e.(type) {
311 case *bzl.ListExpr:
312 e.ForceMultiLine = true
313 case *bzl.CallExpr:
314 e.List[0].(*bzl.DictExpr).ForceMultiLine = true
315 }
316 }
317
318 var parts []bzl.Expr
319 if ps.generic != nil {
320 parts = append(parts, ps.generic)
321 }
322 if ps.os != nil {
323 parts = append(parts, makeSelect(ps.os))
324 }
325 if ps.arch != nil {
326 parts = append(parts, makeSelect(ps.arch))
327 }
328 if ps.platform != nil {
329 parts = append(parts, makeSelect(ps.platform))
330 }
331
332 if len(parts) == 0 {
333 return nil
334 }
335 if len(parts) == 1 {
336 return parts[0]
337 }
338 expr := parts[0]
339 forceMultiline(expr)
340 for _, part := range parts[1:] {
341 forceMultiline(part)
342 expr = &bzl.BinaryExpr{
343 Op: "+",
344 X: expr,
345 Y: part,
346 }
347 }
348 return expr
349 }
350
View as plain text