Merge pull request #122 from tockins/dev

v1.5.2
This commit is contained in:
Alessio Pracchia 2017-11-14 19:34:04 +01:00 committed by GitHub
commit 7f02f5256c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 163 deletions

28
cmd.go
View File

@ -9,12 +9,9 @@ import (
// Tool options customizable, should be moved in Cmd // Tool options customizable, should be moved in Cmd
type tool struct { type tool struct {
dir bool name, err, out string
status bool cmd, options []string
name string dir, status bool
err string
cmd []string
options []string
} }
// Cmds list of go commands // Cmds list of go commands
@ -32,11 +29,11 @@ type Cmds struct {
// Cmd single command fields and options // Cmd single command fields and options
type Cmd struct { type Cmd struct {
Status bool `yaml:"status,omitempty" json:"status,omitempty"`
Method string `yaml:"method,omitempty" json:"method,omitempty"` Method string `yaml:"method,omitempty" json:"method,omitempty"`
Args []string `yaml:"args,omitempty" json:"args,omitempty"` Args []string `yaml:"args,omitempty" json:"args,omitempty"`
method []string Status bool `yaml:"status,omitempty" json:"status,omitempty"`
tool bool tool bool
method []string
name, startTxt, endTxt string name, startTxt, endTxt string
} }
@ -46,6 +43,8 @@ func (r *realize) clean() error {
arr := r.Schema arr := r.Schema
for key, val := range arr { for key, val := range arr {
if _, err := duplicates(val, arr[key+1:]); err != nil { if _, err := duplicates(val, arr[key+1:]); err != nil {
// path validation
r.Schema = append(arr[:key], arr[key+1:]...) r.Schema = append(arr[:key], arr[key+1:]...)
break break
} }
@ -56,14 +55,15 @@ func (r *realize) clean() error {
} }
// Add a new project // Add a new project
func (r *realize) add(p *cli.Context) error { func (r *realize) add(p *cli.Context) (err error) {
path, err := filepath.Abs(p.String("path")) // project init
if err != nil { name := filepath.Base(p.String("path"))
return err if name == "." {
name = filepath.Base(wdir())
} }
project := Project{ project := Project{
Name: filepath.Base(filepath.Clean(p.String("path"))), Name: name,
Path: path, Path: p.String("path"),
Cmds: Cmds{ Cmds: Cmds{
Vet: Cmd{ Vet: Cmd{
Status: p.Bool("vet"), Status: p.Bool("vet"),

33
exec.go
View File

@ -39,6 +39,7 @@ func (p *Project) goCompile(stop <-chan bool, method []string, args []string) (s
if err != nil { if err != nil {
return stderr.String(), err return stderr.String(), err
} }
return "", nil
} }
return "", nil return "", nil
} }
@ -47,7 +48,6 @@ func (p *Project) goCompile(stop <-chan bool, method []string, args []string) (s
func (p *Project) goRun(stop <-chan bool, runner chan bool) { func (p *Project) goRun(stop <-chan bool, runner chan bool) {
var build *exec.Cmd var build *exec.Cmd
var args []string var args []string
// custom error pattern // custom error pattern
isErrorText := func(string) bool { isErrorText := func(string) bool {
return false return false
@ -72,13 +72,16 @@ func (p *Project) goRun(stop <-chan bool, runner chan bool) {
} }
gobin := os.Getenv("GOBIN") gobin := os.Getenv("GOBIN")
path := filepath.Join(gobin, p.name) dirPath := filepath.Base(p.Path)
if p.Path == "." {
dirPath = filepath.Base(wdir())
}
path := filepath.Join(gobin, dirPath)
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
build = exec.Command(path, args...) build = exec.Command(path, args...)
} else if _, err := os.Stat(path + extWindows); err == nil { } else if _, err := os.Stat(path + extWindows); err == nil {
build = exec.Command(path+extWindows, args...) build = exec.Command(path+extWindows, args...)
} else { } else {
path := filepath.Join(p.Path, p.name)
if _, err = os.Stat(path); err == nil { if _, err = os.Stat(path); err == nil {
build = exec.Command(path, args...) build = exec.Command(path, args...)
} else if _, err = os.Stat(path + extWindows); err == nil { } else if _, err = os.Stat(path + extWindows); err == nil {
@ -148,26 +151,26 @@ func (p *Project) command(stop <-chan bool, cmd Command) (string, string) {
var stderr bytes.Buffer var stderr bytes.Buffer
done := make(chan error) done := make(chan error)
args := strings.Split(strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1), " ") args := strings.Split(strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1), " ")
exec := exec.Command(args[0], args[1:]...) ex := exec.Command(args[0], args[1:]...)
exec.Dir = p.Path ex.Dir = p.Path
// make cmd path // make cmd path
if cmd.Path != "" { if cmd.Path != "" {
if strings.Contains(cmd.Path, p.Path) { if strings.Contains(cmd.Path, p.Path) {
exec.Dir = cmd.Path ex.Dir = cmd.Path
} else { } else {
exec.Dir = filepath.Join(p.Path, cmd.Path) ex.Dir = filepath.Join(p.Path, cmd.Path)
} }
} }
exec.Stdout = &stdout ex.Stdout = &stdout
exec.Stderr = &stderr ex.Stderr = &stderr
// Start command // Start command
exec.Start() ex.Start()
go func() { done <- exec.Wait() }() go func() { done <- ex.Wait() }()
// Wait a result // Wait a result
select { select {
case <-stop: case <-stop:
// Stop running command // Stop running command
exec.Process.Kill() ex.Process.Kill()
return "", "" return "", ""
case err := <-done: case err := <-done:
// Command completed // Command completed
@ -206,15 +209,17 @@ func (p *Project) goTool(wg *sync.WaitGroup, stop <-chan bool, result chan<- too
case <-stop: case <-stop:
// Stop running command // Stop running command
cmd.Process.Kill() cmd.Process.Kill()
break return
case err := <-done: case err := <-done:
// Command completed // Command completed
if err != nil { if err != nil {
tool.err = stderr.String() + out.String() tool.err = stderr.String() + out.String()
// send command result // send command result
result <- tool result <- tool
} else {
tool.out = out.String()
} }
break return
} }
} }

View File

@ -95,6 +95,7 @@ func (w *fsNotifyWatcher) Events() <-chan fsnotify.Event {
return w.Watcher.Events return w.Watcher.Events
} }
// Walk fsnotify
func (w *fsNotifyWatcher) Walk(path string, init bool) string { func (w *fsNotifyWatcher) Walk(path string, init bool) string {
if err := w.Add(path); err != nil { if err := w.Add(path); err != nil {
return "" return ""
@ -106,9 +107,8 @@ func (w *fsNotifyWatcher) Walk(path string, init bool) string {
// All watches are stopped, removed, and the poller cannot be added to // All watches are stopped, removed, and the poller cannot be added to
func (w *filePoller) Close() error { func (w *filePoller) Close() error {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock()
if w.closed { if w.closed {
w.mu.Unlock()
return nil return nil
} }
@ -117,6 +117,7 @@ func (w *filePoller) Close() error {
w.remove(name) w.remove(name)
delete(w.watches, name) delete(w.watches, name)
} }
w.mu.Unlock()
return nil return nil
} }
@ -157,6 +158,7 @@ func (w *filePoller) Add(name string) error {
return nil return nil
} }
// Remove poller
func (w *filePoller) remove(name string) error { func (w *filePoller) remove(name string) error {
if w.closed { if w.closed {
return errPollerClosed return errPollerClosed
@ -184,6 +186,7 @@ func (w *filePoller) Events() <-chan fsnotify.Event {
return w.events return w.events
} }
// Walk poller
func (w *filePoller) Walk(path string, init bool) string { func (w *filePoller) Walk(path string, init bool) string {
check := w.watches[path] check := w.watches[path]
if err := w.Add(path); err != nil { if err := w.Add(path); err != nil {
@ -232,12 +235,8 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{}
} }
fi, err := os.Stat(f.Name()) fi, err := os.Stat(f.Name())
if err != nil { switch {
// if we got an error here and lastFi is not set, we can presume that nothing has changed case err != nil && lastFi != nil:
// This should be safe since before `watch()` is called, a stat is performed, there is any error `watch` is not called
if lastFi == nil {
continue
}
// If it doesn't exist at this point, it must have been removed // If it doesn't exist at this point, it must have been removed
// no need to send the error here since this is a valid operation // no need to send the error here since this is a valid operation
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -245,37 +244,25 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{}
return return
} }
lastFi = nil lastFi = nil
continue
} }
// at this point, send the error // at this point, send the error
if err := w.sendErr(err, chClose); err != nil { w.sendErr(err, chClose)
return return
} case lastFi == nil:
continue
}
if lastFi == nil {
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: f.Name()}, chClose); err != nil { if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: f.Name()}, chClose); err != nil {
return return
} }
lastFi = fi lastFi = fi
continue case fi.Mode() != lastFi.Mode():
}
if fi.Mode() != lastFi.Mode() {
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Chmod, Name: f.Name()}, chClose); err != nil { if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Chmod, Name: f.Name()}, chClose); err != nil {
return return
} }
lastFi = fi lastFi = fi
continue case fi.ModTime() != lastFi.ModTime() || fi.Size() != lastFi.Size():
}
if fi.ModTime() != lastFi.ModTime() || fi.Size() != lastFi.Size() {
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Write, Name: f.Name()}, chClose); err != nil { if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Write, Name: f.Name()}, chClose); err != nil {
return return
} }
lastFi = fi lastFi = fi
continue
} }
} }
} }

