From e8a340c1c0e95cc297bbac30d60a1e0316237768 Mon Sep 17 00:00:00 2001 From: anqiansong Date: Tue, 21 Sep 2021 23:13:31 +0800 Subject: [PATCH] Create a symbol link file named protoc-gen-goctl from goctl (#1076) --- go.mod | 1 + go.sum | 2 + tools/goctl/goctl.go | 42 +++++++++++++++++ tools/goctl/internal/version/version.go | 2 +- tools/goctl/rpc/README.md | 47 ++----------------- tools/goctl/rpc/cli/cli.go | 25 +--------- tools/goctl/rpc/generator/defaultgenerator.go | 21 ++++++--- tools/goctl/rpc/generator/genpb.go | 21 +++++++-- tools/goctl/util/console/console.go | 36 ++++++++++++++ tools/goctl/util/env/env.go | 21 ++++++--- 10 files changed, 135 insertions(+), 83 deletions(-) diff --git a/go.mod b/go.mod index 146ea8b1..08764202 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/urfave/cli v1.22.5 github.com/zeromicro/antlr v0.0.1 github.com/zeromicro/ddl-parser v0.0.0-20210712021150-63520aca7348 + github.com/zeromicro/protobuf v0.0.0-20210921042113-636cd51f0c35 // indirect go.etcd.io/etcd/api/v3 v3.5.0 go.etcd.io/etcd/client/v3 v3.5.0 go.opentelemetry.io/otel v1.0.0 diff --git a/go.sum b/go.sum index af9a3225..8f0d4c6d 100644 --- a/go.sum +++ b/go.sum @@ -353,6 +353,8 @@ github.com/zeromicro/antlr v0.0.1 h1:CQpIn/dc0pUjgGQ81y98s/NGOm2Hfru2NNio2I9mQgk github.com/zeromicro/antlr v0.0.1/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M= github.com/zeromicro/ddl-parser v0.0.0-20210712021150-63520aca7348 h1:OhxL9tn28gDeJVzreIUiE5oVxZCjL3tBJ0XBNw8p5R8= github.com/zeromicro/ddl-parser v0.0.0-20210712021150-63520aca7348/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8= +github.com/zeromicro/protobuf v0.0.0-20210921042113-636cd51f0c35 h1:LKLiozf78evAX4+82AHw/rG7TvefD7TJII7XB7Oip7o= +github.com/zeromicro/protobuf v0.0.0-20210921042113-636cd51f0c35/go.mod h1:CJvyESptK/6uU5003fPJQ9Hmubl3vRuGqaLgzUU0R7Y= go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU= diff --git a/tools/goctl/goctl.go b/tools/goctl/goctl.go index 2568dc70..cc83b6b0 100644 --- a/tools/goctl/goctl.go +++ b/tools/goctl/goctl.go @@ -3,7 +3,9 @@ package main import ( "fmt" "os" + "path/filepath" "runtime" + "syscall" "github.com/logrusorgru/aurora" "github.com/tal-tech/go-zero/core/load" @@ -29,7 +31,10 @@ import ( rpc "github.com/tal-tech/go-zero/tools/goctl/rpc/cli" "github.com/tal-tech/go-zero/tools/goctl/tpl" "github.com/tal-tech/go-zero/tools/goctl/upgrade" + "github.com/tal-tech/go-zero/tools/goctl/util/console" + "github.com/tal-tech/go-zero/tools/goctl/util/env" "github.com/urfave/cli" + pluginCtl "github.com/zeromicro/protobuf/protoc-gen-go" ) var commands = []cli.Command{ @@ -637,6 +642,13 @@ func main() { load.Disable() stat.DisableLog() + args := os.Args + pluginName := filepath.Base(args[0]) + if pluginName == protocGenGoctl { + pluginCtl.Generate() + return + } + app := cli.NewApp() app.Usage = "a cli tool to generate code" app.Version = fmt.Sprintf("%s %s/%s", version.BuildVersion, runtime.GOOS, runtime.GOARCH) @@ -646,3 +658,33 @@ func main() { fmt.Println(aurora.Red(errorx.Wrap(err).Error())) } } + +func init() { + err := linkProtocGenGoctl() + if err != nil { + console.Error("%+v", err) + } +} + +const protocGenGoctl = "protoc-gen-goctl" + +func linkProtocGenGoctl() error { + path, err := env.LookPath("goctl") + if err != nil { + return err + } + + dir := filepath.Dir(path) + ext := filepath.Ext(path) + target := filepath.Join(dir, protocGenGoctl) + if len(ext) > 0 { + target = target + ext + } + + err = syscall.Unlink(target) + if err != nil && !os.IsNotExist(err) { + return err + } + + return os.Symlink(path, target) +} diff --git a/tools/goctl/internal/version/version.go b/tools/goctl/internal/version/version.go index bcd8f31d..5d60e9d7 100644 --- a/tools/goctl/internal/version/version.go +++ b/tools/goctl/internal/version/version.go @@ -6,7 +6,7 @@ import ( ) // BuildVersion is the version of goctl. -const BuildVersion = "1.2.1" +const BuildVersion = "1.2.2-beta" // GetGoctlVersion returns BuildVersion func GetGoctlVersion() string { diff --git a/tools/goctl/rpc/README.md b/tools/goctl/rpc/README.md index f277a2c9..231c5359 100644 --- a/tools/goctl/rpc/README.md +++ b/tools/goctl/rpc/README.md @@ -163,12 +163,6 @@ OPTIONS: ### 注意事项 -* `google.golang.org/grpc`需要降级到 `v1.29.1`,且protoc-gen-go版本不能高于v1.3.2(see [https://github.com/grpc/grpc-go/issues/3347](https://github.com/grpc/grpc-go/issues/3347))即 - - ```shell script - replace google.golang.org/grpc => google.golang.org/grpc v1.29.1 - ``` - * proto不支持暂多文件同时生成 * proto不支持外部依赖包引入,message不支持inline * 目前main文件、shared文件、handler文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有 @@ -232,40 +226,9 @@ service Greet { ## 常见问题解决(go mod工程) * 错误一: +```text +A required privilege is not held by the client. +``` +这个问题只有goctl 版本在`goctl.exe version 1.2.1` 以后的 Windows操作系统出现,主要原因是goctl需要以管理员身份运行,这样才能将`goctl.exe` 创建一个 `ptocot-gen-gcotl` +的符号链接。 - ```golang - pb/xx.pb.go:220:7: undefined: grpc.ClientConnInterface - pb/xx.pb.go:224:11: undefined: grpc.SupportPackageIsVersion6 - pb/xx.pb.go:234:5: undefined: grpc.ClientConnInterface - pb/xx.pb.go:237:24: undefined: grpc.ClientConnInterface - ``` - - 解决方法:请将`protoc-gen-go`版本降至v1.3.2及一下 - -* 错误二: - - ```golang - - # go.etcd.io/etcd/clientv3/balancer/picker - ../../../go/pkg/mod/go.etcd.io/etcd@v0.0.0-20200402134248-51bdeb39e698/clientv3/balancer/picker/err.go:25:9: cannot use &errPicker literal (type *errPicker) as type Picker in return argument:*errPicker does not implement Picker (wrong type for Pick method) - have Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) - want Pick(balancer.PickInfo) (balancer.PickResult, error) - ../../../go/pkg/mod/go.etcd.io/etcd@v0.0.0-20200402134248-51bdeb39e698/clientv3/balancer/picker/roundrobin_balanced.go:33:9: cannot use &rrBalanced literal (type *rrBalanced) as type Picker in return argument: - *rrBalanced does not implement Picker (wrong type for Pick method) - have Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) - want Pick(balancer.PickInfo) (balancer.PickResult, error) - #github.com/tal-tech/go-zero/zrpc/internal/balancer/p2c - ../../../go/pkg/mod/github.com/tal-tech/go-zero@v1.0.12/zrpc/internal/balancer/p2c/p2c.go:41:32: not enough arguments in call to base.NewBalancerBuilder - have (string, *p2cPickerBuilder) - want (string, base.PickerBuilder, base.Config) - ../../../go/pkg/mod/github.com/tal-tech/go-zero@v1.0.12/zrpc/internal/balancer/p2c/p2c.go:58:9: cannot use &p2cPicker literal (type *p2cPicker) as type balancer.Picker in return argument: - *p2cPicker does not implement balancer.Picker (wrong type for Pick method) - have Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) - want Pick(balancer.PickInfo) (balancer.PickResult, error) - ``` - - 解决方法: - - ```golang - replace google.golang.org/grpc => google.golang.org/grpc v1.29.1 - ``` diff --git a/tools/goctl/rpc/cli/cli.go b/tools/goctl/rpc/cli/cli.go index 46ec562b..18bc5de0 100644 --- a/tools/goctl/rpc/cli/cli.go +++ b/tools/goctl/rpc/cli/cli.go @@ -4,22 +4,17 @@ import ( "errors" "fmt" "path/filepath" - "runtime" + + "github.com/urfave/cli" "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/env" - "github.com/urfave/cli" ) // 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 { - if err := prepare(); err != nil { - return err - } - src := c.String("src") out := c.String("dir") style := c.String("style") @@ -47,22 +42,6 @@ func RPC(c *cli.Context) error { 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 { diff --git a/tools/goctl/rpc/generator/defaultgenerator.go b/tools/goctl/rpc/generator/defaultgenerator.go index 61725161..68288d4d 100644 --- a/tools/goctl/rpc/generator/defaultgenerator.go +++ b/tools/goctl/rpc/generator/defaultgenerator.go @@ -1,9 +1,11 @@ package generator import ( - "os/exec" + "fmt" + "runtime" "github.com/tal-tech/go-zero/tools/goctl/util/console" + "github.com/tal-tech/go-zero/tools/goctl/util/env" ) // DefaultGenerator defines the environment needs of rpc service generation @@ -25,17 +27,24 @@ func NewDefaultGenerator() Generator { // 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 { - _, err := exec.LookPath("go") - if err != nil { + 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 } - _, err = exec.LookPath("protoc") - if err != nil { + if _, err := env.LookUpProtoc(); err != nil { return err } - _, err = exec.LookPath("protoc-gen-go") + _, err := env.LookUpProtocGenGoctl() + if err == nil { + return nil + } + g.log.Warning("%+v", err) + _, err = env.LookUpProtocGenGo() return err } diff --git a/tools/goctl/rpc/generator/genpb.go b/tools/goctl/rpc/generator/genpb.go index 683fe4c5..289b8067 100644 --- a/tools/goctl/rpc/generator/genpb.go +++ b/tools/goctl/rpc/generator/genpb.go @@ -10,6 +10,7 @@ import ( conf "github.com/tal-tech/go-zero/tools/goctl/config" "github.com/tal-tech/go-zero/tools/goctl/rpc/execx" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser" + "github.com/tal-tech/go-zero/tools/goctl/util/env" ) const googleProtocGenGoErr = `--go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC` @@ -17,6 +18,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 { + var useGoctl bool + _, err := env.LookUpProtocGenGoctl() + if err == nil { + useGoctl = true + } + dir := ctx.GetPb() cw := new(bytes.Buffer) directory, base := filepath.Split(proto.Src) @@ -50,10 +57,15 @@ func (g *DefaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto } cw.WriteString(" " + proto.Name) + outFlag := " --go_out" + if useGoctl { + outFlag = " --goctl_out" + } + if strings.Contains(proto.GoPackage, "/") { - cw.WriteString(" --go_out=plugins=grpc:" + ctx.GetMain().Filename) + cw.WriteString(outFlag + "=plugins=grpc:" + ctx.GetMain().Filename) } else { - cw.WriteString(" --go_out=plugins=grpc:" + dir.Filename) + cw.WriteString(outFlag + "=plugins=grpc:" + dir.Filename) } // Compatible with version 1.4.0,github.com/golang/protobuf/protoc-gen-go@v1.4.0 @@ -66,7 +78,6 @@ func (g *DefaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto } optSet.AddStr(op) - cw.WriteString(" --go_opt=" + op) } var currentFileOpt string @@ -82,13 +93,13 @@ func (g *DefaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto currentFileOpt = " --go_opt=M" + base + "=." } - if !optSet.Contains(currentFileOpt) { + if !optSet.Contains(currentFileOpt) && !useGoctl { cw.WriteString(currentFileOpt) } command := cw.String() g.log.Debug(command) - _, err := execx.Run(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: diff --git a/tools/goctl/util/console/console.go b/tools/goctl/util/console/console.go index 36a48859..dacb8e02 100644 --- a/tools/goctl/util/console/console.go +++ b/tools/goctl/util/console/console.go @@ -23,7 +23,9 @@ type ( MarkDone() Must(err error) } + colorConsole struct{} + // for idea log ideaConsole struct{} ) @@ -140,3 +142,37 @@ func println(msg interface{}) { fmt.Println(msg) } + +var defaultConsole = new(colorConsole) + +func Success(format string, a ...interface{}) { + defaultConsole.Success(format, a...) +} + +func Info(format string, a ...interface{}) { + defaultConsole.Info(format, a...) +} + +func Debug(format string, a ...interface{}) { + defaultConsole.Debug(format, a...) +} + +func Warning(format string, a ...interface{}) { + defaultConsole.Warning(format, a...) +} + +func Error(format string, a ...interface{}) { + defaultConsole.Error(format, a...) +} + +func Fatalln(format string, a ...interface{}) { + defaultConsole.Fatalln(format, a...) +} + +func MarkDone() { + defaultConsole.MarkDone() +} + +func Must(err error) { + defaultConsole.Must(err) +} diff --git a/tools/goctl/util/env/env.go b/tools/goctl/util/env/env.go index 9e9e0a77..6bcef662 100644 --- a/tools/goctl/util/env/env.go +++ b/tools/goctl/util/env/env.go @@ -11,10 +11,11 @@ import ( ) const ( - bin = "bin" - binGo = "go" - binProtoc = "protoc" - binProtocGenGo = "protoc-gen-go" + bin = "bin" + binGo = "go" + binProtoc = "protoc" + binProtocGenGo = "protoc-gen-go" + binProtocGenGoctl = "protoc-gen-goctl" ) // LookUpGo searches an executable go in the directories @@ -38,14 +39,22 @@ func LookUpProtoc() (string, error) { return LookPath(xProtoc) } -// LookUpProtocGenGo searches an executable protoc-gen-go in the directories -// named by the PATH environment variable. +// Deprecated: LookUpProtocGenGo searches an executable protoc-gen-go in the directories +// named by the PATH environment variable, use LookUpProtocGenGoctl instead. func LookUpProtocGenGo() (string, error) { suffix := getExeSuffix() xProtocGenGo := binProtocGenGo + suffix return LookPath(xProtocGenGo) } +// LookUpProtocGenGoctl searches an executable protoc-gen-go in the directories +// named by the PATH environment variable. +func LookUpProtocGenGoctl() (string, error) { + suffix := getExeSuffix() + xProtocGenGo := binProtocGenGoctl + suffix + return LookPath(xProtocGenGo) +} + // LookPath searches for an executable named file in the // directories named by the PATH environment variable, // for the os windows, the named file will be spliced with the