Repository: zeromicro/go-zero
Pull request #1663 (2022-03-19 04:42:49)

fix: the new  RawFieldNames considers the tag with options.

The new function RawFieldNames  has considered the following that the tag could have option.

```go
//`db:"id"`
//`db:"id,type=char,length=16"`
//`db:",type=char,length=16"`

```


Patch:
diff --git a/core/stores/builder/builder.go b/core/stores/builder/builder.go
index cddd1298..27abc8c7 100644
--- a/core/stores/builder/builder.go
+++ b/core/stores/builder/builder.go
@@ -41,10 +41,25 @@ func RawFieldNames(in interface{}, postgresSql ...bool) []string {
 				out = append(out, fmt.Sprintf("`%s`", fi.Name))
 			}
 		default:
-			if pg {
-				out = append(out, tagv)
+			//get tag name with the tag opton, e.g.:
+			//`db:"id"`
+			//`db:"id,type=char,length=16"`
+			//`db:",type=char,length=16"`
+			if strings.Contains(tagv, ",") {
+				tagv = strings.TrimSpace(strings.Split(tagv, ",")[0])
+			}
+			if tagv != "" {
+				if pg {
+					out = append(out, tagv)
+				} else {
+					out = append(out, fmt.Sprintf("`%s`", tagv))
+				}
 			} else {
-				out = append(out, fmt.Sprintf("`%s`", tagv))
+				if pg {
+					out = append(out, fi.Name)
+				} else {
+					out = append(out, fmt.Sprintf("`%s`", fi.Name))
+				}
 			}
 		}
 	}
