fix.go 7.5 KB

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