[protobuf] cmd/protoc-gen-grpc_go: add gRPC code generator

953 views
Skip to first unread message

Damien Neil (Gerrit)

unread,
Sep 24, 2018, 6:18:09 PM9/24/18
to Ian Lance Taylor, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Damien Neil has uploaded this change for review.

View Change

cmd/protoc-gen-grpc_go: add gRPC code generator

This is a straight translation of the v1-api gRPC "plugin" to protogen.

Add a protoc-gen-grpc_go command. The preferred way to generate gRPC
services is to invoke both plugins separately:

protoc --go_out=. --grpc_go_out=. foo.proto

When invoked in this fashion, the generators will produce separate
foo.pb.go and foo_grpc.pb.go files.

The old "--go_out=plugins=grpc:." form is also supported for the
moment, in which case we still generate just one combined foo.pb.go.

Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
---
M cmd/protoc-gen-go/internal_gengo/main.go
A cmd/protoc-gen-go/testdata/grpc/grpc.pb.go
A cmd/protoc-gen-go/testdata/grpc/grpc.proto
A cmd/protoc-gen-grpc_go/main.go
A internal/protogen/gengrpcgo/grpc.go
M protogen/protogen.go
M regenerate.bash
7 files changed, 931 insertions(+), 6 deletions(-)

diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
index 518f427..07643aa 100644
--- a/cmd/protoc-gen-go/internal_gengo/main.go
+++ b/cmd/protoc-gen-go/internal_gengo/main.go
@@ -19,6 +19,7 @@

"github.com/golang/protobuf/proto"
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+ "github.com/golang/protobuf/v2/internal/protogen/gengrpcgo"
"github.com/golang/protobuf/v2/protogen"
"github.com/golang/protobuf/v2/reflect/protoreflect"
)
@@ -33,17 +34,20 @@

func Main() {
var flags flag.FlagSet
- // TODO: Decide what to do for backwards compatibility with plugins=grpc.
- flags.String("plugins", "", "")
+ plugins := flags.String("plugins", "", "list of plugins to invoke")
opts := &protogen.Options{
ParamFunc: flags.Set,
}
protogen.Run(opts, func(gen *protogen.Plugin) error {
+ enabledPlugins := make(map[string]bool)
+ for _, p := range strings.Split(*plugins, "+") {
+ enabledPlugins[p] = true
+ }
for _, f := range gen.Files {
if !f.Generate {
continue
}
- genFile(gen, f)
+ genFile(gen, f, enabledPlugins)
}
return nil
})
@@ -58,7 +62,7 @@
allExtensions []*protogen.Extension
}

