func watchTask(root string, logName string, handler func(e *watcher.FileEvent)) { bufferSize := 2048 watchr, err := watcher.NewWatcher(bufferSize) if err != nil { util.Panic("project", "%v\n", err) } watchr.WatchRecursive(root) watchr.ErrorHandler = func(err error) { util.Error("project", "%v\n", err) } // this function will block forever, Ctrl+C to quit app var lastHappenedTime int64 firstTime := true for { if firstTime { util.Info(logName, "watching %s ...\n", root) firstTime = false } event := <-watchr.Event //util.Debug("DBG", "watchr.Event %+v\n", event) isOlder := event.UnixNano < lastHappenedTime lastHappenedTime = event.UnixNano if isOlder { continue } handler(event) } }
// Expands glob patterns. func (task *Task) expandGlobs() { files, regexps, err := Glob(task.WatchGlobs) if err != nil { util.Error(task.Name, "%v", err) return } task.WatchRegexps = regexps task.WatchFiles = files }
// 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) error { if task.RunOnce && task.Complete { //util.Debug(task.Name, "Already ran\n") return nil } start := time.Now() if len(task.WatchGlobs) > 0 && len(task.WatchFiles) == 0 { task.expandGlobs() if len(task.WatchFiles) == 0 { util.Error("task", "\""+task.Name+"\" '%v' did not match any files\n", task.WatchGlobs) } } // Run this task only if the file matches watch Regexps rebuilt := "" if e != nil { rebuilt = "rebuilt " if !task.isWatchedFile(e) { return nil } if verbose { util.Debug(logName, "%s\n", e.String()) } } var err error log := true if task.Handler != nil { context := Context{Task: task, Args: contextArgm} err = task.Handler.Handle(&context) if err != nil { return fmt.Errorf("%q: %s", logName, err.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 } elapsed := time.Now().Sub(start) if log { util.Info(logName, "%s%vms\n", rebuilt, elapsed.Nanoseconds()/1e6) } task.Complete = true return nil }
func (project *Project) mustTask(name string) (*Project, *Task) { namespace, taskName := project.namespaceTaskName(name) proj := project.Namespace[namespace] if proj == nil { util.Panic("project", "Could not find project having namespace \"%s\"\n", namespace) } task := proj.Tasks[taskName] if task == nil { util.Error("ERR", `"%s" task is not defined`+"\n", name) os.Exit(1) } return proj, task }
func killSpawned(command string) { process := Processes[command] if process == nil { return } err := process.Kill() delete(Processes, command) if err != nil { 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) } }
// Watch watches the Files of a task and reruns the task on a watch event. Any // direct dependency is also watched. Returns true if watching. func (project *Project) Watch(names []string, isParent bool) bool { funcs := []func(){} taskClosure := func(project *Project, task *Task, taskname string, logName string) func() { globs, _ := project.gatherWatchInfo(task) paths := calculateWatchPaths(globs) return func() { if len(paths) == 0 { return } for _, pth := range paths { go func(path string) { watchTask(path, logName, func(e *watcher.FileEvent) { err := project.run(taskname, taskname, e) if err != nil { util.Error("ERR", "%s\n", err.Error()) } }) }(pth) } } } for _, taskname := range names { proj, task := project.mustTask(taskname) if len(task.WatchFiles) > 0 { funcs = append(funcs, taskClosure(proj, task, taskname, taskname)) } } if len(funcs) > 0 { done := all(funcs) <-done return true } return false }
func checkError(err error, format string, args ...interface{}) { if err != nil { util.Error("ERR", format, args...) os.Exit(1) } }
func godo(tasksFunc func(*Project), argv []string) { if argv == nil { argm = minimist.Parse() } else { argm = minimist.ParseArgv(argv) } help = argm.ZeroBool("help", "h", "?") verbose = argm.ZeroBool("verbose", "v") version = argm.ZeroBool("version", "V") watching = argm.ZeroBool("watch", "w") deprecatedWarnings = argm.ZeroBool("D") contextArgm = minimist.ParseArgv(argm.Unparsed()) project := NewProject(tasksFunc) if help { Usage(project.usage()) os.Exit(0) } if version { fmt.Printf("godo %s\n", Version) os.Exit(0) } // Run each task including their dependencies. args := []string{} for _, v := range argm.Leftover() { args = append(args, fmt.Sprintf("%v", v)) } if len(args) == 0 { if project.Tasks["default"] != nil { args = append(args, "default") } else { Usage(project.usage()) os.Exit(0) } } // quick fix to make cascading watch work on default task if len(args) == 1 && args[0] == "default" { args = project.Tasks["default"].Dependencies } for _, name := range args { err := project.Run(name) if err != nil { util.Error("ERR", "%s\n", err.Error()) os.Exit(1) } } if watching { if project.Watch(args, true) { waitgroup.Add(1) waitExit = true } else { fmt.Println("Nothing to watch. Use W{} or Watch{} to specify glob patterns") os.Exit(0) } } if waitExit { waitgroup.Wait() } }