Няма описание

qst.go 3.0KB

    package main import "errors" import "flag" import "fmt" import "log" import "os" import "os/exec" import "os/signal" import "path" import "strings" import "syscall" import "time" /* # the plan qst: run things quickly qst - detects the current project type and runs it qst hello.rb - runs `ruby hello.rb` qst hello.go - compiles & runs hello.go qst -watch hello.go - watches changes to hello.go and recompiles and runs it on changes */ var mappings = map[string]func(string) string{ ".go": func(name string) string { return fmt.Sprintf("go build %s && ./%s", name, strings.TrimSuffix(name, path.Ext(name))) }, ".rb": func(name string) string { return fmt.Sprintf("ruby %s", name) }, ".py": func(name string) string { return fmt.Sprintf("python %s", name) }, } var delay = flag.Duration("delay", 1*time.Second, "time to wait until restart") var autoRestart = flag.Bool("autorestart", true, "restart after command exists") func main() { log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: %s <file>\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() args := flag.Args() if len(args) < 1 { flag.Usage() os.Exit(1) } file := args[0] if !isFile(file) { fmt.Fprintf(os.Stderr, "Error: %s is not a file.\n", file) os.Exit(1) } ext := path.Ext(file) fn, found := mappings[ext] if !found { log.Fatalf("error: no mapping found for `%s'", file) } runner := &Runner{nil, fn(file), false, *autoRestart} go runCmd(file, runner) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill) s := <-c log.Printf("got signal: %s, exiting...", s) runner.Stop() } func runCmd(file string, runner *Runner) { runner.Start() lastMtime := time.Now() for { info, err := os.Stat(file) if os.IsNotExist(err) { log.Fatalf("`%s' disappeared, exiting") } mtime := info.ModTime() if mtime.After(lastMtime) { log.Printf("`%s' changed, trying to restart", file) runner.Restart() } lastMtime = mtime time.Sleep(1 * time.Second) } } type Runner struct { cmd *exec.Cmd shellCmd string started bool restart bool } func (r *Runner) Start() error { if r.started { return errors.New("already started, use Restart()") } r.started = true go func() { for { log.Printf("running %s", r.shellCmd) r.cmd = exec.Command("sh", "-c", r.shellCmd) r.cmd.Stderr = os.Stderr r.cmd.Stdout = os.Stdout r.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} err := r.cmd.Run() log.Printf("%s finished: %s", r.shellCmd, err) time.Sleep(*delay) if !r.restart { r.started = false break } } }() return nil } func (r *Runner) Kill() error { pgid, err := syscall.Getpgid(r.cmd.Process.Pid) if err == nil { syscall.Kill(-pgid, syscall.SIGTERM) } return err } func (r *Runner) Restart() error { if r.started { return r.Kill() } else { return r.Start() } } func (r *Runner) Stop() { r.restart = false r.Kill() } func isFile(file string) bool { info, err := os.Stat(file) return err == nil && !info.IsDir() }