diff --git a/cmd/gf/internal/cmd/cmd_docker.go b/cmd/gf/internal/cmd/cmd_docker.go index 6fc564a6c..53a2b1d7e 100644 --- a/cmd/gf/internal/cmd/cmd_docker.go +++ b/cmd/gf/internal/cmd/cmd_docker.go @@ -38,40 +38,43 @@ It runs "gf build" firstly to compile the project to binary file. It then runs "docker build" command automatically to generate the docker image. You should have docker installed, and there must be a Dockerfile in the root of the project. ` - cDockerMainBrief = `main file path for "gf build", it's "main.go" in default. empty string for no binary build` - cDockerBuildBrief = `binary build options before docker image build, it's "-a amd64 -s linux" in default` - cDockerFileBrief = `file path of the Dockerfile. it's "manifest/docker/Dockerfile" in default` - cDockerShellBrief = `path of the shell file which is executed before docker build` - cDockerPushBrief = `auto push the docker image to docker registry if "-t" option passed` - cDockerTagsBrief = `tag names for this docker, which are usually used for docker push, multiple tags joined with char ","` - cDockerExtraBrief = `extra build options passed to "docker image"` + cDockerMainBrief = `main file path for "gf build", it's "main.go" in default. empty string for no binary build` + cDockerBuildBrief = `binary build options before docker image build, it's "-a amd64 -s linux" in default` + cDockerFileBrief = `file path of the Dockerfile. it's "manifest/docker/Dockerfile" in default` + cDockerShellBrief = `path of the shell file which is executed before docker build` + cDockerPushBrief = `auto push the docker image to docker registry if "-t" option passed` + cDockerTagNameBrief = `tag name for this docker, pattern like "image:tag". this option is required with TagPrefixes` + cDockerTagPrefixesBrief = `tag prefixes for this docker, which are used for docker push. this option is required with TagName` + cDockerExtraBrief = `extra build options passed to "docker image"` ) func init() { gtag.Sets(g.MapStrStr{ - `cDockerUsage`: cDockerUsage, - `cDockerBrief`: cDockerBrief, - `cDockerEg`: cDockerEg, - `cDockerDc`: cDockerDc, - `cDockerMainBrief`: cDockerMainBrief, - `cDockerFileBrief`: cDockerFileBrief, - `cDockerShellBrief`: cDockerShellBrief, - `cDockerBuildBrief`: cDockerBuildBrief, - `cDockerPushBrief`: cDockerPushBrief, - `cDockerTagsBrief`: cDockerTagsBrief, - `cDockerExtraBrief`: cDockerExtraBrief, + `cDockerUsage`: cDockerUsage, + `cDockerBrief`: cDockerBrief, + `cDockerEg`: cDockerEg, + `cDockerDc`: cDockerDc, + `cDockerMainBrief`: cDockerMainBrief, + `cDockerFileBrief`: cDockerFileBrief, + `cDockerShellBrief`: cDockerShellBrief, + `cDockerBuildBrief`: cDockerBuildBrief, + `cDockerPushBrief`: cDockerPushBrief, + `cDockerTagNameBrief`: cDockerTagNameBrief, + `cDockerTagPrefixesBrief`: cDockerTagPrefixesBrief, + `cDockerExtraBrief`: cDockerExtraBrief, }) } type cDockerInput struct { - g.Meta `name:"docker" config:"gfcli.docker"` - Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"` - File string `name:"file" short:"f" brief:"{cDockerFileBrief}" d:"manifest/docker/Dockerfile"` - Shell string `name:"shell" short:"s" brief:"{cDockerShellBrief}" d:"manifest/docker/docker.sh"` - Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}" d:"-a amd64 -s linux"` - Tags string `name:"tags" short:"t" brief:"{cDockerTagsBrief}"` - Push bool `name:"push" short:"p" brief:"{cDockerPushBrief}" orphan:"true"` - Extra string `name:"extra" short:"e" brief:"{cDockerExtraBrief}"` + g.Meta `name:"docker" config:"gfcli.docker"` + Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"` + File string `name:"file" short:"f" brief:"{cDockerFileBrief}" d:"manifest/docker/Dockerfile"` + Shell string `name:"shell" short:"s" brief:"{cDockerShellBrief}" d:"manifest/docker/docker.sh"` + Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}" d:"-a amd64 -s linux"` + TagName string `name:"tagName" short:"tn" brief:"{cDockerTagNameBrief}" v:"required-with:TagPrefixes"` + TagPrefixes []string `name:"tagPrefixes" short:"tp" brief:"{cDockerTagPrefixesBrief}" v:"required-with:TagName"` + Push bool `name:"push" short:"p" brief:"{cDockerPushBrief}" orphan:"true"` + Extra string `name:"extra" short:"e" brief:"{cDockerExtraBrief}"` } type cDockerOutput struct{} @@ -98,8 +101,14 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput // Docker build. var ( dockerBuildOptions string - dockerTags = gstr.SplitAndTrim(in.Tags, ",") + dockerTags []string ) + if len(in.TagPrefixes) > 0 { + for _, tagPrefix := range in.TagPrefixes { + tagPrefix = gstr.TrimRight(tagPrefix, "/") + dockerTags = append(dockerTags, fmt.Sprintf(`%s/%s`, tagPrefix, in.TagName)) + } + } if len(dockerTags) == 0 { dockerTags = []string{""} } diff --git a/cmd/gf/internal/cmd/cmd_gen_service.go b/cmd/gf/internal/cmd/cmd_gen_service.go index 8a3811c1e..05a9ee639 100644 --- a/cmd/gf/internal/cmd/cmd_gen_service.go +++ b/cmd/gf/internal/cmd/cmd_gen_service.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "fmt" "github.com/gogf/gf/cmd/gf/v2/internal/consts" "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog" @@ -15,39 +16,58 @@ import ( type ( cGenServiceInput struct { - g.Meta `name:"service" brief:"parse logic struct and associated functions to generate service go file"` - Logic string `short:"l" name:"logic" brief:"logic folder path to be parsed" d:"internal/logic"` - Path string `short:"p" name:"path" brief:"folder path storing automatically generated go files" d:"internal/service"` - Pattern string `short:"a" name:"pattern" brief:"regular expression matching struct name for generating service" d:"s(\\w+)"` - WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under Logic folder"` + g.Meta `name:"service" brief:"parse struct and associated functions from packages to generate service go file"` + SrcFolder string `short:"s" name:"srcFolder" brief:"source folder path to be parsed" d:"internal/logic"` + DstFolder string `short:"d" name:"dstFolder" brief:"destination folder path storing automatically generated go files" d:"internal/service"` + StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service" d:"s(\\w+)"` + ImportPrefix string `short:"p" name:"importPrefix" brief:"custom import prefix to calculate import path for generated go files"` + WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under Logic folder"` + OverWrite bool `short:"o" name:"overwrite" brief:"overwrite files that already exist in generating folder" d:"true" orphan:"true"` } cGenServiceOutput struct{} ) func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServiceOutput, err error) { - in.Logic = gstr.Trim(in.Logic, `\/`) + in.SrcFolder = gstr.Trim(in.SrcFolder, `\/`) in.WatchFile = gstr.Trim(in.WatchFile, `\/`) - if !gfile.Exists(in.Logic) { - mlog.Fatalf(`logic folder path "%s" does not exist`, in.Logic) + if !gfile.Exists(in.SrcFolder) { + mlog.Fatalf(`logic folder path "%s" does not exist`, in.SrcFolder) } if in.WatchFile != "" { // It works only if given WatchFile is in Logic folder. - if !gstr.Contains(gstr.Replace(in.WatchFile, "\\", "/"), gstr.Replace(in.Logic, "\\", "/")) { - mlog.Printf(`ignore watch file "%s", not in logic path "%s"`, in.WatchFile, in.Logic) + if !gstr.Contains(gstr.Replace(in.WatchFile, "\\", "/"), gstr.Replace(in.SrcFolder, "\\", "/")) { + mlog.Printf(`ignore watch file "%s", not in source path "%s"`, in.WatchFile, in.SrcFolder) return } } + if in.ImportPrefix == "" { + if !gfile.Exists("go.mod") { + mlog.Fatal("go.mod does not exist in current working directory") + } + var ( + goModContent = gfile.GetContents("go.mod") + match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent) + ) + if len(match) > 1 { + in.ImportPrefix = fmt.Sprintf(`%s/%s`, gstr.Trim(match[1]), gstr.Replace(in.SrcFolder, `\`, `/`)) + } + } + var ( - files []string - fileContent string - matches [][]string - packageName = gstr.ToLower(gfile.Basename(in.Path)) + files []string + fileContent string + matches [][]string + srcPackages []string + dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder)) ) - logicFolders, err := gfile.ScanDir(in.Logic, "*", false) + logicFolders, err := gfile.ScanDir(in.SrcFolder, "*", false) if err != nil { return nil, err } for _, logicFolder := range logicFolders { + if !gfile.IsDir(logicFolder) { + continue + } if files, err = gfile.ScanDir(logicFolder, "*.go", false); err != nil { return nil, err } @@ -75,7 +95,7 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic if !gstr.IsLetterUpper(funcTitle[0]) { continue } - structMatch, err = gregex.MatchString(in.Pattern, structName) + structMatch, err = gregex.MatchString(in.StPattern, structName) if err != nil { return nil, err } @@ -90,24 +110,52 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic interfaceFuncArray.Append(funcTitle) } } + srcPackages = append(srcPackages, fmt.Sprintf(`%s/%s`, in.ImportPrefix, gfile.Basename(logicFolder))) // Generating go files for service. for structName, funcArray := range interfaceMap { var ( - filePath = gfile.Join(in.Path, gstr.ToLower(structName)+".go") + filePath = gfile.Join(in.DstFolder, gstr.ToLower(structName)+".go") generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceContent, g.MapStrStr{ "{StructName}": structName, - "{PackageName}": packageName, + "{PackageName}": dstPackageName, "{FuncDefinition}": funcArray.Join("\n\t"), }) ) + if !in.OverWrite && gfile.Exists(filePath) { + mlog.Printf(`ignore generating service go file: %s`, filePath) + continue + } mlog.Printf(`generating service go file: %s`, filePath) if err = gfile.PutContents(filePath, generatedContent); err != nil { return nil, err } } } - mlog.Printf(`goimports go files in "%s", it may take seconds...`, in.Path) - utils.GoImports(in.Path) + // Generate initialization go file. + if len(srcPackages) > 0 { + var ( + srcPackageName = gstr.ToLower(gfile.Basename(in.SrcFolder)) + srcFilePath = gfile.Join(in.SrcFolder, srcPackageName+".go") + srcImports string + generatedContent string + ) + for _, srcPackage := range srcPackages { + srcImports += fmt.Sprintf(`%s_ "%s"%s`, "\t", srcPackage, "\n") + } + generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceLogicContent, g.MapStrStr{ + "{PackageName}": srcPackageName, + "{Imports}": srcImports, + }) + mlog.Printf(`generating init go file: %s`, srcFilePath) + if err = gfile.PutContents(srcFilePath, generatedContent); err != nil { + return nil, err + } + utils.GoFmt(srcFilePath) + } + + // Go imports updating. + mlog.Printf(`goimports go files in "%s", it may take seconds...`, in.DstFolder) + utils.GoImports(in.DstFolder) // Replica v1 to v2 for GoFrame. err = gfile.ReplaceDirFunc(func(path, content string) string { @@ -117,12 +165,12 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic return content } return content - }, in.Path, "*.go", false) + }, in.DstFolder, "*.go", false) if err != nil { return nil, err } - mlog.Printf(`gofmt go files in "%s"`, in.Path) - utils.GoFmt(in.Path) + mlog.Printf(`gofmt go files in "%s"`, in.DstFolder) + utils.GoFmt(in.DstFolder) mlog.Print(`done!`) return } diff --git a/cmd/gf/internal/consts/consts_gen_service_template.go b/cmd/gf/internal/consts/consts_gen_service_template.go index 0f1f4700d..9ce3174cd 100644 --- a/cmd/gf/internal/consts/consts_gen_service_template.go +++ b/cmd/gf/internal/consts/consts_gen_service_template.go @@ -14,6 +14,9 @@ type I{StructName} interface { var local{StructName} I{StructName} func {StructName}() I{StructName} { + if local{StructName} == nil { + panic("implement not found for interface I{StructName}, forgot register?") + } return local{StructName} } diff --git a/cmd/gf/internal/consts/consts_gen_service_template_logic.go b/cmd/gf/internal/consts/consts_gen_service_template_logic.go new file mode 100644 index 000000000..147983438 --- /dev/null +++ b/cmd/gf/internal/consts/consts_gen_service_template_logic.go @@ -0,0 +1,13 @@ +package consts + +const TemplateGenServiceLogicContent = ` +// ========================================================================== +// Code generated by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package {PackageName} + +import( + {Imports} +) +` diff --git a/cmd/gf/internal/utility/utils/utils.go b/cmd/gf/internal/utility/utils/utils.go index a54d9ff55..7079ffff3 100644 --- a/cmd/gf/internal/utility/utils/utils.go +++ b/cmd/gf/internal/utility/utils/utils.go @@ -17,10 +17,10 @@ func GoFmt(path string) { if gofmtPath == "" { mlog.Fatal(`command "gofmt" not found`) } - var command = fmt.Sprintf(`%s -w -s %s`, gofmtPath, path) + var command = fmt.Sprintf(`%s -w %s`, gofmtPath, path) result, err := gproc.ShellExec(command) if err != nil { - mlog.Fatal(`error executing command "%s": %s`, command, result) + mlog.Fatalf(`error executing command "%s": %s`, command, result) } } @@ -29,9 +29,9 @@ func GoImports(path string) { if goimportsPath == "" { mlog.Fatal(`command "goimports" not found`) } - var command = fmt.Sprintf(`%s -w -s %s`, goimportsPath, path) + var command = fmt.Sprintf(`%s -w %s`, goimportsPath, path) result, err := gproc.ShellExec(command) if err != nil { - mlog.Fatal(`error executing command "%s": %s`, command, result) + mlog.Fatalf(`error executing command "%s": %s`, command, result) } } diff --git a/os/gfile/gfile.go b/os/gfile/gfile.go index 7d29a4279..69de54d1c 100644 --- a/os/gfile/gfile.go +++ b/os/gfile/gfile.go @@ -11,6 +11,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "time" @@ -48,7 +49,7 @@ var ( func init() { // Initialize internal package variable: tempDir. - if Separator != "/" || !Exists(tempDir) { + if runtime.GOOS == "windows" || Separator != "/" || !Exists(tempDir) { tempDir = os.TempDir() } // Initialize internal package variable: selfPath.