-func genFile(gen *protogen.Plugin, file *protogen.File) {
+func genFile(gen *protogen.Plugin, file *protogen.File, plugins map[string]bool) {
f := &fileInfo{
File: file,
locationMap: make(map[string][]*descpb.SourceCodeInfo_Location),
@@ -138,7 +142,9 @@
}

genInitFunction(gen, g, f)
-
+ if plugins["grpc"] {
+ gengrpcgo.GenerateFileContent(gen, f.File, g)
+ }
genFileDescriptor(gen, g, f)
}

diff --git a/cmd/protoc-gen-go/testdata/grpc/grpc.pb.go b/cmd/protoc-gen-go/testdata/grpc/grpc.pb.go
new file mode 100644
index 0000000..c759239
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/grpc/grpc.pb.go
@@ -0,0 +1,381 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: grpc/grpc.proto
+
+package grpc
+
+import (
+ context "context"
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ grpc "grpc"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Request struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Request) Reset() { *m = Request{} }
+func (m *Request) String() string { return proto.CompactTextString(m) }
+func (*Request) ProtoMessage() {}
+func (*Request) Descriptor() ([]byte, []int) {
+ return fileDescriptor_81ea47a3f88c2082, []int{0}
+}
+
+func (m *Request) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Request.Unmarshal(m, b)
+}
+func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Request.Marshal(b, m, deterministic)
+}
+func (m *Request) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Request.Merge(m, src)
+}
+func (m *Request) XXX_Size() int {
+ return xxx_messageInfo_Request.Size(m)
+}
+func (m *Request) XXX_DiscardUnknown() {
+ xxx_messageInfo_Request.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Request proto.InternalMessageInfo
+
+type Response struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Response) Reset() { *m = Response{} }
+func (m *Response) String() string { return proto.CompactTextString(m) }
+func (*Response) ProtoMessage() {}
+func (*Response) Descriptor() ([]byte, []int) {
+ return fileDescriptor_81ea47a3f88c2082, []int{1}
+}
+
+func (m *Response) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Response.Unmarshal(m, b)
+}
+func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Response.Marshal(b, m, deterministic)
+}
+func (m *Response) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Response.Merge(m, src)
+}
+func (m *Response) XXX_Size() int {
+ return xxx_messageInfo_Response.Size(m)
+}
+func (m *Response) XXX_DiscardUnknown() {
+ xxx_messageInfo_Response.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Response proto.InternalMessageInfo
+
+func init() {
+ proto.RegisterType((*Request)(nil), "goproto.protoc.grpc.Request")
+ proto.RegisterType((*Response)(nil), "goproto.protoc.grpc.Response")
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// TestClient is the client API for Test service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type TestClient interface {
+ UnaryCall(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
+ // This RPC streams from the server only.
+ Downstream(ctx context.Context, in *Request, opts ...grpc.CallOption) (Test_DownstreamClient, error)
+ // This RPC streams from the client.
+ Upstream(ctx context.Context, opts ...grpc.CallOption) (Test_UpstreamClient, error)
+ // This one streams in both directions.
+ Bidi(ctx context.Context, opts ...grpc.CallOption) (Test_BidiClient, error)
+}
+
+type testClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewTestClient(cc *grpc.ClientConn) TestClient {
+ return &testClient{cc}
+}
+
+func (c *testClient) UnaryCall(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
+ out := new(Response)
+ err := c.cc.Invoke(ctx, "/goproto.protoc.grpc.Test/UnaryCall", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *testClient) Downstream(ctx context.Context, in *Request, opts ...grpc.CallOption) (Test_DownstreamClient, error) {
+ stream, err := c.cc.NewStream(ctx, &_Test_serviceDesc.Streams[0], "/goproto.protoc.grpc.Test/Downstream", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testDownstreamClient{stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+type Test_DownstreamClient interface {
+ Recv() (*Response, error)
+ grpc.ClientStream
+}
+
+type testDownstreamClient struct {
+ grpc.ClientStream
+}
+
+func (x *testDownstreamClient) Recv() (*Response, error) {
+ m := new(Response)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testClient) Upstream(ctx context.Context, opts ...grpc.CallOption) (Test_UpstreamClient, error) {
+ stream, err := c.cc.NewStream(ctx, &_Test_serviceDesc.Streams[1], "/goproto.protoc.grpc.Test/Upstream", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testUpstreamClient{stream}
+ return x, nil
+}
+
+type Test_UpstreamClient interface {
+ Send(*Request) error
+ CloseAndRecv() (*Response, error)
+ grpc.ClientStream
+}
+
+type testUpstreamClient struct {
+ grpc.ClientStream
+}
+
+func (x *testUpstreamClient) Send(m *Request) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testUpstreamClient) CloseAndRecv() (*Response, error) {
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ m := new(Response)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testClient) Bidi(ctx context.Context, opts ...grpc.CallOption) (Test_BidiClient, error) {
+ stream, err := c.cc.NewStream(ctx, &_Test_serviceDesc.Streams[2], "/goproto.protoc.grpc.Test/Bidi", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testBidiClient{stream}
+ return x, nil
+}
+
+type Test_BidiClient interface {
+ Send(*Request) error
+ Recv() (*Response, error)
+ grpc.ClientStream
+}
+
+type testBidiClient struct {
+ grpc.ClientStream
+}
+
+func (x *testBidiClient) Send(m *Request) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testBidiClient) Recv() (*Response, error) {
+ m := new(Response)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// TestServer is the server API for Test service.
+type TestServer interface {
+ UnaryCall(context.Context, *Request) (*Response, error)
+ // This RPC streams from the server only.
+ Downstream(*Request, Test_DownstreamServer) error
+ // This RPC streams from the client.
+ Upstream(Test_UpstreamServer) error
+ // This one streams in both directions.
+ Bidi(Test_BidiServer) error
+}
+
+func RegisterTestServer(s *grpc.Server, srv TestServer) {
+ s.RegisterService(&_Test_serviceDesc, srv)
+}
+
+func _Test_UnaryCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Request)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(TestServer).UnaryCall(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/goproto.protoc.grpc.Test/UnaryCall",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(TestServer).UnaryCall(ctx, req.(*Request))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Test_Downstream_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(Request)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(TestServer).Downstream(m, &testDownstreamServer{stream})
+}
+
+type Test_DownstreamServer interface {
+ Send(*Response) error
+ grpc.ServerStream
+}
+
+type testDownstreamServer struct {
+ grpc.ServerStream
+}
+
+func (x *testDownstreamServer) Send(m *Response) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _Test_Upstream_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServer).Upstream(&testUpstreamServer{stream})
+}
+
+type Test_UpstreamServer interface {
+ SendAndClose(*Response) error
+ Recv() (*Request, error)
+ grpc.ServerStream
+}
+
+type testUpstreamServer struct {
+ grpc.ServerStream
+}
+
+func (x *testUpstreamServer) SendAndClose(m *Response) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testUpstreamServer) Recv() (*Request, error) {
+ m := new(Request)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _Test_Bidi_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServer).Bidi(&testBidiServer{stream})
+}
+
+type Test_BidiServer interface {
+ Send(*Response) error
+ Recv() (*Request, error)
+ grpc.ServerStream
+}
+
+type testBidiServer struct {
+ grpc.ServerStream
+}
+
+func (x *testBidiServer) Send(m *Response) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testBidiServer) Recv() (*Request, error) {
+ m := new(Request)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+var _Test_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "goproto.protoc.grpc.Test",
+ HandlerType: (*TestServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "UnaryCall",
+ Handler: _Test_UnaryCall_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "Downstream",
+ Handler: _Test_Downstream_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "Upstream",
+ Handler: _Test_Upstream_Handler,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "Bidi",
+ Handler: _Test_Bidi_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+ Metadata: "grpc/grpc.proto",
+}
+
+func init() { proto.RegisterFile("grpc/grpc.proto", fileDescriptor_81ea47a3f88c2082) }
+
+var fileDescriptor_81ea47a3f88c2082 = []byte{
+ // 211 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4f, 0x2f, 0x2a, 0x48,
+ 0xd6, 0x07, 0x11, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xc2, 0xe9, 0xf9, 0x60, 0x06, 0x84,
+ 0x9b, 0xac, 0x07, 0x92, 0x52, 0xe2, 0xe4, 0x62, 0x0f, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51,
+ 0xe2, 0xe2, 0xe2, 0x08, 0x4a, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x35, 0xda, 0xc8, 0xc4, 0xc5,
+ 0x12, 0x92, 0x5a, 0x5c, 0x22, 0xe4, 0xc1, 0xc5, 0x19, 0x9a, 0x97, 0x58, 0x54, 0xe9, 0x9c, 0x98,
+ 0x93, 0x23, 0x24, 0xa3, 0x87, 0xc5, 0x08, 0x3d, 0xa8, 0x7e, 0x29, 0x59, 0x1c, 0xb2, 0x10, 0x23,
+ 0x85, 0xbc, 0xb9, 0xb8, 0x5c, 0xf2, 0xcb, 0xf3, 0x8a, 0x4b, 0x8a, 0x52, 0x13, 0x73, 0x29, 0x32,
+ 0xca, 0x80, 0x51, 0xc8, 0x93, 0x8b, 0x23, 0xb4, 0x80, 0x0a, 0x46, 0x69, 0x30, 0x0a, 0xb9, 0x73,
+ 0xb1, 0x38, 0x65, 0xa6, 0x64, 0x52, 0x68, 0x8c, 0x01, 0xa3, 0x93, 0x7d, 0x94, 0x6d, 0x7a, 0x66,
+ 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x3e,
+ 0x58, 0x75, 0x52, 0x69, 0x9a, 0x7e, 0x99, 0x91, 0x7e, 0x72, 0x6e, 0x0a, 0x84, 0x9f, 0xac, 0x9b,
+ 0x9e, 0x9a, 0xa7, 0x9b, 0x9e, 0xaf, 0x5f, 0x92, 0x5a, 0x5c, 0x92, 0x92, 0x58, 0x92, 0x08, 0x8e,
+ 0xa6, 0x24, 0x36, 0xb0, 0xa4, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x29, 0xd5, 0xc4, 0xd0, 0xba,
+ 0x01, 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/grpc/grpc.proto b/cmd/protoc-gen-go/testdata/grpc/grpc.proto
new file mode 100644
index 0000000..c484ebe
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/grpc/grpc.proto
@@ -0,0 +1,25 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+syntax = "proto3";
+
+package goproto.protoc.grpc;
+
+option go_package = "github.com/golang/protobuf/v2/cmd/protoc-gen-go/testdata/grpc";
+
+message Request {}
+message Response {}
+
+service Test {
+ rpc UnaryCall(Request) returns (Response);
+
+ // This RPC streams from the server only.
+ rpc Downstream(Request) returns (stream Response);
+
+ // This RPC streams from the client.
+ rpc Upstream(stream Request) returns (Response);
+
+ // This one streams in both directions.
+ rpc Bidi(stream Request) returns (stream Response);
+}
diff --git a/cmd/protoc-gen-grpc_go/main.go b/cmd/protoc-gen-grpc_go/main.go
new file mode 100644
index 0000000..7bd1e29
--- /dev/null
+++ b/cmd/protoc-gen-grpc_go/main.go
@@ -0,0 +1,24 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The protoc-gen-go binary is a protoc plugin to generate Go gRPC
+// service definitions.
+package main
+
+import (
+ "github.com/golang/protobuf/v2/internal/protogen/gengrpcgo"
+ "github.com/golang/protobuf/v2/protogen"
+)
+
+func main() {
+ protogen.Run(nil, func(gen *protogen.Plugin) error {
+ for _, file := range gen.Files {
+ if !file.Generate {
+ continue
+ }
+ gengrpcgo.GenerateFile(gen, file)
+ }
+ return nil
+ })
+}
diff --git a/internal/protogen/gengrpcgo/grpc.go b/internal/protogen/gengrpcgo/grpc.go
new file mode 100644
index 0000000..0c90ac9
--- /dev/null
+++ b/internal/protogen/gengrpcgo/grpc.go
@@ -0,0 +1,412 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gengrpc generates Go gRPC services.
+package gengrpcgo
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+ "github.com/golang/protobuf/v2/protogen"
+)
+
+type fileInfo struct {
+ *protogen.File
+ locationMap map[string][]*descpb.SourceCodeInfo_Location
+}
+
+// GenerateFile generates a _grpc.pb.go file containing gRPC service definitions.
+func GenerateFile(gen *protogen.Plugin, f *protogen.File) {
+ if len(f.Services) == 0 {
+ return
+ }
+ filename := f.GeneratedFilenamePrefix + "_grpc.pb.go"
+ g := gen.NewGeneratedFile(filename, f.GoImportPath)
+ g.P("// Code generated by protoc-gen-grpc_go. DO NOT EDIT.")
+ g.P()
+ g.P("package ", f.GoPackageName)
+ g.P()
+ GenerateFileContent(gen, f, g)
+}
+
+// GenerateFileContent generates the gRPC service definitions, excluding the package statement.
+func GenerateFileContent(gen *protogen.Plugin, f *protogen.File, g *protogen.GeneratedFile) {
+ if len(f.Services) == 0 {
+ return
+ }
+ file := &fileInfo{
+ File: f,
+ locationMap: make(map[string][]*descpb.SourceCodeInfo_Location),
+ }
+ for _, loc := range file.Proto.GetSourceCodeInfo().GetLocation() {
+ key := pathKey(loc.Path)
+ file.locationMap[key] = append(file.locationMap[key], loc)
+ }
+
+ // TODO: Remove this. We don't need to include these references any more.
+ g.P("// Reference imports to suppress errors if they are not otherwise used.")
+ g.P("var _ ", protogen.GoIdent{GoImportPath: "context", GoName: "Context"})
+ g.P("var _ ", protogen.GoIdent{GoImportPath: "grpc", GoName: "ClientConn"})
+ g.P()
+
+ g.P("// This is a compile-time assertion to ensure that this generated file")
+ g.P("// is compatible with the grpc package it is being compiled against.")
+ g.P("const _ = grpc.SupportPackageIsVersion4")
+ g.P()
+ for _, service := range file.Services {
+ genService(gen, file, g, service)
+ }
+}
+
+func genService(gen *protogen.Plugin, file *fileInfo, g *protogen.GeneratedFile, service *protogen.Service) {
+ clientName := service.GoName + "Client"
+
+ g.P("// ", clientName, " is the client API for ", service.GoName, " service.")
+ g.P("//")
+ g.P("// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.")
+
+ // Client interface.
+ // TODO deprecation
+ g.P("type ", clientName, " interface {")
+ for _, method := range service.Methods {
+ genComment(g, file, method.Path)
+ g.P(clientSignature(g, method))
+ }
+ g.P("}")
+ g.P()
+
+ // Client structure.
+ g.P("type ", unexport(clientName), " struct {")
+ g.P("cc *", ident("grpc.ClientConn"))
+ g.P("}")
+ g.P()
+
+ // NewClient factory.
+ // TODO deprecation
+ g.P("func New", clientName, " (cc *", ident("grpc.ClientConn"), ") ", clientName, " {")
+ g.P("return &", unexport(clientName), "{cc}")
+ g.P("}")
+ g.P()
+
+ var methodIndex, streamIndex int
+ // Client method implementations.
+ for _, method := range service.Methods {
+ if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() {
+ // Unary RPC method
+ genClientMethod(gen, file, g, method, methodIndex)
+ methodIndex++
+ } else {
+ // Streaming RPC method
+ genClientMethod(gen, file, g, method, streamIndex)
+ streamIndex++
+ }
+ }
+
+ // Server interface.
+ serverType := service.GoName + "Server"
+ g.P("// ", serverType, " is the server API for ", service.GoName, " service.")
+ // TODO deprecation
+ g.P("type ", serverType, " interface {")
+ for _, method := range service.Methods {
+ genComment(g, file, method.Path)
+ g.P(serverSignature(g, method))
+ }
+ g.P("}")
+ g.P()
+
+ // Server registration.
+ // TODO deprecation
+ serviceDescVar := "_" + service.GoName + "_serviceDesc"
+ g.P("func Register", service.GoName, "Server(s *", ident("grpc.Server"), ", srv ", serverType, ") {")
+ g.P("s.RegisterService(&", serviceDescVar, `, srv)`)
+ g.P("}")
+ g.P()
+
+ // Server handler implementations.
+ var handlerNames []string
+ for _, method := range service.Methods {
+ hname := genServerMethod(gen, file, g, method)
+ handlerNames = append(handlerNames, hname)
+ }
+
+ // Service descriptor.
+ g.P("var ", serviceDescVar, " = ", ident("grpc.ServiceDesc"), " {")
+ g.P("ServiceName: ", strconv.Quote(string(service.Desc.FullName())), ",")
+ g.P("HandlerType: (*", serverType, ")(nil),")
+ g.P("Methods: []", ident("grpc.MethodDesc"), "{")
+ for i, method := range service.Methods {
+ if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
+ continue
+ }
+ g.P("{")
+ g.P("MethodName: ", strconv.Quote(method.GoName), ",")
+ g.P("Handler: ", handlerNames[i], ",")
+ g.P("},")
+ }
+ g.P("},")
+ g.P("Streams: []", ident("grpc.StreamDesc"), "{")
+ for i, method := range service.Methods {
+ if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
+ continue
+ }
+ g.P("{")
+ g.P("StreamName: ", strconv.Quote(method.GoName), ",")
+ g.P("Handler: ", handlerNames[i], ",")
+ if method.Desc.IsStreamingServer() {
+ g.P("ServerStreams: true,")
+ }
+ if method.Desc.IsStreamingClient() {
+ g.P("ClientStreams: true,")
+ }
+ g.P("},")
+ }
+ g.P("},")
+ g.P("Metadata: \"", file.Desc.Path(), "\",")
+ g.P("}")
+ g.P()
+}
+
+func clientSignature(g *protogen.GeneratedFile, method *protogen.Method) string {
+ s := method.GoName + "(ctx " + g.QualifiedGoIdent(ident("context.Context"))
+ if !method.Desc.IsStreamingClient() {
+ s += ", in *" + g.QualifiedGoIdent(method.InputType.GoIdent)
+ }
+ s += ", opts ..." + g.QualifiedGoIdent(ident("grpc.CallOption")) + ") ("
+ if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
+ s += "*" + g.QualifiedGoIdent(method.OutputType.GoIdent)
+ } else {
+ s += method.ParentService.GoName + "_" + method.GoName + "Client"
+ }
+ s += ", error)"
+ return s
+}
+
+func genClientMethod(gen *protogen.Plugin, file *fileInfo, g *protogen.GeneratedFile, method *protogen.Method, index int) {
+ service := method.ParentService
+ sname := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())
+
+ // TODO deprecation
+ g.P("func (c *", unexport(service.GoName), "Client) ", clientSignature(g, method), "{")
+ if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() {
+ g.P("out := new(", method.OutputType.GoIdent, ")")
+ g.P(`err := c.cc.Invoke(ctx, "`, sname, `", in, out, opts...)`)
+ g.P("if err != nil { return nil, err }")
+ g.P("return out, nil")
+ g.P("}")
+ g.P()
+ return
+ }
+ streamType := unexport(service.GoName) + method.GoName + "Client"
+ serviceDescVar := "_" + service.GoName + "_serviceDesc"
+ g.P("stream, err := c.cc.NewStream(ctx, &", serviceDescVar, ".Streams[", index, `], "`, sname, `", opts...)`)
+ g.P("if err != nil { return nil, err }")
+ g.P("x := &", streamType, "{stream}")
+ if !method.Desc.IsStreamingClient() {
+ g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }")
+ g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
+ }
+ g.P("return x, nil")
+ g.P("}")
+ g.P()
+
+ genSend := method.Desc.IsStreamingClient()
+ genRecv := method.Desc.IsStreamingServer()
+ genCloseAndRecv := !method.Desc.IsStreamingServer()
+
+ // Stream auxiliary types and methods.
+ g.P("type ", service.GoName, "_", method.GoName, "Client interface {")
+ if genSend {
+ g.P("Send(*", method.InputType.GoIdent, ") error")
+ }
+ if genRecv {
+ g.P("Recv() (*", method.OutputType.GoIdent, ", error)")
+ }
+ if genCloseAndRecv {
+ g.P("CloseAndRecv() (*", method.OutputType.GoIdent, ", error)")
+ }
+ g.P(ident("grpc.ClientStream"))
+ g.P("}")
+ g.P()
+
+ g.P("type ", streamType, " struct {")
+ g.P(ident("grpc.ClientStream"))
+ g.P("}")
+ g.P()
+
+ if genSend {
+ g.P("func (x *", streamType, ") Send(m *", method.InputType.GoIdent, ") error {")
+ g.P("return x.ClientStream.SendMsg(m)")
+ g.P("}")
+ g.P()
+ }
+ if genRecv {
+ g.P("func (x *", streamType, ") Recv() (*", method.OutputType.GoIdent, ", error) {")
+ g.P("m := new(", method.OutputType.GoIdent, ")")
+ g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
+ g.P("return m, nil")
+ g.P("}")
+ g.P()
+ }
+ if genCloseAndRecv {
+ g.P("func (x *", streamType, ") CloseAndRecv() (*", method.OutputType.GoIdent, ", error) {")
+ g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
+ g.P("m := new(", method.OutputType.GoIdent, ")")
+ g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
+ g.P("return m, nil")
+ g.P("}")
+ g.P()
+ }
+}
+
+func serverSignature(g *protogen.GeneratedFile, method *protogen.Method) string {
+ var reqArgs []string
+ ret := "error"
+ if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
+ reqArgs = append(reqArgs, g.QualifiedGoIdent(ident("context.Context")))
+ ret = "(*" + g.QualifiedGoIdent(method.OutputType.GoIdent) + ", error)"
+ }
+ if !method.Desc.IsStreamingClient() {
+ reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.InputType.GoIdent))
+ }
+ if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
+ reqArgs = append(reqArgs, method.ParentService.GoName+"_"+method.GoName+"Server")
+ }
+ return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
+}
+
+func genServerMethod(gen *protogen.Plugin, file *fileInfo, g *protogen.GeneratedFile, method *protogen.Method) string {
+ service := method.ParentService
+ hname := fmt.Sprintf("_%s_%s_Handler", service.GoName, method.GoName)
+
+ if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
+ g.P("func ", hname, "(srv interface{}, ctx ", ident("context.Context"), ", dec func(interface{}) error, interceptor ", ident("grpc.UnaryServerInterceptor"), ") (interface{}, error) {")
+ g.P("in := new(", method.InputType.GoIdent, ")")
+ g.P("if err := dec(in); err != nil { return nil, err }")
+ g.P("if interceptor == nil { return srv.(", service.GoName, "Server).", method.GoName, "(ctx, in) }")
+ g.P("info := &", ident("grpc.UnaryServerInfo"), "{")
+ g.P("Server: srv,")
+ g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())), ",")
+ g.P("}")
+ g.P("handler := func(ctx ", ident("context.Context"), ", req interface{}) (interface{}, error) {")
+ g.P("return srv.(", service.GoName, "Server).", method.GoName, "(ctx, req.(*", method.InputType.GoIdent, "))")
+ g.P("}")
+ g.P("return interceptor(ctx, in, info, handler)")
+ g.P("}")
+ g.P()
+ return hname
+ }
+ streamType := unexport(service.GoName) + method.GoName + "Server"
+ g.P("func ", hname, "(srv interface{}, stream ", ident("grpc.ServerStream"), ") error {")
+ if !method.Desc.IsStreamingClient() {
+ g.P("m := new(", method.InputType.GoIdent, ")")
+ g.P("if err := stream.RecvMsg(m); err != nil { return err }")
+ g.P("return srv.(", service.GoName, "Server).", method.GoName, "(m, &", streamType, "{stream})")
+ } else {
+ g.P("return srv.(", service.GoName, "Server).", method.GoName, "(&", streamType, "{stream})")
+ }
+ g.P("}")
+ g.P()
+
+ genSend := method.Desc.IsStreamingServer()
+ genSendAndClose := !method.Desc.IsStreamingServer()
+ genRecv := method.Desc.IsStreamingClient()
+
+ // Stream auxiliary types and methods.
+ g.P("type ", service.GoName, "_", method.GoName, "Server interface {")
+ if genSend {
+ g.P("Send(*", method.OutputType.GoIdent, ") error")
+ }
+ if genSendAndClose {
+ g.P("SendAndClose(*", method.OutputType.GoIdent, ") error")
+ }
+ if genRecv {
+ g.P("Recv() (*", method.InputType.GoIdent, ", error)")
+ }
+ g.P(ident("grpc.ServerStream"))
+ g.P("}")
+ g.P()
+
+ g.P("type ", streamType, " struct {")
+ g.P(ident("grpc.ServerStream"))
+ g.P("}")
+ g.P()
+
+ if genSend {
+ g.P("func (x *", streamType, ") Send(m *", method.OutputType.GoIdent, ") error {")
+ g.P("return x.ServerStream.SendMsg(m)")
+ g.P("}")
+ g.P()
+ }
+ if genSendAndClose {
+ g.P("func (x *", streamType, ") SendAndClose(m *", method.OutputType.GoIdent, ") error {")
+ g.P("return x.ServerStream.SendMsg(m)")
+ g.P("}")
+ g.P()
+ }
+ if genRecv {
+ g.P("func (x *", streamType, ") Recv() (*", method.InputType.GoIdent, ", error) {")
+ g.P("m := new(", method.InputType.GoIdent, ")")
+ g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
+ g.P("return m, nil")
+ g.P("}")
+ g.P()
+ }
+
+ return hname
+}
+
+var (
+ contextContext = protogen.GoIdent{
+ GoImportPath: "context",
+ GoName: "Context",
+ }
+ grpcUnaryServerInterceptor = protogen.GoIdent{}
+)
+
+func ident(name string) protogen.GoIdent {
+ idx := strings.LastIndex(name, ".")
+ return protogen.GoIdent{
+ GoImportPath: protogen.GoImportPath(name[:idx]),
+ GoName: name[idx+1:],
+ }
+}
+
+func genComment(g *protogen.GeneratedFile, file *fileInfo, path []int32) (hasComment bool) {
+ for _, loc := range file.locationMap[pathKey(path)] {
+ if loc.LeadingComments == nil {
+ continue
+ }
+ for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") {
+ hasComment = true
+ g.P("//", line)
+ }
+ break
+ }
+ return hasComment
+}
+
+// deprecationComment returns a standard deprecation comment if deprecated is true.
+func deprecationComment(deprecated bool) string {
+ if !deprecated {
+ return ""
+ }
+ return "// Deprecated: Do not use."
+}
+
+// pathKey converts a location path to a string suitable for use as a map key.
+func pathKey(path []int32) string {
+ var buf []byte
+ for i, x := range path {
+ if i != 0 {
+ buf = append(buf, ',')
+ }
+ buf = strconv.AppendInt(buf, int64(x), 10)
+ }
+ return string(buf)
+}
+
+func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
diff --git a/protogen/protogen.go b/protogen/protogen.go
index 03a2ff0..87e643c 100644
--- a/protogen/protogen.go
+++ b/protogen/protogen.go
@@ -349,6 +349,7 @@
Messages []*Message // top-level message declarations
Enums []*Enum // top-level enum declarations
Extensions []*Extension // top-level extension declarations
+ Services []*Service // top-level service declarations
Generate bool // true if we should generate code for this file

