fix.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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 merger
  13. import (
  14. "fmt"
  15. "strings"
  16. "github.com/bazelbuild/bazel-gazelle/internal/rule"
  17. )
  18. // FixLoads removes loads of unused go rules and adds loads of newly used rules.
  19. // This should be called after FixFile and MergeFile, since symbols
  20. // may be introduced that aren't loaded.
  21. //
  22. // This function calls File.Sync before processing loads.
  23. func FixLoads(f *rule.File, knownLoads []rule.LoadInfo) {
  24. knownFiles := make(map[string]bool)
  25. knownKinds := make(map[string]string)
  26. for _, l := range knownLoads {
  27. knownFiles[l.Name] = true
  28. for _, k := range l.Symbols {
  29. knownKinds[k] = l.Name
  30. }
  31. }
  32. // Sync the file. We need File.Loads and File.Rules to contain inserted
  33. // statements and not deleted statements.
  34. f.Sync()
  35. // Scan load statements in the file. Keep track of loads of known files,
  36. // since these may be changed. Keep track of symbols loaded from unknown
  37. // files; we will not add loads for these.
  38. var loads []*rule.Load
  39. otherLoadedKinds := make(map[string]bool)
  40. for _, l := range f.Loads {
  41. if knownFiles[l.Name()] {
  42. loads = append(loads, l)
  43. continue
  44. }
  45. for _, sym := range l.Symbols() {
  46. otherLoadedKinds[sym] = true
  47. }
  48. }
  49. // Make a map of all the symbols from known files used in this file.
  50. usedKinds := make(map[string]map[string]bool)
  51. for _, r := range f.Rules {
  52. kind := r.Kind()
  53. if file, ok := knownKinds[kind]; ok && !otherLoadedKinds[kind] {
  54. if usedKinds[file] == nil {
  55. usedKinds[file] = make(map[string]bool)
  56. }
  57. usedKinds[file][kind] = true
  58. }
  59. }
  60. // Fix the load statements. The order is important, so we iterate over
  61. // knownLoads instead of knownFiles.
  62. for _, known := range knownLoads {
  63. file := known.Name
  64. first := true
  65. for _, l := range loads {
  66. if l.Name() != file {
  67. continue
  68. }
  69. if first {
  70. fixLoad(l, file, usedKinds[file], knownKinds)
  71. first = false
  72. } else {
  73. fixLoad(l, file, nil, knownKinds)
  74. }
  75. if l.IsEmpty() {
  76. l.Delete()
  77. }
  78. }
  79. if first {
  80. load := fixLoad(nil, file, usedKinds[file], knownKinds)
  81. if load != nil {
  82. index := newLoadIndex(f, known.After)
  83. load.Insert(f, index)
  84. }
  85. }
  86. }
  87. }
  88. // fixLoad updates a load statement with the given symbols. If load is nil,
  89. // a new load may be created and returned. Symbols in kinds will be added
  90. // to the load if they're not already present. Known symbols not in kinds
  91. // will be removed if present. Other symbols will be preserved. If load is
  92. // empty, nil is returned.
  93. func fixLoad(load *rule.Load, file string, kinds map[string]bool, knownKinds map[string]string) *rule.Load {
  94. if load == nil {
  95. if len(kinds) == 0 {
  96. return nil
  97. }
  98. load = rule.NewLoad(file)
  99. }
  100. for k := range kinds {
  101. load.Add(k)
  102. }
  103. for _, k := range load.Symbols() {
  104. if knownKinds[k] != "" && !kinds[k] {
  105. load.Remove(k)
  106. }
  107. }
  108. return load
  109. }
  110. // newLoadIndex returns the index in stmts where a new load statement should
  111. // be inserted. after is a list of function names that the load should not
  112. // be inserted before.
  113. func newLoadIndex(f *rule.File, after []string) int {
  114. if len(after) == 0 {
  115. return 0
  116. }
  117. index := 0
  118. for _, r := range f.Rules {
  119. for _, a := range after {
  120. if r.Kind() == a && r.Index() >= index {
  121. index = r.Index() + 1
  122. }
  123. }
  124. }
  125. return index
  126. }
  127. // FixWorkspace updates rules in the WORKSPACE file f that were used with an
  128. // older version of rules_go or gazelle.
  129. func FixWorkspace(f *rule.File) {
  130. removeLegacyGoRepository(f)
  131. }
  132. // CheckGazelleLoaded searches the given WORKSPACE file for a repository named
  133. // "bazel_gazelle". If no such repository is found *and* the repo is not
  134. // declared with a directive *and* at least one load statement mentions
  135. // the repository, a descriptive error will be returned.
  136. //
  137. // This should be called after modifications have been made to WORKSPACE
  138. // (i.e., after FixLoads) before writing it to disk.
  139. func CheckGazelleLoaded(f *rule.File) error {
  140. needGazelle := false
  141. for _, l := range f.Loads {
  142. if strings.HasPrefix(l.Name(), "@bazel_gazelle//") {
  143. needGazelle = true
  144. }
  145. }
  146. if !needGazelle {
  147. return nil
  148. }
  149. for _, r := range f.Rules {
  150. if r.Name() == "bazel_gazelle" {
  151. return nil
  152. }
  153. }
  154. for _, d := range f.Directives {
  155. if d.Key != "repo" {
  156. continue
  157. }
  158. if fs := strings.Fields(d.Value); len(fs) > 0 && fs[0] == "bazel_gazelle" {
  159. return nil
  160. }
  161. }
  162. return fmt.Errorf(`%s: error: bazel_gazelle is not declared in WORKSPACE.
  163. Without this repository, Gazelle cannot safely modify the WORKSPACE file.
  164. See the instructions at https://github.com/bazelbuild/bazel-gazelle.
  165. If the bazel_gazelle is declared inside a macro, you can suppress this error
  166. by adding a comment like this to WORKSPACE:
  167. # gazelle:repo bazel_gazelle
  168. `, f.Path)
  169. }
  170. // removeLegacyGoRepository removes loads of go_repository from
  171. // @io_bazel_rules_go. FixLoads should be called after this; it will load from
  172. // @bazel_gazelle.
  173. func removeLegacyGoRepository(f *rule.File) {
  174. for _, l := range f.Loads {
  175. if l.Name() == "@io_bazel_rules_go//go:def.bzl" {
  176. l.Remove("go_repository")
  177. if l.IsEmpty() {
  178. l.Delete()
  179. }
  180. }
  181. }
  182. }