diff --git a/README.md b/README.md index 3144b43..86e8c8f 100644 --- a/README.md +++ b/README.md @@ -19,5 +19,13 @@ Then verify that `gin` was installed correctly: gin -h ~~~ +## Installing with fsnotify support + +If you're in an evironment like Linux, your operating system may support fsnotify events to trigger rebuilds. If you'd like support for this you can use the following command instead of the one above: + +~~~ shell +go get -tags fsnotify github.com/codegangsta/gin +~~~ + ## Supporting Gin in Your Web app `gin` assumes that your web app binds itself to the `PORT` environment variable so it can properly proxy requests to your app. Web frameworks like [Martini](http://github.com/codegangsta/martini) do this out of the box. diff --git a/fsnotify.go b/fsnotify.go new file mode 100644 index 0000000..e663959 --- /dev/null +++ b/fsnotify.go @@ -0,0 +1,103 @@ +//+build fsnotify +// Only build when the fsnotify tag is used + +package main + +import ( + "code.google.com/p/go.exp/fsnotify" + "os" + "path/filepath" + "time" +) + +// This is to prevent unneeded rebuilds +var ( + watched = make(map[string]bool) +) + +func addPaths(watcher *fsnotify.Watcher, path string) error { + if watched[path] { + return nil + } + + return filepath.Walk(path, func(wpath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if wpath == ".git" { + return filepath.SkipDir + } + + if watched[wpath] { + return nil + } + + if info.IsDir() { + watched[wpath] = true + err = watcher.Watch(wpath) + if err != nil { + return err + } + if path != wpath { + return addPaths(watcher, wpath) + } + } else { + if filepath.Ext(wpath) == ".go" { + watched[wpath] = true + } + } + + return nil + }) +} + +func scanChanges(watchPath string, cb scanCallback) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + logger.Fatal(err) + } + + err = addPaths(watcher, watchPath) + if err != nil { + logger.Fatal(err) + } + + for { + select { + case ev := <-watcher.Event: + path := ev.Name + if watched[path] { + if ev.IsDelete() { + err = watcher.RemoveWatch(path) + if err != nil { + logger.Fatal(err) + } + watched[path] = false + continue + } + + if filepath.Ext(path) == ".go" { + cb(path) + // Drain events that might have been triggered since this build + drain := true + for drain { + select { + case <-watcher.Event: + case <-time.After(250 * time.Millisecond): + drain = false + } + } + } + } else { + if ev.IsCreate() { + // We don't really care /too/ much if this fails... + addPaths(watcher, path) + } + } + + case err := <-watcher.Error: + logger.Fatal(err) + } + } +} diff --git a/main.go b/main.go index 705bcfb..11eb405 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "github.com/codegangsta/cli" "github.com/codegangsta/envy/lib" @@ -14,11 +13,12 @@ import ( ) var ( - startTime = time.Now() logger = log.New(os.Stdout, "[gin] ", 0) buildError error ) +type scanCallback func(path string) + func main() { app := cli.NewApp() app.Name = "gin" @@ -119,24 +119,3 @@ func build(builder gin.Builder, logger *log.Logger) { time.Sleep(100 * time.Millisecond) } - -type scanCallback func(path string) - -func scanChanges(watchPath string, cb scanCallback) { - for { - filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error { - if path == ".git" { - return filepath.SkipDir - } - - if filepath.Ext(path) == ".go" && info.ModTime().After(startTime) { - cb(path) - startTime = time.Now() - return errors.New("done") - } - - return nil - }) - time.Sleep(500 * time.Millisecond) - } -} diff --git a/scan.go b/scan.go new file mode 100644 index 0000000..46009a2 --- /dev/null +++ b/scan.go @@ -0,0 +1,35 @@ +//+build !fsnotify +// The default is to build this scanner + +package main + +import ( + "errors" + "os" + "path/filepath" + "time" +) + +var ( + startTime = time.Now() +) + +func scanChanges(watchPath string, cb scanCallback) { + for { + filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error { + if path == ".git" { + return filepath.SkipDir + } + + if filepath.Ext(path) == ".go" && info.ModTime().After(startTime) { + cb(path) + startTime = time.Now() + return errors.New("done") + } + + return nil + }) + + time.Sleep(500 * time.Millisecond) + } +}