Skip to content

Commit 9412391

Browse files
authored
Starlark plugins/rules: allow registration via the yaml config file (#291)
* Starlark plugins/rules: allow registration via the yaml config file * Implement missing out and options attributes. * Fix tests * Add to proto_repository_test
1 parent 439ce4a commit 9412391

File tree

9 files changed

+147
-69
lines changed

9 files changed

+147
-69
lines changed

.bazelrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
build:bazelci --deleted_packages=docs
22
# workaround for scala
3-
build --incompatible_java_common_parameters=false
3+
build --incompatible_java_common_parameters=false

example/golden/testdata/proto_repository/config.yaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
starlarkPlugins:
2+
# protoc-gen-java duplicates the functionality of the
3+
# builtin 'java' one, we have it here to test that it can
4+
# be loaded via the YAML config.
5+
# The format is IMPLEMENTATION_FILENAME%PLUGIN_NAME.
6+
- starlark/plugins.star%protoc-gen-java
17
plugins:
28
- name: protoc-gen-go
39
implementation: golang:protobuf:protoc-gen-go
410
deps:
511
- "@org_golang_google_protobuf//reflect/protoreflect"
612
- "@org_golang_google_protobuf//runtime/protoimpl"
7-
813
- name: protoc-gen-go-grpc
914
implementation: grpc:grpc-go:protoc-gen-go-grpc
1015
deps:
@@ -14,13 +19,13 @@ plugins:
1419

1520
- name: java
1621
implementation: builtin:java
17-
22+
- name: protoc-gen-java
23+
implementation: starlark/plugins.star%protoc-gen-java
1824
- name: protoc-gen-grpc-java
1925
implementation: grpc:grpc-java:protoc-gen-grpc-java
2026

2127
- name: python
2228
implementation: builtin:python
23-
2429
- name: protoc-gen-grpc-python
2530
implementation: grpc:grpc:protoc-gen-grpc-python
2631

@@ -44,7 +49,6 @@ rules:
4449
- "//visibility:public"
4550
deps:
4651
- "@com_google_protobuf//:protobuf_java"
47-
4852
- name: grpc_java_library
4953
implementation: stackb:rules_proto:grpc_java_library
5054
visibility:
@@ -58,7 +62,6 @@ rules:
5862
- "//visibility:public"
5963
deps:
6064
- "@tms_py_deps_protobuf//:pkg"
61-
6265
- name: grpc_py_library
6366
implementation: stackb:rules_proto:grpc_py_library
6467
visibility:
@@ -80,6 +83,7 @@ languages:
8083
enabled: false
8184
plugins:
8285
- java
86+
- protoc-gen-java
8387
- protoc-gen-grpc-java
8488
rules:
8589
- proto_compile
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""starlark plugin definitions"""
2+
3+
def _configure_protoc_gen_java(ctx):
4+
"""_configure_protoc_gen_java prepares the PluginConfiguration for a fictitious protoc java plugin.
5+
6+
Args:
7+
ctx (protoc.PluginContext): The context object.
8+
Returns:
9+
config (PluginConfiguration): The configured PluginConfiguration object.
10+
"""
11+
12+
srcjar = ctx.proto_library.base_name + ".srcjar"
13+
if ctx.rel:
14+
srcjar = "/".join(ctx.rel, srcjar)
15+
16+
config = protoc.PluginConfiguration(
17+
label = "@build_stack_rules_proto//plugin/builtin:java",
18+
outputs = [srcjar],
19+
out = srcjar,
20+
options = ctx.plugin_config.options,
21+
)
22+
23+
return config
24+
25+
protoc.Plugin(
26+
name = "protoc-gen-java",
27+
configure = _configure_protoc_gen_java,
28+
)

example/golden/testdata/starlark_java/BUILD.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ java_library(
3030

3131
proto_compile(
3232
name = "example_java_compile",
33+
outs = {"@build_stack_rules_proto//plugin/builtin:java": "example.srcjar"},
3334
outputs = ["example.srcjar"],
3435
plugins = ["@build_stack_rules_proto//plugin/builtin:java"],
3536
proto = "example_proto",

pkg/language/protobuf/config.go

Lines changed: 12 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ func (pl *protobufLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
4646
cfg := protoc.NewPackageConfig(c)
4747
c.Exts[pl.name] = cfg
4848

49+
for _, starlarkPlugin := range pl.starlarkPlugins {
50+
if err := protoc.RegisterStarlarkPlugin(c, starlarkPlugin); err != nil {
51+
return err
52+
}
53+
}
54+
55+
for _, starlarkRule := range pl.starlarkRules {
56+
if err := protoc.RegisterStarlarkRule(c, starlarkRule); err != nil {
57+
return err
58+
}
59+
}
60+
4961
if pl.configFiles != "" {
5062
for _, filename := range strings.Split(pl.configFiles, ",") {
5163
if err := protoc.LoadYConfigFile(c, cfg, filename); err != nil {
@@ -62,18 +74,6 @@ func (pl *protobufLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
6274
}
6375
}
6476

65-
for _, starlarkPlugin := range pl.starlarkPlugins {
66-
if err := registerStarlarkPlugin(c, starlarkPlugin); err != nil {
67-
return err
68-
}
69-
}
70-
71-
for _, starlarkRule := range pl.starlarkRules {
72-
if err := registerStarlarkRule(c, starlarkRule); err != nil {
73-
return err
74-
}
75-
}
76-
7777
return nil
7878
}
7979

@@ -128,52 +128,6 @@ func (pl *protobufLang) getOrCreatePackageConfig(config *config.Config) *protoc.
128128
return cfg
129129
}
130130

131-
func registerStarlarkPlugin(c *config.Config, starlarkPlugin string) error {
132-
parts := strings.Split(starlarkPlugin, "%")
133-
if len(parts) != 2 {
134-
return fmt.Errorf("invalid starlark plugin name %q", starlarkPlugin)
135-
}
136-
fileName := parts[0]
137-
ruleName := parts[1]
138-
var configureError error
139-
impl, err := protoc.LoadStarlarkPluginFromFile(c.WorkDir, fileName, ruleName, func(msg string) {
140-
}, func(err error) {
141-
configureError = err
142-
})
143-
if err != nil {
144-
return err
145-
}
146-
if configureError != nil {
147-
return configureError
148-
}
149-
configureError = err
150-
protoc.Plugins().RegisterPlugin(starlarkPlugin, impl)
151-
return nil
152-
}
153-
154-
func registerStarlarkRule(c *config.Config, starlarkRule string) error {
155-
parts := strings.Split(starlarkRule, "%")
156-
if len(parts) != 2 {
157-
return fmt.Errorf("invalid starlark rule name %q", starlarkRule)
158-
}
159-
fileName := parts[0]
160-
ruleName := parts[1]
161-
var configureError error
162-
impl, err := protoc.LoadStarlarkLanguageRuleFromFile(c.WorkDir, fileName, ruleName, func(msg string) {
163-
}, func(err error) {
164-
configureError = err
165-
})
166-
if err != nil {
167-
return err
168-
}
169-
if configureError != nil {
170-
return configureError
171-
}
172-
configureError = err
173-
protoc.Rules().MustRegisterRule(starlarkRule, impl)
174-
return nil
175-
}
176-
177131
func registerWellKnownProtos(resolver protoc.ImportResolver) {
178132
for k, v := range map[string]label.Label{
179133
"google/protobuf/any.proto": label.New("com_google_protobuf", "", "any_proto"),

pkg/protoc/package_config.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package protoc
22

33
import (
44
"fmt"
5+
"log"
56
"sort"
67
"strings"
78

@@ -189,6 +190,16 @@ func (c *PackageConfig) configuredLangs() []*LanguageConfig {
189190
}
190191

191192
func (c *PackageConfig) LoadYConfig(y *YConfig) error {
193+
for _, starlarkPlugin := range y.StarlarkPlugin {
194+
if err := c.loadYStarlarkPlugin(starlarkPlugin); err != nil {
195+
return err
196+
}
197+
}
198+
for _, starlarkRule := range y.StarlarkRule {
199+
if err := c.loadYStarlarkRule(starlarkRule); err != nil {
200+
return err
201+
}
202+
}
192203
for _, plugin := range y.Plugin {
193204
if err := c.loadYPlugin(plugin); err != nil {
194205
return err
@@ -207,6 +218,14 @@ func (c *PackageConfig) LoadYConfig(y *YConfig) error {
207218
return nil
208219
}
209220

221+
func (c *PackageConfig) loadYStarlarkPlugin(y string) error {
222+
return RegisterStarlarkPlugin(c.Config, y)
223+
}
224+
225+
func (c *PackageConfig) loadYStarlarkRule(y string) error {
226+
return RegisterStarlarkRule(c.Config, y)
227+
}
228+
210229
func (c *PackageConfig) loadYPlugin(y *YPlugin) error {
211230
if y.Name == "" {
212231
return fmt.Errorf("yaml plugin name missing in: %+v", y)
@@ -240,3 +259,51 @@ func (c *PackageConfig) loadYLanguage(y *YLanguage) error {
240259
}
241260
return lang.fromYAML(y)
242261
}
262+
263+
func RegisterStarlarkPlugin(c *config.Config, starlarkPlugin string) error {
264+
parts := strings.Split(starlarkPlugin, "%")
265+
if len(parts) != 2 {
266+
return fmt.Errorf("invalid starlark plugin name %q", starlarkPlugin)
267+
}
268+
fileName := parts[0]
269+
ruleName := parts[1]
270+
var configureError error
271+
impl, err := LoadStarlarkPluginFromFile(c.WorkDir, fileName, ruleName, func(msg string) {
272+
log.Printf("%s: %v", starlarkPlugin, msg)
273+
}, func(err error) {
274+
configureError = err
275+
})
276+
if err != nil {
277+
return err
278+
}
279+
if configureError != nil {
280+
return configureError
281+
}
282+
Plugins().RegisterPlugin(starlarkPlugin, impl)
283+
return nil
284+
}
285+
286+
func RegisterStarlarkRule(c *config.Config, starlarkRule string) error {
287+
parts := strings.Split(starlarkRule, "%")
288+
if len(parts) != 2 {
289+
return fmt.Errorf("invalid starlark rule name %q", starlarkRule)
290+
}
291+
fileName := parts[0]
292+
ruleName := parts[1]
293+
294+
var configureError error
295+
impl, err := LoadStarlarkLanguageRuleFromFile(c.WorkDir, fileName, ruleName, func(msg string) {
296+
}, func(err error) {
297+
configureError = err
298+
})
299+
if err != nil {
300+
return err
301+
}
302+
if configureError != nil {
303+
return configureError
304+
}
305+
configureError = err
306+
Rules().MustRegisterRule(starlarkRule, impl)
307+
log.Println("Registered rule:", starlarkRule, impl.Name())
308+
return nil
309+
}

pkg/protoc/starlark_plugin.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func loadStarlarkPlugin(name, filename string, src interface{}, reporter func(ms
5656
}
5757
}
5858

59-
// starlarkPlugin is a plugin implemented in starlark that implements the protoc
59+
// starlarkPlugin is an adapter for starlark code that implements the protoc
6060
// plugin interface.
6161
type starlarkPlugin struct {
6262
name string
@@ -85,7 +85,7 @@ func (p *starlarkPlugin) Configure(ctx *PluginContext) *PluginConfiguration {
8585
newPluginContextStruct(ctx),
8686
}, []starlark.Tuple{})
8787
if err != nil {
88-
p.errorReporter("plugin %q configure failed: %w", p.name, err)
88+
p.errorReporter("plugin %q configure failed: %v", p.name, err)
8989
return nil
9090
}
9191

@@ -116,9 +116,30 @@ func (p *starlarkPlugin) Configure(ctx *PluginContext) *PluginConfiguration {
116116
outputs[i] = outputsList.Index(i).(starlark.String).GoString()
117117
}
118118

119+
optionsValue, err := value.Attr("options")
120+
if err != nil {
121+
p.errorReporter("PluginConfiguration.options get value: %v", err)
122+
}
123+
optionsList := optionsValue.(*starlark.List)
124+
options := make([]string, optionsList.Len())
125+
for i := 0; i < optionsList.Len(); i++ {
126+
options[i] = optionsList.Index(i).(starlark.String).GoString()
127+
}
128+
129+
outValue, err := value.Attr("out")
130+
if err != nil {
131+
p.errorReporter("PluginConfiguration.out get value: %v", err)
132+
}
133+
var out string
134+
if outString, ok := outValue.(starlark.String); ok {
135+
out = outString.GoString()
136+
}
137+
119138
result = &PluginConfiguration{
120139
Label: lbl,
121140
Outputs: outputs,
141+
Out: out,
142+
Options: options,
122143
}
123144
default:
124145
p.errorReporter("plugin %q configure returned invalid type: %T", p.name, value)

pkg/protoc/starlark_plugin_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ protoc.Plugin(
6666
want: &PluginConfiguration{
6767
Label: label.New("", "mypkg", "python_plugin"),
6868
Outputs: []string{"foo.py", "bar.py"},
69+
Options: []string{},
6970
},
7071
wantPrinted: `PluginContext(package_config = PackageConfig(config = Config(repo_name = "", repo_root = "", work_dir = "")), plugin_config = LanguagePluginConfig(deps = [], enabled = False, implementation = "", label = "", name = "", options = []), proto_library = ProtoLibrary(base_name = "", deps = [], files = [], imports = [], name = "", srcs = [], strip_import_prefix = ""), rel = "mypkg")` + "\n",
7172
},

pkg/protoc/yconfig.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import (
1313
// YConfig is used to configure a combined set of plugins, rules, and languages
1414
// in a single YAML file. This is the format of the -proto_config flag.
1515
type YConfig struct {
16-
Plugin []*YPlugin `yaml:"plugins"`
17-
Rule []*YRule `yaml:"rules"`
18-
Language []*YLanguage `yaml:"languages"`
16+
Plugin []*YPlugin `yaml:"plugins"`
17+
Rule []*YRule `yaml:"rules"`
18+
Language []*YLanguage `yaml:"languages"`
19+
StarlarkPlugin []string `yaml:"starlarkPlugins"`
20+
StarlarkRule []string `yaml:"starlarkRules"`
1921
}
2022

2123
// YPlugin represents a LanguagePluginConfig in YAML.

0 commit comments

Comments
 (0)