github.com/wolfd/bazel-gazelle@v0.14.0/internal/language/go/fix.go (about) 1 /* Copyright 2017 The Bazel Authors. All rights reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package golang 17 18 import ( 19 "log" 20 21 "github.com/bazelbuild/bazel-gazelle/internal/config" 22 "github.com/bazelbuild/bazel-gazelle/internal/language/proto" 23 "github.com/bazelbuild/bazel-gazelle/internal/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 } 36 37 // migrateLibraryEmbed converts "library" attributes to "embed" attributes, 38 // preserving comments. This only applies to Go rules, and only if there is 39 // no keep comment on "library" and no existing "embed" attribute. 40 func migrateLibraryEmbed(c *config.Config, f *rule.File) { 41 for _, r := range f.Rules { 42 if !isGoRule(r.Kind()) { 43 continue 44 } 45 libExpr := r.Attr("library") 46 if libExpr == nil || rule.ShouldKeep(libExpr) || r.Attr("embed") != nil { 47 continue 48 } 49 r.DelAttr("library") 50 r.SetAttr("embed", &bzl.ListExpr{List: []bzl.Expr{libExpr}}) 51 } 52 } 53 54 // migrateGrpcCompilers converts "go_grpc_library" rules into "go_proto_library" 55 // rules with a "compilers" attribute. 56 func migrateGrpcCompilers(c *config.Config, f *rule.File) { 57 for _, r := range f.Rules { 58 if r.Kind() != "go_grpc_library" || r.ShouldKeep() || r.Attr("compilers") != nil { 59 continue 60 } 61 r.SetKind("go_proto_library") 62 r.SetAttr("compilers", []string{config.GrpcCompilerLabel}) 63 } 64 } 65 66 // squashCgoLibrary removes cgo_library rules with the default name and 67 // merges their attributes with go_library with the default name. If no 68 // go_library rule exists, a new one will be created. 69 // 70 // Note that the library attribute is disregarded, so cgo_library and 71 // go_library attributes will be squashed even if the cgo_library was unlinked. 72 // MergeFile will remove unused values and attributes later. 73 func squashCgoLibrary(c *config.Config, f *rule.File) { 74 // Find the default cgo_library and go_library rules. 75 var cgoLibrary, goLibrary *rule.Rule 76 for _, r := range f.Rules { 77 if r.Kind() == "cgo_library" && r.Name() == config.DefaultCgoLibName && !r.ShouldKeep() { 78 if cgoLibrary != nil { 79 log.Printf("%s: when fixing existing file, multiple cgo_library rules with default name found", f.Path) 80 continue 81 } 82 cgoLibrary = r 83 continue 84 } 85 if r.Kind() == "go_library" && r.Name() == config.DefaultLibName { 86 if goLibrary != nil { 87 log.Printf("%s: when fixing existing file, multiple go_library rules with default name referencing cgo_library found", f.Path) 88 } 89 goLibrary = r 90 continue 91 } 92 } 93 94 if cgoLibrary == nil { 95 return 96 } 97 if !c.ShouldFix { 98 log.Printf("%s: cgo_library is deprecated. Run 'gazelle fix' to squash with go_library.", f.Path) 99 return 100 } 101 102 if goLibrary == nil { 103 cgoLibrary.SetKind("go_library") 104 cgoLibrary.SetName(config.DefaultLibName) 105 cgoLibrary.SetAttr("cgo", true) 106 return 107 } 108 109 if err := rule.SquashRules(cgoLibrary, goLibrary, f.Path); err != nil { 110 log.Print(err) 111 return 112 } 113 goLibrary.DelAttr("embed") 114 goLibrary.SetAttr("cgo", true) 115 cgoLibrary.Delete() 116 } 117 118 // squashXtest removes go_test rules with the default external name and merges 119 // their attributes with a go_test rule with the default internal name. If 120 // no internal go_test rule exists, a new one will be created (effectively 121 // renaming the old rule). 122 func squashXtest(c *config.Config, f *rule.File) { 123 // Search for internal and external tests. 124 var itest, xtest *rule.Rule 125 for _, r := range f.Rules { 126 if r.Kind() != "go_test" { 127 continue 128 } 129 if r.Name() == config.DefaultTestName { 130 itest = r 131 } else if r.Name() == config.DefaultXTestName { 132 xtest = r 133 } 134 } 135 136 if xtest == nil || xtest.ShouldKeep() || (itest != nil && itest.ShouldKeep()) { 137 return 138 } 139 if !c.ShouldFix { 140 if itest == nil { 141 log.Printf("%s: go_default_xtest is no longer necessary. Run 'gazelle fix' to rename to go_default_test.", f.Path) 142 } else { 143 log.Printf("%s: go_default_xtest is no longer necessary. Run 'gazelle fix' to squash with go_default_test.", f.Path) 144 } 145 return 146 } 147 148 // If there was no internal test, we can just rename the external test. 149 if itest == nil { 150 xtest.SetName(config.DefaultTestName) 151 return 152 } 153 154 // Attempt to squash. 155 if err := rule.SquashRules(xtest, itest, f.Path); err != nil { 156 log.Print(err) 157 return 158 } 159 xtest.Delete() 160 } 161 162 // flattenSrcs transforms srcs attributes structured as concatenations of 163 // lists and selects (generated from PlatformStrings; see 164 // extractPlatformStringsExprs for matching details) into a sorted, 165 // de-duplicated list. Comments are accumulated and de-duplicated across 166 // duplicate expressions. 167 func flattenSrcs(c *config.Config, f *rule.File) { 168 for _, r := range f.Rules { 169 if !isGoRule(r.Kind()) { 170 continue 171 } 172 oldSrcs := r.Attr("srcs") 173 if oldSrcs == nil { 174 continue 175 } 176 flatSrcs := rule.FlattenExpr(oldSrcs) 177 if flatSrcs != oldSrcs { 178 r.SetAttr("srcs", flatSrcs) 179 } 180 } 181 } 182 183 // removeLegacyProto removes uses of the old proto rules. It deletes loads 184 // from go_proto_library.bzl. It deletes proto filegroups. It removes 185 // go_proto_library attributes which are no longer recognized. New rules 186 // are generated in place of the deleted rules, but attributes and comments 187 // are not migrated. 188 func removeLegacyProto(c *config.Config, f *rule.File) { 189 // Don't fix if the proto mode was set to something other than the default. 190 pc := proto.GetProtoConfig(c) 191 if pc.Mode != proto.DefaultMode { 192 return 193 } 194 195 // Scan for definitions to delete. 196 var protoLoads []*rule.Load 197 for _, l := range f.Loads { 198 if l.Name() == "@io_bazel_rules_go//proto:go_proto_library.bzl" { 199 protoLoads = append(protoLoads, l) 200 } 201 } 202 var protoFilegroups, protoRules []*rule.Rule 203 for _, r := range f.Rules { 204 if r.Kind() == "filegroup" && r.Name() == legacyProtoFilegroupName { 205 protoFilegroups = append(protoFilegroups, r) 206 } 207 if r.Kind() == "go_proto_library" { 208 protoRules = append(protoRules, r) 209 } 210 } 211 if len(protoLoads)+len(protoFilegroups) == 0 { 212 return 213 } 214 if !c.ShouldFix { 215 log.Printf("%s: go_proto_library.bzl is deprecated. Run 'gazelle fix' to replace old rules.", f.Path) 216 return 217 } 218 219 // Delete legacy proto loads and filegroups. Only delete go_proto_library 220 // rules if we deleted a load. 221 for _, l := range protoLoads { 222 l.Delete() 223 } 224 for _, r := range protoFilegroups { 225 r.Delete() 226 } 227 if len(protoLoads) > 0 { 228 for _, r := range protoRules { 229 r.Delete() 230 } 231 } 232 } 233 234 // removeLegacyGazelle removes loads of the "gazelle" macro from 235 // @io_bazel_rules_go//go:def.bzl. The definition has moved to 236 // @bazel_gazelle//:def.bzl, and the old one will be deleted soon. 237 func removeLegacyGazelle(c *config.Config, f *rule.File) { 238 for _, l := range f.Loads { 239 if l.Name() == "@io_bazel_rules_go//go:def.bzl" && l.Has("gazelle") { 240 l.Remove("gazelle") 241 if l.IsEmpty() { 242 l.Delete() 243 } 244 } 245 } 246 } 247 248 func isGoRule(kind string) bool { 249 return kind == "go_library" || 250 kind == "go_binary" || 251 kind == "go_test" || 252 kind == "go_proto_library" || 253 kind == "go_grpc_library" 254 }