View File

@ -15,7 +15,7 @@ import (
) )
const ( const (
version = "1.5.1r2" version = "1.5.2"
) )
// New realize instance // New realize instance
@ -35,26 +35,16 @@ type realize struct {
// Cli commands // Cli commands
func main() { func main() {
app := &cli.App{ app := &cli.App{
Name: "Realize", Name: "Realize",
Version: version, Version: version,
Authors: []*cli.Author{
{
Name: "Alessio Pracchia",
Email: "pracchia@hastega.it",
},
{
Name: "Daniele Conventi",
Email: "conventi@hastega.it",
},
},
Description: "Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths", Description: "Go build system with file watchers, output streams and live reload. Run, build and watch file changes with custom paths",
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "start", Name: "start",
Aliases: []string{"r"}, Aliases: []string{"s"},
Description: "Start a toolchain on a project or a list of projects. If not exist a config file it creates a new one", Description: "Start a toolchain on a project or a list of projects. If not exist a config file it creates a new one",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: wdir(), Usage: "Project base path"}, &cli.StringFlag{Name: "path", Aliases: []string{"p"}, Value: ".", Usage: "Project base path"},
&cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: "", Usage: "Run a project by its name"}, &cli.StringFlag{Name: "name", Aliases: []string{"n"}, Value: "", Usage: "Run a project by its name"},
&cli.BoolFlag{Name: "fmt", Aliases: []string{"f"}, Value: false, Usage: "Enable go fmt"}, &cli.BoolFlag{Name: "fmt", Aliases: []string{"f"}, Value: false, Usage: "Enable go fmt"},
&cli.BoolFlag{Name: "vet", Aliases: []string{"v"}, Value: false, Usage: "Enable go vet"}, &cli.BoolFlag{Name: "vet", Aliases: []string{"v"}, Value: false, Usage: "Enable go vet"},
@ -172,7 +162,7 @@ func main() {
if err != nil { if err != nil {
return d.Err() return d.Err()
} }
r.Settings.FileLimit = val r.Settings.FileLimit = int32(val)
return nil return nil
}, },
}, },
@ -1217,11 +1207,11 @@ func prefix(s string) string {
if s != "" { if s != "" {
return fmt.Sprint(yellow.bold("["), "REALIZE", yellow.bold("]"), " : ", s) return fmt.Sprint(yellow.bold("["), "REALIZE", yellow.bold("]"), " : ", s)
} }
return "" return s
} }
// Before is launched before each command // Before is launched before each command
func before(*cli.Context) error { func before(*cli.Context) (err error) {
// custom log // custom log
log.SetFlags(0) log.SetFlags(0)
log.SetOutput(logWriter{}) log.SetOutput(logWriter{})
@ -1230,7 +1220,7 @@ func before(*cli.Context) error {
if gopath == "" { if gopath == "" {
return errors.New("$GOPATH isn't set properly") return errors.New("$GOPATH isn't set properly")
} }
if err := os.Setenv("GOPATH", gopath); err != nil { if err = os.Setenv("GOPATH", gopath); err != nil {
return err return err
} }
// new realize instance // new realize instance
@ -1239,11 +1229,11 @@ func before(*cli.Context) error {
r.Settings.read(&r) r.Settings.read(&r)
// increase the file limit // increase the file limit
if r.Settings.FileLimit != 0 { if r.Settings.FileLimit != 0 {
if err := r.Settings.flimit(); err != nil { if err = r.Settings.flimit(); err != nil {
return err return err
} }
} }
return nil return
} }
// Rewrite the layout of the log timestamp // Rewrite the layout of the log timestamp

View File

@ -26,16 +26,15 @@ type Server struct {
parent *realize parent *realize
Status bool `yaml:"status" json:"status"` Status bool `yaml:"status" json:"status"`
Open bool `yaml:"open" json:"open"` Open bool `yaml:"open" json:"open"`
Host string `yaml:"host" json:"host"`
Port int `yaml:"port" json:"port"` Port int `yaml:"port" json:"port"`
Host string `yaml:"host" json:"host"`
} }
// Websocket projects // Websocket projects
func (s *Server) projects(c echo.Context) error { func (s *Server) projects(c echo.Context) (err error) {
websocket.Handler(func(ws *websocket.Conn) { websocket.Handler(func(ws *websocket.Conn) {
defer ws.Close()
msg, _ := json.Marshal(s.parent) msg, _ := json.Marshal(s.parent)
err := websocket.Message.Send(ws, string(msg)) err = websocket.Message.Send(ws, string(msg))
go func() { go func() {
for { for {
select { select {
@ -51,7 +50,7 @@ func (s *Server) projects(c echo.Context) error {
for { for {
// Read // Read
text := "" text := ""
err := websocket.Message.Receive(ws, &text) err = websocket.Message.Receive(ws, &text)
if err != nil { if err != nil {
break break
} else { } else {
@ -62,6 +61,7 @@ func (s *Server) projects(c echo.Context) error {
} }
} }
} }
ws.Close()
}).ServeHTTP(c.Response(), c.Request()) }).ServeHTTP(c.Response(), c.Request())
return nil return nil
} }
@ -69,7 +69,9 @@ func (s *Server) projects(c echo.Context) error {
// Start the web server // Start the web server
func (s *Server) start(p *cli.Context) (err error) { func (s *Server) start(p *cli.Context) (err error) {
if p.Bool("server") { if p.Bool("server") {
s.parent.Server.Status = p.Bool("server") s.parent.Server.Status = true
}
if p.Bool("open") {
s.parent.Server.Open = true s.parent.Server.Open = true
} }

View File

@ -32,8 +32,8 @@ const (
type Settings struct { type Settings struct {
file string file string
Files `yaml:"files,omitempty" json:"files,omitempty"` Files `yaml:"files,omitempty" json:"files,omitempty"`
FileLimit int64 `yaml:"flimit,omitempty" json:"flimit,omitempty"`
Legacy Legacy `yaml:"legacy" json:"legacy"` Legacy Legacy `yaml:"legacy" json:"legacy"`
FileLimit int32 `yaml:"flimit,omitempty" json:"flimit,omitempty"`
Recovery bool `yaml:"recovery,omitempty" json:"recovery,omitempty"` Recovery bool `yaml:"recovery,omitempty" json:"recovery,omitempty"`
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"github.com/fsnotify/fsnotify"
"log" "log"
"math/big" "math/big"
"os" "os"
@ -12,8 +13,6 @@ import (
"sync" "sync"
"syscall" "syscall"
"time" "time"
"github.com/fsnotify/fsnotify"
) )
var ( var (
@ -54,12 +53,12 @@ type Project struct {
parent *realize parent *realize
watcher FileWatcher watcher FileWatcher
init bool init bool
Settings `yaml:"-" json:"-"`
files, folders int64 files, folders int64
name, lastFile string name, lastFile string
tools []tool tools []tool
paths []string paths []string
lastTime time.Time lastTime time.Time
Settings `yaml:"-" json:"-"`
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
Path string `yaml:"path" json:"path"` Path string `yaml:"path" json:"path"`
Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"` Environment map[string]string `yaml:"environment,omitempty" json:"environment,omitempty"`
@ -119,16 +118,27 @@ L:
select { select {
case event := <-p.watcher.Events(): case event := <-p.watcher.Events():
if time.Now().Truncate(time.Second).After(p.lastTime) || event.Name != p.lastFile { if time.Now().Truncate(time.Second).After(p.lastTime) || event.Name != p.lastFile {
// event time
eventTime := time.Now()
// file extension
ext := ext(event.Name)
if ext == "" {
ext = "DIR"
}
// change message
msg = fmt.Sprintln(p.pname(p.Name, 4), ":", magenta.bold(strings.ToUpper(ext)), "changed", magenta.bold(event.Name))
out = BufferOut{Time: time.Now(), Text: ext + " changed " + event.Name}
// switch event type
switch event.Op { switch event.Op {
case fsnotify.Chmod: case fsnotify.Chmod:
case fsnotify.Remove: case fsnotify.Remove:
ext := ext(event.Name) p.watcher.Remove(event.Name)
if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) { if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
close(stop) close(stop)
stop = make(chan bool) stop = make(chan bool)
p.changed(event, stop) // stop p.stamp("log", out, msg, "")
go p.routines(stop, p.watcher, "")
} }
p.watcher.Remove(event.Name)
default: default:
file, err := os.Stat(event.Name) file, err := os.Stat(event.Name)
if err != nil { if err != nil {
@ -137,15 +147,20 @@ L:
if file.IsDir() { if file.IsDir() {
filepath.Walk(event.Name, p.walk) filepath.Walk(event.Name, p.walk)
} else if file.Size() > 0 { } else if file.Size() > 0 {
// used only for test and debug
if p.parent.Settings.Recovery { if p.parent.Settings.Recovery {
log.Println(event) log.Println(event)
} }
ext := ext(event.Name)
if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) { if !strings.Contains(ext, "_") && !strings.Contains(ext, ".") && array(ext, p.Watcher.Exts) {
// change watched // change watched
close(stop) // check if a file is still writing #119
stop = make(chan bool) if event.Op != fsnotify.Write || eventTime.Truncate(time.Millisecond).After(file.ModTime().Truncate(time.Millisecond)) {
p.changed(event, stop) close(stop)
stop = make(chan bool)
// stop and start again
p.stamp("log", out, msg, "")
go p.routines(stop, p.watcher, event.Name)
}
} }
p.lastTime = time.Now().Truncate(time.Second) p.lastTime = time.Now().Truncate(time.Second)
p.lastFile = event.Name p.lastFile = event.Name
@ -172,12 +187,6 @@ func (p *Project) err(err error) {
// Config project init // Config project init
func (p *Project) config(r *realize) { func (p *Project) config(r *realize) {
// validate project path, if invalid get wdir or clean current
if !filepath.IsAbs(p.Path) {
p.Path = wdir()
} else {
p.Path = filepath.Clean(p.Path)
}
// get basepath name // get basepath name
p.name = filepath.Base(p.Path) p.name = filepath.Base(p.Path)
// env variables // env variables
@ -190,24 +199,12 @@ func (p *Project) config(r *realize) {
if len(p.Cmds.Fmt.Args) == 0 { if len(p.Cmds.Fmt.Args) == 0 {
p.Cmds.Fmt.Args = []string{"-s", "-w", "-e", "./"} p.Cmds.Fmt.Args = []string{"-s", "-w", "-e", "./"}
} }
p.tools = append(p.tools, tool{
status: p.Cmds.Fix.Status,
cmd: replace([]string{"go fix"}, p.Cmds.Fix.Method),
options: split([]string{}, p.Cmds.Fix.Args),
name: "Fix",
})
p.tools = append(p.tools, tool{ p.tools = append(p.tools, tool{
status: p.Cmds.Clean.Status, status: p.Cmds.Clean.Status,
cmd: replace([]string{"go clean"}, p.Cmds.Clean.Method), cmd: replace([]string{"go clean"}, p.Cmds.Clean.Method),
options: split([]string{}, p.Cmds.Clean.Args), options: split([]string{}, p.Cmds.Clean.Args),
name: "Clean", name: "Clean",
}) })
p.tools = append(p.tools, tool{
status: p.Cmds.Fmt.Status,
cmd: replace([]string{"gofmt"}, p.Cmds.Fmt.Method),
options: split([]string{}, p.Cmds.Fmt.Args),
name: "Fmt",
})
p.tools = append(p.tools, tool{ p.tools = append(p.tools, tool{
status: p.Cmds.Generate.Status, status: p.Cmds.Generate.Status,
cmd: replace([]string{"go", "generate"}, p.Cmds.Generate.Method), cmd: replace([]string{"go", "generate"}, p.Cmds.Generate.Method),
@ -216,11 +213,16 @@ func (p *Project) config(r *realize) {
dir: true, dir: true,
}) })
p.tools = append(p.tools, tool{ p.tools = append(p.tools, tool{
status: p.Cmds.Test.Status, status: p.Cmds.Fix.Status,
cmd: replace([]string{"go", "test"}, p.Cmds.Test.Method), cmd: replace([]string{"go fix"}, p.Cmds.Fix.Method),
options: split([]string{}, p.Cmds.Test.Args), options: split([]string{}, p.Cmds.Fix.Args),
name: "Test", name: "Fix",
dir: true, })
p.tools = append(p.tools, tool{
status: p.Cmds.Fmt.Status,
cmd: replace([]string{"gofmt"}, p.Cmds.Fmt.Method),
options: split([]string{}, p.Cmds.Fmt.Args),
name: "Fmt",
}) })
p.tools = append(p.tools, tool{ p.tools = append(p.tools, tool{
status: p.Cmds.Vet.Status, status: p.Cmds.Vet.Status,
@ -229,6 +231,13 @@ func (p *Project) config(r *realize) {
name: "Vet", name: "Vet",
dir: true, dir: true,
}) })
p.tools = append(p.tools, tool{
status: p.Cmds.Test.Status,
cmd: replace([]string{"go", "test"}, p.Cmds.Test.Method),
options: split([]string{}, p.Cmds.Test.Args),
name: "Test",
dir: true,
})
p.Cmds.Install = Cmd{ p.Cmds.Install = Cmd{
Status: p.Cmds.Install.Status, Status: p.Cmds.Install.Status,
Args: append([]string{}, p.Cmds.Install.Args...), Args: append([]string{}, p.Cmds.Install.Args...),
@ -292,10 +301,11 @@ func (p *Project) cmd(stop <-chan bool, flag string, global bool) {
// Compile is used for run and display the result of a compiling // Compile is used for run and display the result of a compiling
func (p *Project) compile(stop <-chan bool, cmd Cmd) error { func (p *Project) compile(stop <-chan bool, cmd Cmd) error {
if cmd.Status { if cmd.Status {
start := time.Now() var start time.Time
channel := make(chan Result) channel := make(chan Result)
go func() { go func() {
log.Println(p.pname(p.Name, 1), ":", cmd.startTxt) log.Println(p.pname(p.Name, 1), ":", cmd.startTxt)
start = time.Now()
stream, err := p.goCompile(stop, cmd.method, cmd.Args) stream, err := p.goCompile(stop, cmd.method, cmd.Args)
if stream != msgStop { if stream != msgStop {
channel <- Result{stream, err} channel <- Result{stream, err}
@ -349,10 +359,12 @@ func (p *Project) tool(stop <-chan bool, path string) error {
result := make(chan tool) result := make(chan tool)
go func() { go func() {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(p.tools))
for _, element := range p.tools { for _, element := range p.tools {
// no need a sequence, these commands can be asynchronous // no need a sequence, these commands can be asynchronous
go p.goTool(&wg, stop, result, path, element) if element.status {
wg.Add(1)
p.goTool(&wg, stop, result, path, element)
}
} }
wg.Wait() wg.Wait()
close(done) close(done)
@ -361,9 +373,15 @@ func (p *Project) tool(stop <-chan bool, path string) error {
for { for {
select { select {
case tool := <-result: case tool := <-result:
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(tool.name), red.regular("there are some errors in"), ":", magenta.bold(path)) if tool.err != "" {
buff := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: tool.name, Stream: tool.err} msg = fmt.Sprintln(p.pname(p.Name, 2), ":", red.bold(tool.name), red.regular("there are some errors in"), ":", magenta.bold(path))
p.stamp("error", buff, msg, tool.err) buff := BufferOut{Time: time.Now(), Text: "there are some errors in", Path: path, Type: tool.name, Stream: tool.err}
p.stamp("error", buff, msg, tool.err)
} else if tool.out != "" {
msg = fmt.Sprintln(p.pname(p.Name, 3), ":", red.bold(tool.name), red.regular("outputs"), ":", blue.bold(path))
buff := BufferOut{Time: time.Now(), Text: "outputs", Path: path, Type: tool.name, Stream: tool.out}
p.stamp("out", buff, msg, tool.out)
}
case <-done: case <-done:
break loop break loop
case <-stop: case <-stop:
@ -374,19 +392,6 @@ func (p *Project) tool(stop <-chan bool, path string) error {
return nil return nil
} }
// Changed detect a file/directory change
func (p *Project) changed(event fsnotify.Event, stop chan bool) {
e := ext(event.Name)
if e == "" {
e = "DIR"
}
msg = fmt.Sprintln(p.pname(p.Name, 4), ":", magenta.bold(strings.ToUpper(e)), "changed", magenta.bold(event.Name))
out = BufferOut{Time: time.Now(), Text: ext(event.Name) + " changed " + event.Name}
p.stamp("log", out, msg, "")
//stop running process
go p.routines(stop, p.watcher, event.Name)
}
// Watch the files tree of a project // Watch the files tree of a project
func (p *Project) walk(path string, info os.FileInfo, err error) error { func (p *Project) walk(path string, info os.FileInfo, err error) error {
for _, v := range p.Watcher.Ignore { for _, v := range p.Watcher.Ignore {
@ -475,45 +480,53 @@ func (p *Project) routines(stop <-chan bool, watcher FileWatcher, path string) {
} }
} }
}() }()
if !done { if done {
// before command return
p.cmd(stop, "before", false)
} }
if !done { // before command
// Go supported tools p.cmd(stop, "before", false)
p.tool(stop, path) if done {
// Prevent fake events on polling startup return
p.init = true
} }
// Go supported tools
p.tool(stop, path)
// Prevent fake events on polling startup
p.init = true
// prevent errors using realize without config with only run flag // prevent errors using realize without config with only run flag
if p.Cmds.Run && !p.Cmds.Install.Status && !p.Cmds.Build.Status { if p.Cmds.Run && !p.Cmds.Install.Status && !p.Cmds.Build.Status {
p.Cmds.Install.Status = true p.Cmds.Install.Status = true
} }
if !done { if done {
install = p.compile(stop, p.Cmds.Install) return
} }
if !done { install = p.compile(stop, p.Cmds.Install)
build = p.compile(stop, p.Cmds.Build) if done {
return
} }
if !done && (install == nil && build == nil) { build = p.compile(stop, p.Cmds.Build)
if p.Cmds.Run { if done {
start := time.Now() return
runner := make(chan bool, 1) }
go func() { if install == nil && build == nil && p.Cmds.Run {
log.Println(p.pname(p.Name, 1), ":", "Running..") var start time.Time
p.goRun(stop, runner) runner := make(chan bool, 1)
}() go func() {
select { log.Println(p.pname(p.Name, 1), ":", "Running..")
case <-runner: start = time.Now()
msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s")) p.goRun(stop, runner)
out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"} }()
p.stamp("log", out, msg, "") select {
case <-stop: case <-runner:
return msg = fmt.Sprintln(p.pname(p.Name, 5), ":", green.regular("Started"), "in", magenta.regular(big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3), " s"))
} out = BufferOut{Time: time.Now(), Text: "Started in " + big.NewFloat(float64(time.Since(start).Seconds())).Text('f', 3) + " s"}
p.stamp("log", out, msg, "")
case <-stop:
return
} }
} }
if !done { if done {
p.cmd(stop, "after", false) return
} }
p.cmd(stop, "after", false)
} }