// GeneratedFilenamePrefix is used to construct filenames for generated
@@ -401,6 +402,9 @@
for i, extdescs := 0, desc.Extensions(); i < extdescs.Len(); i++ {
f.Extensions = append(f.Extensions, newField(gen, f, nil, extdescs.Get(i)))
}
+ for i, sdescs := 0, desc.Services(); i < sdescs.Len(); i++ {
+ f.Services = append(f.Services, newService(gen, f, sdescs.Get(i)))
+ }
for _, message := range f.Messages {
if err := message.init(gen); err != nil {
return nil, err
@@ -411,6 +415,13 @@
return nil, err
}
}
+ for _, service := range f.Services {
+ for _, method := range service.Methods {
+ if err := method.init(gen); err != nil {
+ return nil, err
+ }
+ }
+ }
return f, nil
}

@@ -723,6 +734,68 @@
return g
}

+// A Service describes a service.
+type Service struct {
+ Desc protoreflect.ServiceDescriptor
+
+ GoName string
+ Path []int32 // location path of this service
+ Methods []*Method // service method definitions
+}
+
+func newService(gen *Plugin, f *File, desc protoreflect.ServiceDescriptor) *Service {
+ service := &Service{
+ Desc: desc,
+ GoName: camelCase(string(desc.Name())),
+ Path: []int32{fileServiceField, int32(desc.Index())},
+ }
+ for i, mdescs := 0, desc.Methods(); i < mdescs.Len(); i++ {
+ service.Methods = append(service.Methods, newMethod(gen, f, service, mdescs.Get(i)))
+ }
+ return service
+}
+
+// A Method describes a method in a service.
+type Method struct {
+ Desc protoreflect.MethodDescriptor
+
+ GoName string
+ ParentService *Service
+ Path []int32 // location path of this method
+ InputType *Message
+ OutputType *Message
+}
+
+func newMethod(gen *Plugin, f *File, service *Service, desc protoreflect.MethodDescriptor) *Method {
+ method := &Method{
+ Desc: desc,
+ GoName: camelCase(string(desc.Name())),
+ ParentService: service,
+ Path: pathAppend(service.Path, serviceMethodField, int32(desc.Index())),
+ }
+ return method
+}
+
+func (method *Method) init(gen *Plugin) error {
+ desc := method.Desc
+
+ inName := desc.InputType().FullName()
+ in, ok := gen.messagesByName[inName]
+ if !ok {
+ return fmt.Errorf("method %v: no descriptor for type %v", desc.FullName(), inName)
+ }
+ method.InputType = in
+
+ outName := desc.OutputType().FullName()
+ out, ok := gen.messagesByName[outName]
+ if !ok {
+ return fmt.Errorf("method %v: no descriptor for type %v", desc.FullName(), outName)
+ }
+ method.OutputType = out
+
+ return nil
+}
+
// P prints a line to the generated output. It converts each parameter to a
// string following the same rules as fmt.Print. It never inserts spaces
// between parameters.
@@ -843,6 +916,7 @@
filePackageField = 2 // package
fileMessageField = 4 // message_type
fileEnumField = 5 // enum_type
+ fileServiceField = 6 // service
fileExtensionField = 7 // extension
// field numbers in DescriptorProto
messageFieldField = 2 // field
@@ -852,6 +926,9 @@
messageOneofField = 8 // oneof_decl
// field numbers in EnumDescriptorProto
enumValueField = 2 // value
+ // field numbers in ServiceDescriptorProto
+ serviceMethodField = 2 // method
+ serviceStreamField = 4 // stream
)

