/
package_config.go
299 lines (272 loc) · 8.3 KB
/
package_config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
package protoc
import (
"fmt"
"log"
"sort"
"strings"
"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/rule"
)
const (
// RuleDirective is the directive for toggling rule generation.
RuleDirective = "proto_rule"
// LanguageDirective tells gazelle which languages a package should
// produce and how it is configured.
LanguageDirective = "proto_language"
// PluginDirective created an association between proto_lang
// and the label of a proto_plugin.
PluginDirective = "proto_plugin"
// importpathPrefixDirective is the same as 'gazelle:prefix'
importpathPrefixDirective = "prefix"
)
// PackageConfig represents the config extension for the protobuf language.
type PackageConfig struct {
// config is the parent gazelle config.
Config *config.Config
// the gazelle:prefix for golang
importpathPrefix string
// configured languages for this package
langs map[string]*LanguageConfig
// exclude patterns for rules that should be skipped for this package.
plugins map[string]*LanguagePluginConfig
// exclude patterns for rules that should be skipped for this package.
rules map[string]*LanguageRuleConfig
// IMPORTANT! Adding new fields here? Don't forget to copy it in the Clone
// method!
}
// GetPackageConfig returns the associated package config.
func GetPackageConfig(config *config.Config) *PackageConfig {
if cfg, ok := config.Exts["protobuf"].(*PackageConfig); ok {
return cfg
}
return nil
}
// NewPackageConfig initializes a new PackageConfig.
func NewPackageConfig(config *config.Config) *PackageConfig {
return &PackageConfig{
Config: config,
langs: make(map[string]*LanguageConfig),
plugins: make(map[string]*LanguagePluginConfig),
rules: make(map[string]*LanguageRuleConfig),
}
}
// Plugin returns a readonly copy of the plugin configuration having the given
// name. If the plugin is not known the bool return arg is false.
func (c *PackageConfig) Plugin(name string) (LanguagePluginConfig, bool) {
if c.plugins == nil {
return LanguagePluginConfig{}, false
}
if plugin, ok := c.plugins[name]; ok {
return *plugin, true
} else {
return LanguagePluginConfig{}, false
}
}
// Clone copies this config to a new one.
func (c *PackageConfig) Clone() *PackageConfig {
clone := NewPackageConfig(c.Config)
clone.importpathPrefix = c.importpathPrefix
for k, v := range c.rules {
clone.rules[k] = v.clone()
}
for k, v := range c.langs {
clone.langs[k] = v.clone()
}
for k, v := range c.plugins {
clone.plugins[k] = v.clone()
}
return clone
}
// ParseDirectives is called in each directory visited by gazelle. The relative
// directory name is given by 'rel' and the list of directives in the BUILD file
// are specified by 'directives'.
func (c *PackageConfig) ParseDirectives(rel string, directives []rule.Directive) (err error) {
for _, d := range directives {
switch d.Key {
case importpathPrefixDirective:
err = c.parsePrefixDirective(d)
case PluginDirective:
err = c.parsePluginDirective(d)
case RuleDirective:
err = c.parseRuleDirective(d)
case LanguageDirective:
err = c.parseLanguageDirective(d)
}
if err != nil {
return fmt.Errorf("parse %v: %w", d, err)
}
}
return
}
func (c *PackageConfig) parsePrefixDirective(d rule.Directive) error {
c.importpathPrefix = strings.TrimSpace(d.Value)
return nil
}
func (c *PackageConfig) parseLanguageDirective(d rule.Directive) error {
fields := strings.Fields(d.Value)
if len(fields) != 3 {
return fmt.Errorf("invalid directive %v: expected three fields, got %d", d, len(fields))
}
name, param, value := fields[0], fields[1], fields[2]
lang, ok := c.langs[name]
if !ok {
lang = newLanguageConfig(name)
c.langs[name] = lang
}
return lang.parseDirective(c, name, param, value)
}
func (c *PackageConfig) parsePluginDirective(d rule.Directive) error {
fields := strings.Fields(d.Value)
if len(fields) != 3 {
return fmt.Errorf("invalid directive %v: expected three fields, got %d", d, len(fields))
}
name, param, value := fields[0], fields[1], fields[2]
plugin, err := c.getOrCreateLanguagePluginConfig(name)
if err != nil {
return fmt.Errorf("invalid proto_plugin directive %+v: %w", d, err)
}
return plugin.parseDirective(c, name, param, value)
}
func (c *PackageConfig) parseRuleDirective(d rule.Directive) error {
fields := strings.Fields(d.Value)
if len(fields) < 3 {
return fmt.Errorf("invalid directive %v: expected three or more fields, got %d", d, len(fields))
}
name, param, value := fields[0], fields[1], strings.Join(fields[2:], " ")
r, err := c.getOrCreateLanguageRuleConfig(c.Config, name)
if err != nil {
return fmt.Errorf("invalid proto_rule directive %+v: %w", d, err)
}
return r.parseDirective(c, name, param, value)
}
func (c *PackageConfig) getOrCreateLanguagePluginConfig(name string) (*LanguagePluginConfig, error) {
plugin, ok := c.plugins[name]
if !ok {
plugin = newLanguagePluginConfig(name)
c.plugins[name] = plugin
}
return plugin, nil
}
func (c *PackageConfig) getOrCreateLanguageRuleConfig(config *config.Config, name string) (*LanguageRuleConfig, error) {
r, ok := c.rules[name]
if !ok {
r = NewLanguageRuleConfig(config, name)
r.Implementation = name
c.rules[name] = r
}
return r, nil
}
// configuredLangs returns a determinstic ordered list of configured
// langs
func (c *PackageConfig) configuredLangs() []*LanguageConfig {
names := make([]string, 0)
for name := range c.langs {
names = append(names, name)
}
sort.Strings(names)
langs := make([]*LanguageConfig, 0)
for _, name := range names {
langs = append(langs, c.langs[name])
}
return langs
}
func (c *PackageConfig) LoadYConfig(y *YConfig) error {
for _, starlarkPlugin := range y.StarlarkPlugin {
if err := c.loadYStarlarkPlugin(starlarkPlugin); err != nil {
return err
}
}
for _, starlarkRule := range y.StarlarkRule {
if err := c.loadYStarlarkRule(starlarkRule); err != nil {
return err
}
}
for _, plugin := range y.Plugin {
if err := c.loadYPlugin(plugin); err != nil {
return err
}
}
for _, rule := range y.Rule {
if err := c.loadYRule(rule); err != nil {
return err
}
}
for _, lang := range y.Language {
if err := c.loadYLanguage(lang); err != nil {
return err
}
}
return nil
}
func (c *PackageConfig) loadYStarlarkPlugin(y string) error {
return RegisterStarlarkPlugin(c.Config, y)
}
func (c *PackageConfig) loadYStarlarkRule(y string) error {
return RegisterStarlarkRule(c.Config, y)
}
func (c *PackageConfig) loadYPlugin(y *YPlugin) error {
if y.Name == "" {
return fmt.Errorf("yaml plugin name missing in: %+v", y)
}
plugin, err := c.getOrCreateLanguagePluginConfig(y.Name)
if err != nil {
return err
}
return plugin.fromYAML(y)
}
func (c *PackageConfig) loadYRule(y *YRule) error {
if y.Name == "" {
return fmt.Errorf("yaml rule name missing in: %+v", y)
}
rule, err := c.getOrCreateLanguageRuleConfig(c.Config, y.Name)
if err != nil {
return err
}
return rule.fromYAML(y)
}
func (c *PackageConfig) loadYLanguage(y *YLanguage) error {
if y.Name == "" {
return fmt.Errorf("yaml language name missing in: %+v", y)
}
lang, ok := c.langs[y.Name]
if !ok {
lang = newLanguageConfig(y.Name)
c.langs[y.Name] = lang
}
return lang.fromYAML(y)
}
func RegisterStarlarkPlugin(c *config.Config, starlarkPlugin string) error {
parts := strings.Split(starlarkPlugin, "%")
if len(parts) != 2 {
return fmt.Errorf("invalid starlark plugin name %q", starlarkPlugin)
}
fileName := parts[0]
ruleName := parts[1]
impl, err := LoadStarlarkPluginFromFile(c.WorkDir, fileName, ruleName, func(msg string) {
log.Printf("%s: %v", starlarkPlugin, msg)
}, func(err error) {
log.Fatalf("starlark plugin configuration error (plugin %q will not be registered): %v", starlarkPlugin, err)
})
if err != nil {
return err
}
Plugins().RegisterPlugin(starlarkPlugin, impl)
return nil
}
func RegisterStarlarkRule(c *config.Config, starlarkRule string) error {
parts := strings.Split(starlarkRule, "%")
if len(parts) != 2 {
return fmt.Errorf("invalid starlark rule name %q", starlarkRule)
}
fileName := parts[0]
ruleName := parts[1]
impl, err := LoadStarlarkLanguageRuleFromFile(c.WorkDir, fileName, ruleName, func(msg string) {
}, func(err error) {
log.Panicf("starlark rule configuration error (rule %q will not be registered): %v", starlarkRule, err)
})
if err != nil {
return err
}
Rules().MustRegisterRule(starlarkRule, impl)
return nil
}