diff --git a/tools/goctl/Dockerfile b/tools/goctl/Dockerfile
new file mode 100644
index 00000000..fe1f3e4c
--- /dev/null
+++ b/tools/goctl/Dockerfile
@@ -0,0 +1,34 @@
+FROM golang:alpine AS builder
+
+LABEL stage=gobuilder
+
+ENV CGO_ENABLED 0
+ENV GOPROXY https://goproxy.cn,direct
+
+RUN apk update --no-cache && apk add --no-cache tzdata
+RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
+RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
+
+WORKDIR /build
+
+ADD go.mod .
+ADD go.sum .
+RUN go mod download
+COPY . .
+RUN go build -ldflags="-s -w" -o /app/goctl ./goctl.go
+
+
+FROM alpine
+
+RUN apk update --no-cache && apk add --no-cache protoc
+
+COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
+COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
+COPY --from=builder /go/bin/protoc-gen-go /usr/bin/protoc-gen-go
+COPY --from=builder /go/bin/protoc-gen-go-grpc /usr/bin/protoc-gen-go-grpc
+ENV TZ Asia/Shanghai
+
+WORKDIR /app
+COPY --from=builder /app/goctl /app/goctl
+
+CMD ["./goctl"]
diff --git a/tools/goctl/completion/completion.go b/tools/goctl/completion/completion.go
index 67493b99..5e29e52e 100644
--- a/tools/goctl/completion/completion.go
+++ b/tools/goctl/completion/completion.go
@@ -48,17 +48,15 @@ func Completion(c *cli.Context) error {
 
 	flag := magic
 	err = ioutil.WriteFile(zshF, zsh, os.ModePerm)
-	if err != nil {
-		return err
+	if err == nil {
+		flag |= flagZsh
 	}
 
-	flag |= flagZsh
 	err = ioutil.WriteFile(bashF, bash, os.ModePerm)
-	if err != nil {
-		return err
+	if err == nil {
+		flag |= flagBash
 	}
 
-	flag |= flagBash
 	buffer.WriteString(aurora.BrightGreen("generation auto completion success!\n").String())
 	buffer.WriteString(aurora.BrightGreen("executes the following script to setting shell:\n").String())
 	switch flag {
diff --git a/tools/goctl/docker/docker.go b/tools/goctl/docker/docker.go
index d8292509..90ac7e2d 100644
--- a/tools/goctl/docker/docker.go
+++ b/tools/goctl/docker/docker.go
@@ -28,7 +28,7 @@ type Docker struct {
 	GoRelPath   string
 	GoFile      string
 	ExeFile     string
-	Scratch     bool
+	BaseImage   string
 	HasPort     bool
 	Port        int
 	Argument    string
@@ -74,10 +74,10 @@ func DockerCommand(c *cli.Context) (err error) {
 		return fmt.Errorf("file %q not found", goFile)
 	}
 
-	scratch := c.Bool("scratch")
+	base := c.String("base")
 	port := c.Int("port")
 	if _, err := os.Stat(etcDir); os.IsNotExist(err) {
-		return generateDockerfile(goFile, scratch, port, version, timezone)
+		return generateDockerfile(goFile, base, port, version, timezone)
 	}
 
 	cfg, err := findConfig(goFile, etcDir)
@@ -85,7 +85,7 @@ func DockerCommand(c *cli.Context) (err error) {
 		return err
 	}
 
-	if err := generateDockerfile(goFile, scratch, port, version, timezone, "-f", "etc/"+cfg); err != nil {
+	if err := generateDockerfile(goFile, base, port, version, timezone, "-f", "etc/"+cfg); err != nil {
 		return err
 	}
 
@@ -126,7 +126,7 @@ func findConfig(file, dir string) (string, error) {
 	return files[0], nil
 }
 
-func generateDockerfile(goFile string, scratch bool, port int, version, timezone string, args ...string) error {
+func generateDockerfile(goFile, base string, port int, version, timezone string, args ...string) error {
 	projPath, err := getFilePath(filepath.Dir(goFile))
 	if err != nil {
 		return err
@@ -159,7 +159,7 @@ func generateDockerfile(goFile string, scratch bool, port int, version, timezone
 		GoRelPath:   projPath,
 		GoFile:      goFile,
 		ExeFile:     pathx.FileNameWithoutExt(filepath.Base(goFile)),
-		Scratch:     scratch,
+		BaseImage:   base,
 		HasPort:     port > 0,
 		Port:        port,
 		Argument:    builder.String(),
diff --git a/tools/goctl/docker/template.go b/tools/goctl/docker/template.go
index 908418e0..8c1b6ab7 100644
--- a/tools/goctl/docker/template.go
+++ b/tools/goctl/docker/template.go
@@ -27,9 +27,9 @@ COPY . .
 {{end}}RUN go build -ldflags="-s -w" -o /app/{{.ExeFile}} {{.GoRelPath}}/{{.GoFile}}
 
 
-FROM {{if .Scratch}}scratch{{else}}alpine{{end}}
+FROM {{.BaseImage}}
 
-{{if .Scratch}}COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt{{else}}RUN apk update --no-cache && apk add --no-cache ca-certificates{{end}}
+COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
 {{if .HasTimezone}}COPY --from=builder /usr/share/zoneinfo/{{.Timezone}} /usr/share/zoneinfo/{{.Timezone}}
 ENV TZ {{.Timezone}}
 {{end}}
diff --git a/tools/goctl/goctl.go b/tools/goctl/goctl.go
index cccf38bd..308c5aab 100644
--- a/tools/goctl/goctl.go
+++ b/tools/goctl/goctl.go
@@ -345,9 +345,10 @@ var commands = []cli.Command{
 				Name:  "go",
 				Usage: "the file that contains main function",
 			},
-			cli.BoolFlag{
-				Name:  "scratch",
-				Usage: "use scratch for the base docker image",
+			cli.StringFlag{
+				Name:  "base",
+				Usage: "the base image to build the docker image, default scratch",
+				Value: "scratch",
 			},
 			cli.IntFlag{
 				Name:  "port",
@@ -492,9 +493,8 @@ var commands = []cli.Command{
 		Usage: "generate rpc code",
 		Subcommands: []cli.Command{
 			{
-				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(),
+				Name:  "new",
+				Usage: `generate rpc demo service`,
 				Flags: []cli.Flag{
 					cli.StringFlag{
 						Name:  "style",
@@ -603,53 +603,6 @@ var commands = []cli.Command{
 					},
 				},
 			},
-			{
-				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",
-						Usage: "the file path of the proto source file",
-					},
-					cli.StringSliceFlag{
-						Name:  "proto_path, I",
-						Usage: `native command of protoc, specify the directory in which to search for imports. [optional]`,
-					},
-					cli.StringSliceFlag{
-						Name:  "go_opt",
-						Usage: `native command of protoc-gen-go, specify the mapping from proto to go, eg --go_opt=proto_import=go_package_import. [optional]`,
-					},
-					cli.StringFlag{
-						Name:  "dir, d",
-						Usage: `the target path of the code`,
-					},
-					cli.StringFlag{
-						Name:  "style",
-						Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-					},
-					cli.BoolFlag{
-						Name:  "idea",
-						Usage: "whether the command execution environment is from idea plugin. [optional]",
-					},
-					cli.StringFlag{
-						Name: "home",
-						Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-							"if they are, --remote has higher priority",
-					},
-					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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-				},
-				Action: rpc.RPC,
-			},
 		},
 	},
 	{
diff --git a/tools/goctl/rpc/cli/cli.go b/tools/goctl/rpc/cli/cli.go
index 0229ff6a..b9716ac6 100644
--- a/tools/goctl/rpc/cli/cli.go
+++ b/tools/goctl/rpc/cli/cli.go
@@ -4,84 +4,16 @@ import (
 	"errors"
 	"fmt"
 	"path/filepath"
-	"runtime"
 
 	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/generator"
 	"github.com/zeromicro/go-zero/tools/goctl/util"
-	"github.com/zeromicro/go-zero/tools/goctl/util/console"
-	"github.com/zeromicro/go-zero/tools/goctl/util/env"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
-// 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
-	}
-
-	src := c.String("src")
-	out := c.String("dir")
-	style := c.String("style")
-	protoImportPath := c.StringSlice("proto_path")
-	goOptions := c.StringSlice("go_opt")
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
-	if len(remote) > 0 {
-		repo, _ := util.CloneIntoGitHome(remote, branch)
-		if len(repo) > 0 {
-			home = repo
-		}
-	}
-	if len(home) > 0 {
-		pathx.RegisterGoctlHome(home)
-	}
-
-	if len(src) == 0 {
-		return errors.New("missing -src")
-	}
-
-	if len(out) == 0 {
-		return errors.New("missing -dir")
-	}
-
-	g, err := generator.NewDefaultRPCGenerator(style)
-	if err != nil {
-		return err
-	}
-
-	return g.Generate(src, out, protoImportPath, goOptions...)
-}
-
-func prepare() error {
-	if !env.CanExec() {
-		return fmt.Errorf("%s: can not start new processes using os.StartProcess or exec.Command", runtime.GOOS)
-	}
-	if _, err := env.LookUpGo(); err != nil {
-		return err
-	}
-	if _, err := env.LookUpProtoc(); err != nil {
-		return err
-	}
-	if _, err := env.LookUpProtocGenGo(); err != nil {
-		return err
-	}
-	return nil
-}
-
 // 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 {
@@ -113,12 +45,15 @@ func RPCNew(c *cli.Context) error {
 		return err
 	}
 
-	g, err := generator.NewDefaultRPCGenerator(style)
-	if err != nil {
-		return err
-	}
-
-	return g.Generate(src, filepath.Dir(src), nil)
+	var ctx generator.ZRpcContext
+	ctx.Src = src
+	ctx.GoOutput = filepath.Dir(src)
+	ctx.GrpcOutput = filepath.Dir(src)
+	ctx.IsGooglePlugin = true
+	ctx.Output = filepath.Dir(src)
+	ctx.ProtocCmd = fmt.Sprintf("protoc -I=%s %s --go_out=%s --go-grpc_out=%s", filepath.Dir(src), filepath.Base(src), filepath.Dir(src), filepath.Dir(src))
+	g := generator.NewGenerator(style)
+	return g.Generate(&ctx)
 }
 
 // RPCTemplate is the entry for generate rpc template
diff --git a/tools/goctl/rpc/cli/zrpc.go b/tools/goctl/rpc/cli/zrpc.go
index 8910b97a..e0ab934b 100644
--- a/tools/goctl/rpc/cli/zrpc.go
+++ b/tools/goctl/rpc/cli/zrpc.go
@@ -107,12 +107,8 @@ func ZRPC(c *cli.Context) error {
 	ctx.IsGooglePlugin = isGooglePlugin
 	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)
+	g := generator.NewGenerator(style)
+	return g.Generate(&ctx)
 }
 
 func removeGoctlFlag(args []string) []string {
diff --git a/tools/goctl/rpc/generator/base/common.proto b/tools/goctl/rpc/generator/base/common.proto
index 42c9107e..883c65fe 100644
--- a/tools/goctl/rpc/generator/base/common.proto
+++ b/tools/goctl/rpc/generator/base/common.proto
@@ -1,6 +1,7 @@
 syntax = "proto3";
 
 package common;
+option go_package="./common";
 
 message User {
   string name = 1;
diff --git a/tools/goctl/rpc/generator/defaultgenerator.go b/tools/goctl/rpc/generator/defaultgenerator.go
deleted file mode 100644
index 8d20ddfa..00000000
--- a/tools/goctl/rpc/generator/defaultgenerator.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package generator
-
-import (
-	"github.com/zeromicro/go-zero/tools/goctl/env"
-	"github.com/zeromicro/go-zero/tools/goctl/util/console"
-)
-
-// DefaultGenerator defines the environment needs of rpc service generation
-type DefaultGenerator struct {
-	log console.Console
-}
-
-// just test interface implement
-var _ Generator = (*DefaultGenerator)(nil)
-
-// NewDefaultGenerator returns an instance of DefaultGenerator
-func NewDefaultGenerator() Generator {
-	log := console.NewColorConsole()
-	return &DefaultGenerator{
-		log: log,
-	}
-}
-
-// Prepare provides environment detection generated by rpc service,
-// including go environment, protoc, whether protoc-gen-go is installed or not
-func (g *DefaultGenerator) Prepare() error {
-	return env.Prepare(true, true)
-}
diff --git a/tools/goctl/rpc/generator/gen.go b/tools/goctl/rpc/generator/gen.go
index 2d6a4683..636f126d 100644
--- a/tools/goctl/rpc/generator/gen.go
+++ b/tools/goctl/rpc/generator/gen.go
@@ -3,22 +3,12 @@ package generator
 import (
 	"path/filepath"
 
-	conf "github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/parser"
 	"github.com/zeromicro/go-zero/tools/goctl/util/console"
 	"github.com/zeromicro/go-zero/tools/goctl/util/ctx"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
-// RPCGenerator defines a generator and configure
-type RPCGenerator struct {
-	g   Generator
-	cfg *conf.Config
-	ctx *ZRpcContext
-}
-
-type RPCGeneratorOption func(g *RPCGenerator)
-
 type ZRpcContext struct {
 	Src             string
 	ProtocCmd       string
@@ -30,38 +20,11 @@ type ZRpcContext struct {
 	Output          string
 }
 
-// NewDefaultRPCGenerator wraps Generator with configure
-func NewDefaultRPCGenerator(style string, options ...RPCGeneratorOption) (*RPCGenerator, error) {
-	cfg, err := conf.NewConfig(style)
-	if err != nil {
-		return nil, err
-	}
-	return NewRPCGenerator(NewDefaultGenerator(), cfg, options...), nil
-}
-
-// NewRPCGenerator creates an instance for 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,
 // code storage directory, and proto import parameters to control
 // the source file and target location of the rpc service that needs to be generated
-func (g *RPCGenerator) Generate(src, target string, protoImportPath []string, goOptions ...string) error {
-	abs, err := filepath.Abs(target)
+func (g *Generator) Generate(zctx *ZRpcContext) error {
+	abs, err := filepath.Abs(zctx.Output)
 	if err != nil {
 		return err
 	}
@@ -71,7 +34,7 @@ func (g *RPCGenerator) Generate(src, target string, protoImportPath []string, go
 		return err
 	}
 
-	err = g.g.Prepare()
+	err = g.Prepare()
 	if err != nil {
 		return err
 	}
@@ -82,52 +45,52 @@ func (g *RPCGenerator) Generate(src, target string, protoImportPath []string, go
 	}
 
 	p := parser.NewDefaultProtoParser()
-	proto, err := p.Parse(src)
+	proto, err := p.Parse(zctx.Src)
 	if err != nil {
 		return err
 	}
 
-	dirCtx, err := mkdir(projectCtx, proto, g.cfg, g.ctx)
+	dirCtx, err := mkdir(projectCtx, proto, g.cfg, zctx)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenEtc(dirCtx, proto, g.cfg)
+	err = g.GenEtc(dirCtx, proto, g.cfg)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenPb(dirCtx, protoImportPath, proto, g.cfg, g.ctx, goOptions...)
+	err = g.GenPb(dirCtx, zctx)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenConfig(dirCtx, proto, g.cfg)
+	err = g.GenConfig(dirCtx, proto, g.cfg)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenSvc(dirCtx, proto, g.cfg)
+	err = g.GenSvc(dirCtx, proto, g.cfg)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenLogic(dirCtx, proto, g.cfg)
+	err = g.GenLogic(dirCtx, proto, g.cfg)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenServer(dirCtx, proto, g.cfg)
+	err = g.GenServer(dirCtx, proto, g.cfg)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenMain(dirCtx, proto, g.cfg)
+	err = g.GenMain(dirCtx, proto, g.cfg)
 	if err != nil {
 		return err
 	}
 
-	err = g.g.GenCall(dirCtx, proto, g.cfg)
+	err = g.GenCall(dirCtx, proto, g.cfg)
 
 	console.NewColorConsole().MarkDone()
 
diff --git a/tools/goctl/rpc/generator/gencall.go b/tools/goctl/rpc/generator/gencall.go
index 04544cc0..f8258a3f 100644
--- a/tools/goctl/rpc/generator/gencall.go
+++ b/tools/goctl/rpc/generator/gencall.go
@@ -58,7 +58,7 @@ func New{{.serviceName}}(cli zrpc.Client) {{.serviceName}} {
 	callFunctionTemplate = `
 {{if .hasComment}}{{.comment}}{{end}}
 func (m *default{{.serviceName}}) {{.method}}(ctx context.Context{{if .hasReq}}, in *{{.pbRequest}}{{end}}, opts ...grpc.CallOption) ({{if .notStream}}*{{.pbResponse}}, {{else}}{{.streamBody}},{{end}} error) {
-	client := {{.package}}.New{{.rpcServiceName}}Client(m.cli.Conn())
+	client := {{if .isCallPkgSameToGrpcPkg}}{{else}}{{.package}}.{{end}}New{{.rpcServiceName}}Client(m.cli.Conn())
 	return client.{{.method}}(ctx{{if .hasReq}}, in{{end}}, opts...)
 }
 `
@@ -66,10 +66,12 @@ func (m *default{{.serviceName}}) {{.method}}(ctx context.Context{{if .hasReq}},
 
 // GenCall generates the rpc client code, which is the entry point for the rpc service call.
 // It is a layer of encapsulation for the rpc client and shields the details in the pb.
-func (g *DefaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
+func (g *Generator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
 	dir := ctx.GetCall()
 	service := proto.Service
 	head := util.GetHead(proto.Name)
+	isCallPkgSameToPbPkg := ctx.GetCall().Filename == ctx.GetPb().Filename
+	isCallPkgSameToGrpcPkg := ctx.GetCall().Filename == ctx.GetProtoGo().Filename
 
 	callFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name)
 	if err != nil {
@@ -77,12 +79,12 @@ func (g *DefaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf
 	}
 
 	filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", callFilename))
-	functions, err := g.genFunction(proto.PbPackage, service)
+	functions, err := g.genFunction(proto.PbPackage, service, isCallPkgSameToGrpcPkg)
 	if err != nil {
 		return err
 	}
 
-	iFunctions, err := g.getInterfaceFuncs(proto.PbPackage, service)
+	iFunctions, err := g.getInterfaceFuncs(proto.PbPackage, service, isCallPkgSameToGrpcPkg)
 	if err != nil {
 		return err
 	}
@@ -93,11 +95,19 @@ func (g *DefaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf
 	}
 
 	alias := collection.NewSet()
-	for _, item := range proto.Message {
-		msgName := getMessageName(*item.Message)
-		alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(msgName), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(msgName))))
+	if !isCallPkgSameToPbPkg {
+		for _, item := range proto.Message {
+			msgName := getMessageName(*item.Message)
+			alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(msgName), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(msgName))))
+		}
 	}
 
+	pbPackage := fmt.Sprintf(`"%s"`, ctx.GetPb().Package)
+	protoGoPackage := fmt.Sprintf(`"%s"`, ctx.GetProtoGo().Package)
+	if isCallPkgSameToGrpcPkg {
+		pbPackage = ""
+		protoGoPackage = ""
+	}
 	aliasKeys := alias.KeysStr()
 	sort.Strings(aliasKeys)
 	err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
@@ -105,8 +115,8 @@ func (g *DefaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf
 		"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),
+		"pbPackage":      pbPackage,
+		"protoGoPackage": protoGoPackage,
 		"serviceName":    stringx.From(service.Name).ToCamel(),
 		"functions":      strings.Join(functions, pathx.NL),
 		"interface":      strings.Join(iFunctions, pathx.NL),
@@ -136,7 +146,7 @@ func getMessageName(msg proto.Message) string {
 	return strings.Join(list, "_")
 }
 
-func (g *DefaultGenerator) genFunction(goPackage string, service parser.Service) ([]string, error) {
+func (g *Generator) genFunction(goPackage string, service parser.Service, isCallPkgSameToGrpcPkg bool) ([]string, error) {
 	functions := make([]string, 0)
 
 	for _, rpc := range service.RPC {
@@ -147,18 +157,22 @@ func (g *DefaultGenerator) genFunction(goPackage string, service parser.Service)
 
 		comment := parser.GetComment(rpc.Doc())
 		streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
+		if isCallPkgSameToGrpcPkg {
+			streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
+		}
 		buffer, err := util.With("sharedFn").Parse(text).Execute(map[string]interface{}{
-			"serviceName":    stringx.From(service.Name).ToCamel(),
-			"rpcServiceName": parser.CamelCase(service.Name),
-			"method":         parser.CamelCase(rpc.Name),
-			"package":        goPackage,
-			"pbRequest":      parser.CamelCase(rpc.RequestType),
-			"pbResponse":     parser.CamelCase(rpc.ReturnsType),
-			"hasComment":     len(comment) > 0,
-			"comment":        comment,
-			"hasReq":         !rpc.StreamsRequest,
-			"notStream":      !rpc.StreamsRequest && !rpc.StreamsReturns,
-			"streamBody":     streamServer,
+			"serviceName":            stringx.From(service.Name).ToCamel(),
+			"rpcServiceName":         parser.CamelCase(service.Name),
+			"method":                 parser.CamelCase(rpc.Name),
+			"package":                goPackage,
+			"pbRequest":              parser.CamelCase(rpc.RequestType),
+			"pbResponse":             parser.CamelCase(rpc.ReturnsType),
+			"hasComment":             len(comment) > 0,
+			"comment":                comment,
+			"hasReq":                 !rpc.StreamsRequest,
+			"notStream":              !rpc.StreamsRequest && !rpc.StreamsReturns,
+			"streamBody":             streamServer,
+			"isCallPkgSameToGrpcPkg": isCallPkgSameToGrpcPkg,
 		})
 		if err != nil {
 			return nil, err
@@ -170,7 +184,7 @@ func (g *DefaultGenerator) genFunction(goPackage string, service parser.Service)
 	return functions, nil
 }
 
-func (g *DefaultGenerator) getInterfaceFuncs(goPackage string, service parser.Service) ([]string, error) {
+func (g *Generator) getInterfaceFuncs(goPackage string, service parser.Service, isCallPkgSameToGrpcPkg bool) ([]string, error) {
 	functions := make([]string, 0)
 
 	for _, rpc := range service.RPC {
@@ -181,6 +195,9 @@ func (g *DefaultGenerator) getInterfaceFuncs(goPackage string, service parser.Se
 
 		comment := parser.GetComment(rpc.Doc())
 		streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
+		if isCallPkgSameToGrpcPkg {
+			streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
+		}
 		buffer, err := util.With("interfaceFn").Parse(text).Execute(
 			map[string]interface{}{
 				"hasComment": len(comment) > 0,
diff --git a/tools/goctl/rpc/generator/genconfig.go b/tools/goctl/rpc/generator/genconfig.go
index 61250035..46567a5a 100644
--- a/tools/goctl/rpc/generator/genconfig.go
+++ b/tools/goctl/rpc/generator/genconfig.go
@@ -24,7 +24,7 @@ type Config struct {
 // which contains the zrpc.RpcServerConf configuration item by default.
 // You can specify the naming style of the target file name through config.Config. For details,
 // see https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/config.go
-func (g *DefaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
+func (g *Generator) GenConfig(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
 	dir := ctx.GetConfig()
 	configFilename, err := format.FileNamingFormat(cfg.NamingFormat, "config")
 	if err != nil {
diff --git a/tools/goctl/rpc/generator/generator.go b/tools/goctl/rpc/generator/generator.go
index 3c4aa0de..c66627cc 100644
--- a/tools/goctl/rpc/generator/generator.go
+++ b/tools/goctl/rpc/generator/generator.go
@@ -1,19 +1,34 @@
 package generator
 
 import (
+	"log"
+
 	conf "github.com/zeromicro/go-zero/tools/goctl/config"
-	"github.com/zeromicro/go-zero/tools/goctl/rpc/parser"
+	"github.com/zeromicro/go-zero/tools/goctl/env"
+	"github.com/zeromicro/go-zero/tools/goctl/util/console"
 )
 
-// Generator defines a generator interface to describe how to generate rpc service
-type Generator interface {
-	Prepare() error
-	GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
-	GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
-	GenEtc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
-	GenConfig(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
-	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, c *ZRpcContext, goOptions ...string) error
+// Generator defines the environment needs of rpc service generation
+type Generator struct {
+	log console.Console
+	cfg *conf.Config
+}
+
+// NewGenerator returns an instance of Generator
+func NewGenerator(style string) *Generator {
+	cfg, err := conf.NewConfig(style)
+	if err != nil {
+		log.Fatalln(err)
+	}
+	log := console.NewColorConsole()
+	return &Generator{
+		log: log,
+		cfg: cfg,
+	}
+}
+
+// Prepare provides environment detection generated by rpc service,
+// including go environment, protoc, whether protoc-gen-go is installed or not
+func (g *Generator) Prepare() error {
+	return env.Prepare(true, true)
 }
diff --git a/tools/goctl/rpc/generator/genetc.go b/tools/goctl/rpc/generator/genetc.go
index efa99f49..4b9fb6fe 100644
--- a/tools/goctl/rpc/generator/genetc.go
+++ b/tools/goctl/rpc/generator/genetc.go
@@ -23,7 +23,7 @@ Etcd:
 
 // GenEtc generates the yaml configuration file of the rpc service,
 // including host, port monitoring configuration items and etcd configuration
-func (g *DefaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
+func (g *Generator) GenEtc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
 	dir := ctx.GetEtc()
 	etcFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
 	if err != nil {
diff --git a/tools/goctl/rpc/generator/genlogic.go b/tools/goctl/rpc/generator/genlogic.go
index 135ca522..968c3720 100644
--- a/tools/goctl/rpc/generator/genlogic.go
+++ b/tools/goctl/rpc/generator/genlogic.go
@@ -50,7 +50,7 @@ func (l *{{.logicName}}) {{.method}} ({{if .hasReq}}in {{.request}}{{if .stream}
 )
 
 // GenLogic generates the logic file of the rpc service, which corresponds to the RPC definition items in proto.
-func (g *DefaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
+func (g *Generator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
 	dir := ctx.GetLogic()
 	service := proto.Service.Service.Name
 	for _, rpc := range proto.Service.RPC {
@@ -84,7 +84,7 @@ func (g *DefaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *con
 	return nil
 }
 
-func (g *DefaultGenerator) genLogicFunction(serviceName, goPackage string, rpc *parser.RPC) (string, error) {
+func (g *Generator) genLogicFunction(serviceName, goPackage string, rpc *parser.RPC) (string, error) {
 	functions := make([]string, 0)
 	text, err := pathx.LoadTemplate(category, logicFuncTemplateFileFile, logicFunctionTemplate)
 	if err != nil {
diff --git a/tools/goctl/rpc/generator/genmain.go b/tools/goctl/rpc/generator/genmain.go
index d7e59ade..91bfae85 100644
--- a/tools/goctl/rpc/generator/genmain.go
+++ b/tools/goctl/rpc/generator/genmain.go
@@ -53,7 +53,7 @@ func main() {
 `
 
 // GenMain generates the main file of the rpc service, which is an rpc service program call entry
-func (g *DefaultGenerator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
+func (g *Generator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
 	mainFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
 	if err != nil {
 		return err
diff --git a/tools/goctl/rpc/generator/genpb.go b/tools/goctl/rpc/generator/genpb.go
index 8e09b538..797a1fc8 100644
--- a/tools/goctl/rpc/generator/genpb.go
+++ b/tools/goctl/rpc/generator/genpb.go
@@ -1,118 +1,22 @@
 package generator
 
 import (
-	"bytes"
-	"errors"
 	"fmt"
 	"io/fs"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/zeromicro/go-zero/core/collection"
-	conf "github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
-	"github.com/zeromicro/go-zero/tools/goctl/rpc/parser"
 )
 
-const googleProtocGenGoErr = `--go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC`
-
 // 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, c *ZRpcContext, goOptions ...string) error {
-	if c != nil {
-		return g.genPbDirect(ctx, c)
-	}
-
-	// deprecated: use genPbDirect instead.
-	dir := ctx.GetPb()
-	cw := new(bytes.Buffer)
-	directory, base := filepath.Split(proto.Src)
-	directory = filepath.Clean(directory)
-	cw.WriteString("protoc ")
-	protoImportPathSet := collection.NewSet()
-	isSamePackage := true
-	for _, ip := range protoImportPath {
-		pip := " --proto_path=" + ip
-		if protoImportPathSet.Contains(pip) {
-			continue
-		}
-
-		abs, err := filepath.Abs(ip)
-		if err != nil {
-			return err
-		}
-
-		if abs == directory {
-			isSamePackage = true
-		} else {
-			isSamePackage = false
-		}
-
-		protoImportPathSet.AddStr(pip)
-		cw.WriteString(pip)
-	}
-	currentPath := " --proto_path=" + directory
-	if !protoImportPathSet.Contains(currentPath) {
-		cw.WriteString(currentPath)
-	}
-
-	cw.WriteString(" " + proto.Name)
-	if strings.Contains(proto.GoPackage, "/") {
-		cw.WriteString(" --go_out=plugins=grpc:" + ctx.GetMain().Filename)
-	} else {
-		cw.WriteString(" --go_out=plugins=grpc:" + dir.Filename)
-	}
-
-	// Compatible with version 1.4.0，github.com/golang/protobuf/protoc-gen-go@v1.4.0
-	// --go_opt usage please see https://developers.google.com/protocol-buffers/docs/reference/go-generated#package
-	optSet := collection.NewSet()
-	for _, op := range goOptions {
-		opt := " --go_opt=" + op
-		if optSet.Contains(opt) {
-			continue
-		}
-
-		optSet.AddStr(op)
-		cw.WriteString(" --go_opt=" + op)
-	}
-
-	var currentFileOpt string
-	if !isSamePackage || (len(proto.GoPackage) > 0 && proto.GoPackage != proto.Package.Name) {
-		if filepath.IsAbs(proto.GoPackage) {
-			currentFileOpt = " --go_opt=M" + base + "=" + proto.GoPackage
-		} else if strings.Contains(proto.GoPackage, string(filepath.Separator)) {
-			currentFileOpt = " --go_opt=M" + base + "=./" + proto.GoPackage
-		} else {
-			currentFileOpt = " --go_opt=M" + base + "=../" + proto.GoPackage
-		}
-	} else {
-		currentFileOpt = " --go_opt=M" + base + "=."
-	}
-
-	if !optSet.Contains(currentFileOpt) {
-		cw.WriteString(currentFileOpt)
-	}
-
-	command := cw.String()
-	g.log.Debug(command)
-	_, err := execx.Run(command, "")
-	if err != nil {
-		if strings.Contains(err.Error(), googleProtocGenGoErr) {
-			return errors.New(`unsupported plugin protoc-gen-go which installed from the following source:
-google.golang.org/protobuf/cmd/protoc-gen-go, 
-github.com/protocolbuffers/protobuf-go/cmd/protoc-gen-go;
-
-Please replace it by the following command, we recommend to use version before v1.3.5:
-go get -u github.com/golang/protobuf/protoc-gen-go`)
-		}
-
-		return err
-	}
-	return nil
+func (g *Generator) GenPb(ctx DirContext, c *ZRpcContext) error {
+	return g.genPbDirect(ctx, c)
 }
 
-func (g *DefaultGenerator) genPbDirect(ctx DirContext, c *ZRpcContext) error {
+func (g *Generator) genPbDirect(ctx DirContext, c *ZRpcContext) error {
 	g.log.Debug("[command]: %s", c.ProtocCmd)
 	pwd, err := os.Getwd()
 	if err != nil {
@@ -126,7 +30,7 @@ func (g *DefaultGenerator) genPbDirect(ctx DirContext, c *ZRpcContext) error {
 	return g.setPbDir(ctx, c)
 }
 
-func (g *DefaultGenerator) setPbDir(ctx DirContext, c *ZRpcContext) error {
+func (g *Generator) setPbDir(ctx DirContext, c *ZRpcContext) error {
 	pbDir, err := findPbFile(c.GoOutput, false)
 	if err != nil {
 		return err
diff --git a/tools/goctl/rpc/generator/genserver.go b/tools/goctl/rpc/generator/genserver.go
index b5dafda9..587a04e6 100644
--- a/tools/goctl/rpc/generator/genserver.go
+++ b/tools/goctl/rpc/generator/genserver.go
@@ -48,7 +48,7 @@ func (s *{{.server}}Server) {{.method}} ({{if .notStream}}ctx context.Context,{{
 )
 
 // GenServer generates rpc server file, which is an implementation of rpc server
-func (g *DefaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
+func (g *Generator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
 	dir := ctx.GetServer()
 	logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package)
 	svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -94,7 +94,7 @@ func (g *DefaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *co
 	return err
 }
 
-func (g *DefaultGenerator) genFunctions(goPackage string, service parser.Service) ([]string, error) {
+func (g *Generator) genFunctions(goPackage string, service parser.Service) ([]string, error) {
 	var functionList []string
 	for _, rpc := range service.RPC {
 		text, err := pathx.LoadTemplate(category, serverFuncTemplateFile, functionTemplate)
diff --git a/tools/goctl/rpc/generator/gensvc.go b/tools/goctl/rpc/generator/gensvc.go
index cd128cc6..c96726fd 100644
--- a/tools/goctl/rpc/generator/gensvc.go
+++ b/tools/goctl/rpc/generator/gensvc.go
@@ -28,7 +28,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
 
 // GenSvc generates the servicecontext.go file, which is the resource dependency of a service,
 // such as rpc dependency, model dependency, etc.
-func (g *DefaultGenerator) GenSvc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
+func (g *Generator) GenSvc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
 	dir := ctx.GetSvc()
 	svcFilename, err := format.FileNamingFormat(cfg.NamingFormat, "service_context")
 	if err != nil {
diff --git a/tools/goctl/rpc/generator/prototmpl.go b/tools/goctl/rpc/generator/prototmpl.go
index 526058f8..e858e9d8 100644
--- a/tools/goctl/rpc/generator/prototmpl.go
+++ b/tools/goctl/rpc/generator/prototmpl.go
@@ -12,6 +12,7 @@ import (
 const rpcTemplateText = `syntax = "proto3";
 
 package {{.package}};
+option go_package="./{{.package}}";
 
 message Request {
   string ping = 1;


Test patch:
diff --git a/core/stores/builder/builder_test.go b/core/stores/builder/builder_test.go
index cc8a3517..69932483 100644
--- a/core/stores/builder/builder_test.go
+++ b/core/stores/builder/builder_test.go
@@ -22,3 +22,20 @@ func TestFieldNames(t *testing.T) {
 		assert.Equal(t, expected, out)
 	})
 }
+
+type mockedUserWithOptions struct {
+	ID       string `db:"id" json:"id,omitempty"`
+	UserName string `db:"user_name,type=varchar,length=255" json:"userName,omitempty"`
+	Sex      int    `db:"sex" json:"sex,omitempty"`
+	UUID     string `db:",type=varchar,length=16" uuid:"uuid,omitempty"`
+	Age      int    `db:"age" json:"age"`
+}
+
+func TestFieldNamesWithTagOptions(t *testing.T) {
+	t.Run("new", func(t *testing.T) {
+		var u mockedUserWithOptions
+		out := RawFieldNames(&u)
+		expected := []string{"`id`", "`user_name`", "`sex`", "`UUID`", "`age`"}
+		assert.Equal(t, expected, out)
+	})
+}
diff --git a/tools/goctl/rpc/generator/gen_test.go b/tools/goctl/rpc/generator/gen_test.go
index d849ba46..1bdd5383 100644
--- a/tools/goctl/rpc/generator/gen_test.go
+++ b/tools/goctl/rpc/generator/gen_test.go
@@ -1,6 +1,7 @@
 package generator
 
 import (
+	"fmt"
 	"go/build"
 	"os"
 	"path/filepath"
@@ -10,26 +11,19 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/zeromicro/go-zero/core/logx"
 	"github.com/zeromicro/go-zero/core/stringx"
-	conf "github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
-var cfg = &conf.Config{
-	NamingFormat: "gozero",
-}
-
 func TestRpcGenerate(t *testing.T) {
 	_ = Clean()
-	dispatcher := NewDefaultGenerator()
-	err := dispatcher.Prepare()
+	g := NewGenerator("gozero")
+	err := g.Prepare()
 	if err != nil {
 		logx.Error(err)
 		return
 	}
 	projectName := stringx.Rand()
-	g := NewRPCGenerator(dispatcher, cfg)
-
 	src := filepath.Join(build.Default.GOPATH, "src")
 	_, err = os.Stat(src)
 	if err != nil {
@@ -46,7 +40,15 @@ func TestRpcGenerate(t *testing.T) {
 
 	// case go path
 	t.Run("GOPATH", func(t *testing.T) {
-		err = g.Generate("./test.proto", projectDir, []string{common}, "Mbase/common.proto=./base")
+		ctx := &ZRpcContext{
+			Src:            "./test.proto",
+			ProtocCmd:      fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s", common, projectDir, projectDir),
+			IsGooglePlugin: true,
+			GoOutput:       projectDir,
+			GrpcOutput:     projectDir,
+			Output:         projectDir,
+		}
+		err = g.Generate(ctx)
 		assert.Nil(t, err)
 		_, err = execx.Run("go test "+projectName, projectDir)
 		if err != nil {
@@ -67,7 +69,15 @@ func TestRpcGenerate(t *testing.T) {
 		}
 
 		projectDir = filepath.Join(workDir, projectName)
-		err = g.Generate("./test.proto", projectDir, []string{common}, "Mbase/common.proto=./base")
+		ctx := &ZRpcContext{
+			Src:            "./test.proto",
+			ProtocCmd:      fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s", common, projectDir, projectDir),
+			IsGooglePlugin: true,
+			GoOutput:       projectDir,
+			GrpcOutput:     projectDir,
+			Output:         projectDir,
+		}
+		err = g.Generate(ctx)
 		assert.Nil(t, err)
 		_, err = execx.Run("go test "+projectName, projectDir)
 		if err != nil {
@@ -79,7 +89,15 @@ func TestRpcGenerate(t *testing.T) {
 
 	// case not in go mod and go path
 	t.Run("OTHER", func(t *testing.T) {
-		err = g.Generate("./test.proto", projectDir, []string{common, src}, "Mbase/common.proto=./base")
+		ctx := &ZRpcContext{
+			Src:            "./test.proto",
+			ProtocCmd:      fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s", common, projectDir, projectDir),
+			IsGooglePlugin: true,
+			GoOutput:       projectDir,
+			GrpcOutput:     projectDir,
+			Output:         projectDir,
+		}
+		err = g.Generate(ctx)
 		assert.Nil(t, err)
 		_, err = execx.Run("go test "+projectName, projectDir)
 		if err != nil {