func (gcmd *command) toExecCmd() (cmd *exec.Cmd, err error) { cmd = exec.Command(gcmd.executable, gcmd.argv...) if gcmd.wd != "" { cmd.Dir = gcmd.wd } cmd.Env = effectiveEnv(gcmd.env) cmd.Stdin = os.Stdin if gcmd.capture&CaptureStderr > 0 { cmd.Stderr = newFileWrapper(os.Stderr, &gcmd.buf, ansi.Red) } else { cmd.Stderr = os.Stderr } if gcmd.capture&CaptureStdout > 0 { cmd.Stdout = newFileWrapper(os.Stdout, &gcmd.buf, "") } else { cmd.Stdout = os.Stdout } if verbose { if Env != "" { util.Debug("#", "Env: %s\n", Env) } util.Debug("#", "%s\n", gcmd.commandstr) } return cmd, nil }
func runAndWatch(godoFile string) { done := make(chan bool, 1) run := func(forceBuild bool) (*exec.Cmd, string) { cmd, exe := buildCommand(godoFile, forceBuild) cmd.Start() go func() { err := cmd.Wait() done <- true if err != nil { if isVerbose { util.Debug("godo", "godo process killed\n") } } }() return cmd, exe } bufferSize := 2048 watchr, err := watcher.NewWatcher(bufferSize) if err != nil { util.Panic("project", "%v\n", err) } godoDir := filepath.Dir(godoFile) watchr.WatchRecursive(godoDir) watchr.ErrorHandler = func(err error) { util.Error("godo", "Watcher error %v\n", err) } cmd, exe := run(false) // this function will block forever, Ctrl+C to quit app // var lastHappenedTime int64 watchr.Start() util.Info("godo", "watching %s ...\n", godoDir) <-time.After(godo.GetWatchDelay() + (300 * time.Millisecond)) // forloop: for { select { case event := <-watchr.Event: if event.Path == exe { continue } util.Debug("watchmain", "%+v\n", event) syscall.Kill(cmd.Process.Pid, syscall.SIGQUIT) cmd.Process.Kill() <-done cmd, _ = run(true) } } }
// Run runs all the dependencies of this task and when they have completed, // runs this task. func (task *Task) Run() error { if !watching && task.Complete { util.Debug(task.Name, "Already ran\n") return nil } return task.RunWithEvent(task.Name, nil) }
func (gcmd *command) runAsync() error { cmd, err := gcmd.toExecCmd() if err != nil { return err } id := gcmd.commandstr // kills previously spawned process (if exists) killSpawned(id) runnerWaitGroup.Add(1) waitExit = true go func() { err = cmd.Start() if err != nil { fmt.Println(err.Error()) return } Processes[id] = cmd.Process if verbose { util.Debug("#", "Processes[%q] added\n", id) } cmd.Wait() runnerWaitGroup.Done() }() return nil }
func killSpawned(command string) { process := Processes[command] if process == nil { return } err := process.Kill() delete(Processes, command) if err != nil && !strings.Contains(err.Error(), "process already finished") { util.Error("Start", "Could not kill existing process %+v\n%s\n", process, err.Error()) return } if verbose { util.Debug("#", "Processes[%q] killed\n", command) } }
func buildMain(src string, forceBuild bool) string { mustBeMain(src) dir := filepath.Dir(src) exeFile := "godobin-" + godo.Version if isWindows { exeFile += ".exe" } exe := filepath.Join(dir, exeFile) build := false reasonFormat := "" if isRebuild || forceBuild { build = true reasonFormat = "Rebuilding %s...\n" } else { build = util.Outdated([]string{dir + "/**/*.go"}, []string{exe}) reasonFormat = "Godo tasks changed. Rebuilding %s...\n" } if build { util.Debug("godo", reasonFormat, exe) _, err := godo.Run("go build -a -o "+exeFile, godo.M{"$in": dir}) if err != nil { panic(fmt.Sprintf("Error building %s: %s\n", src, err.Error())) } // for some reason go build does not delete the exe named after the dir // which ends up with Gododir/Gododir if filepath.Base(dir) == "Gododir" { orphanedFile := filepath.Join(dir, filepath.Base(dir)) if _, err := os.Stat(orphanedFile); err == nil { os.Remove(orphanedFile) } } } if isRebuild { util.Info("godo", "ok\n") } return exe }
// RunWithEvent runs this task when triggered from a watch. // *e* FileEvent contains information about the file/directory which changed // in watch mode. func (task *Task) RunWithEvent(logName string, e *watcher.FileEvent) (err error) { if task.RunOnce && task.Complete { util.Debug(task.Name, "Already ran\n") return nil } task.expandGlobs() if !task.shouldRun(e) { util.Info(logName, "up-to-date 0ms\n") return nil } start := time.Now() if len(task.SrcGlobs) > 0 && len(task.SrcFiles) == 0 { util.Error("task", "\""+task.Name+"\" '%v' did not match any files\n", task.SrcGlobs) } // Run this task only if the file matches watch Regexps rebuilt := "" if e != nil { rebuilt = "rebuilt " if !task.isWatchedFile(e.Path) && len(task.SrcGlobs) > 0 { return nil } if verbose { util.Debug(logName, "%s\n", e.String()) } } log := true if task.Handler != nil { context := Context{Task: task, Args: task.argm} defer func() { if p := recover(); p != nil { sp, ok := p.(*softPanic) if !ok { panic(p) } err = fmt.Errorf("%q: %s", logName, sp) } }() task.Handler.Handle(&context) if context.Error != nil { return fmt.Errorf("%q: %s", logName, context.Error.Error()) } } else if len(task.dependencies) > 0 { // no need to log if just dependency log = false } else { util.Info(task.Name, "Ignored. Task does not have a handler or dependencies.\n") return nil } if log { util.Info(logName, "%s%vms\n", rebuilt, time.Since(start).Nanoseconds()/1e6) } task.Complete = true return nil }
func logVerbose(msg string, format string, args ...interface{}) { if !verbose { return } util.Debug(msg, format, args...) }