暫無描述

qst.go 2.5KB

    package main import "flag" import "fmt" import "log" import "os" import "os/exec" import "path" import "strings" import "sync" 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) }, } 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 := os.Args[1] 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 { runAndWatch(file, fn(file)) } else { fmt.Fprintf(os.Stderr, "Error: No command defined for %s files", ext) os.Exit(1) } } func runAndWatch(file string, cmdLine string) { // run command, if file changes (mtime) restart command lastMtime := time.Now() cmd := ShellCmd(cmdLine) cmd.Start() for { info, err := os.Stat(file) if err != nil { log.Fatalf("Error: %s disappeared, exiting.", file) } mtime := info.ModTime() if mtime.After(lastMtime) { log.Printf("%s changed, rerunning", file) cmd.Restart() } lastMtime = mtime time.Sleep(1 * time.Second) } } type RestartableCommand struct { Cmd *exec.Cmd Lock sync.Mutex Name string Args []string } func ShellCmd(cmd string) RestartableCommand { return RestartableCommand{nil, sync.Mutex{}, "sh", []string{"-c", cmd}} } func (c *RestartableCommand) Start() { c.Cmd = exec.Command(c.Name, c.Args...) c.Cmd.Stderr = os.Stderr c.Cmd.Stdout = os.Stdout c.Lock.Lock() go func() { c.Cmd.Run() c.Lock.Unlock() }() } func (c *RestartableCommand) Restart() { c.Cmd.Process.Kill() c.Start() } func runShellCmd(cmd string) { log.Printf("Running: `%s'", cmd) output, err := exec.Command("sh", "-c", cmd).CombinedOutput() os.Stderr.Write(output) log.Printf("Error running command: %s\n", err.Error()) } func isFile(file string) bool { info, err := os.Stat(file) return err == nil && !info.IsDir() }