123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- /* Copyright 2017 The Bazel Authors. All rights reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package golang
- import (
- "log"
- "github.com/bazelbuild/bazel-gazelle/internal/config"
- "github.com/bazelbuild/bazel-gazelle/internal/language/proto"
- "github.com/bazelbuild/bazel-gazelle/internal/rule"
- bzl "github.com/bazelbuild/buildtools/build"
- )
- func (_ *goLang) Fix(c *config.Config, f *rule.File) {
- migrateLibraryEmbed(c, f)
- migrateGrpcCompilers(c, f)
- flattenSrcs(c, f)
- squashCgoLibrary(c, f)
- squashXtest(c, f)
- removeLegacyProto(c, f)
- removeLegacyGazelle(c, f)
- }
- // migrateLibraryEmbed converts "library" attributes to "embed" attributes,
- // preserving comments. This only applies to Go rules, and only if there is
- // no keep comment on "library" and no existing "embed" attribute.
- func migrateLibraryEmbed(c *config.Config, f *rule.File) {
- for _, r := range f.Rules {
- if !isGoRule(r.Kind()) {
- continue
- }
- libExpr := r.Attr("library")
- if libExpr == nil || rule.ShouldKeep(libExpr) || r.Attr("embed") != nil {
- continue
- }
- r.DelAttr("library")
- r.SetAttr("embed", &bzl.ListExpr{List: []bzl.Expr{libExpr}})
- }
- }
- // migrateGrpcCompilers converts "go_grpc_library" rules into "go_proto_library"
- // rules with a "compilers" attribute.
- func migrateGrpcCompilers(c *config.Config, f *rule.File) {
- for _, r := range f.Rules {
- if r.Kind() != "go_grpc_library" || r.ShouldKeep() || r.Attr("compilers") != nil {
- continue
- }
- r.SetKind("go_proto_library")
- r.SetAttr("compilers", []string{config.GrpcCompilerLabel})
- }
- }
- // squashCgoLibrary removes cgo_library rules with the default name and
- // merges their attributes with go_library with the default name. If no
- // go_library rule exists, a new one will be created.
- //
- // Note that the library attribute is disregarded, so cgo_library and
- // go_library attributes will be squashed even if the cgo_library was unlinked.
- // MergeFile will remove unused values and attributes later.
- func squashCgoLibrary(c *config.Config, f *rule.File) {
- // Find the default cgo_library and go_library rules.
- var cgoLibrary, goLibrary *rule.Rule
- for _, r := range f.Rules {
- if r.Kind() == "cgo_library" && r.Name() == config.DefaultCgoLibName && !r.ShouldKeep() {
- if cgoLibrary != nil {
- log.Printf("%s: when fixing existing file, multiple cgo_library rules with default name found", f.Path)
- continue
- }
- cgoLibrary = r
- continue
- }
- if r.Kind() == "go_library" && r.Name() == config.DefaultLibName {
- if goLibrary != nil {
- log.Printf("%s: when fixing existing file, multiple go_library rules with default name referencing cgo_library found", f.Path)
- }
- goLibrary = r
- continue
- }
- }
- if cgoLibrary == nil {
- return
- }
- if !c.ShouldFix {
- log.Printf("%s: cgo_library is deprecated. Run 'gazelle fix' to squash with go_library.", f.Path)
- return
- }
- if goLibrary == nil {
- cgoLibrary.SetKind("go_library")
- cgoLibrary.SetName(config.DefaultLibName)
- cgoLibrary.SetAttr("cgo", true)
- return
- }
- if err := rule.SquashRules(cgoLibrary, goLibrary, f.Path); err != nil {
- log.Print(err)
- return
- }
- goLibrary.DelAttr("embed")
- goLibrary.SetAttr("cgo", true)
- cgoLibrary.Delete()
- }
- // squashXtest removes go_test rules with the default external name and merges
- // their attributes with a go_test rule with the default internal name. If
- // no internal go_test rule exists, a new one will be created (effectively
- // renaming the old rule).
- func squashXtest(c *config.Config, f *rule.File) {
- // Search for internal and external tests.
- var itest, xtest *rule.Rule
- for _, r := range f.Rules {
- if r.Kind() != "go_test" {
- continue
- }
- if r.Name() == config.DefaultTestName {
- itest = r
- } else if r.Name() == config.DefaultXTestName {
- xtest = r
- }
- }
- if xtest == nil || xtest.ShouldKeep() || (itest != nil && itest.ShouldKeep()) {
- return
- }
- if !c.ShouldFix {
- if itest == nil {
- log.Printf("%s: go_default_xtest is no longer necessary. Run 'gazelle fix' to rename to go_default_test.", f.Path)
- } else {
- log.Printf("%s: go_default_xtest is no longer necessary. Run 'gazelle fix' to squash with go_default_test.", f.Path)
- }
- return
- }
- // If there was no internal test, we can just rename the external test.
- if itest == nil {
- xtest.SetName(config.DefaultTestName)
- return
- }
- // Attempt to squash.
- if err := rule.SquashRules(xtest, itest, f.Path); err != nil {
- log.Print(err)
- return
- }
- xtest.Delete()
- }
- // flattenSrcs transforms srcs attributes structured as concatenations of
- // lists and selects (generated from PlatformStrings; see
- // extractPlatformStringsExprs for matching details) into a sorted,
- // de-duplicated list. Comments are accumulated and de-duplicated across
- // duplicate expressions.
- func flattenSrcs(c *config.Config, f *rule.File) {
- for _, r := range f.Rules {
- if !isGoRule(r.Kind()) {
- continue
- }
- oldSrcs := r.Attr("srcs")
- if oldSrcs == nil {
- continue
- }
- flatSrcs := rule.FlattenExpr(oldSrcs)
- if flatSrcs != oldSrcs {
- r.SetAttr("srcs", flatSrcs)
- }
- }
- }
- // removeLegacyProto removes uses of the old proto rules. It deletes loads
- // from go_proto_library.bzl. It deletes proto filegroups. It removes
- // go_proto_library attributes which are no longer recognized. New rules
- // are generated in place of the deleted rules, but attributes and comments
- // are not migrated.
- func removeLegacyProto(c *config.Config, f *rule.File) {
- // Don't fix if the proto mode was set to something other than the default.
- pc := proto.GetProtoConfig(c)
- if pc.Mode != proto.DefaultMode {
- return
- }
- // Scan for definitions to delete.
- var protoLoads []*rule.Load
- for _, l := range f.Loads {
- if l.Name() == "@io_bazel_rules_go//proto:go_proto_library.bzl" {
- protoLoads = append(protoLoads, l)
- }
- }
- var protoFilegroups, protoRules []*rule.Rule
- for _, r := range f.Rules {
- if r.Kind() == "filegroup" && r.Name() == legacyProtoFilegroupName {
- protoFilegroups = append(protoFilegroups, r)
- }
- if r.Kind() == "go_proto_library" {
- protoRules = append(protoRules, r)
- }
- }
- if len(protoLoads)+len(protoFilegroups) == 0 {
- return
- }
- if !c.ShouldFix {
- log.Printf("%s: go_proto_library.bzl is deprecated. Run 'gazelle fix' to replace old rules.", f.Path)
- return
- }
- // Delete legacy proto loads and filegroups. Only delete go_proto_library
- // rules if we deleted a load.
- for _, l := range protoLoads {
- l.Delete()
- }
- for _, r := range protoFilegroups {
- r.Delete()
- }
- if len(protoLoads) > 0 {
- for _, r := range protoRules {
- r.Delete()
- }
- }
- }
- // removeLegacyGazelle removes loads of the "gazelle" macro from
- // @io_bazel_rules_go//go:def.bzl. The definition has moved to
- // @bazel_gazelle//:def.bzl, and the old one will be deleted soon.
- func removeLegacyGazelle(c *config.Config, f *rule.File) {
- for _, l := range f.Loads {
- if l.Name() == "@io_bazel_rules_go//go:def.bzl" && l.Has("gazelle") {
- l.Remove("gazelle")
- if l.IsEmpty() {
- l.Delete()
- }
- }
- }
- }
- func isGoRule(kind string) bool {
- return kind == "go_library" ||
- kind == "go_binary" ||
- kind == "go_test" ||
- kind == "go_proto_library" ||
- kind == "go_grpc_library"
- }
|