mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2024-12-26 21:24:22 +00:00
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 <thebrokentoaster@gmail.com>
This commit is contained in:
parent
0d1a064764
commit
2dc6718b59
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.cache
|
||||
vendor
|
||||
cmd/protoc-gen-go/protoc-gen-go
|
||||
cmd/protoc-gen-go-grpc/protoc-gen-go-grpc
|
||||
|
25
cmd/protoc-gen-go-grpc/golden_test.go
Normal file
25
cmd/protoc-gen-go-grpc/golden_test.go
Normal file
@ -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)
|
||||
}
|
409
cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
Normal file
409
cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
Normal file
@ -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"))
|
||||
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 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]],
|
||||
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:] }
|
24
cmd/protoc-gen-go-grpc/main.go
Normal file
24
cmd/protoc-gen-go-grpc/main.go
Normal file
@ -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
|
||||
})
|
||||
}
|
108
cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
vendored
Normal file
108
cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: grpc/grpc.proto
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
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")
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
25
cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
vendored
Normal file
25
cmd/protoc-gen-go-grpc/testdata/grpc/grpc.proto
vendored
Normal file
@ -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);
|
||||
}
|
279
cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
vendored
Normal file
279
cmd/protoc-gen-go-grpc/testdata/grpc/grpc_grpc.pb.go
vendored
Normal file
@ -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.
|
||||
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",
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
@ -33,12 +34,14 @@ const protoPackage = "github.com/golang/protobuf/proto"
|
||||
|
||||
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 @@ func genFile(gen *protogen.Plugin, file *protogen.File) {
|
||||
}
|
||||
|
||||
genInitFunction(gen, g, f)
|
||||
|
||||
genFileDescriptor(gen, g, f)
|
||||
}
|
||||
|
||||
|
130
internal/protogen/goldentest/goldentest.go
Normal file
130
internal/protogen/goldentest/goldentest.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
@ -349,6 +349,7 @@ type File struct {
|
||||
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 @@ func newFile(gen *Plugin, p *descpb.FileDescriptorProto, packageName GoPackageNa
|
||||
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 @@ func newFile(gen *Plugin, p *descpb.FileDescriptorProto, packageName GoPackageNa
|
||||
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 @@ func (gen *Plugin) NewGeneratedFile(filename string, goImportPath GoImportPath)
|
||||
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 @@ const (
|
||||
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 @@ const (
|
||||
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.
|
||||
|
@ -11,24 +11,19 @@ trap 'rm -rf $tmpdir' EXIT
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user