Skip to content

Commit

Permalink
Support multiple generators
Browse files Browse the repository at this point in the history
The latest version of protoc-gen-go doesn't support gRPC. Supporting
multiple generators is the way to support gRPC going forward.

Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
  • Loading branch information
kzys committed Sep 27, 2021
1 parent 6b023c6 commit 5aff242
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 19 deletions.
56 changes: 49 additions & 7 deletions config.go
Expand Up @@ -27,10 +27,18 @@ import (
const configVersion = "unstable"

type config struct {
Version string
Version string
Generators []string

// Generator is a code generator which is used from protoc.
// Deprecated: Use Generators instead.
Generator string
Plugins []string
Includes struct {

// Plugins will be deprecated. It has to be per-Generator setting, but neither protoc-gen-go nor protoc-gen-go-grpc
// don't support Plugins. So the refactoring is not worth to do.
Plugins []string

Includes struct {
Before []string
Vendored []string
Packages []string
Expand All @@ -40,9 +48,12 @@ type config struct {
Packages map[string]string

Overrides []struct {
Prefixes []string
Generator string
Plugins *[]string
Prefixes []string
// Generator is a code generator which is used from protoc.
// Deprecated: Use Generators instead.
Generator string
Generators []string
Plugins *[]string

// TODO(stevvooe): We could probably support overriding of includes and
// package maps, but they don't seem to be as useful. Likely,
Expand All @@ -59,7 +70,6 @@ type config struct {

func newDefaultConfig() config {
return config{
Generator: "go",
Includes: struct {
Before []string
Vendored []string
Expand All @@ -77,6 +87,10 @@ func readConfig(path string) (config, error) {
if err != nil {
log.Fatalln(err)
}
return readConfigFrom(p)
}

func readConfigFrom(p []byte) (config, error) {
c := newDefaultConfig()
if err := toml.Unmarshal(p, &c); err != nil {
log.Fatalln(err)
Expand All @@ -86,5 +100,33 @@ func readConfig(path string) (config, error) {
return config{}, fmt.Errorf("unknown file version %v; please upgrade to %v", c.Version, configVersion)
}

if c.Generator != "" {
if len(c.Generators) > 0 {
return config{}, fmt.Errorf(
`specify either "generators = %v" or "generator = %v", not both`,
c.Generators, c.Generator,
)
}
c.Generators = []string{c.Generator}
c.Generator = ""
}

for i, o := range c.Overrides {
if o.Generator != "" {
if len(o.Generators) > 0 {
return config{}, fmt.Errorf(
`specify either "overrides[%d].generators" or "overrides[%d].generator", not both`,
i, i,
)
}
c.Overrides[i].Generators = []string{o.Generator}
c.Overrides[i].Generator = ""
}
}

if len(c.Generators) == 0 {
c.Generators = []string{"go"}
}

return c, nil
}
60 changes: 60 additions & 0 deletions config_test.go
@@ -0,0 +1,60 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import "testing"

func TestReadConfigFrom(t *testing.T) {
testcases := []struct {
name string
toml string
}{
{
name: "empty",
toml: `version="unstable"`,
},
{
name: "generator",
toml: `
version="unstable"
generator="go"
`,
},
{
name: "generators",
toml: `
version="unstable"
generators=["go"]
`,
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
c, err := readConfigFrom([]byte(tc.toml))
if err != nil {
t.Fatalf("err must be nil, but got %v", err)
}
if c.Generator == "go" {
t.Fatalf("Generator must be cleared, but got %v", c.Generator)
}
if len(c.Generators) != 1 || c.Generators[0] != "go" {
t.Fatalf("Generators must be [go], but got %v", c.Generators)
}
})
}
}
13 changes: 7 additions & 6 deletions main.go
Expand Up @@ -77,9 +77,10 @@ func main() {

// Index overrides by target import path
overrides := map[string]struct {
Prefixes []string
Generator string
Plugins *[]string
Prefixes []string
Generator string
Generators []string
Plugins *[]string
}{}
for _, override := range c.Overrides {
for _, prefix := range override.Prefixes {
Expand Down Expand Up @@ -166,7 +167,7 @@ func main() {
includes = append(includes, c.Includes.After...)

protoc := protocCmd{
Name: c.Generator,
Names: c.Generators,
ImportPath: pkg.GoImportPath,
PackageMap: c.Packages,
Plugins: c.Plugins,
Expand All @@ -182,8 +183,8 @@ func main() {

if override, ok := overrides[importDirPath]; ok {
// selectively apply the overrides to the protoc structure.
if override.Generator != "" {
protoc.Name = override.Generator
if len(override.Generators) > 0 {
protoc.Names = override.Generators
}

if override.Plugins != nil {
Expand Down
16 changes: 10 additions & 6 deletions protoc.go
Expand Up @@ -30,12 +30,16 @@ var (
{{if $index}}` + string(filepath.ListSeparator) + `{{end -}}
{{.}}
{{- end -}}
{{- if .Descriptors}} --include_imports --descriptor_set_out={{.Descriptors}}{{- end }} --
{{- .Name -}}_out={{if .Plugins}}plugins={{- range $index, $plugin := .Plugins -}}
{{- if $index}}+{{end}}
{{- $plugin}}
{{- if .Descriptors}} --include_imports --descriptor_set_out={{.Descriptors}}{{- end -}}
{{- range $index, $name := .Names }} --{{- $name -}}_out=
{{- if $.Plugins}}plugins={{- range $index, $plugin := $.Plugins -}}
{{- if $index}}+{{end}}
{{- $plugin}}
{{- end -}},{{- end -}}
import_path={{$.ImportPath}}
{{- end -}}
,{{- end -}}import_path={{.ImportPath}}
{{- range $proto, $gopkg := .PackageMap -}},M
{{- $proto}}={{$gopkg -}}
{{- end -}}
Expand All @@ -46,7 +50,7 @@ var (

// protocParams defines inputs to a protoc command string.
type protocCmd struct {
Name string // backend name
Names []string
Includes []string
Plugins []string
Descriptors string
Expand Down
57 changes: 57 additions & 0 deletions protoc_test.go
@@ -0,0 +1,57 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import "testing"

func TestMkcmd(t *testing.T) {
testcases := []struct {
name string
cmd protocCmd
expected string
}{
{
name: "basic",
cmd: protocCmd{Names: []string{"go"}},
expected: "protoc -I --go_out=import_path=:",
},
{
name: "plugin",
cmd: protocCmd{Names: []string{"go"}, Plugins: []string{"grpc"}},
expected: "protoc -I --go_out=plugins=grpc,import_path=:",
},
{
name: "use protoc-gen-go-grpc instead of plugins",
cmd: protocCmd{Names: []string{"go", "go-grpc"}},
expected: "protoc -I --go_out=import_path= --go-grpc_out=import_path=:",
},
}
for _, tc := range testcases {
t.Run(tc.name, func(subtest *testing.T) {
cmd := &tc.cmd

s, err := cmd.mkcmd()
if err != nil {
subtest.Fatalf("err must be nil but %+v", err)
}

if s != tc.expected {
subtest.Fatalf(`s must be %q, but %q`, tc.expected, s)
}
})
}
}

0 comments on commit 5aff242

Please sign in to comment.