// pathAppend appends elements to a location path.
diff --git a/regenerate.bash b/regenerate.bash
index d6c8652..d22de1d 100755
--- a/regenerate.bash
+++ b/regenerate.bash
@@ -29,6 +29,6 @@
continue;
fi
echo "# $p"
- protoc -I$dir --go_out=paths=source_relative:$dir $p
+ protoc -I$dir --go_out=paths=source_relative,plugins=grpc:$dir $p
done
done

To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Gerrit-Change-Number: 137037
Gerrit-PatchSet: 1
Gerrit-Owner: Damien Neil <dn...@google.com>
Gerrit-MessageType: newchange

Joe Tsai (Gerrit)

unread,
Sep 24, 2018, 8:40:22 PM9/24/18
to Damien Neil, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

View Change

10 comments:

To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Gerrit-Change-Number: 137037
Gerrit-PatchSet: 1
Gerrit-Owner: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Tue, 25 Sep 2018 00:40:19 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

Damien Neil (Gerrit)

unread,
Sep 26, 2018, 2:23:32 PM9/26/18
to Joe Tsai, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Damien Neil uploaded patch set #2 to this change.

View Change

cmd/protoc-gen-grpc_go: add gRPC code generator

This is a straight translation of the v1 API gRPC "plugin" to protogen.


