1
15
16 package proto
17
18 import (
19 "flag"
20 "fmt"
21 "log"
22 "path"
23 "strings"
24
25 "github.com/bazelbuild/bazel-gazelle/config"
26 "github.com/bazelbuild/bazel-gazelle/pathtools"
27 "github.com/bazelbuild/bazel-gazelle/rule"
28 )
29
30
31
32
33
34 type ProtoConfig struct {
35
36 Mode Mode
37
38
39 ModeExplicit bool
40
41
42
43
44
45
46 GoPrefix string
47
48
49
50 groupOption string
51
52
53
54
55 StripImportPrefix string
56
57
58
59
60 ImportPrefix string
61 }
62
63
64
65 func GetProtoConfig(c *config.Config) *ProtoConfig {
66 pc := c.Exts[protoName]
67 if pc == nil {
68 return nil
69 }
70 return pc.(*ProtoConfig)
71 }
72
73
74 type Mode int
75
76 const (
77
78
79
80
81 DefaultMode Mode = iota
82
83
84
85
86 DisableMode
87
88
89
90
91 DisableGlobalMode
92
93
94
95 LegacyMode
96
97
98
99 PackageMode
100
101
102 FileMode
103 )
104
105 func ModeFromString(s string) (Mode, error) {
106 switch s {
107 case "default":
108 return DefaultMode, nil
109 case "disable":
110 return DisableMode, nil
111 case "disable_global":
112 return DisableGlobalMode, nil
113 case "legacy":
114 return LegacyMode, nil
115 case "package":
116 return PackageMode, nil
117 case "file":
118 return FileMode, nil
119 default:
120 return 0, fmt.Errorf("unrecognized proto mode: %q", s)
121 }
122 }
123
124 func (m Mode) String() string {
125 switch m {
126 case DefaultMode:
127 return "default"
128 case DisableMode:
129 return "disable"
130 case DisableGlobalMode:
131 return "disable_global"
132 case LegacyMode:
133 return "legacy"
134 case PackageMode:
135 return "package"
136 case FileMode:
137 return "file"
138 default:
139 log.Panicf("unknown mode %d", m)
140 return ""
141 }
142 }
143
144 func (m Mode) ShouldGenerateRules() bool {
145 switch m {
146 case DisableMode, DisableGlobalMode, LegacyMode:
147 return false
148 default:
149 return true
150 }
151 }
152
153 func (m Mode) ShouldIncludePregeneratedFiles() bool {
154 switch m {
155 case DisableMode, DisableGlobalMode, LegacyMode:
156 return true
157 default:
158 return false
159 }
160 }
161
162 func (m Mode) ShouldUseKnownImports() bool {
163 return m != DisableGlobalMode
164 }
165
166 type modeFlag struct {
167 mode *Mode
168 }
169
170 func (f *modeFlag) Set(value string) error {
171 if mode, err := ModeFromString(value); err != nil {
172 return err
173 } else {
174 *f.mode = mode
175 return nil
176 }
177 }
178
179 func (f *modeFlag) String() string {
180 var mode Mode
181 if f != nil && f.mode != nil {
182 mode = *f.mode
183 }
184 return mode.String()
185 }
186
187 func (*protoLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
188 pc := &ProtoConfig{}
189 c.Exts[protoName] = pc
190
191
192
193
194 fs.Var(&modeFlag{&pc.Mode}, "proto", "default: generates a proto_library rule for one package\n\tpackage: generates a proto_library rule for for each package\n\tdisable: does not touch proto rules\n\tdisable_global: does not touch proto rules and does not use special cases for protos in dependency resolution")
195 fs.StringVar(&pc.groupOption, "proto_group", "", "option name used to group .proto files into proto_library rules")
196 fs.StringVar(&pc.ImportPrefix, "proto_import_prefix", "", "When set, .proto source files in the srcs attribute of the rule are accessible at their path with this prefix appended on.")
197 }
198
199 func (*protoLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
200 return nil
201 }
202
203 func (*protoLang) KnownDirectives() []string {
204 return []string{"proto", "proto_group", "proto_strip_import_prefix", "proto_import_prefix"}
205 }
206
207 func (*protoLang) Configure(c *config.Config, rel string, f *rule.File) {
208 pc := &ProtoConfig{}
209 *pc = *GetProtoConfig(c)
210 c.Exts[protoName] = pc
211 if f != nil {
212 for _, d := range f.Directives {
213 switch d.Key {
214 case "proto":
215 mode, err := ModeFromString(d.Value)
216 if err != nil {
217 log.Print(err)
218 continue
219 }
220 pc.Mode = mode
221 pc.ModeExplicit = true
222 case "proto_group":
223 pc.groupOption = d.Value
224 case "proto_strip_import_prefix":
225 pc.StripImportPrefix = d.Value
226 if err := checkStripImportPrefix(pc.StripImportPrefix, rel); err != nil {
227 log.Print(err)
228 }
229 case "proto_import_prefix":
230 pc.ImportPrefix = d.Value
231 }
232 }
233 }
234 inferProtoMode(c, rel, f)
235 }
236
237
238
239
240
241
242
243
244
245 func inferProtoMode(c *config.Config, rel string, f *rule.File) {
246 pc := GetProtoConfig(c)
247 if pc.Mode != DefaultMode || pc.ModeExplicit {
248 return
249 }
250 if pc.GoPrefix == wellKnownTypesGoPrefix {
251 pc.Mode = LegacyMode
252 return
253 }
254 if path.Base(rel) == "vendor" {
255 pc.Mode = DisableMode
256 return
257 }
258 if f == nil {
259 return
260 }
261 mode := DefaultMode
262 outer:
263 for _, l := range f.Loads {
264 name := l.Name()
265 if name == "@io_bazel_rules_go//proto:def.bzl" {
266 break
267 }
268 if name == "@io_bazel_rules_go//proto:go_proto_library.bzl" {
269 mode = LegacyMode
270 break
271 }
272 for _, sym := range l.Symbols() {
273 if sym == "go_proto_library" {
274 mode = DisableMode
275 break outer
276 }
277 }
278 }
279 if mode == DefaultMode || pc.Mode == mode || c.ShouldFix && mode == LegacyMode {
280 return
281 }
282 pc.Mode = mode
283 }
284
285 func checkStripImportPrefix(prefix, rel string) error {
286 if prefix == "" {
287 return nil
288 }
289 if !strings.HasPrefix(prefix, "/") {
290 return fmt.Errorf("proto_strip_import_prefix should start with '/' for a prefix relative to the repository root")
291 }
292 if rel != "" && !pathtools.HasPrefix(rel, prefix[1:]) {
293 return fmt.Errorf("proto_strip_import_prefix %q not in directory %s", prefix, rel)
294 }
295 return nil
296 }
297
View as plain text