snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
28
cmd/protoc-gen-go-grpc/README.md
Normal file
28
cmd/protoc-gen-go-grpc/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# protoc-gen-go-grpc
|
||||
|
||||
This tool generates Go language bindings of `service`s in protobuf definition
|
||||
files for gRPC. For usage information, please see our [quick start
|
||||
guide](https://grpc.io/docs/languages/go/quickstart/).
|
||||
|
||||
## Future-proofing services
|
||||
|
||||
By default, to register services using the methods generated by this tool, the
|
||||
service implementations must embed the corresponding
|
||||
`Unimplemented<ServiceName>Server` for future compatibility. This is a behavior
|
||||
change from the grpc code generator previously included with `protoc-gen-go`.
|
||||
To restore this behavior, set the option `require_unimplemented_servers=false`.
|
||||
E.g.:
|
||||
|
||||
```sh
|
||||
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false[,other options...] \
|
||||
```
|
||||
|
||||
Note that this is not recommended, and the option is only provided to restore
|
||||
backward compatibility with previously-generated code.
|
||||
|
||||
When embedding the `Unimplemented<ServiceName>Server` in a struct that
|
||||
implements the service, it should be embedded by _value_ instead of as a
|
||||
_pointer_. If it is embedded as a pointer, it must be assigned to a valid,
|
||||
non-nil pointer or else unimplemented methods would panic when called. This is
|
||||
tested at service registration time, and will lead to a panic in
|
||||
`Register<ServiceName>Server` if it is not embedded properly.
|
||||
15
cmd/protoc-gen-go-grpc/go.mod
Normal file
15
cmd/protoc-gen-go-grpc/go.mod
Normal file
@@ -0,0 +1,15 @@
|
||||
module google.golang.org/grpc/cmd/protoc-gen-go-grpc
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
google.golang.org/grpc v1.82.1
|
||||
google.golang.org/protobuf v1.36.12
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect
|
||||
)
|
||||
38
cmd/protoc-gen-go-grpc/go.sum
Normal file
38
cmd/protoc-gen-go-grpc/go.sum
Normal file
@@ -0,0 +1,38 @@
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 h1:qEHAMpSaUhtD0p3NbEEI83HwNGFxEwaSJ1G9PLnCBZE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
|
||||
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
513
cmd/protoc-gen-go-grpc/grpc.go
Normal file
513
cmd/protoc-gen-go-grpc/grpc.go
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2020 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/compiler/protogen"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
)
|
||||
|
||||
const (
|
||||
contextPackage = protogen.GoImportPath("context")
|
||||
grpcPackage = protogen.GoImportPath("google.golang.org/grpc")
|
||||
codesPackage = protogen.GoImportPath("google.golang.org/grpc/codes")
|
||||
statusPackage = protogen.GoImportPath("google.golang.org/grpc/status")
|
||||
)
|
||||
|
||||
type serviceGenerateHelperInterface interface {
|
||||
formatFullMethodSymbol(service *protogen.Service, method *protogen.Method) string
|
||||
genFullMethods(g *protogen.GeneratedFile, service *protogen.Service)
|
||||
generateClientStruct(g *protogen.GeneratedFile, clientName string)
|
||||
generateNewClientDefinitions(g *protogen.GeneratedFile, service *protogen.Service, clientName string)
|
||||
generateUnimplementedServerType(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service)
|
||||
generateServerFunctions(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, serverType string, serviceDescVar string)
|
||||
formatHandlerFuncName(service *protogen.Service, hname string) string
|
||||
}
|
||||
|
||||
type serviceGenerateHelper struct{}
|
||||
|
||||
func (serviceGenerateHelper) formatFullMethodSymbol(service *protogen.Service, method *protogen.Method) string {
|
||||
return fmt.Sprintf("%s_%s_FullMethodName", service.GoName, method.GoName)
|
||||
}
|
||||
|
||||
func (serviceGenerateHelper) genFullMethods(g *protogen.GeneratedFile, service *protogen.Service) {
|
||||
if len(service.Methods) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
g.P("const (")
|
||||
for _, method := range service.Methods {
|
||||
fmSymbol := helper.formatFullMethodSymbol(service, method)
|
||||
fmName := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())
|
||||
g.P(fmSymbol, ` = "`, fmName, `"`)
|
||||
}
|
||||
g.P(")")
|
||||
g.P()
|
||||
}
|
||||
|
||||
func (serviceGenerateHelper) generateClientStruct(g *protogen.GeneratedFile, clientName string) {
|
||||
g.P("type ", unexport(clientName), " struct {")
|
||||
g.P("cc ", grpcPackage.Ident("ClientConnInterface"))
|
||||
g.P("}")
|
||||
g.P()
|
||||
}
|
||||
|
||||
func (serviceGenerateHelper) generateNewClientDefinitions(g *protogen.GeneratedFile, _ *protogen.Service, clientName string) {
|
||||
g.P("return &", unexport(clientName), "{cc}")
|
||||
}
|
||||
|
||||
func (serviceGenerateHelper) generateUnimplementedServerType(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) {
|
||||
serverType := service.GoName + "Server"
|
||||
mustOrShould := "must"
|
||||
if !*requireUnimplemented {
|
||||
mustOrShould = "should"
|
||||
}
|
||||
// Server Unimplemented struct for forward compatibility.
|
||||
g.P("// Unimplemented", serverType, " ", mustOrShould, " be embedded to have")
|
||||
g.P("// forward compatible implementations.")
|
||||
g.P("//")
|
||||
g.P("// NOTE: this should be embedded by value instead of pointer to avoid a nil")
|
||||
g.P("// pointer dereference when methods are called.")
|
||||
g.P("type Unimplemented", serverType, " struct {}")
|
||||
g.P()
|
||||
for _, method := range service.Methods {
|
||||
nilArg := ""
|
||||
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||
nilArg = "nil,"
|
||||
}
|
||||
g.P("func (Unimplemented", serverType, ") ", serverSignature(g, method), "{")
|
||||
g.P("return ", nilArg, statusPackage.Ident("Error"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`)
|
||||
g.P("}")
|
||||
}
|
||||
if *requireUnimplemented {
|
||||
g.P("func (Unimplemented", serverType, ") mustEmbedUnimplemented", serverType, "() {}")
|
||||
}
|
||||
g.P("func (Unimplemented", serverType, ") testEmbeddedByValue() {}")
|
||||
g.P()
|
||||
}
|
||||
|
||||
func (serviceGenerateHelper) generateServerFunctions(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, serverType string, serviceDescVar string) {
|
||||
// Server handler implementations.
|
||||
handlerNames := make([]string, 0, len(service.Methods))
|
||||
for _, method := range service.Methods {
|
||||
hname := genServerMethod(gen, file, g, method, func(hname string) string {
|
||||
return hname
|
||||
})
|
||||
handlerNames = append(handlerNames, hname)
|
||||
}
|
||||
genServiceDesc(file, g, serviceDescVar, serverType, service, handlerNames)
|
||||
}
|
||||
|
||||
func (serviceGenerateHelper) formatHandlerFuncName(_ *protogen.Service, hname string) string {
|
||||
return hname
|
||||
}
|
||||
|
||||
var helper serviceGenerateHelperInterface = serviceGenerateHelper{}
|
||||
|
||||
// FileDescriptorProto.package field number
|
||||
const fileDescriptorProtoPackageFieldNumber = 2
|
||||
|
||||
// FileDescriptorProto.syntax field number
|
||||
const fileDescriptorProtoSyntaxFieldNumber = 12
|
||||
|
||||
// generateFile generates a _grpc.pb.go file containing gRPC service definitions.
|
||||
func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
|
||||
if len(file.Services) == 0 {
|
||||
return nil
|
||||
}
|
||||
filename := file.GeneratedFilenamePrefix + "_grpc.pb.go"
|
||||
g := gen.NewGeneratedFile(filename, file.GoImportPath)
|
||||
// Attach all comments associated with the syntax field.
|
||||
genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoSyntaxFieldNumber}))
|
||||
g.P("// Code generated by protoc-gen-go-grpc. DO NOT EDIT.")
|
||||
g.P("// versions:")
|
||||
g.P("// - protoc-gen-go-grpc v", version)
|
||||
g.P("// - protoc ", protocVersion(gen))
|
||||
if file.Proto.GetOptions().GetDeprecated() {
|
||||
g.P("// ", file.Desc.Path(), " is a deprecated file.")
|
||||
} else {
|
||||
g.P("// source: ", file.Desc.Path())
|
||||
}
|
||||
g.P()
|
||||
// Attach all comments associated with the package field.
|
||||
genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoPackageFieldNumber}))
|
||||
g.P("package ", file.GoPackageName)
|
||||
g.P()
|
||||
generateFileContent(gen, file, g)
|
||||
return g
|
||||
}
|
||||
|
||||
func protocVersion(gen *protogen.Plugin) string {
|
||||
v := gen.Request.GetCompilerVersion()
|
||||
if v == nil {
|
||||
return "(unknown)"
|
||||
}
|
||||
var suffix string
|
||||
if s := v.GetSuffix(); s != "" {
|
||||
suffix = "-" + s
|
||||
}
|
||||
return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix)
|
||||
}
|
||||
|
||||
// generateFileContent generates the gRPC service definitions, excluding the package statement.
|
||||
func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) {
|
||||
if len(file.Services) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
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("// Requires gRPC-Go v1.64.0 or later.")
|
||||
g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion9"))
|
||||
g.P()
|
||||
for _, service := range file.Services {
|
||||
genService(gen, file, g, service)
|
||||
}
|
||||
}
|
||||
|
||||
// genServiceComments copies the comments from the RPC proto definitions
|
||||
// to the corresponding generated interface file.
|
||||
func genServiceComments(g *protogen.GeneratedFile, service *protogen.Service) {
|
||||
if service.Comments.Leading != "" {
|
||||
// Add empty comment line to attach this service's comments to
|
||||
// the godoc comments previously output for all services.
|
||||
g.P("//")
|
||||
g.P(strings.TrimSpace(service.Comments.Leading.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) {
|
||||
// Full methods constants.
|
||||
helper.genFullMethods(g, service)
|
||||
|
||||
// Client interface.
|
||||
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://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.")
|
||||
|
||||
// Copy comments from proto file.
|
||||
genServiceComments(g, service)
|
||||
|
||||
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||
g.P("//")
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
g.AnnotateSymbol(clientName, protogen.Annotation{Location: service.Location})
|
||||
g.P("type ", clientName, " interface {")
|
||||
for _, method := range service.Methods {
|
||||
g.AnnotateSymbol(clientName+"."+method.GoName, protogen.Annotation{Location: method.Location})
|
||||
if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() {
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
g.P(method.Comments.Leading,
|
||||
clientSignature(g, method))
|
||||
}
|
||||
g.P("}")
|
||||
g.P()
|
||||
|
||||
// Client structure.
|
||||
helper.generateClientStruct(g, clientName)
|
||||
|
||||
// NewClient factory.
|
||||
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
g.P("func New", clientName, " (cc ", grpcPackage.Ident("ClientConnInterface"), ") ", clientName, " {")
|
||||
helper.generateNewClientDefinitions(g, service, clientName)
|
||||
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++
|
||||
}
|
||||
}
|
||||
|
||||
mustOrShould := "must"
|
||||
if !*requireUnimplemented {
|
||||
mustOrShould = "should"
|
||||
}
|
||||
|
||||
// Server interface.
|
||||
serverType := service.GoName + "Server"
|
||||
g.P("// ", serverType, " is the server API for ", service.GoName, " service.")
|
||||
g.P("// All implementations ", mustOrShould, " embed Unimplemented", serverType)
|
||||
g.P("// for forward compatibility.")
|
||||
|
||||
// Copy comments from proto file.
|
||||
genServiceComments(g, service)
|
||||
|
||||
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||
g.P("//")
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
g.AnnotateSymbol(serverType, protogen.Annotation{Location: service.Location})
|
||||
g.P("type ", serverType, " interface {")
|
||||
for _, method := range service.Methods {
|
||||
g.AnnotateSymbol(serverType+"."+method.GoName, protogen.Annotation{Location: method.Location})
|
||||
if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() {
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
g.P(method.Comments.Leading,
|
||||
serverSignature(g, method))
|
||||
}
|
||||
if *requireUnimplemented {
|
||||
g.P("mustEmbedUnimplemented", serverType, "()")
|
||||
}
|
||||
g.P("}")
|
||||
g.P()
|
||||
|
||||
// Server Unimplemented struct for forward compatibility.
|
||||
helper.generateUnimplementedServerType(gen, file, g, service)
|
||||
|
||||
// Unsafe Server interface to opt-out of forward compatibility.
|
||||
g.P("// Unsafe", serverType, " may be embedded to opt out of forward compatibility for this service.")
|
||||
g.P("// Use of this interface is not recommended, as added methods to ", serverType, " will")
|
||||
g.P("// result in compilation errors.")
|
||||
g.P("type Unsafe", serverType, " interface {")
|
||||
g.P("mustEmbedUnimplemented", serverType, "()")
|
||||
g.P("}")
|
||||
|
||||
// Server registration.
|
||||
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
serviceDescVar := service.GoName + "_ServiceDesc"
|
||||
g.P("func Register", service.GoName, "Server(s ", grpcPackage.Ident("ServiceRegistrar"), ", srv ", serverType, ") {")
|
||||
g.P("// If the following call panics, it indicates Unimplemented", serverType, " was")
|
||||
g.P("// embedded by pointer and is nil. This will cause panics if an")
|
||||
g.P("// unimplemented method is ever invoked, so we test this at initialization")
|
||||
g.P("// time to prevent it from happening at runtime later due to I/O.")
|
||||
g.P("if t, ok := srv.(interface { testEmbeddedByValue() }); ok {")
|
||||
g.P("t.testEmbeddedByValue()")
|
||||
g.P("}")
|
||||
g.P("s.RegisterService(&", serviceDescVar, `, srv)`)
|
||||
g.P("}")
|
||||
g.P()
|
||||
|
||||
helper.generateServerFunctions(gen, file, g, service, serverType, serviceDescVar)
|
||||
}
|
||||
|
||||
func clientSignature(g *protogen.GeneratedFile, method *protogen.Method) string {
|
||||
s := method.GoName + "(ctx " + g.QualifiedGoIdent(contextPackage.Ident("Context"))
|
||||
if !method.Desc.IsStreamingClient() {
|
||||
s += ", in *" + g.QualifiedGoIdent(method.Input.GoIdent)
|
||||
}
|
||||
s += ", opts ..." + g.QualifiedGoIdent(grpcPackage.Ident("CallOption")) + ") ("
|
||||
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||
s += "*" + g.QualifiedGoIdent(method.Output.GoIdent)
|
||||
} else {
|
||||
s += clientStreamInterface(g, method)
|
||||
}
|
||||
s += ", error)"
|
||||
return s
|
||||
}
|
||||
|
||||
func clientStreamInterface(g *protogen.GeneratedFile, method *protogen.Method) string {
|
||||
typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
|
||||
if method.Desc.IsStreamingClient() && method.Desc.IsStreamingServer() {
|
||||
return g.QualifiedGoIdent(grpcPackage.Ident("BidiStreamingClient")) + "[" + typeParam + "]"
|
||||
}
|
||||
if method.Desc.IsStreamingClient() {
|
||||
return g.QualifiedGoIdent(grpcPackage.Ident("ClientStreamingClient")) + "[" + typeParam + "]"
|
||||
}
|
||||
return g.QualifiedGoIdent(grpcPackage.Ident("ServerStreamingClient")) + "[" + g.QualifiedGoIdent(method.Output.GoIdent) + "]"
|
||||
}
|
||||
|
||||
func genClientMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, index int) {
|
||||
service := method.Parent
|
||||
fmSymbol := helper.formatFullMethodSymbol(service, method)
|
||||
|
||||
if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() {
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
g.P("func (c *", unexport(service.GoName), "Client) ", clientSignature(g, method), "{")
|
||||
g.P("cOpts := append([]", grpcPackage.Ident("CallOption"), "{", grpcPackage.Ident("StaticMethod()"), "}, opts...)")
|
||||
if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() {
|
||||
g.P("out := new(", method.Output.GoIdent, ")")
|
||||
g.P(`err := c.cc.Invoke(ctx, `, fmSymbol, `, in, out, cOpts...)`)
|
||||
g.P("if err != nil { return nil, err }")
|
||||
g.P("return out, nil")
|
||||
g.P("}")
|
||||
g.P()
|
||||
return
|
||||
}
|
||||
|
||||
typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
|
||||
streamImpl := g.QualifiedGoIdent(grpcPackage.Ident("GenericClientStream")) + "[" + typeParam + "]"
|
||||
serviceDescVar := service.GoName + "_ServiceDesc"
|
||||
g.P("stream, err := c.cc.NewStream(ctx, &", serviceDescVar, ".Streams[", index, `], `, fmSymbol, `, cOpts...)`)
|
||||
g.P("if err != nil { return nil, err }")
|
||||
g.P("x := &", streamImpl, "{ClientStream: 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()
|
||||
|
||||
// Auxiliary types aliases, for backwards compatibility.
|
||||
g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.")
|
||||
g.P("type ", service.GoName, "_", method.GoName, "Client = ", clientStreamInterface(g, method))
|
||||
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(contextPackage.Ident("Context")))
|
||||
ret = "(*" + g.QualifiedGoIdent(method.Output.GoIdent) + ", error)"
|
||||
}
|
||||
if !method.Desc.IsStreamingClient() {
|
||||
reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.Input.GoIdent))
|
||||
}
|
||||
if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
|
||||
reqArgs = append(reqArgs, serverStreamInterface(g, method))
|
||||
}
|
||||
return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
|
||||
}
|
||||
|
||||
func genServiceDesc(file *protogen.File, g *protogen.GeneratedFile, serviceDescVar string, serverType string, service *protogen.Service, handlerNames []string) {
|
||||
// Service descriptor.
|
||||
g.P("// ", serviceDescVar, " is the ", grpcPackage.Ident("ServiceDesc"), " for ", service.GoName, " service.")
|
||||
g.P("// It's only intended for direct use with ", grpcPackage.Ident("RegisterService"), ",")
|
||||
g.P("// and not to be introspected or modified (even as a copy)")
|
||||
g.P("var ", serviceDescVar, " = ", grpcPackage.Ident("ServiceDesc"), " {")
|
||||
g.P("ServiceName: ", strconv.Quote(string(service.Desc.FullName())), ",")
|
||||
g.P("HandlerType: (*", serverType, ")(nil),")
|
||||
g.P("Methods: []", grpcPackage.Ident("MethodDesc"), "{")
|
||||
for i, method := range service.Methods {
|
||||
if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
|
||||
continue
|
||||
}
|
||||
g.P("{")
|
||||
g.P("MethodName: ", strconv.Quote(string(method.Desc.Name())), ",")
|
||||
g.P("Handler: ", handlerNames[i], ",")
|
||||
g.P("},")
|
||||
}
|
||||
g.P("},")
|
||||
g.P("Streams: []", grpcPackage.Ident("StreamDesc"), "{")
|
||||
for i, method := range service.Methods {
|
||||
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||
continue
|
||||
}
|
||||
g.P("{")
|
||||
g.P("StreamName: ", strconv.Quote(string(method.Desc.Name())), ",")
|
||||
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 serverStreamInterface(g *protogen.GeneratedFile, method *protogen.Method) string {
|
||||
typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
|
||||
if method.Desc.IsStreamingClient() && method.Desc.IsStreamingServer() {
|
||||
return g.QualifiedGoIdent(grpcPackage.Ident("BidiStreamingServer")) + "[" + typeParam + "]"
|
||||
}
|
||||
if method.Desc.IsStreamingClient() {
|
||||
return g.QualifiedGoIdent(grpcPackage.Ident("ClientStreamingServer")) + "[" + typeParam + "]"
|
||||
}
|
||||
|
||||
return g.QualifiedGoIdent(grpcPackage.Ident("ServerStreamingServer")) + "[" + g.QualifiedGoIdent(method.Output.GoIdent) + "]"
|
||||
}
|
||||
|
||||
func genServerMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, hnameFuncNameFormatter func(string) string) string {
|
||||
service := method.Parent
|
||||
hname := fmt.Sprintf("_%s_%s_Handler", service.GoName, method.GoName)
|
||||
|
||||
if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() {
|
||||
g.P("func ", hnameFuncNameFormatter(hname), "(srv interface{}, ctx ", contextPackage.Ident("Context"), ", dec func(interface{}) error, interceptor ", grpcPackage.Ident("UnaryServerInterceptor"), ") (interface{}, error) {")
|
||||
g.P("in := new(", method.Input.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 := &", grpcPackage.Ident("UnaryServerInfo"), "{")
|
||||
g.P("Server: srv,")
|
||||
fmSymbol := helper.formatFullMethodSymbol(service, method)
|
||||
g.P("FullMethod: ", fmSymbol, ",")
|
||||
g.P("}")
|
||||
g.P("handler := func(ctx ", contextPackage.Ident("Context"), ", req interface{}) (interface{}, error) {")
|
||||
g.P("return srv.(", service.GoName, "Server).", method.GoName, "(ctx, req.(*", method.Input.GoIdent, "))")
|
||||
g.P("}")
|
||||
g.P("return interceptor(ctx, in, info, handler)")
|
||||
g.P("}")
|
||||
g.P()
|
||||
return hname
|
||||
}
|
||||
|
||||
typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent)
|
||||
streamImpl := g.QualifiedGoIdent(grpcPackage.Ident("GenericServerStream")) + "[" + typeParam + "]"
|
||||
|
||||
g.P("func ", hnameFuncNameFormatter(hname), "(srv interface{}, stream ", grpcPackage.Ident("ServerStream"), ") error {")
|
||||
if !method.Desc.IsStreamingClient() {
|
||||
g.P("m := new(", method.Input.GoIdent, ")")
|
||||
g.P("if err := stream.RecvMsg(m); err != nil { return err }")
|
||||
g.P("return srv.(", service.GoName, "Server).", method.GoName, "(m, &", streamImpl, "{ServerStream: stream})")
|
||||
} else {
|
||||
g.P("return srv.(", service.GoName, "Server).", method.GoName, "(&", streamImpl, "{ServerStream: stream})")
|
||||
}
|
||||
g.P("}")
|
||||
g.P()
|
||||
|
||||
// Auxiliary types aliases, for backwards compatibility.
|
||||
g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.")
|
||||
g.P("type ", service.GoName, "_", method.GoName, "Server = ", serverStreamInterface(g, method))
|
||||
g.P()
|
||||
return hname
|
||||
}
|
||||
|
||||
func genLeadingComments(g *protogen.GeneratedFile, loc protoreflect.SourceLocation) {
|
||||
for _, s := range loc.LeadingDetachedComments {
|
||||
g.P(protogen.Comments(s))
|
||||
g.P()
|
||||
}
|
||||
if s := loc.LeadingComments; s != "" {
|
||||
g.P(protogen.Comments(s))
|
||||
g.P()
|
||||
}
|
||||
}
|
||||
|
||||
const deprecationComment = "// Deprecated: Do not use."
|
||||
|
||||
func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
|
||||
74
cmd/protoc-gen-go-grpc/main.go
Normal file
74
cmd/protoc-gen-go-grpc/main.go
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2020 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// protoc-gen-go-grpc is a plugin for the Google protocol buffer compiler to
|
||||
// generate Go code. Install it by building this program and making it
|
||||
// accessible within your PATH with the name:
|
||||
//
|
||||
// protoc-gen-go-grpc
|
||||
//
|
||||
// The 'go-grpc' suffix becomes part of the argument for the protocol compiler,
|
||||
// such that it can be invoked as:
|
||||
//
|
||||
// protoc --go-grpc_out=. path/to/file.proto
|
||||
//
|
||||
// This generates Go service definitions for the protocol buffer defined by
|
||||
// file.proto. With that input, the output will be written to:
|
||||
//
|
||||
// path/to/file_grpc.pb.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/compiler/protogen"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
"google.golang.org/protobuf/types/pluginpb"
|
||||
)
|
||||
|
||||
const version = "1.6.2"
|
||||
|
||||
var requireUnimplemented *bool
|
||||
|
||||
func main() {
|
||||
showVersion := flag.Bool("version", false, "print the version and exit")
|
||||
flag.Parse()
|
||||
if *showVersion {
|
||||
fmt.Printf("protoc-gen-go-grpc %v\n", version)
|
||||
return
|
||||
}
|
||||
|
||||
var flags flag.FlagSet
|
||||
requireUnimplemented = flags.Bool("require_unimplemented_servers", true, "set to false to match legacy behavior")
|
||||
|
||||
protogen.Options{
|
||||
ParamFunc: flags.Set,
|
||||
}.Run(func(gen *protogen.Plugin) error {
|
||||
gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) | uint64(pluginpb.CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS)
|
||||
gen.SupportedEditionsMinimum = descriptorpb.Edition_EDITION_PROTO2
|
||||
gen.SupportedEditionsMaximum = descriptorpb.Edition_EDITION_2024
|
||||
for _, f := range gen.Files {
|
||||
if !f.Generate {
|
||||
continue
|
||||
}
|
||||
generateFile(gen, f)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
50
cmd/protoc-gen-go-grpc/protoc-gen-go-grpc_test.sh
Executable file
50
cmd/protoc-gen-go-grpc/protoc-gen-go-grpc_test.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# Copyright 2024 gRPC authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Uncomment to enable debugging.
|
||||
# set -x
|
||||
|
||||
WORKDIR="$(dirname $0)"
|
||||
TEMPDIR=$(mktemp -d)
|
||||
|
||||
trap "rm -rf ${TEMPDIR}" EXIT
|
||||
|
||||
# Build protoc-gen-go-grpc binary and add to $PATH.
|
||||
pushd "${WORKDIR}"
|
||||
go build -o "${TEMPDIR}" .
|
||||
PATH="${TEMPDIR}:${PATH}"
|
||||
popd
|
||||
|
||||
protoc \
|
||||
--go-grpc_out="${TEMPDIR}" \
|
||||
--go-grpc_opt=paths=source_relative \
|
||||
"examples/route_guide/routeguide/route_guide.proto"
|
||||
|
||||
GOLDENFILE="examples/route_guide/routeguide/route_guide_grpc.pb.go"
|
||||
GENFILE="${TEMPDIR}/examples/route_guide/routeguide/route_guide_grpc.pb.go"
|
||||
|
||||
# diff is piped to [[ $? == 1 ]] to avoid exiting on diff but exit on error
|
||||
# (like if the file was not found). See man diff for more info.
|
||||
DIFF=$(diff "${GOLDENFILE}" "${GENFILE}" || [[ $? == 1 ]])
|
||||
if [[ -n "${DIFF}" ]]; then
|
||||
echo -e "ERROR: Generated file differs from golden file:\n${DIFF}"
|
||||
echo -e "If you have made recent changes to protoc-gen-go-grpc," \
|
||||
"please regenerate the golden files by running:" \
|
||||
"\n\t go generate google.golang.org/grpc/..." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo SUCCESS
|
||||
47
cmd/protoc-gen-go-grpc/unimpl_test.go
Normal file
47
cmd/protoc-gen-go-grpc/unimpl_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2024 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
type unimplEmbeddedByPointer struct {
|
||||
*testgrpc.UnimplementedTestServiceServer
|
||||
}
|
||||
|
||||
type unimplEmbeddedByValue struct {
|
||||
testgrpc.UnimplementedTestServiceServer
|
||||
}
|
||||
|
||||
func TestUnimplementedEmbedding(t *testing.T) {
|
||||
// Embedded by value, this should succeed.
|
||||
testgrpc.RegisterTestServiceServer(grpc.NewServer(), &unimplEmbeddedByValue{})
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Fatalf("Expected panic; received none")
|
||||
}
|
||||
}()
|
||||
|
||||
// Embedded by pointer, this should panic.
|
||||
testgrpc.RegisterTestServiceServer(grpc.NewServer(), &unimplEmbeddedByPointer{})
|
||||
}
|
||||
Reference in New Issue
Block a user