Add a protoc-gen-grpc_go command. The preferred way to generate gRPC
services is to invoke both plugins separately:

protoc --go_out=. --grpc_go_out=. foo.proto

When invoked in this fashion, the generators will produce separate
foo.pb.go and foo_grpc.pb.go files.

The old "--go_out=plugins=grpc:." form is also supported for the
moment, in which case we still generate just one combined foo.pb.go.

Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
---
A cmd/protoc-gen-go-grpc/golden_test.go
A cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
A cmd/protoc-gen-go-grpc/main.go
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
M cmd/protoc-gen-go/golden_test.go
M cmd/protoc-gen-go/internal_gengo/main.go
A internal/protogen/goldentest/goldentest.go
M protogen/protogen.go
M regenerate.bash
11 files changed, 1,090 insertions(+), 125 deletions(-)

To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Gerrit-Change-Number: 137037
Gerrit-PatchSet: 2
Gerrit-Owner: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-MessageType: newpatchset

Damien Neil (Gerrit)

unread,
Sep 26, 2018, 2:26:37 PM9/26/18
to Joe Tsai, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Damien Neil uploaded patch set #3 to this change.

View Change

cmd/protoc-gen-grpc_go: add gRPC code generator

This is a straight translation of the v1 API gRPC "plugin" to protogen.


Add a protoc-gen-grpc_go command. The preferred way to generate gRPC
services is to invoke both plugins separately:

protoc --go_out=. --grpc_go_out=. foo.proto

When invoked in this fashion, the generators will produce separate
foo.pb.go and foo_grpc.pb.go files.

The old "--go_out=plugins=grpc:." form is also supported for the
moment, in which case we still generate just one combined foo.pb.go.

Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
---
A cmd/protoc-gen-go-grpc/golden_test.go
A cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
A cmd/protoc-gen-go-grpc/main.go
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
M cmd/protoc-gen-go/golden_test.go
M cmd/protoc-gen-go/internal_gengo/main.go
A internal/protogen/goldentest/goldentest.go
M protogen/protogen.go
M regenerate.bash
11 files changed, 1,094 insertions(+), 125 deletions(-)

To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Gerrit-Change-Number: 137037
Gerrit-PatchSet: 3

Damien Neil (Gerrit)

unread,
Sep 26, 2018, 2:26:45 PM9/26/18
to goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

View Change

