瀏覽代碼

Create a symbol link file named protoc-gen-goctl from goctl (#1076)

anqiansong 3 年之前
父節點
當前提交
e8a340c1c0

+ 1 - 0
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

+ 2 - 0
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=

+ 42 - 0
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)
+}

+ 1 - 1
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 {

+ 5 - 42
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
-    ```

+ 2 - 23
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 {

+ 15 - 6
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
 }

+ 16 - 5
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:

+ 36 - 0
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)
+}

+ 15 - 6
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