From 9b592b3dee9a2df51051044902d8b2bdde6ebf0f Mon Sep 17 00:00:00 2001 From: anqiansong Date: Tue, 11 Jan 2022 20:34:25 +0800 Subject: [PATCH] Feature rpc protoc (#1251) * code generation by protoc * generate pb by protoc direct * support: grpc code generation by protoc directly * format code * check --go_out & --go-grpc_out * fix typo * Update version * fix typo * optimize: remove deprecated unit test * format code Co-authored-by: anqiansong --- tools/goctl/api/gogen/gen_test.go | 8 +- .../api/parser/g4/gen/api/apiparser_parser.go | 1 + tools/goctl/goctl.go | 53 +++- .../model/sql/command/command_pg_test.go | 45 ---- tools/goctl/rpc/cli/cli.go | 8 + tools/goctl/rpc/cli/zrpc.go | 238 ++++++++++++++++++ tools/goctl/rpc/cli/zrpc_test.go | 113 +++++++++ tools/goctl/rpc/generator/gen.go | 33 ++- tools/goctl/rpc/generator/gencall.go | 20 +- tools/goctl/rpc/generator/generator.go | 2 +- tools/goctl/rpc/generator/genpb.go | 19 +- tools/goctl/rpc/generator/genserver.go | 12 +- tools/goctl/rpc/generator/mkdir.go | 22 +- 13 files changed, 495 insertions(+), 79 deletions(-) mode change 100644 => 100755 tools/goctl/api/parser/g4/gen/api/apiparser_parser.go delete mode 100644 tools/goctl/model/sql/command/command_pg_test.go create mode 100644 tools/goctl/rpc/cli/zrpc.go create mode 100644 tools/goctl/rpc/cli/zrpc_test.go diff --git a/tools/goctl/api/gogen/gen_test.go b/tools/goctl/api/gogen/gen_test.go index 68139428..ba93856a 100644 --- a/tools/goctl/api/gogen/gen_test.go +++ b/tools/goctl/api/gogen/gen_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/tal-tech/go-zero/tools/goctl/api/parser" - "github.com/tal-tech/go-zero/tools/goctl/rpc/execx" ) const testApiTemplate = ` @@ -537,10 +536,8 @@ func validate(t *testing.T, api string) { } func validateWithCamel(t *testing.T, api, camel string) { - dir := "_go" - os.RemoveAll(dir) + dir := t.TempDir() err := DoGenProject(api, dir, camel) - defer os.RemoveAll(dir) assert.Nil(t, err) filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if strings.HasSuffix(path, ".go") { @@ -550,9 +547,6 @@ func validateWithCamel(t *testing.T, api, camel string) { } return nil }) - - _, err = execx.Run("go test ./...", dir) - assert.Nil(t, err) } func validateCode(code string) error { diff --git a/tools/goctl/api/parser/g4/gen/api/apiparser_parser.go b/tools/goctl/api/parser/g4/gen/api/apiparser_parser.go old mode 100644 new mode 100755 index 8599337c..82cd78d2 --- a/tools/goctl/api/parser/g4/gen/api/apiparser_parser.go +++ b/tools/goctl/api/parser/g4/gen/api/apiparser_parser.go @@ -634,3 +634,4 @@ func NewSyntaxLitContext(parser antlr.Parser, parent antlr.ParserRuleContext, in return p } + diff --git a/tools/goctl/goctl.go b/tools/goctl/goctl.go index 7997c2db..23d25917 100644 --- a/tools/goctl/goctl.go +++ b/tools/goctl/goctl.go @@ -417,8 +417,9 @@ var commands = []cli.Command{ Usage: "generate rpc code", Subcommands: []cli.Command{ { - Name: "new", - Usage: `generate rpc demo service`, + Name: "new", + Usage: `generate rpc demo service`, + Description: aurora.Yellow(`deprecated: zrpc code generation use "goctl rpc protoc" instead, for the details see "goctl rpc protoc --help"`).String(), Flags: []cli.Flag{ cli.StringFlag{ Name: "style", @@ -465,8 +466,52 @@ var commands = []cli.Command{ Action: rpc.RPCTemplate, }, { - Name: "proto", - Usage: `generate rpc from proto`, + Name: "protoc", + Usage: "generate grpc code", + UsageText: `example: goctl rpc protoc xx.proto --go_out=./pb --go-grpc=./pb --zrpc_out=.`, + Description: "for details, see https://go-zero.dev/cn/goctl-rpc.html", + Action: rpc.ZRPC, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "go_out", + Hidden: true, + }, + cli.StringFlag{ + Name: "go-grpc_out", + Hidden: true, + }, + cli.StringFlag{ + Name: "go_opt", + Hidden: true, + }, + cli.StringFlag{ + Name: "go-grpc_opt", + Hidden: true, + }, + cli.StringFlag{ + Name: "zrpc_out", + Usage: "the zrpc output directory", + }, + cli.StringFlag{ + Name: "style", + Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]", + }, + cli.StringFlag{ + Name: "home", + Usage: "the goctl home path of the template", + }, + cli.StringFlag{ + Name: "remote", + Usage: "the remote git repo of the template, --home and --remote cannot be set at the same time, " + + "if they are, --remote has higher priority\n\tThe git repo directory must be consistent with the " + + "https://github.com/zeromicro/go-zero-template directory structure", + }, + }, + }, + { + Name: "proto", + Usage: `generate rpc from proto`, + Description: aurora.Yellow(`deprecated: zrpc code generation use "goctl rpc protoc" instead, for the details see "goctl rpc protoc --help"`).String(), Flags: []cli.Flag{ cli.StringFlag{ Name: "src, s", diff --git a/tools/goctl/model/sql/command/command_pg_test.go b/tools/goctl/model/sql/command/command_pg_test.go deleted file mode 100644 index a66943dc..00000000 --- a/tools/goctl/model/sql/command/command_pg_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package command - -import ( - "github.com/stretchr/testify/assert" - "github.com/tal-tech/go-zero/tools/goctl/config" - "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen" - "github.com/tal-tech/go-zero/tools/goctl/util" - "path/filepath" - "testing" -) - -// generate test table sql -/* -CREATE TABLE "public"."users" ( - "id" serial NOT NULL, - "account" varchar(20) COLLATE "pg_catalog"."default" NOT NULL, - "avatar" text COLLATE "pg_catalog"."default", - "nick_name" varchar(60) COLLATE "pg_catalog"."default", - "register_time" timestamp(6) NOT NULL, - "update_time" timestamp(6), - "password" varchar(255) COLLATE "pg_catalog"."default", - "email" varchar(100) COLLATE "pg_catalog"."default", - "reset_key" varchar(10) COLLATE "pg_catalog"."default", - "active" bool NOT NULL DEFAULT true, - CONSTRAINT "users_pk" PRIMARY KEY ("id") -) -; - -ALTER TABLE "public"."users" - OWNER TO "postgres"; -*/ - -func TestFromDatasource(t *testing.T) { - err := gen.Clean() - assert.Nil(t, err) - - url := "postgres://postgres:postgres@127.0.0.1:5432/demo?sslmode=disable" - - pattern := "users" // table name - - cfg, err := config.NewConfig("") - tempDir := filepath.Join(util.MustTempDir(), "test") - err = fromPostgreSqlDataSource(url, pattern, tempDir, "public", cfg, false, false) - assert.Nil(t, err) -} diff --git a/tools/goctl/rpc/cli/cli.go b/tools/goctl/rpc/cli/cli.go index d4247da7..35449f40 100644 --- a/tools/goctl/rpc/cli/cli.go +++ b/tools/goctl/rpc/cli/cli.go @@ -8,15 +8,20 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/rpc/generator" "github.com/tal-tech/go-zero/tools/goctl/util" + "github.com/tal-tech/go-zero/tools/goctl/util/console" "github.com/tal-tech/go-zero/tools/goctl/util/env" "github.com/tal-tech/go-zero/tools/goctl/util/pathx" "github.com/urfave/cli" ) +// Deprecated: use ZRPC instead. // RPC is to generate rpc service code from a proto file by specifying a proto file using flag src, // you can specify a target folder for code generation, when the proto file has import, you can specify // the import search directory through the proto_path command, for specific usage, please refer to protoc -h func RPC(c *cli.Context) error { + console.Warning("deprecated: use %q instead, for the details see %q", + "goctl rpc protoc", "goctl rpc protoc --help") + if err := prepare(); err != nil { return err } @@ -73,6 +78,9 @@ func prepare() error { // RPCNew is to generate rpc greet service, this greet service can speed // up your understanding of the zrpc service structure func RPCNew(c *cli.Context) error { + console.Warning("deprecated: it will be removed in the feature, zrpc code generation please use %q instead", + "goctl rpc protoc") + rpcname := c.Args().First() ext := filepath.Ext(rpcname) if len(ext) > 0 { diff --git a/tools/goctl/rpc/cli/zrpc.go b/tools/goctl/rpc/cli/zrpc.go new file mode 100644 index 00000000..23ec7f87 --- /dev/null +++ b/tools/goctl/rpc/cli/zrpc.go @@ -0,0 +1,238 @@ +package cli + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/emicklei/proto" + "github.com/tal-tech/go-zero/tools/goctl/rpc/generator" + "github.com/tal-tech/go-zero/tools/goctl/util" + "github.com/tal-tech/go-zero/tools/goctl/util/pathx" + "github.com/urfave/cli" +) + +var ( + errInvalidGrpcOutput = errors.New("ZRPC: missing grpc output") + errInvalidZrpcOutput = errors.New("ZRPC: missing zrpc output, please use --zrpc_out to specify the output") + errInvalidInput = errors.New("ZRPC: missing source") + errMultiInput = errors.New("ZRPC: only one source is expected") +) + +const ( + optImport = "import" + optSourceRelative = "source_relative" +) + +// ZRPC generates grpc code directly by protoc and generates +// zrpc code by goctl. +func ZRPC(c *cli.Context) error { + args := c.Parent().Args() + protocArgs := removeGoctlFlag(args) + pwd, err := os.Getwd() + if err != nil { + return err + } + + source, err := getSourceProto(c.Args(), pwd) + if err != nil { + return err + } + src := filepath.Dir(source) + goPackage, protoPkg, err := getGoPackage(source) + if err != nil { + return err + } + + grpcOut := c.String("go-grpc_out") + goOut := c.String("go_out") + goOpt := c.String("go_opt") + grpcOpt := c.String("go-grpc_opt") + zrpcOut := c.String("zrpc_out") + style := c.String("style") + home := c.String("home") + remote := c.String("remote") + if len(remote) > 0 { + repo, _ := util.CloneIntoGitHome(remote) + if len(repo) > 0 { + home = repo + } + } + + if len(home) > 0 { + pathx.RegisterGoctlHome(home) + } + if len(goOut) == 0 { + return errInvalidGrpcOutput + } + if len(zrpcOut) == 0 { + return errInvalidZrpcOutput + } + if !filepath.IsAbs(zrpcOut) { + zrpcOut = filepath.Join(pwd, zrpcOut) + } + + goOut = removePluginFlag(goOut) + goOut, err = parseOutOut(src, goOut, goOpt, goPackage) + if err != nil { + return err + } + + var isGoolePlugin = len(grpcOut) > 0 + // If grpcOut is not empty means that user generates grpc code by + // https://google.golang.org/protobuf/cmd/protoc-gen-go and + // https://google.golang.org/grpc/cmd/protoc-gen-go-grpc, + // for details please see https://grpc.io/docs/languages/go/quickstart/ + if isGoolePlugin { + grpcOut, err = parseOutOut(src, grpcOut, grpcOpt, goPackage) + if err != nil { + return err + } + } else { + // Else it means that user generates grpc code by + // https://github.com/golang/protobuf/tree/master/protoc-gen-go + grpcOut = goOut + } + + goOut, err = filepath.Abs(goOut) + if err != nil { + return err + } + grpcOut, err = filepath.Abs(grpcOut) + if err != nil { + return err + } + zrpcOut, err = filepath.Abs(zrpcOut) + if err != nil { + return err + } + + if isGoolePlugin && grpcOut != goOut { + return fmt.Errorf("the --go_out and --go-grpc_out must be the same") + } + + if goOut == zrpcOut || grpcOut == zrpcOut { + recommendName := goPackage + if len(recommendName) == 0 { + recommendName = protoPkg + } + return fmt.Errorf("the zrpc and grpc output can not be the same, it is recommended to output grpc to the %q", + filepath.Join(goOut, recommendName)) + } + + var ctx generator.ZRpcContext + ctx.Src = source + ctx.ProtoGenGoDir = goOut + ctx.ProtoGenGrpcDir = grpcOut + ctx.Output = zrpcOut + ctx.ProtocCmd = strings.Join(protocArgs, " ") + g, err := generator.NewDefaultRPCGenerator(style, generator.WithZRpcContext(&ctx)) + if err != nil { + return err + } + + return g.Generate(source, zrpcOut, nil) +} + +// parseOutOut calculates the output place to grpc code, about to calculate logic for details +// please see https://developers.google.com/protocol-buffers/docs/reference/go-generated#invocation. +func parseOutOut(sourceDir, grpcOut, grpcOpt, goPackage string) (string, error) { + if !filepath.IsAbs(grpcOut) { + grpcOut = filepath.Join(sourceDir, grpcOut) + } + switch grpcOpt { + case "", optImport: + grpcOut = filepath.Join(grpcOut, goPackage) + case optSourceRelative: + grpcOut = filepath.Join(grpcOut) + default: + return "", fmt.Errorf("parseAndSetGrpcOut: unknown path type %q: want %q or %q", + grpcOpt, optImport, optSourceRelative) + } + + return grpcOut, nil +} + +func getGoPackage(source string) (string, string, error) { + r, err := os.Open(source) + if err != nil { + return "", "", err + } + defer func() { + _ = r.Close() + }() + + parser := proto.NewParser(r) + set, err := parser.Parse() + if err != nil { + return "", "", err + } + + var goPackage, protoPkg string + proto.Walk(set, proto.WithOption(func(option *proto.Option) { + if option.Name == "go_package" { + goPackage = option.Constant.Source + } + }), proto.WithPackage(func(p *proto.Package) { + protoPkg = p.Name + })) + + return goPackage, protoPkg, nil +} + +func removeGoctlFlag(args []string) []string { + var ret []string + var step int + for step < len(args) { + arg := args[step] + switch { + case arg == "--style", arg == "--home", arg == "--zrpc_out": + step += 2 + continue + case strings.HasPrefix(arg, "--style="), + strings.HasPrefix(arg, "--home="), + strings.HasPrefix(arg, "--zrpc_out="): + step += 1 + continue + } + step += 1 + ret = append(ret, arg) + } + + return ret +} + +func getSourceProto(args []string, pwd string) (string, error) { + var source []string + for _, p := range args { + if strings.HasSuffix(p, ".proto") { + source = append(source, p) + } + } + + switch len(source) { + case 0: + return "", errInvalidInput + case 1: + isAbs := filepath.IsAbs(source[0]) + if isAbs { + return source[0], nil + } + + abs := filepath.Join(pwd, source[0]) + return abs, nil + default: + return "", errMultiInput + } +} + +func removePluginFlag(goOut string) string { + goOut = strings.ReplaceAll(goOut, "plugins=", "") + index := strings.LastIndex(goOut, ":") + if index < 0 { + return goOut + } + return goOut[index+1:] +} diff --git a/tools/goctl/rpc/cli/zrpc_test.go b/tools/goctl/rpc/cli/zrpc_test.go new file mode 100644 index 00000000..ac56a85b --- /dev/null +++ b/tools/goctl/rpc/cli/zrpc_test.go @@ -0,0 +1,113 @@ +package cli + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/util/console" +) + +type test struct { + source []string + expected string + expectedErr error +} + +func Test_GetSourceProto(t *testing.T) { + pwd, err := os.Getwd() + if err != nil { + console.Error(err.Error()) + return + } + + var testData = []test{ + { + source: []string{"a.proto"}, + expected: filepath.Join(pwd, "a.proto"), + }, + { + source: []string{"/foo/bar/a.proto"}, + expected: "/foo/bar/a.proto", + }, + { + source: []string{"a.proto", "b.proto"}, + expectedErr: errMultiInput, + }, + { + source: []string{"", "--go_out=."}, + expectedErr: errInvalidInput, + }, + } + + for _, d := range testData { + ret, err := getSourceProto(d.source, pwd) + if d.expectedErr != nil { + assert.Equal(t, d.expectedErr, err) + continue + } + + assert.Equal(t, d.expected, ret) + } +} + +func Test_RemoveGoctlFlag(t *testing.T) { + var testData = []test{ + { + source: strings.Fields("protoc foo.proto --go_out=. --go_opt=bar --zrpc_out=. --style go-zero --home=foo"), + expected: "protoc foo.proto --go_out=. --go_opt=bar", + }, + { + source: strings.Fields("foo bar foo.proto"), + expected: "foo bar foo.proto", + }, + { + source: strings.Fields("protoc foo.proto --go_out . --style=go_zero --home ."), + expected: "protoc foo.proto --go_out .", + }, + { + source: strings.Fields(`protoc foo.proto --go_out . --style="go_zero" --home="."`), + expected: "protoc foo.proto --go_out .", + }, + { + source: strings.Fields(`protoc foo.proto --go_opt=. --zrpc_out . --style=goZero --home=bar`), + expected: "protoc foo.proto --go_opt=.", + }, + { + source: strings.Fields(`protoc foo.proto --go_opt=. --zrpc_out="bar" --style=goZero --home=bar`), + expected: "protoc foo.proto --go_opt=.", + }, + } + for _, e := range testData { + cmd := strings.Join(removeGoctlFlag(e.source), " ") + assert.Equal(t, e.expected, cmd) + } +} + +func Test_RemovePluginFlag(t *testing.T) { + var testData = []test{ + { + source: strings.Fields("plugins=grpc:."), + expected: ".", + }, + { + source: strings.Fields("plugins=g1,g2:."), + expected: ".", + }, + { + source: strings.Fields("g1,g2:."), + expected: ".", + }, + { + source: strings.Fields("plugins=g1,g2:foo"), + expected: "foo", + }, + } + + for _, e := range testData { + data := removePluginFlag(e.source[0]) + assert.Equal(t, e.expected, data) + } +} diff --git a/tools/goctl/rpc/generator/gen.go b/tools/goctl/rpc/generator/gen.go index d0808357..4fad5071 100644 --- a/tools/goctl/rpc/generator/gen.go +++ b/tools/goctl/rpc/generator/gen.go @@ -14,23 +14,44 @@ import ( type RPCGenerator struct { g Generator cfg *conf.Config + ctx *ZRpcContext +} + +type RPCGeneratorOption func(g *RPCGenerator) + +type ZRpcContext struct { + Src string + ProtocCmd string + ProtoGenGrpcDir string + ProtoGenGoDir string + Output string } // NewDefaultRPCGenerator wraps Generator with configure -func NewDefaultRPCGenerator(style string) (*RPCGenerator, error) { +func NewDefaultRPCGenerator(style string, options ...RPCGeneratorOption) (*RPCGenerator, error) { cfg, err := conf.NewConfig(style) if err != nil { return nil, err } - return NewRPCGenerator(NewDefaultGenerator(), cfg), nil + return NewRPCGenerator(NewDefaultGenerator(), cfg, options...), nil } // NewRPCGenerator creates an instance for RPCGenerator -func NewRPCGenerator(g Generator, cfg *conf.Config) *RPCGenerator { - return &RPCGenerator{ +func NewRPCGenerator(g Generator, cfg *conf.Config, options ...RPCGeneratorOption) *RPCGenerator { + out := &RPCGenerator{ g: g, cfg: cfg, } + for _, opt := range options { + opt(out) + } + return out +} + +func WithZRpcContext(c *ZRpcContext) RPCGeneratorOption { + return func(g *RPCGenerator) { + g.ctx = c + } } // Generate generates an rpc service, through the proto file, @@ -63,7 +84,7 @@ func (g *RPCGenerator) Generate(src, target string, protoImportPath []string, go return err } - dirCtx, err := mkdir(projectCtx, proto, g.cfg) + dirCtx, err := mkdir(projectCtx, proto, g.cfg, g.ctx) if err != nil { return err } @@ -73,7 +94,7 @@ func (g *RPCGenerator) Generate(src, target string, protoImportPath []string, go return err } - err = g.g.GenPb(dirCtx, protoImportPath, proto, g.cfg, goOptions...) + err = g.g.GenPb(dirCtx, protoImportPath, proto, g.cfg, g.ctx, goOptions...) if err != nil { return err } diff --git a/tools/goctl/rpc/generator/gencall.go b/tools/goctl/rpc/generator/gencall.go index 2eb022ac..60e4cf05 100644 --- a/tools/goctl/rpc/generator/gencall.go +++ b/tools/goctl/rpc/generator/gencall.go @@ -24,7 +24,8 @@ package {{.filePackage}} import ( "context" - {{.package}} + {{.pbPackage}} + {{if ne .pbPackage .protoGoPackage}}{{.protoGoPackage}}{{end}} "github.com/zeromicro/go-zero/zrpc" "google.golang.org/grpc" @@ -100,14 +101,15 @@ func (g *DefaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf aliasKeys := alias.KeysStr() sort.Strings(aliasKeys) err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{ - "name": callFilename, - "alias": strings.Join(aliasKeys, pathx.NL), - "head": head, - "filePackage": dir.Base, - "package": fmt.Sprintf(`"%s"`, ctx.GetPb().Package), - "serviceName": stringx.From(service.Name).ToCamel(), - "functions": strings.Join(functions, pathx.NL), - "interface": strings.Join(iFunctions, pathx.NL), + "name": callFilename, + "alias": strings.Join(aliasKeys, pathx.NL), + "head": head, + "filePackage": dir.Base, + "pbPackage": fmt.Sprintf(`"%s"`, ctx.GetPb().Package), + "protoGoPackage": fmt.Sprintf(`"%s"`, ctx.GetProtoGo().Package), + "serviceName": stringx.From(service.Name).ToCamel(), + "functions": strings.Join(functions, pathx.NL), + "interface": strings.Join(iFunctions, pathx.NL), }, filename, true) return err } diff --git a/tools/goctl/rpc/generator/generator.go b/tools/goctl/rpc/generator/generator.go index cad3895b..89291967 100644 --- a/tools/goctl/rpc/generator/generator.go +++ b/tools/goctl/rpc/generator/generator.go @@ -15,5 +15,5 @@ type Generator interface { GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error GenSvc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error - GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, cfg *conf.Config, goOptions ...string) error + GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, cfg *conf.Config, c *ZRpcContext, goOptions ...string) error } diff --git a/tools/goctl/rpc/generator/genpb.go b/tools/goctl/rpc/generator/genpb.go index 189d74b2..8294dd81 100644 --- a/tools/goctl/rpc/generator/genpb.go +++ b/tools/goctl/rpc/generator/genpb.go @@ -3,6 +3,7 @@ package generator import ( "bytes" "errors" + "os" "path/filepath" "strings" @@ -16,7 +17,12 @@ const googleProtocGenGoErr = `--go_out: protoc-gen-go: plugins are not supported // GenPb generates the pb.go file, which is a layer of packaging for protoc to generate gprc, // but the commands and flags in protoc are not completely joined in goctl. At present, proto_path(-I) is introduced -func (g *DefaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, _ *conf.Config, goOptions ...string) error { +func (g *DefaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, _ *conf.Config, c *ZRpcContext, goOptions ...string) error { + if c != nil { + return g.genPbDirect(c) + } + + // deprecated: use genPbDirect instead. dir := ctx.GetPb() cw := new(bytes.Buffer) directory, base := filepath.Split(proto.Src) @@ -103,3 +109,14 @@ go get -u github.com/golang/protobuf/protoc-gen-go`) } return nil } + +func (g *DefaultGenerator) genPbDirect(c *ZRpcContext) error { + g.log.Debug(c.ProtocCmd) + pwd, err := os.Getwd() + if err != nil { + return err + } + + _, err = execx.Run(c.ProtocCmd, pwd) + return err +} diff --git a/tools/goctl/rpc/generator/genserver.go b/tools/goctl/rpc/generator/genserver.go index b974e659..c579b603 100644 --- a/tools/goctl/rpc/generator/genserver.go +++ b/tools/goctl/rpc/generator/genserver.go @@ -27,6 +27,7 @@ import ( type {{.server}}Server struct { svcCtx *svc.ServiceContext + {{.unimplementedServer}} } func New{{.server}}Server(svcCtx *svc.ServiceContext) *{{.server}}Server { @@ -83,11 +84,12 @@ func (g *DefaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *co } err = util.With("server").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{ - "head": head, - "server": stringx.From(service.Name).ToCamel(), - "imports": strings.Join(imports.KeysStr(), pathx.NL), - "funcs": strings.Join(funcList, pathx.NL), - "notStream": notStream, + "head": head, + "unimplementedServer": fmt.Sprintf("%s.Unimplemented%sServer", proto.PbPackage, stringx.From(service.Name).ToCamel()), + "server": stringx.From(service.Name).ToCamel(), + "imports": strings.Join(imports.KeysStr(), pathx.NL), + "funcs": strings.Join(funcList, pathx.NL), + "notStream": notStream, }, serverFile, true) return err } diff --git a/tools/goctl/rpc/generator/mkdir.go b/tools/goctl/rpc/generator/mkdir.go index f99c7acc..fce2656b 100644 --- a/tools/goctl/rpc/generator/mkdir.go +++ b/tools/goctl/rpc/generator/mkdir.go @@ -20,6 +20,7 @@ const ( server = "server" svc = "svc" pb = "pb" + protoGo = "proto-go" call = "call" ) @@ -34,6 +35,7 @@ type ( GetServer() Dir GetSvc() Dir GetPb() Dir + GetProtoGo() Dir GetMain() Dir GetServiceName() stringx.String } @@ -51,7 +53,7 @@ type ( } ) -func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, _ *conf.Config) (DirContext, error) { +func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, _ *conf.Config, c *ZRpcContext) (DirContext, error) { inner := make(map[string]Dir) etcDir := filepath.Join(ctx.WorkDir, "etc") internalDir := filepath.Join(ctx.WorkDir, "internal") @@ -60,6 +62,12 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, _ *conf.Config) (DirCont serverDir := filepath.Join(internalDir, "server") svcDir := filepath.Join(internalDir, "svc") pbDir := filepath.Join(ctx.WorkDir, proto.GoPackage) + protoGoDir := pbDir + if c != nil { + pbDir = c.ProtoGenGrpcDir + protoGoDir = c.ProtoGenGoDir + } + callDir := filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name).ToCamel())) if strings.EqualFold(proto.Service.Name, proto.GoPackage) { callDir = filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name+"_client").ToCamel())) @@ -100,11 +108,19 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, _ *conf.Config) (DirCont Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(svcDir, ctx.Dir))), Base: filepath.Base(svcDir), } + inner[pb] = Dir{ Filename: pbDir, Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(pbDir, ctx.Dir))), Base: filepath.Base(pbDir), } + + inner[protoGo] = Dir{ + Filename: protoGoDir, + Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(protoGoDir, ctx.Dir))), + Base: filepath.Base(protoGoDir), + } + inner[call] = Dir{ Filename: callDir, Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(callDir, ctx.Dir))), @@ -155,6 +171,10 @@ func (d *defaultDirContext) GetPb() Dir { return d.inner[pb] } +func (d *defaultDirContext) GetProtoGo() Dir { + return d.inner[protoGo] +} + func (d *defaultDirContext) GetMain() Dir { return d.inner[wd] }