10 comments:

  • Commit Message:

    • Patch Set #1, Line 7: protoc-gen-grpc_go

      Eww... mixing both "-" and "_"... […]

      Weird. I have no idea why they aren't following the protoc plugin naming convention (--FOO_out=... invokes protoc-gen-FOO).

      To make things even more confusing (which is why I was confused), the instructions for some languages obscure this. For example, the Objective C tutorial tells you to run "protoc --objc_out=... --objcgrpc_out=..." without mentioning the need for an additional --plugin=protoc-gen-objcgrpc=... flag.

      I'll go with protoc-gen-go-grpc. It's only consistent with Java, but at least it's consistent with *something* and I think it's clearly superior to something that requires an additional --plugin flag to protoc. Do note that it means the protoc flag is --go-grpc_out; we either get mixed -/_ in the binary name or the flag name.

    • More broadly speaking... […]

      How does this sound?

      • We don't support go_out=plugins= in the api-v2 protoc-gen-go.
      • We support go_out=plugins= with a big warning in the api-v1 protoc-gen-go.
      • We make other cleanups in the api-v2 protoc-gen-go, like defaulting to paths=source_relative.
      • We put this binary here for now.
      • When we get closer to release, we look into moving it to the gRPC repo.
    • Done

    • Looking around it seems like idiomatic usage is something like: […]

      Ew.

      Several langauges (Python, ObjC) have tutorials that recommend --lang_out=. --gprc_lang_out=.; I think it's okay to default to that approach for Go unless the Go gRPC team asks otherwise.

  • File cmd/protoc-gen-go/internal_gengo/main.go:

    • Done

    • ident(... […]

      Done

    • The import path for the grpc package is "google.golang. […]

      Done

To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Gerrit-Change-Number: 137037
Gerrit-PatchSet: 1
Gerrit-Owner: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Wed, 26 Sep 2018 18:26:43 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Joe Tsai <thebroke...@gmail.com>
Gerrit-MessageType: comment

Joe Tsai (Gerrit)

unread,
Sep 27, 2018, 3:14:52 PM9/27/18
to Damien Neil, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

Do we need to update test.bash to run the golden tests in non-race mode?
Do we need to add protoc-gen-go-grpc to .gitignore?

Patch set 3:Code-Review +1

View Change

5 comments:

    • The old "--go_out=plugins=grpc:." form is also supported for the
      moment, in which case we still generate just one combined foo.pb.go.

    • Stale?

To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Gerrit-Change-Number: 137037
Gerrit-PatchSet: 3
Gerrit-Owner: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-Comment-Date: Thu, 27 Sep 2018 19:14:50 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Comment-In-Reply-To: Joe Tsai <thebroke...@gmail.com>
Comment-In-Reply-To: Damien Neil <dn...@google.com>
Gerrit-MessageType: comment

Damien Neil (Gerrit)

unread,
Sep 27, 2018, 3:27:35 PM9/27/18
to Joe Tsai, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Damien Neil uploaded patch set #4 to this change.

View Change

cmd/protoc-gen-grpc_go: add gRPC code generator

This is a straight translation of the v1 API gRPC "plugin" to protogen.


Add a protoc-gen-grpc_go command. The preferred way to generate gRPC
services is to invoke both plugins separately:

protoc --go_out=. --grpc_go_out=. foo.proto

When invoked in this fashion, the generators will produce separate
foo.pb.go and foo_grpc.pb.go files.

The old "--go_out=plugins=grpc:." form is also supported for the
moment, in which case we still generate just one combined foo.pb.go.

Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
---
M .gitignore

A cmd/protoc-gen-go-grpc/golden_test.go
A cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
A cmd/protoc-gen-go-grpc/main.go
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
A cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
M cmd/protoc-gen-go/golden_test.go
M cmd/protoc-gen-go/internal_gengo/main.go
A internal/protogen/goldentest/goldentest.go
M protogen/protogen.go
M regenerate.bash
12 files changed, 1,095 insertions(+), 125 deletions(-)

To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: protobuf
Gerrit-Branch: master
Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Gerrit-Change-Number: 137037
Gerrit-PatchSet: 4
Gerrit-Owner: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
Gerrit-MessageType: newpatchset

Damien Neil (Gerrit)

unread,
Sep 27, 2018, 3:27:37 PM9/27/18
to goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

Do we need to update test.bash to run the golden tests in non-race mode?

The golden tests have a "// +build !race" constraint, so no.

Do we need to add protoc-gen-go-grpc to .gitignore?

Added.

View Change

    To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: protobuf
    Gerrit-Branch: master
    Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
    Gerrit-Change-Number: 137037
    Gerrit-PatchSet: 3
    Gerrit-Owner: Damien Neil <dn...@google.com>
    Gerrit-Reviewer: Damien Neil <dn...@google.com>
    Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
    Gerrit-Comment-Date: Thu, 27 Sep 2018 19:27:35 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: No
    Gerrit-MessageType: comment

    Joe Tsai (Gerrit)

    unread,
    Sep 27, 2018, 3:29:24 PM9/27/18
    to Damien Neil, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

    Patch Set 3:

    Do we need to update test.bash to run the golden tests in non-race mode?

    The golden tests have a "// +build !race" constraint, so no.

    Do we need to add protoc-gen-go-grpc to .gitignore?

    Added.

    Maybe I'm missing something, but test.bash currently only runs the tests with "-race" enabled. So wouldn't that imply that we're not running the golden tests?

    View Change

      To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: protobuf
      Gerrit-Branch: master
      Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
      Gerrit-Change-Number: 137037
      Gerrit-PatchSet: 4
      Gerrit-Owner: Damien Neil <dn...@google.com>
      Gerrit-Reviewer: Damien Neil <dn...@google.com>
      Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
      Gerrit-Comment-Date: Thu, 27 Sep 2018 19:29:23 +0000

      Damien Neil (Gerrit)

      unread,
      Sep 27, 2018, 3:29:34 PM9/27/18
      to Joe Tsai, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

      Damien Neil uploaded patch set #5 to this change.

      View Change

      cmd/protoc-gen-grpc_go: add gRPC code generator

      This is a straight translation of the v1 API gRPC "plugin" to protogen.


      Add a protoc-gen-grpc_go command. The preferred way to generate gRPC
      services is to invoke both plugins separately:

      protoc --go_out=. --grpc_go_out=. foo.proto

      When invoked in this fashion, the generators will produce separate
      foo.pb.go and foo_grpc.pb.go files.

      The old "--go_out=plugins=grpc:." form is also supported for the
      moment, in which case we still generate just one combined foo.pb.go.

      Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
      ---
      M .gitignore
      A cmd/protoc-gen-go-grpc/golden_test.go
      A cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
      A cmd/protoc-gen-go-grpc/main.go
      A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
      A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
      A cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
      M cmd/protoc-gen-go/golden_test.go
      M cmd/protoc-gen-go/internal_gengo/main.go
      A internal/protogen/goldentest/goldentest.go
      M protogen/protogen.go
      M regenerate.bash
      M test.bash
      13 files changed, 1,096 insertions(+), 125 deletions(-)

      To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: protobuf
      Gerrit-Branch: master
      Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
      Gerrit-Change-Number: 137037
      Gerrit-PatchSet: 5
      Gerrit-Owner: Damien Neil <dn...@google.com>
      Gerrit-Reviewer: Damien Neil <dn...@google.com>
      Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
      Gerrit-MessageType: newpatchset

      Damien Neil (Gerrit)

      unread,
      Sep 27, 2018, 3:29:40 PM9/27/18
      to goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

      The golden tests have a "// +build !race" constraint, so no.

      Oh, wait, I didn't realize test.bash didn't run anything in non-race mode. Added.

      View Change

        To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: protobuf
        Gerrit-Branch: master
        Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
        Gerrit-Change-Number: 137037
        Gerrit-PatchSet: 4
        Gerrit-Owner: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
        Gerrit-Comment-Date: Thu, 27 Sep 2018 19:29:39 +0000

        Joe Tsai (Gerrit)

        unread,
        Sep 27, 2018, 3:31:36 PM9/27/18
        to Damien Neil, goph...@pubsubhelper.golang.org, Joe Tsai, golang-co...@googlegroups.com

        Don't forget to fix the CL description before submission.

        Patch set 5:Code-Review +2

        View Change

          To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: protobuf
          Gerrit-Branch: master
          Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
          Gerrit-Change-Number: 137037
          Gerrit-PatchSet: 5
          Gerrit-Owner: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
          Gerrit-Comment-Date: Thu, 27 Sep 2018 19:31:35 +0000
          Gerrit-HasComments: No
          Gerrit-Has-Labels: Yes
          Gerrit-MessageType: comment

          Damien Neil (Gerrit)

          unread,
          Sep 27, 2018, 3:32:26 PM9/27/18
          to Joe Tsai, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

          Damien Neil uploaded patch set #6 to this change.

          View Change

          cmd/protoc-gen-go-grpc: add gRPC code generator

          This is a straight translation of the v1 API gRPC "plugin" to protogen.

          Add a protoc-gen-go-grpc command. The preferred way to generate gRPC

          services is to invoke both plugins separately:

            protoc --go_out=. --go-grpc_out=. foo.proto


          When invoked in this fashion, the generators will produce separate
          foo.pb.go and foo_grpc.pb.go files.

          Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
          ---
          M .gitignore
          A cmd/protoc-gen-go-grpc/golden_test.go
          A cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
          A cmd/protoc-gen-go-grpc/main.go
          A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
          A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
          A cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
          M cmd/protoc-gen-go/golden_test.go
          M cmd/protoc-gen-go/internal_gengo/main.go
          A internal/protogen/goldentest/goldentest.go
          M protogen/protogen.go
          M regenerate.bash
          M test.bash
          13 files changed, 1,096 insertions(+), 125 deletions(-)

          To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: protobuf
          Gerrit-Branch: master
          Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
          Gerrit-Change-Number: 137037
          Gerrit-PatchSet: 6
          Gerrit-Owner: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
          Gerrit-MessageType: newpatchset

          Damien Neil (Gerrit)

          unread,
          Sep 27, 2018, 3:32:36 PM9/27/18
          to goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Joe Tsai, golang-co...@googlegroups.com

          Damien Neil merged this change.

          View Change

          Approvals: Joe Tsai: Looks good to me, approved
          cmd/protoc-gen-go-grpc: add gRPC code generator

          This is a straight translation of the v1 API gRPC "plugin" to protogen.

          Add a protoc-gen-go-grpc command. The preferred way to generate gRPC

          services is to invoke both plugins separately:

            protoc --go_out=. --go-grpc_out=. foo.proto


          When invoked in this fashion, the generators will produce separate
          foo.pb.go and foo_grpc.pb.go files.

          Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
          Reviewed-on: https://go-review.googlesource.com/137037
          Reviewed-by: Joe Tsai <thebroke...@gmail.com>

          ---
          M .gitignore
          A cmd/protoc-gen-go-grpc/golden_test.go
          A cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
          A cmd/protoc-gen-go-grpc/main.go
          A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
          A cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
          A cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
          M cmd/protoc-gen-go/golden_test.go
          M cmd/protoc-gen-go/internal_gengo/main.go
          A internal/protogen/goldentest/goldentest.go
          M protogen/protogen.go
          M regenerate.bash
          M test.bash
          13 files changed, 1,096 insertions(+), 125 deletions(-)

          diff --git a/.gitignore b/.gitignore
          index 5c986d1..9c6aa35 100644
          --- a/.gitignore
          +++ b/.gitignore
          @@ -1,3 +1,4 @@
          .cache
          vendor
          cmd/protoc-gen-go/protoc-gen-go
          +cmd/protoc-gen-go-grpc/protoc-gen-go-grpc
          diff --git a/cmd/protoc-gen-go-grpc/golden_test.go b/cmd/protoc-gen-go-grpc/golden_test.go
          new file mode 100644
          index 0000000..e7efb3d
          --- /dev/null
          +++ b/cmd/protoc-gen-go-grpc/golden_test.go

          @@ -0,0 +1,25 @@
          +// Copyright 2018 The Go Authors. All rights reserved.
          +// Use of this source code is governed by a BSD-style
          +// license that can be found in the LICENSE file.
          +
          +// +build !race
          +

          +package main
          +
          +import (
          +	"flag"
          + "testing"
          +
          + "github.com/golang/protobuf/v2/internal/protogen/goldentest"
          +)
          +
          +// Set --regenerate to regenerate the golden files.
          +var regenerate = flag.Bool("regenerate", false, "regenerate golden files")
          +
          +func init() {
          + goldentest.Plugin(main)
          +}
          +
          +func TestGolden(t *testing.T) {
          + goldentest.Run(t, *regenerate)
          +}
          diff --git a/cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go b/cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
          new file mode 100644
          index 0000000..f8cf4b3
          --- /dev/null
          +++ b/cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
          @@ -0,0 +1,409 @@

          +// Copyright 2018 The Go Authors. All rights reserved.
          +// Use of this source code is governed by a BSD-style
          +// license that can be found in the LICENSE file.
          +
          +// Package internal_gengogrpc is internal to the protobuf module.
          +package internal_gengogrpc

          +
          +import (
          + "fmt"
          + "strconv"
          + "strings"
          +
          + descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
          + "github.com/golang/protobuf/v2/protogen"
          +)
          +
          +type fileInfo struct {
          + *protogen.File
          + locationMap map[string][]*descpb.SourceCodeInfo_Location
          +}
          +
          +// GenerateFile generates a _grpc.pb.go file containing gRPC service definitions.
          +func GenerateFile(gen *protogen.Plugin, f *protogen.File) {
          + if len(f.Services) == 0 {
          + return
          + }
          + filename := f.GeneratedFilenamePrefix + "_grpc.pb.go"
          + g := gen.NewGeneratedFile(filename, f.GoImportPath)
          +	g.P("// Code generated by protoc-gen-go-grpc. DO NOT EDIT.")

          + g.P()
          + g.P("package ", f.GoPackageName)
          + g.P()
          + GenerateFileContent(gen, f, g)
          +}
          +
          +// GenerateFileContent generates the gRPC service definitions, excluding the package statement.
          +func GenerateFileContent(gen *protogen.Plugin, f *protogen.File, g *protogen.GeneratedFile) {
          + if len(f.Services) == 0 {
          + return
          + }
          + file := &fileInfo{
          + File: f,
          + locationMap: make(map[string][]*descpb.SourceCodeInfo_Location),
          + }
          + for _, loc := range file.Proto.GetSourceCodeInfo().GetLocation() {
          + key := pathKey(loc.Path)
          + file.locationMap[key] = append(file.locationMap[key], loc)
          + }
          +
          + // TODO: Remove this. We don't need to include these references any more.
          + g.P("// Reference imports to suppress errors if they are not otherwise used.")
          +	g.P("var _ ", ident("context.Context"))
          + g.P("var _ ", ident("grpc.ClientConn"))

          + g.P()
          +
          + g.P("// This is a compile-time assertion to ensure that this generated file")
          + g.P("// is compatible with the grpc package it is being compiled against.")
          +	g.P("const _ = ", ident("grpc.SupportPackageIsVersion4"))
          +var packages = map[string]protogen.GoImportPath{
          + "context": "golang.org/x/net/context",
          + "grpc": "google.golang.org/grpc",
          +}

          +
          +func ident(name string) protogen.GoIdent {
          + idx := strings.LastIndex(name, ".")
          + return protogen.GoIdent{
          +		GoImportPath: packages[name[:idx]],
          diff --git a/cmd/protoc-gen-go-grpc/main.go b/cmd/protoc-gen-go-grpc/main.go
          new file mode 100644
          index 0000000..238a63a
          --- /dev/null
          +++ b/cmd/protoc-gen-go-grpc/main.go

          @@ -0,0 +1,24 @@
          +// Copyright 2018 The Go Authors. All rights reserved.
          +// Use of this source code is governed by a BSD-style
          +// license that can be found in the LICENSE file.
          +
          +// The protoc-gen-go-grpc binary is a protoc plugin to generate Go gRPC

          +// service definitions.
          +package main
          +
          +import (
          +	"github.com/golang/protobuf/v2/cmd/protoc-gen-go-grpc/internal_gengogrpc"

          + "github.com/golang/protobuf/v2/protogen"
          +)
          +
          +func main() {
          + protogen.Run(nil, func(gen *protogen.Plugin) error {
          + for _, file := range gen.Files {
          + if !file.Generate {
          + continue
          + }
          +			internal_gengogrpc.GenerateFile(gen, file)

          + }
          + return nil
          + })
          +}
          diff --git a/cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go b/cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
          new file mode 100644
          index 0000000..d18855e
          --- /dev/null
          +++ b/cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
          @@ -0,0 +1,108 @@

          +// Code generated by protoc-gen-go. DO NOT EDIT.
          +// source: grpc/grpc.proto
          +
          +package grpc
          +
          +import (
          +func init() { proto.RegisterFile("grpc/grpc.proto", fileDescriptor_81ea47a3f88c2082) }
          +
          +var fileDescriptor_81ea47a3f88c2082 = []byte{
          + // 211 bytes of a gzipped FileDescriptorProto
          + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4f, 0x2f, 0x2a, 0x48,
          + 0xd6, 0x07, 0x11, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xc2, 0xe9, 0xf9, 0x60, 0x06, 0x84,
          + 0x9b, 0xac, 0x07, 0x92, 0x52, 0xe2, 0xe4, 0x62, 0x0f, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51,
          + 0xe2, 0xe2, 0xe2, 0x08, 0x4a, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x35, 0xda, 0xc8, 0xc4, 0xc5,
          + 0x12, 0x92, 0x5a, 0x5c, 0x22, 0xe4, 0xc1, 0xc5, 0x19, 0x9a, 0x97, 0x58, 0x54, 0xe9, 0x9c, 0x98,
          + 0x93, 0x23, 0x24, 0xa3, 0x87, 0xc5, 0x08, 0x3d, 0xa8, 0x7e, 0x29, 0x59, 0x1c, 0xb2, 0x10, 0x23,
          + 0x85, 0xbc, 0xb9, 0xb8, 0x5c, 0xf2, 0xcb, 0xf3, 0x8a, 0x4b, 0x8a, 0x52, 0x13, 0x73, 0x29, 0x32,
          + 0xca, 0x80, 0x51, 0xc8, 0x93, 0x8b, 0x23, 0xb4, 0x80, 0x0a, 0x46, 0x69, 0x30, 0x0a, 0xb9, 0x73,
          + 0xb1, 0x38, 0x65, 0xa6, 0x64, 0x52, 0x68, 0x8c, 0x01, 0xa3, 0x93, 0x7d, 0x94, 0x6d, 0x7a, 0x66,
          + 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x3e,
          + 0x58, 0x75, 0x52, 0x69, 0x9a, 0x7e, 0x99, 0x91, 0x7e, 0x72, 0x6e, 0x0a, 0x84, 0x9f, 0xac, 0x9b,
          + 0x9e, 0x9a, 0xa7, 0x9b, 0x9e, 0xaf, 0x5f, 0x92, 0x5a, 0x5c, 0x92, 0x92, 0x58, 0x92, 0x08, 0x8e,
          + 0xa6, 0x24, 0x36, 0xb0, 0xa4, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x29, 0xd5, 0xc4, 0xd0, 0xba,
          + 0x01, 0x00, 0x00,
          +}
          diff --git a/cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto b/cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto

          new file mode 100644
          index 0000000..c484ebe
          --- /dev/null
          +++ b/cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
          diff --git a/cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go b/cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
          new file mode 100644
          index 0000000..9db0a3a
          --- /dev/null
          +++ b/cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
          @@ -0,0 +1,279 @@
          +// Code generated by protoc-gen-go-grpc. DO NOT EDIT.

          +
          +package grpc
          +
          +import (
          +	context "golang.org/x/net/context"
          + grpc "google.golang.org/grpc"

          +)
          +
          +// Reference imports to suppress errors if they are not otherwise used.
          diff --git a/cmd/protoc-gen-go/golden_test.go b/cmd/protoc-gen-go/golden_test.go
          index 80e2d8c..e7efb3d 100644
          --- a/cmd/protoc-gen-go/golden_test.go
          +++ b/cmd/protoc-gen-go/golden_test.go
          @@ -2,129 +2,24 @@

          // Use of this source code is governed by a BSD-style
           // license that can be found in the LICENSE file.

          +// +build !race
          +
          package main

          import (
          - "bytes"
          "flag"
          - "io/ioutil"
          - "os"
          - "os/exec"
          - "path/filepath"
          - "regexp"
          - "strings"
          "testing"
          +
          + "github.com/golang/protobuf/v2/internal/protogen/goldentest"
          )

          // Set --regenerate to regenerate the golden files.
          var regenerate = flag.Bool("regenerate", false, "regenerate golden files")

          -// When the environment variable RUN_AS_PROTOC_GEN_GO is set, we skip running
          -// tests and instead act as protoc-gen-go. This allows the test binary to
          -// pass itself to protoc.
          func init() {
          - if os.Getenv("RUN_AS_PROTOC_GEN_GO") != "" {
          - main()
          - os.Exit(0)
          - }
          + goldentest.Plugin(main)
          }

          func TestGolden(t *testing.T) {
          - workdir, err := ioutil.TempDir("", "proto-test")
          - if err != nil {
          - t.Fatal(err)
          - }
          - defer os.RemoveAll(workdir)
          -
          - // Find all the proto files we need to compile. We assume that each directory
          - // contains the files for a single package.
          - packages := map[string][]string{}
          - err = filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
          - if !strings.HasSuffix(path, ".proto") {
          - return nil
          - }
          - dir := filepath.Dir(path)
          - packages[dir] = append(packages[dir], path)
          - return nil
          - })
          - if err != nil {
          - t.Fatal(err)
          - }
          -
          - // Compile each package, using this binary as protoc-gen-go.
          - for _, sources := range packages {
          - args := []string{"-Itestdata", "--go_out=plugins=grpc,paths=source_relative:" + workdir}
          - args = append(args, sources...)
          - protoc(t, args)
          - }
          -
          - // Compare each generated file to the golden version.
          - filepath.Walk(workdir, func(genPath string, info os.FileInfo, _ error) error {
          - if info.IsDir() {
          - return nil
          - }
          -
          - // For each generated file, figure out the path to the corresponding
          - // golden file in the testdata directory.
          - relPath, err := filepath.Rel(workdir, genPath)
          - if err != nil {
          - t.Errorf("filepath.Rel(%q, %q): %v", workdir, genPath, err)
          - return nil
          - }
          - if filepath.SplitList(relPath)[0] == ".." {
          - t.Errorf("generated file %q is not relative to %q", genPath, workdir)
          - }
          - goldenPath := filepath.Join("testdata", relPath)
          -
          - got, err := ioutil.ReadFile(genPath)
          - if err != nil {
          - t.Error(err)
          - return nil
          - }
          - if *regenerate {
          - // If --regenerate set, just rewrite the golden files.
          - err := ioutil.WriteFile(goldenPath, got, 0666)
          - if err != nil {
          - t.Error(err)
          - }
          - return nil
          - }
          -
          - want, err := ioutil.ReadFile(goldenPath)
          - if err != nil {
          - t.Error(err)
          - return nil
          - }
          -
          - want = fdescRE.ReplaceAll(want, nil)
          - got = fdescRE.ReplaceAll(got, nil)
          - if bytes.Equal(got, want) {
          - return nil
          - }
          -
          - cmd := exec.Command("diff", "-u", goldenPath, genPath)
          - out, _ := cmd.CombinedOutput()
          - t.Errorf("golden file differs: %v\n%v", relPath, string(out))
          - return nil
          - })
          -}
          -
          -var fdescRE = regexp.MustCompile(`(?ms)^var fileDescriptor.*}`)
          -
          -func protoc(t *testing.T, args []string) {
          - cmd := exec.Command("protoc", "--plugin=protoc-gen-go="+os.Args[0])
          - cmd.Args = append(cmd.Args, args...)
          - // We set the RUN_AS_PROTOC_GEN_GO environment variable to indicate that
          - // the subprocess should act as a proto compiler rather than a test.
          - cmd.Env = append(os.Environ(), "RUN_AS_PROTOC_GEN_GO=1")
          - out, err := cmd.CombinedOutput()
          - if len(out) > 0 || err != nil {
          - t.Log("RUNNING: ", strings.Join(cmd.Args, " "))
          - }
          - if len(out) > 0 {
          - t.Log(string(out))
          - }
          - if err != nil {
          - t.Fatalf("protoc: %v", err)
          - }
          + goldentest.Run(t, *regenerate)
          }
          diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
          index 518f427..a337c5f 100644
          --- a/cmd/protoc-gen-go/internal_gengo/main.go
          +++ b/cmd/protoc-gen-go/internal_gengo/main.go
          @@ -10,6 +10,7 @@
          "compress/gzip"
          "crypto/sha256"
          "encoding/hex"
          + "errors"
          "flag"
          "fmt"
          "math"
          @@ -33,12 +34,14 @@


          func Main() {
          var flags flag.FlagSet
          - // TODO: Decide what to do for backwards compatibility with plugins=grpc.
          - flags.String("plugins", "", "")
          +	plugins := flags.String("plugins", "", "deprecated option")

          opts := &protogen.Options{
          ParamFunc: flags.Set,
          }
          protogen.Run(opts, func(gen *protogen.Plugin) error {
          +		if *plugins != "" {
          + return errors.New("protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC")

          + }
          for _, f := range gen.Files {
          if !f.Generate {
          continue
          @@ -138,7 +141,6 @@
          }

          genInitFunction(gen, g, f)
          -
          genFileDescriptor(gen, g, f)
          }

          diff --git a/internal/protogen/goldentest/goldentest.go b/internal/protogen/goldentest/goldentest.go
          new file mode 100644
          index 0000000..82468b7
          --- /dev/null
          +++ b/internal/protogen/goldentest/goldentest.go
          @@ -0,0 +1,130 @@

          +// Copyright 2018 The Go Authors. All rights reserved.
          +// Use of this source code is governed by a BSD-style
          +// license that can be found in the LICENSE file.
          +
          +// Package goldentest compares the output of a protoc plugin to golden files.
          +package goldentest
          +
          +import (
          + "bytes"
          + "io/ioutil"
          + "os"
          + "os/exec"
          + "path/filepath"
          + "regexp"
          + "strings"
          + "testing"
          +)
          +
          +// Plugin should be called at init time with a function that acts as a
          +// protoc plugin.
          +func Plugin(f func()) {
          + // When the environment variable RUN_AS_PROTOC_PLUGIN is set, we skip
          + // running tests and instead act as protoc-gen-go. This allows the
          + // test binary to pass itself to protoc.
          + if os.Getenv("RUN_AS_PROTOC_PLUGIN") != "" {
          + f()
          + os.Exit(0)
          + }
          +}
          +
          +// Run executes golden tests.
          +func Run(t *testing.T, regenerate bool) {
          + workdir, err := ioutil.TempDir("", "proto-test")

          + if err != nil {
          +		t.Fatal(err)
          + }
          + defer os.RemoveAll(workdir)
          +
          + // Find all the proto files we need to compile. We assume that each directory
          + // contains the files for a single package.
          + packages := map[string][]string{}
          + err = filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
          + if !strings.HasSuffix(path, ".proto") {
          + return nil
          + }
          + dir := filepath.Dir(path)
          + packages[dir] = append(packages[dir], path)

          + return nil
          + })
          +	if err != nil {
          +		t.Fatal(err)
          + }
          +
          + // Compile each package, using this binary as protoc-gen-go.
          + for _, sources := range packages {
          + args := []string{"-Itestdata", "--go_out=paths=source_relative:" + workdir}
          + args = append(args, sources...)
          + protoc(t, args)
          + }
          +
          + // Compare each generated file to the golden version.
          + filepath.Walk(workdir, func(genPath string, info os.FileInfo, _ error) error {
          + if info.IsDir() {

          + return nil
          + }
          +
          +		// For each generated file, figure out the path to the corresponding
          + // golden file in the testdata directory.
          + relPath, err := filepath.Rel(workdir, genPath)

          + if err != nil {
          +			t.Errorf("filepath.Rel(%q, %q): %v", workdir, genPath, err)
          + return nil
          + }
          + if filepath.SplitList(relPath)[0] == ".." {
          + t.Errorf("generated file %q is not relative to %q", genPath, workdir)
          + }
          + goldenPath := filepath.Join("testdata", relPath)
          +
          + got, err := ioutil.ReadFile(genPath)

          + if err != nil {
          +			t.Error(err)
          + return nil
          + }
          + if regenerate {
          + // If --regenerate set, just rewrite the golden files.
          + err := ioutil.WriteFile(goldenPath, got, 0666)

          + if err != nil {
          +				t.Error(err)

          + }
          + return nil
          + }
          +
          +		want, err := ioutil.ReadFile(goldenPath)

          + if err != nil {
          +			t.Error(err)

          + return nil
          + }
          +
          +		want = fdescRE.ReplaceAll(want, nil)
          + got = fdescRE.ReplaceAll(got, nil)
          + if bytes.Equal(got, want) {

          + return nil
          + }
          +
          +		cmd := exec.Command("diff", "-u", goldenPath, genPath)
          + out, _ := cmd.CombinedOutput()
          + t.Errorf("golden file differs: %v\n%v", relPath, string(out))

          + return nil
          + })
          +}
          +
          +var fdescRE = regexp.MustCompile(`(?ms)^var fileDescriptor.*}`)
          +
          +func protoc(t *testing.T, args []string) {
          + cmd := exec.Command("protoc", "--plugin=protoc-gen-go="+os.Args[0])
          + cmd.Args = append(cmd.Args, args...)
          + // We set the RUN_AS_PROTOC_PLUGIN environment variable to indicate that
          + // the subprocess should act as a proto compiler rather than a test.
          + cmd.Env = append(os.Environ(), "RUN_AS_PROTOC_PLUGIN=1")
          + out, err := cmd.CombinedOutput()
          + if len(out) > 0 || err != nil {
          + t.Log("RUNNING: ", strings.Join(cmd.Args, " "))
          + }
          + if len(out) > 0 {
          + t.Log(string(out))
          + }

          + if err != nil {
          +		t.Fatalf("protoc: %v", err)
          + }
          +}
          index d6c8652..af3e487 100755
          --- a/regenerate.bash
          +++ b/regenerate.bash
          @@ -11,24 +11,19 @@
          mkdir -p $tmpdir/bin
          PATH=$tmpdir/bin:$PATH
          GOBIN=$tmpdir/bin go install ./cmd/protoc-gen-go
          -
          -# Public imports require at least Go 1.9.
          -supportTypeAliases=""
          -if go list -f '{{context.ReleaseTags}}' runtime | grep -q go1.9; then
          - supportTypeAliases=1
          -fi
          +GOBIN=$tmpdir/bin go install ./cmd/protoc-gen-go-grpc

          # Generate various test protos.
          PROTO_DIRS=(
          cmd/protoc-gen-go/testdata
          + cmd/protoc-gen-go-grpc/testdata
          )
          for dir in ${PROTO_DIRS[@]}; do
          for p in `find $dir -name "*.proto"`; do
          - if [[ $p == */import_public/* && ! $supportTypeAliases ]]; then
          - echo "# $p (skipped)"
          - continue;
          - fi

          echo "# $p"
          - protoc -I$dir --go_out=paths=source_relative:$dir $p
          +    protoc -I$dir \
          + --go_out=paths=source_relative:$dir \
          + --go-grpc_out=paths=source_relative:$dir \
          + $p
          done
          done
          diff --git a/test.bash b/test.bash
          index 8d25906..7c625a5 100755
          --- a/test.bash
          +++ b/test.bash
          @@ -85,6 +85,7 @@
          }

          go build ./...
          + go test ./...
          go test -race ./...
          go test -race -tags purego ./...
          go test -race -tags proto1_legacy ./...

          To view, visit change 137037. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: protobuf
          Gerrit-Branch: master
          Gerrit-Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
          Gerrit-Change-Number: 137037
          Gerrit-PatchSet: 7
          Gerrit-Owner: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Damien Neil <dn...@google.com>
          Gerrit-Reviewer: Joe Tsai <thebroke...@gmail.com>
          Gerrit-MessageType: merged
          Reply all
          Reply to author
          Forward
          0 new messages