1
0
mirror of https://github.com/beego/bee.git synced 2024-05-28 22:43:28 +00:00
bee/watch.go
Faissal Elamraoui cf7aef47f0 Implementing the new logging infrastructure
Moved logging to the new logging infrastructure by removing the use of
ColorLog() function. Added more documentation. Also fixed some typos in
comments and function names.
2016-11-13 15:14:48 +01:00

268 lines
6.0 KiB
Go

// Copyright 2013 bee authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package main
import (
"bytes"
"github.com/howeyc/fsnotify"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"sync"
"time"
)
var (
cmd *exec.Cmd
state sync.Mutex
eventTime = make(map[string]int64)
scheduleTime time.Time
)
// NewWatcher starts an fsnotify Watcher on the specified paths
func NewWatcher(paths []string, files []string, isgenerate bool) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
logger.Fatalf("Failed to create watcher: %s", err)
}
go func() {
for {
select {
case e := <-watcher.Event:
isbuild := true
// Skip ignored files
if shouldIgnoreFile(e.Name) {
continue
}
if !checkIfWatchExt(e.Name) {
continue
}
mt := getFileModTime(e.Name)
if t := eventTime[e.Name]; mt == t {
logger.Infof(bold("Skipping: ")+"%s", e.String())
isbuild = false
}
eventTime[e.Name] = mt
if isbuild {
logger.Infof("Event fired: %s", e)
go func() {
// Wait 1s before autobuild util there is no file change.
scheduleTime = time.Now().Add(1 * time.Second)
for {
time.Sleep(scheduleTime.Sub(time.Now()))
if time.Now().After(scheduleTime) {
break
}
return
}
AutoBuild(files, isgenerate)
}()
}
case err := <-watcher.Error:
logger.Warnf("Watcher error: %s", err.Error()) // No need to exit here
}
}
}()
logger.Info("Initializing watcher...")
for _, path := range paths {
logger.Infof(bold("Watching: ")+"%s", path)
err = watcher.Watch(path)
if err != nil {
logger.Fatalf("Failed to watch directory: %s", err)
}
}
}
// getFileModTime retuens unix timestamp of `os.File.ModTime` by given path.
func getFileModTime(path string) int64 {
path = strings.Replace(path, "\\", "/", -1)
f, err := os.Open(path)
if err != nil {
logger.Errorf("Failed to open file on '%s': %s", path, err)
return time.Now().Unix()
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
logger.Errorf("Failed to get file stats: %s", err)
return time.Now().Unix()
}
return fi.ModTime().Unix()
}
// AutoBuild builds the specified set of files
func AutoBuild(files []string, isgenerate bool) {
state.Lock()
defer state.Unlock()
logger.Info("Start building...")
os.Chdir(currpath)
cmdName := "go"
if conf.Gopm.Enable {
cmdName = "gopm"
}
var err error
// For applications use full import path like "github.com/.../.."
// are able to use "go install" to reduce build time.
if conf.GoInstall || conf.Gopm.Install {
icmd := exec.Command("go", "list", "./...")
buf := bytes.NewBuffer([]byte(""))
icmd.Stdout = buf
icmd.Env = append(os.Environ(), "GOGC=off")
err = icmd.Run()
if err == nil {
list := strings.Split(buf.String(), "\n")[1:]
for _, pkg := range list {
if len(pkg) == 0 {
continue
}
icmd = exec.Command(cmdName, "install", pkg)
icmd.Stdout = os.Stdout
icmd.Stderr = os.Stderr
icmd.Env = append(os.Environ(), "GOGC=off")
err = icmd.Run()
if err != nil {
break
}
}
}
}
if isgenerate {
icmd := exec.Command("bee", "generate", "docs")
icmd.Env = append(os.Environ(), "GOGC=off")
icmd.Stdout = os.Stdout
icmd.Stderr = os.Stderr
icmd.Run()
logger.Info("============== Generate Docs ===================")
}
if err == nil {
appName := appname
if runtime.GOOS == "windows" {
appName += ".exe"
}
args := []string{"build"}
args = append(args, "-o", appName)
if buildTags != "" {
args = append(args, "-tags", buildTags)
}
args = append(args, files...)
bcmd := exec.Command(cmdName, args...)
bcmd.Env = append(os.Environ(), "GOGC=off")
bcmd.Stdout = os.Stdout
bcmd.Stderr = os.Stderr
err = bcmd.Run()
}
if err != nil {
logger.Error("============== Build Failed ===================")
return
}
logger.Success("Built Successfully!")
Restart(appname)
}
// Kill kills the running command process
func Kill() {
defer func() {
if e := recover(); e != nil {
logger.Infof("Kill recover: %s", e)
}
}()
if cmd != nil && cmd.Process != nil {
err := cmd.Process.Kill()
if err != nil {
logger.Errorf("Error while killing cmd process: %s", err)
}
}
}
// Restart kills the running command process and starts it again
func Restart(appname string) {
logger.Debugf("Kill running process")
Kill()
go Start(appname)
}
// Start starts the command process
func Start(appname string) {
logger.Infof("Restarting '%s'...", appname)
if strings.Index(appname, "./") == -1 {
appname = "./" + appname
}
cmd = exec.Command(appname)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Args = append([]string{appname}, conf.CmdArgs...)
cmd.Env = append(os.Environ(), conf.Envs...)
go cmd.Run()
logger.Successf("'%s' is running...", appname)
started <- true
}
// shouldIgnoreFile ignores filenames generated by Emacs, Vim or SublimeText.
// It returns true if the file should be ignored, false otherwise.
func shouldIgnoreFile(filename string) bool {
for _, regex := range ignoredFilesRegExps {
r, err := regexp.Compile(regex)
if err != nil {
logger.Fatalf("Could not compile regular expression: %s", err)
}
if r.MatchString(filename) {
return true
}
continue
}
return false
}
var watchExts = []string{".go"}
var ignoredFilesRegExps = []string{
`.#(\w+).go`,
`.(\w+).go.swp`,
`(\w+).go~`,
`(\w+).tmp`,
}
// checkIfWatchExt returns true if the name HasSuffix <watch_ext>.
func checkIfWatchExt(name string) bool {
for _, s := range watchExts {
if strings.HasSuffix(name, s) {
return true
}
}
return false
}