diff --git a/cmd/gf/internal/cmd/cmd_build.go b/cmd/gf/internal/cmd/cmd_build.go index 5f3a28be6..82dd5742c 100644 --- a/cmd/gf/internal/cmd/cmd_build.go +++ b/cmd/gf/internal/cmd/cmd_build.go @@ -124,13 +124,16 @@ type cBuildInput struct { PackSrc string `short:"ps" name:"packSrc" brief:"pack one or more folders into one go file before building"` PackDst string `short:"pd" name:"packDst" brief:"temporary go file path for pack, this go file will be automatically removed after built" d:"internal/packed/build_pack_data.go"` ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, default is false" orphan:"true"` + DumpENV bool `short:"de" name:"dumpEnv" brief:"dump current go build environment before building binary" orphan:"true"` } type cBuildOutput struct{} func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) { // print used go env - _, _ = Env.Index(ctx, cEnvInput{}) + if in.DumpENV { + _, _ = Env.Index(ctx, cEnvInput{}) + } mlog.SetHeaderPrint(true) diff --git a/cmd/gf/internal/cmd/genpb/genpb.go b/cmd/gf/internal/cmd/genpb/genpb.go index 8528f9096..0c2724dd7 100644 --- a/cmd/gf/internal/cmd/genpb/genpb.go +++ b/cmd/gf/internal/cmd/genpb/genpb.go @@ -12,9 +12,10 @@ import ( type ( CGenPb struct{} CGenPbInput struct { - g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"` - Path string `name:"path" short:"p" dc:"protobuf file folder path" d:"manifest/protobuf"` - Output string `name:"output" short:"o" dc:"output folder path storing generated go files" d:"api"` + g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"` + Path string `name:"path" short:"p" dc:"protobuf file folder path" d:"manifest/protobuf"` + OutputApi string `name:"outputApi" short:"oa" dc:"output folder path storing generated go files of api" d:"api"` + OutputCtrl string `name:"outputCtrl" short:"oc" dc:"output folder path storing generated go files of controller" d:"internal/controller"` } CGenPbOutput struct{} ) @@ -32,9 +33,13 @@ func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err mlog.Fatalf(`proto files folder "%s" does not exist`, in.Path) } // output path checks. - outputPath := gfile.RealPath(in.Output) - if outputPath == "" { - mlog.Fatalf(`output folder "%s" does not exist`, in.Output) + outputApiPath := gfile.RealPath(in.OutputApi) + if outputApiPath == "" { + mlog.Fatalf(`output api folder "%s" does not exist`, in.OutputApi) + } + outputCtrlPath := gfile.RealPath(in.OutputCtrl) + if outputCtrlPath == "" { + mlog.Fatalf(`output controller folder "%s" does not exist`, in.OutputCtrl) } // folder scanning. @@ -52,8 +57,8 @@ func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err for _, file := range files { var command = gproc.NewProcess(protoc, nil) command.Args = append(command.Args, "--proto_path="+gfile.Pwd()) - command.Args = append(command.Args, "--go_out=paths=source_relative:"+outputPath) - command.Args = append(command.Args, "--go-grpc_out=paths=source_relative:"+outputPath) + command.Args = append(command.Args, "--go_out=paths=source_relative:"+outputApiPath) + command.Args = append(command.Args, "--go-grpc_out=paths=source_relative:"+outputApiPath) command.Args = append(command.Args, file) mlog.Print(command.String()) if err = command.Run(ctx); err != nil { @@ -61,7 +66,15 @@ func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err } } // Generate struct tag according comment rules. - err = c.generateStructTag(ctx, generateStructTagInput{PbPath: outputPath}) + err = c.generateStructTag(ctx, generateStructTagInput{OutputApiPath: outputApiPath}) + if err != nil { + return + } + // Generate controllers according comment rules. + err = c.generateController(ctx, generateControllerInput{ + OutputApiPath: outputApiPath, + OutputCtrlPath: outputCtrlPath, + }) if err != nil { return } diff --git a/cmd/gf/internal/cmd/genpb/genpb_controller.go b/cmd/gf/internal/cmd/genpb/genpb_controller.go new file mode 100644 index 000000000..2dad3fc6e --- /dev/null +++ b/cmd/gf/internal/cmd/genpb/genpb_controller.go @@ -0,0 +1,189 @@ +package genpb + +import ( + "context" + "fmt" + + "github.com/gogf/gf/cmd/gf/v2/internal/utility/utils" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" +) + +type generateControllerInput struct { + OutputApiPath string + OutputCtrlPath string +} + +type generateCtrl struct { + Name string + Package string + Version string + Methods []generateCtrlMethod +} + +type generateCtrlMethod struct { + Name string + Definition string +} + +const ( + controllerTemplate = ` +package {Package} + +type Controller struct { + {Version}.Unimplemented{Name}Server +} + +func Register(s *grpcx.GrpcServer) { + {Version}.Register{Name}Server(s.Server, &Controller{}) +} +` + controllerMethodTemplate = ` +func (*Controller) {Definition} { + return nil, gerror.NewCode(gcode.CodeNotImplemented) +} +` +) + +func (c CGenPb) generateController(ctx context.Context, in generateControllerInput) (err error) { + files, err := gfile.ScanDirFile(in.OutputApiPath, "*_grpc.pb.go", true) + if err != nil { + return err + } + var controllers []generateCtrl + for _, file := range files { + fileControllers, err := c.parseControllers(file) + if err != nil { + return err + } + controllers = append(controllers, fileControllers...) + } + if len(controllers) == 0 { + return nil + } + // Generate controller files. + err = c.doGenerateControllers(in, controllers) + return +} + +func (c CGenPb) parseControllers(filePath string) ([]generateCtrl, error) { + var ( + controllers []generateCtrl + content = gfile.GetContents(filePath) + ) + _, err := gregex.ReplaceStringFuncMatch( + `type (\w+)Server interface {([\s\S]+?)}`, + content, + func(match []string) string { + ctrl := generateCtrl{ + Name: match[1], + Package: gfile.Basename(gfile.Dir(gfile.Dir(filePath))), + Version: gfile.Basename(gfile.Dir(filePath)), + Methods: make([]generateCtrlMethod, 0), + } + lines := gstr.Split(match[2], "\n") + for _, line := range lines { + line = gstr.Trim(line) + if line == "" || !gstr.IsLetterUpper(line[0]) { + continue + } + // Comment. + if gregex.IsMatchString(`^//.+`, line) { + continue + } + line, _ = gregex.ReplaceStringFuncMatch( + `^(\w+)\(context\.Context, \*(\w+)\) \(\*(\w+), error\)$`, + line, + func(match []string) string { + return fmt.Sprintf( + `%s(ctx context.Context, req *%s.%s) (res *%s.%s, err error)`, + match[1], ctrl.Version, match[2], ctrl.Version, match[3], + ) + }, + ) + ctrl.Methods = append(ctrl.Methods, generateCtrlMethod{ + Name: gstr.Split(line, "(")[0], + Definition: line, + }) + } + if len(ctrl.Methods) > 0 { + controllers = append(controllers, ctrl) + } + return match[0] + }, + ) + return controllers, err +} + +func (c CGenPb) doGenerateControllers(in generateControllerInput, controllers []generateCtrl) (err error) { + for _, controller := range controllers { + err = c.doGenerateController(in, controller) + if err != nil { + return err + } + } + err = utils.ReplaceGeneratedContentGFV2(in.OutputCtrlPath) + return nil +} + +func (c CGenPb) doGenerateController(in generateControllerInput, controller generateCtrl) (err error) { + var ( + folderPath = gfile.Join(in.OutputCtrlPath, controller.Package) + filePath = gfile.Join(folderPath, controller.Package+".go") + isDirty bool + ) + if !gfile.Exists(folderPath) { + if err = gfile.Mkdir(folderPath); err != nil { + return err + } + } + if !gfile.Exists(filePath) { + templateContent := gstr.ReplaceByMap(controllerTemplate, g.MapStrStr{ + "{Name}": controller.Name, + "{Version}": controller.Version, + "{Package}": controller.Package, + }) + if err = gfile.PutContents(filePath, templateContent); err != nil { + return err + } + isDirty = true + } + // Exist controller content. + var ctrlContent string + files, err := gfile.ScanDirFile(folderPath, "*.go", false) + if err != nil { + return err + } + for _, file := range files { + if ctrlContent != "" { + ctrlContent += "\n" + } + ctrlContent += gfile.GetContents(file) + } + // Generate method content. + var generatedContent string + for _, method := range controller.Methods { + if gstr.Contains(ctrlContent, fmt.Sprintf(`%s(`, method.Name)) { + continue + } + if generatedContent != "" { + generatedContent += "\n" + } + generatedContent += gstr.ReplaceByMap(controllerMethodTemplate, g.MapStrStr{ + "{Definition}": method.Definition, + }) + } + if generatedContent != "" { + err = gfile.PutContentsAppend(filePath, generatedContent) + if err != nil { + return err + } + isDirty = true + } + if isDirty { + utils.GoFmt(filePath) + } + return nil +} diff --git a/cmd/gf/internal/cmd/genpb/genpb_tag.go b/cmd/gf/internal/cmd/genpb/genpb_tag.go index e9738bb80..5ebf69157 100644 --- a/cmd/gf/internal/cmd/genpb/genpb_tag.go +++ b/cmd/gf/internal/cmd/genpb/genpb_tag.go @@ -13,11 +13,11 @@ import ( ) type generateStructTagInput struct { - PbPath string + OutputApiPath string } func (c CGenPb) generateStructTag(ctx context.Context, in generateStructTagInput) (err error) { - files, err := gfile.ScanDirFile(in.PbPath, "*.pb.go", true) + files, err := gfile.ScanDirFile(in.OutputApiPath, "*.pb.go", true) if err != nil { return err } diff --git a/cmd/gf/internal/cmd/genservice/genservice.go b/cmd/gf/internal/cmd/genservice/genservice.go index 413f5f5c6..1d71bcae7 100644 --- a/cmd/gf/internal/cmd/genservice/genservice.go +++ b/cmd/gf/internal/cmd/genservice/genservice.go @@ -258,7 +258,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe } // Replace v1 to v2 for GoFrame. - if err = c.replaceGeneratedServiceContentGFV2(in); err != nil { + if err = utils.ReplaceGeneratedContentGFV2(in.DstFolder); err != nil { return nil, err } mlog.Printf(`gofmt go files in "%s"`, in.DstFolder) @@ -268,14 +268,3 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe mlog.Print(`done!`) return } - -func (c CGenService) replaceGeneratedServiceContentGFV2(in CGenServiceInput) (err error) { - return gfile.ReplaceDirFunc(func(path, content string) string { - if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) { - content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`) - content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`) - return content - } - return content - }, in.DstFolder, "*.go", false) -} diff --git a/cmd/gf/internal/utility/utils/utils.go b/cmd/gf/internal/utility/utils/utils.go index 31394c3e4..7ef00d6b0 100644 --- a/cmd/gf/internal/utility/utils/utils.go +++ b/cmd/gf/internal/utility/utils/utils.go @@ -3,12 +3,13 @@ package utils import ( "context" "fmt" + "golang.org/x/tools/imports" + "github.com/gogf/gf/cmd/gf/v2/internal/consts" "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gproc" "github.com/gogf/gf/v2/text/gstr" - "golang.org/x/tools/imports" ) // GoFmt formats the source file and adds or removes import statements as necessary. @@ -52,3 +53,16 @@ func IsFileDoNotEdit(filePath string) bool { } return gstr.Contains(gfile.GetContents(filePath), consts.DoNotEditKey) } + +// ReplaceGeneratedContentGFV2 replaces generated go content from goframe v1 to v2. +func ReplaceGeneratedContentGFV2(folderPath string) (err error) { + return gfile.ReplaceDirFunc(func(path, content string) string { + if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) { + content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`) + content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`) + content = gstr.Replace(content, `"github.com/gogf/gf/v2/contrib/`, `"github.com/gogf/gf/contrib/`) + return content + } + return content + }, folderPath, "*.go", true) +}