...
1
15
16 package merger
17
18 import (
19 "fmt"
20 "strings"
21
22 "github.com/bazelbuild/bazel-gazelle/rule"
23 bzl "github.com/bazelbuild/buildtools/build"
24 )
25
26
27
28
29
30
31 func FixLoads(f *rule.File, knownLoads []rule.LoadInfo) {
32 knownFiles := make(map[string]bool)
33 knownSymbols := make(map[string]string)
34 for _, l := range knownLoads {
35 knownFiles[l.Name] = true
36 for _, k := range l.Symbols {
37 knownSymbols[k] = l.Name
38 }
39 }
40
41
42
43 f.Sync()
44
45
46
47
48 var loads []*rule.Load
49 otherLoadedKinds := make(map[string]bool)
50 for _, l := range f.Loads {
51 if knownFiles[l.Name()] {
52 loads = append(loads, l)
53 continue
54 }
55 for _, sym := range l.Symbols() {
56 otherLoadedKinds[sym] = true
57 }
58 }
59
60
61 usedSymbols := make(map[string]map[string]bool)
62 bzl.Walk(f.File, func(x bzl.Expr, stk []bzl.Expr) {
63 ce, ok := x.(*bzl.CallExpr)
64 if !ok {
65 return
66 }
67
68 var functionIdent *bzl.Ident
69
70 d, ok := ce.X.(*bzl.DotExpr)
71 if ok {
72 functionIdent, ok = d.X.(*bzl.Ident)
73 } else {
74 functionIdent, ok = ce.X.(*bzl.Ident)
75 }
76
77 if !ok {
78 return
79 }
80
81 idents := []*bzl.Ident{functionIdent}
82
83 for _, arg := range ce.List {
84 if argIdent, ok := arg.(*bzl.Ident); ok {
85 idents = append(idents, argIdent)
86 }
87 }
88
89 for _, id := range idents {
90 file, ok := knownSymbols[id.Name]
91 if !ok || otherLoadedKinds[id.Name] {
92 continue
93 }
94
95 if usedSymbols[file] == nil {
96 usedSymbols[file] = make(map[string]bool)
97 }
98 usedSymbols[file][id.Name] = true
99 }
100 })
101
102
103
104 for _, known := range knownLoads {
105 file := known.Name
106 first := true
107 for _, l := range loads {
108 if l.Name() != file {
109 continue
110 }
111 if first {
112 fixLoad(l, file, usedSymbols[file], knownSymbols)
113 first = false
114 } else {
115 fixLoad(l, file, nil, knownSymbols)
116 }
117 if l.IsEmpty() {
118 l.Delete()
119 }
120 }
121 if first {
122 load := fixLoad(nil, file, usedSymbols[file], knownSymbols)
123 if load != nil {
124 index := newLoadIndex(f, known.After)
125 load.Insert(f, index)
126 }
127 }
128 }
129 }
130
131
132
133
134
135
136 func fixLoad(load *rule.Load, file string, symbols map[string]bool, knownSymbols map[string]string) *rule.Load {
137 if load == nil {
138 if len(symbols) == 0 {
139 return nil
140 }
141 load = rule.NewLoad(file)
142 }
143
144 for k := range symbols {
145 load.Add(k)
146 }
147 for _, k := range load.Symbols() {
148 if knownSymbols[k] != "" && !symbols[k] {
149 load.Remove(k)
150 }
151 }
152
153 return load
154 }
155
156
157
158
159 func newLoadIndex(f *rule.File, after []string) int {
160 if len(after) == 0 {
161 return 0
162 }
163 index := 0
164 for _, r := range f.Rules {
165 for _, a := range after {
166 if r.Kind() == a && r.Index() >= index {
167 index = r.Index() + 1
168 }
169 }
170 }
171 return index
172 }
173
174
175
176
177
178
179
180
181 func CheckGazelleLoaded(f *rule.File) error {
182 needGazelle := false
183 for _, l := range f.Loads {
184 if strings.HasPrefix(l.Name(), "@bazel_gazelle//") {
185 needGazelle = true
186 }
187 }
188 if !needGazelle {
189 return nil
190 }
191 for _, r := range f.Rules {
192 if r.Name() == "bazel_gazelle" {
193 return nil
194 }
195 }
196 for _, d := range f.Directives {
197 if d.Key != "repo" {
198 continue
199 }
200 if fs := strings.Fields(d.Value); len(fs) > 0 && fs[0] == "bazel_gazelle" {
201 return nil
202 }
203 }
204 return fmt.Errorf(`%s: error: bazel_gazelle is not declared in WORKSPACE.
205 Without this repository, Gazelle cannot safely modify the WORKSPACE file.
206 See the instructions at https://github.com/bazelbuild/bazel-gazelle.
207 If the bazel_gazelle is declared inside a macro, you can suppress this error
208 by adding a comment like this to WORKSPACE:
209 # gazelle:repo bazel_gazelle
210 `, f.Path)
211 }
212
View as plain text