1
15
16 package golang
17
18 import (
19 "log"
20
21 "github.com/bazelbuild/bazel-gazelle/config"
22 "github.com/bazelbuild/bazel-gazelle/language/proto"
23 "github.com/bazelbuild/bazel-gazelle/rule"
24 bzl "github.com/bazelbuild/buildtools/build"
25 )
26
27 func (*goLang) Fix(c *config.Config, f *rule.File) {
28 migrateLibraryEmbed(c, f)
29 migrateGrpcCompilers(c, f)
30 flattenSrcs(c, f)
31 squashCgoLibrary(c, f)
32 squashXtest(c, f)
33 removeLegacyProto(c, f)
34 removeLegacyGazelle(c, f)
35 migrateNamingConvention(c, f)
36 }
37
38
39
40 func migrateNamingConvention(c *config.Config, f *rule.File) {
41
42 nc := getGoConfig(c).goNamingConvention
43 importPath := InferImportPath(c, f.Pkg)
44 if importPath == "" {
45 return
46 }
47 var pkgName string
48 if fileContainsGoBinary(c, f) {
49 pkgName = "main"
50 }
51 libName := libNameByConvention(nc, importPath, pkgName)
52 testName := testNameByConvention(nc, importPath)
53 var migrateLibName, migrateTestName string
54 switch nc {
55 case goDefaultLibraryNamingConvention:
56 migrateLibName = libNameByConvention(importNamingConvention, importPath, pkgName)
57 migrateTestName = testNameByConvention(importNamingConvention, importPath)
58 case importNamingConvention, importAliasNamingConvention:
59 migrateLibName = defaultLibName
60 migrateTestName = defaultTestName
61 default:
62 return
63 }
64
65
66
67 var haveLib, haveMigrateLib, haveTest, haveMigrateTest bool
68 for _, r := range f.Rules {
69 switch {
70 case r.Name() == libName:
71 haveLib = true
72 case r.Kind() == "go_library" && r.Name() == migrateLibName && r.AttrString("importpath") == importPath:
73 haveMigrateLib = true
74 case r.Name() == testName:
75 haveTest = true
76 case r.Kind() == "go_test" && r.Name() == migrateTestName && strListAttrContains(r, "embed", ":"+migrateLibName):
77 haveMigrateTest = true
78 }
79 }
80 if haveLib && haveMigrateLib {
81 log.Printf("%[1]s: Tried to rename %[2]s to %[3]s, but %[3]s already exists.", f.Path, migrateLibName, libName)
82 }
83 if haveTest && haveMigrateTest {
84 log.Printf("%[1]s: Tried to rename %[2]s to %[3]s, but %[3]s already exists.", f.Path, migrateTestName, testName)
85 }
86 shouldMigrateLib := haveMigrateLib && !haveLib
87 shouldMigrateTest := haveMigrateTest && !haveTest
88
89
90 for _, r := range f.Rules {
91
92
93 switch r.Kind() {
94 case "go_binary":
95 if haveMigrateLib && shouldMigrateLib {
96 replaceInStrListAttr(r, "embed", ":"+migrateLibName, ":"+libName)
97 }
98 case "go_library":
99 if r.Name() == migrateLibName && shouldMigrateLib {
100 r.SetName(libName)
101 }
102 case "go_test":
103 if r.Name() == migrateTestName && shouldMigrateTest {
104 r.SetName(testName)
105 }
106 if shouldMigrateLib {
107 replaceInStrListAttr(r, "embed", ":"+migrateLibName, ":"+libName)
108 }
109 }
110 }
111 }
112
113
114 func fileContainsGoBinary(c *config.Config, f *rule.File) bool {
115 if f == nil {
116 return false
117 }
118 for _, r := range f.Rules {
119 kind := r.Kind()
120 if kind == "go_binary" {
121 return true
122 }
123
124 if mappedKind, ok := c.KindMap["go_binary"]; ok {
125 if mappedKind.KindName == kind {
126 return true
127 }
128 }
129 }
130 return false
131 }
132
133 func replaceInStrListAttr(r *rule.Rule, attr, old, new string) {
134 items := r.AttrStrings(attr)
135 changed := false
136 for i := range items {
137 if items[i] == old {
138 changed = true
139 items[i] = new
140 }
141 }
142 if changed {
143 r.SetAttr(attr, items)
144 }
145 }
146
147 func strListAttrContains(r *rule.Rule, attr, s string) bool {
148 items := r.AttrStrings(attr)
149 for _, item := range items {
150 if item == s {
151 return true
152 }
153 }
154 return false
155 }
156
157
158
159
160 func migrateLibraryEmbed(c *config.Config, f *rule.File) {
161 for _, r := range f.Rules {
162 if !isGoRule(r.Kind()) {
163 continue
164 }
165 libExpr := r.Attr("library")
166 if libExpr == nil || rule.ShouldKeep(libExpr) || r.Attr("embed") != nil {
167 continue
168 }
169 r.DelAttr("library")
170 r.SetAttr("embed", &bzl.ListExpr{List: []bzl.Expr{libExpr}})
171 }
172 }
173
174
175
176 func migrateGrpcCompilers(c *config.Config, f *rule.File) {
177 for _, r := range f.Rules {
178 if r.Kind() != "go_grpc_library" || r.ShouldKeep() || r.Attr("compilers") != nil {
179 continue
180 }
181 r.SetKind("go_proto_library")
182 r.SetAttr("compilers", []string{grpcCompilerLabel})
183 }
184 }
185
186
187
188
189
190
191
192
193 func squashCgoLibrary(c *config.Config, f *rule.File) {
194
195 var cgoLibrary, goLibrary *rule.Rule
196 for _, r := range f.Rules {
197 if r.Kind() == "cgo_library" && r.Name() == "cgo_default_library" && !r.ShouldKeep() {
198 if cgoLibrary != nil {
199 log.Printf("%s: when fixing existing file, multiple cgo_library rules with default name found", f.Path)
200 continue
201 }
202 cgoLibrary = r
203 continue
204 }
205 if r.Kind() == "go_library" && r.Name() == defaultLibName {
206 if goLibrary != nil {
207 log.Printf("%s: when fixing existing file, multiple go_library rules with default name referencing cgo_library found", f.Path)
208 }
209 goLibrary = r
210 continue
211 }
212 }
213
214 if cgoLibrary == nil {
215 return
216 }
217 if !c.ShouldFix {
218 log.Printf("%s: cgo_library is deprecated. Run 'gazelle fix' to squash with go_library.", f.Path)
219 return
220 }
221
222 if goLibrary == nil {
223 cgoLibrary.SetKind("go_library")
224 cgoLibrary.SetName(defaultLibName)
225 cgoLibrary.SetAttr("cgo", true)
226 return
227 }
228
229 if err := rule.SquashRules(cgoLibrary, goLibrary, f.Path); err != nil {
230 log.Print(err)
231 return
232 }
233 goLibrary.DelAttr("embed")
234 goLibrary.SetAttr("cgo", true)
235 cgoLibrary.Delete()
236 }
237
238
239
240
241
242 func squashXtest(c *config.Config, f *rule.File) {
243
244 var itest, xtest *rule.Rule
245 for _, r := range f.Rules {
246 if r.Kind() != "go_test" {
247 continue
248 }
249 if r.Name() == defaultTestName {
250 itest = r
251 } else if r.Name() == "go_default_xtest" {
252 xtest = r
253 }
254 }
255
256 if xtest == nil || xtest.ShouldKeep() || (itest != nil && itest.ShouldKeep()) {
257 return
258 }
259 if !c.ShouldFix {
260 if itest == nil {
261 log.Printf("%s: go_default_xtest is no longer necessary. Run 'gazelle fix' to rename to go_default_test.", f.Path)
262 } else {
263 log.Printf("%s: go_default_xtest is no longer necessary. Run 'gazelle fix' to squash with go_default_test.", f.Path)
264 }
265 return
266 }
267
268
269 if itest == nil {
270 xtest.SetName(defaultTestName)
271 return
272 }
273
274
275 if err := rule.SquashRules(xtest, itest, f.Path); err != nil {
276 log.Print(err)
277 return
278 }
279 xtest.Delete()
280 }
281
282
283
284
285
286
287 func flattenSrcs(c *config.Config, f *rule.File) {
288 for _, r := range f.Rules {
289 if !isGoRule(r.Kind()) {
290 continue
291 }
292 oldSrcs := r.Attr("srcs")
293 if oldSrcs == nil {
294 continue
295 }
296 flatSrcs := rule.FlattenExpr(oldSrcs)
297 if flatSrcs != oldSrcs {
298 r.SetAttr("srcs", flatSrcs)
299 }
300 }
301 }
302
303
304
305
306
307
308 func removeLegacyProto(c *config.Config, f *rule.File) {
309
310 if pcMode := getProtoMode(c); pcMode != proto.DefaultMode {
311 return
312 }
313
314
315 var protoLoads []*rule.Load
316 for _, l := range f.Loads {
317 if l.Name() == "@io_bazel_rules_go//proto:go_proto_library.bzl" {
318 protoLoads = append(protoLoads, l)
319 }
320 }
321 var protoFilegroups, protoRules []*rule.Rule
322 for _, r := range f.Rules {
323 if r.Kind() == "filegroup" && r.Name() == legacyProtoFilegroupName {
324 protoFilegroups = append(protoFilegroups, r)
325 }
326 if r.Kind() == "go_proto_library" {
327 protoRules = append(protoRules, r)
328 }
329 }
330 if len(protoLoads)+len(protoFilegroups) == 0 {
331 return
332 }
333 if !c.ShouldFix {
334 log.Printf("%s: go_proto_library.bzl is deprecated. Run 'gazelle fix' to replace old rules.", f.Path)
335 return
336 }
337
338
339
340 for _, l := range protoLoads {
341 l.Delete()
342 }
343 for _, r := range protoFilegroups {
344 r.Delete()
345 }
346 if len(protoLoads) > 0 {
347 for _, r := range protoRules {
348 r.Delete()
349 }
350 }
351 }
352
353
354
355
356 func removeLegacyGazelle(c *config.Config, f *rule.File) {
357 for _, l := range f.Loads {
358 if l.Name() == "@io_bazel_rules_go//go:def.bzl" && l.Has("gazelle") {
359 l.Remove("gazelle")
360 if l.IsEmpty() {
361 l.Delete()
362 }
363 }
364 }
365 }
366
367 func isGoRule(kind string) bool {
368 return kind == "go_library" ||
369 kind == "go_binary" ||
370 kind == "go_test" ||
371 kind == "go_proto_library" ||
372 kind == "go_grpc_library"
373 }
374
View as plain text