diff --git a/cmd/gf/internal/cmd/cmd_run.go b/cmd/gf/internal/cmd/cmd_run.go index bcdbea2901c..c428cfcbefe 100644 --- a/cmd/gf/internal/cmd/cmd_run.go +++ b/cmd/gf/internal/cmd/cmd_run.go @@ -9,6 +9,7 @@ package cmd import ( "context" "fmt" + "os" "runtime" "strings" @@ -19,6 +20,7 @@ import ( "github.com/gogf/gf/v2/os/gproc" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtimer" + "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gtag" "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog" @@ -33,10 +35,11 @@ type cRun struct { } type cRunApp struct { - File string // Go run file name. - Path string // Directory storing built binary. - Options string // Extra "go run" options. - Args string // Custom arguments. + File string // Go run file name. + Path string // Directory storing built binary. + Options string // Extra "go run" options. + Args string // Custom arguments. + WatchPaths []string // Watch paths for live reload. } const ( @@ -51,10 +54,11 @@ gf run main.go -mod=vendor The "run" command is used for running go codes with hot-compiled-like feature, which compiles and runs the go codes asynchronously when codes change. ` - cRunFileBrief = `building file path.` - cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default` - cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined` - cRunArgsBrief = `custom arguments for your process` + cRunFileBrief = `building file path.` + cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default` + cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined` + cRunArgsBrief = `custom arguments for your process` + cRunWatchPathsBrief = `watch additional paths for live reload, separated by ",". i.e. "manifest/config/config.yaml"` ) var ( @@ -63,24 +67,26 @@ var ( func init() { gtag.Sets(g.MapStrStr{ - `cRunUsage`: cRunUsage, - `cRunBrief`: cRunBrief, - `cRunEg`: cRunEg, - `cRunDc`: cRunDc, - `cRunFileBrief`: cRunFileBrief, - `cRunPathBrief`: cRunPathBrief, - `cRunExtraBrief`: cRunExtraBrief, - `cRunArgsBrief`: cRunArgsBrief, + `cRunUsage`: cRunUsage, + `cRunBrief`: cRunBrief, + `cRunEg`: cRunEg, + `cRunDc`: cRunDc, + `cRunFileBrief`: cRunFileBrief, + `cRunPathBrief`: cRunPathBrief, + `cRunExtraBrief`: cRunExtraBrief, + `cRunArgsBrief`: cRunArgsBrief, + `cRunWatchPathsBrief`: cRunWatchPathsBrief, }) } type ( cRunInput struct { - g.Meta `name:"run"` - File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"` - Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"` - Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"` - Args string `name:"args" short:"a" brief:"{cRunArgsBrief}"` + g.Meta `name:"run"` + File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"` + Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"` + Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"` + Args string `name:"args" short:"a" brief:"{cRunArgsBrief}"` + WatchPaths string `name:"watchPaths" short:"w" brief:"{cRunWatchPathsBrief}"` } cRunOutput struct{} ) @@ -92,24 +98,27 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err } app := &cRunApp{ - File: in.File, - Path: in.Path, - Options: in.Extra, - Args: in.Args, + File: in.File, + Path: in.Path, + Options: in.Extra, + Args: in.Args, + WatchPaths: strings.Split(in.WatchPaths, ","), } dirty := gtype.NewBool() _, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) { - if gfile.ExtName(event.Path) != "go" { + if gfile.ExtName(event.Path) != "go" && !matchWatchPaths(app.WatchPaths, event.Path) { return } + // Variable `dirty` is used for running the changes only one in one second. if !dirty.Cas(false, true) { return } + // With some delay in case of multiple code changes in very short interval. gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) { defer dirty.Set(false) - mlog.Printf(`go file changes: %s`, event.String()) + mlog.Printf(`file changes: %s`, event.String()) app.Run(ctx) }) }) @@ -152,7 +161,6 @@ func (app *cRunApp) Run(ctx context.Context) { if process != nil { if err := process.Kill(); err != nil { mlog.Debugf("kill process error: %s", err.Error()) - //return } } // Run the binary file. @@ -171,3 +179,15 @@ func (app *cRunApp) Run(ctx context.Context) { mlog.Printf("build running pid: %d", pid) } } + +func matchWatchPaths(watchPaths []string, eventPath string) bool { + pathSeparator := string(os.PathSeparator) + for _, path := range watchPaths { + path = gstr.TrimLeftStr(path, pathSeparator) + path = gstr.TrimLeftStr(path, "."+pathSeparator) + if gstr.HasSuffix(eventPath, pathSeparator+path) { + return true + } + } + return false +}