From f0ed2370a3b33e8606562dfbba03f83d21898b81 Mon Sep 17 00:00:00 2001 From: anqiansong Date: Sat, 11 Sep 2021 12:48:32 +0800 Subject: [PATCH] fix #1014 (#1018) * fix #1014 * remove unused code * * optimize generate pb.go on Windows * format code * optimize console.go * version rollback Co-authored-by: anqiansong --- tools/goctl/rpc/cli/cli.go | 6 +- tools/goctl/rpc/generator/defaultgenerator.go | 20 --- tools/goctl/rpc/generator/gen.go | 5 - tools/goctl/rpc/generator/gen_test.go | 18 ++- tools/goctl/rpc/generator/generator.go | 1 - tools/goctl/rpc/generator/genpb.go | 131 +++++++++++------- tools/goctl/util/console/console.go | 26 +++- tools/goctl/util/path.go | 7 + 8 files changed, 128 insertions(+), 86 deletions(-) diff --git a/tools/goctl/rpc/cli/cli.go b/tools/goctl/rpc/cli/cli.go index 46ec562b..9574fd15 100644 --- a/tools/goctl/rpc/cli/cli.go +++ b/tools/goctl/rpc/cli/cli.go @@ -6,10 +6,11 @@ import ( "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, @@ -57,9 +58,6 @@ func prepare() error { if _, err := env.LookUpProtoc(); err != nil { return err } - if _, err := env.LookUpProtocGenGo(); err != nil { - return err - } return nil } diff --git a/tools/goctl/rpc/generator/defaultgenerator.go b/tools/goctl/rpc/generator/defaultgenerator.go index 61725161..d70f96ca 100644 --- a/tools/goctl/rpc/generator/defaultgenerator.go +++ b/tools/goctl/rpc/generator/defaultgenerator.go @@ -1,8 +1,6 @@ package generator import ( - "os/exec" - "github.com/tal-tech/go-zero/tools/goctl/util/console" ) @@ -21,21 +19,3 @@ func NewDefaultGenerator() Generator { 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 { - _, err := exec.LookPath("go") - if err != nil { - return err - } - - _, err = exec.LookPath("protoc") - if err != nil { - return err - } - - _, err = exec.LookPath("protoc-gen-go") - - return err -} diff --git a/tools/goctl/rpc/generator/gen.go b/tools/goctl/rpc/generator/gen.go index 02b32b74..2ca559d6 100644 --- a/tools/goctl/rpc/generator/gen.go +++ b/tools/goctl/rpc/generator/gen.go @@ -47,11 +47,6 @@ func (g *RPCGenerator) Generate(src, target string, protoImportPath []string, go return err } - err = g.g.Prepare() - if err != nil { - return err - } - projectCtx, err := ctx.Prepare(abs) if err != nil { return err diff --git a/tools/goctl/rpc/generator/gen_test.go b/tools/goctl/rpc/generator/gen_test.go index 948cb1e0..3970fcae 100644 --- a/tools/goctl/rpc/generator/gen_test.go +++ b/tools/goctl/rpc/generator/gen_test.go @@ -1,9 +1,11 @@ package generator import ( + "fmt" "go/build" "os" "path/filepath" + "runtime" "strings" "testing" @@ -13,6 +15,7 @@ import ( "github.com/tal-tech/go-zero/core/stringx" 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/util/env" ) var cfg = &conf.Config{ @@ -22,7 +25,7 @@ var cfg = &conf.Config{ func TestRpcGenerate(t *testing.T) { _ = Clean() dispatcher := NewDefaultGenerator() - err := dispatcher.Prepare() + err := prepare() if err != nil { logx.Error(err) return @@ -89,3 +92,16 @@ func TestRpcGenerate(t *testing.T) { } }) } + +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 + } + return nil +} diff --git a/tools/goctl/rpc/generator/generator.go b/tools/goctl/rpc/generator/generator.go index cad3895b..deade549 100644 --- a/tools/goctl/rpc/generator/generator.go +++ b/tools/goctl/rpc/generator/generator.go @@ -7,7 +7,6 @@ import ( // 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 diff --git a/tools/goctl/rpc/generator/genpb.go b/tools/goctl/rpc/generator/genpb.go index 683fe4c5..181ae01d 100644 --- a/tools/goctl/rpc/generator/genpb.go +++ b/tools/goctl/rpc/generator/genpb.go @@ -2,45 +2,36 @@ package generator import ( "bytes" - "errors" + "fmt" + "io/ioutil" + "os" "path/filepath" + "runtime" "strings" "github.com/tal-tech/go-zero/core/collection" 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" + "github.com/tal-tech/go-zero/tools/goctl/vars" ) -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, goOptions ...string) error { dir := ctx.GetPb() cw := new(bytes.Buffer) - directory, base := filepath.Split(proto.Src) + directory, _ := 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) } @@ -56,50 +47,88 @@ func (g *DefaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto 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 - } + return g.generatePbWithVersion132(cw.String()) +} - optSet.AddStr(op) - cw.WriteString(" --go_opt=" + op) +// generatePbWithVersion132 generates pb.go by specifying protoc-gen-go@1.3.2 version +func (g *DefaultGenerator) generatePbWithVersion132(cmd string) error { + goctlHome, err := util.GetGoctlHome() + if err != nil { + return err } - 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 + "=." + err = util.MkdirIfNotExist(goctlHome) + if err != nil { + return err } - if !optSet.Contains(currentFileOpt) { - cw.WriteString(currentFileOpt) + goctlHomeBin := filepath.Join(goctlHome, "bin") + err = util.MkdirIfNotExist(goctlHomeBin) + if err != nil { + return err } - 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; + protocGenGo := filepath.Join(goctlHome, "bin", "protoc-gen-go") + g.log.Debug("checking protoc-gen-go state ...") + goGetCmd := "\ngo install github.com/golang/protobuf/protoc-gen-go@v1.3.2" -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`) + if util.FileExists(protocGenGo) { + g.log.Success("protoc-gen-go exists ...") + goGetCmd = "" + } else { + g.log.Error("missing protoc-gen-go: downloading ...") + } + + goos := runtime.GOOS + switch goos { + case vars.OsLinux, vars.OsMac: + cmd = getUnixLikeCmd(goctlHome, goctlHomeBin, goGetCmd, cmd) + g.log.Debug("%s", cmd) + case vars.OsWindows: + cmd = getWindowsCmd(goctlHome, goctlHomeBin, goGetCmd, cmd) + // Do not support to execute commands in context, the solution is created + // a batch file to execute it on Windows. + batFile, err := createBatchFile(goctlHome, cmd) + if err != nil { + return err } - return err + g.log.Debug("%s", cmd) + cmd = batFile + default: + return fmt.Errorf("unsupported os: %s", goos) + } + + _, err = execx.Run(cmd, "") + return err +} + +func getUnixLikeCmd(goctlHome, goctlHomeBin, goGetCmd, cmd string) string { + return fmt.Sprintf(`export GOPATH=%s +export GOBIN=%s +export PATH=$PATH:$GOPATH:$GOBIN +export GO111MODULE=on +export GOPROXY=https://goproxy.cn %s +%s`, goctlHome, goctlHomeBin, goGetCmd, cmd) +} + +func getWindowsCmd(goctlHome, goctlHomeBin, goGetCmd, cmd string) string { + return fmt.Sprintf(`set GOPATH=%s +set GOBIN=%s +set path=%s +set GO111MODULE=on +set GOPROXY=https://goproxy.cn %s +%s`, goctlHome, goctlHomeBin, "%path%;"+goctlHome+";"+goctlHomeBin, goGetCmd, cmd) +} + +func createBatchFile(goctlHome, cmd string) (string, error) { + batFile := filepath.Join(goctlHome, ".generate.bat") + if !util.FileExists(batFile) { + err := ioutil.WriteFile(batFile, []byte(cmd), os.ModePerm) + if err != nil { + return "", err + } } - return nil + + return batFile, nil } diff --git a/tools/goctl/util/console/console.go b/tools/goctl/util/console/console.go index 9ed43371..a4502345 100644 --- a/tools/goctl/util/console/console.go +++ b/tools/goctl/util/console/console.go @@ -3,8 +3,11 @@ package console import ( "fmt" "os" + "runtime" "github.com/logrusorgru/aurora" + + "github.com/tal-tech/go-zero/tools/goctl/vars" ) type ( @@ -46,22 +49,22 @@ func (c *colorConsole) Info(format string, a ...interface{}) { func (c *colorConsole) Debug(format string, a ...interface{}) { msg := fmt.Sprintf(format, a...) - fmt.Println(aurora.Blue(msg)) + println(aurora.Blue(msg)) } func (c *colorConsole) Success(format string, a ...interface{}) { msg := fmt.Sprintf(format, a...) - fmt.Println(aurora.Green(msg)) + println(aurora.Green(msg)) } func (c *colorConsole) Warning(format string, a ...interface{}) { msg := fmt.Sprintf(format, a...) - fmt.Println(aurora.Yellow(msg)) + println(aurora.Yellow(msg)) } func (c *colorConsole) Error(format string, a ...interface{}) { msg := fmt.Sprintf(format, a...) - fmt.Println(aurora.Red(msg)) + println(aurora.Red(msg)) } func (c *colorConsole) Fatalln(format string, a ...interface{}) { @@ -123,3 +126,18 @@ func (i *ideaConsole) Must(err error) { i.Fatalln("%+v", err) } } + +func println(msg interface{}) { + value, ok := msg.(aurora.Value) + if !ok { + fmt.Println(msg) + } + + goos := runtime.GOOS + if goos == vars.OsWindows { + fmt.Println(value.Value()) + return + } + + fmt.Println(msg) +} diff --git a/tools/goctl/util/path.go b/tools/goctl/util/path.go index d4ae2c24..3c94e1a8 100644 --- a/tools/goctl/util/path.go +++ b/tools/goctl/util/path.go @@ -5,6 +5,7 @@ import ( "os" "path" "path/filepath" + "runtime" "strings" "github.com/tal-tech/go-zero/tools/goctl/vars" @@ -115,6 +116,12 @@ func FindProjectPath(loc string) (string, bool) { // ReadLink returns the destination of the named symbolic link recursively. func ReadLink(name string) (string, error) { + goos := runtime.GOOS + switch goos { + case vars.OsWindows: + return name, nil + } + name, err := filepath.Abs(name) if err != nil { return "", err