realize/exec.go

229 lines
5.6 KiB
Go
Raw Normal View History

package main
2016-08-03 08:28:58 +00:00
import (
2016-08-04 19:57:19 +00:00
"bufio"
2016-08-17 23:35:37 +00:00
"bytes"
2016-12-18 23:29:50 +00:00
"fmt"
2017-10-31 12:28:21 +00:00
"github.com/pkg/errors"
2016-08-04 19:57:19 +00:00
"log"
2016-08-17 23:35:37 +00:00
"os"
"os/exec"
2016-08-23 00:07:07 +00:00
"path/filepath"
"regexp"
2016-09-05 15:53:37 +00:00
"strings"
2016-08-17 23:35:37 +00:00
"sync"
"time"
2016-08-03 08:28:58 +00:00
)
// GoCompile is used for compile a project
2017-10-15 20:15:50 +00:00
func (p *Project) goCompile(stop <-chan bool, method []string, args []string) (string, error) {
2017-09-17 21:36:56 +00:00
var out bytes.Buffer
var stderr bytes.Buffer
done := make(chan error)
2017-10-15 20:15:50 +00:00
args = append(method, args...)
cmd := exec.Command(args[0], args[1:]...)
2017-10-22 22:35:45 +00:00
cmd.Dir = p.Path
cmd.Stdout = &out
cmd.Stderr = &stderr
// Start command
cmd.Start()
go func() { done <- cmd.Wait() }()
// Wait a result
select {
case <-stop:
// Stop running command
cmd.Process.Kill()
2017-10-16 16:59:35 +00:00
return msgStop, nil
case err := <-done:
// Command completed
if err != nil {
return stderr.String(), err
2017-09-17 21:36:56 +00:00
}
2017-11-14 15:12:37 +00:00
return "", nil
2017-09-17 21:36:56 +00:00
}
return "", nil
}
2016-08-18 07:29:36 +00:00
// GoRun is an implementation of the bin execution
func (p *Project) goRun(stop <-chan bool, runner chan bool) {
2016-11-01 09:56:12 +00:00
var build *exec.Cmd
var args []string
2017-10-22 18:50:51 +00:00
// custom error pattern
isErrorText := func(string) bool {
return false
}
errRegexp, err := regexp.Compile(p.ErrorOutputPattern)
if err != nil {
msg := fmt.Sprintln(p.pname(p.Name, 3), ":", blue.regular(err.Error()))
out := BufferOut{Time: time.Now(), Text: err.Error(), Type: "Go Run"}
2017-08-28 08:49:38 +00:00
p.stamp("error", out, msg, "")
} else {
isErrorText = func(t string) bool {
return errRegexp.MatchString(t)
}
}
2017-10-15 20:15:50 +00:00
2017-10-22 18:50:51 +00:00
// add additional arguments
for _, arg := range p.Args {
2017-10-15 20:15:50 +00:00
a := strings.FieldsFunc(arg, func(i rune) bool {
return i == '"' || i == '=' || i == '\''
})
args = append(args, a...)
2016-12-16 22:54:07 +00:00
}
2017-09-05 14:58:33 +00:00
2017-10-22 18:50:51 +00:00
gobin := os.Getenv("GOBIN")
2017-11-14 18:17:34 +00:00
dirPath := filepath.Base(p.Path)
if p.Path == "." {
dirPath = filepath.Base(wdir())
}
path := filepath.Join(gobin, dirPath)
2017-10-22 18:50:51 +00:00
if _, err := os.Stat(path); err == nil {
build = exec.Command(path, args...)
} else if _, err := os.Stat(path + extWindows); err == nil {
build = exec.Command(path+extWindows, args...)
2016-08-27 13:07:00 +00:00
} else {
2017-10-16 11:58:13 +00:00
if _, err = os.Stat(path); err == nil {
build = exec.Command(path, args...)
2017-10-16 16:59:35 +00:00
} else if _, err = os.Stat(path + extWindows); err == nil {
build = exec.Command(path+extWindows, args...)
2017-10-16 11:58:13 +00:00
} else {
2017-10-22 22:23:24 +00:00
p.err(errors.New("Build not found"))
return
2017-10-16 11:58:13 +00:00
}
2016-08-26 12:52:27 +00:00
}
2017-10-22 22:23:24 +00:00
2016-08-16 10:20:55 +00:00
defer func() {
2016-08-16 16:36:54 +00:00
if err := build.Process.Kill(); err != nil {
2016-10-14 08:47:43 +00:00
p.Buffer.StdLog = append(p.Buffer.StdLog, BufferOut{Time: time.Now(), Text: "Failed to stop: " + err.Error()})
p.fatal(err, "Failed to stop", ":")
2016-08-16 16:36:54 +00:00
}
msg := fmt.Sprintln(p.pname(p.Name, 2), ":", red.regular("Ended"))
2016-12-18 23:29:50 +00:00
out := BufferOut{Time: time.Now(), Text: "Ended", Type: "Go Run"}
2017-08-28 08:49:38 +00:00
p.stamp("log", out, msg, "")
2016-08-16 10:20:55 +00:00
}()
2016-08-04 19:57:19 +00:00
2017-10-22 18:50:51 +00:00
// scan project stream
2016-08-04 19:57:19 +00:00
stdout, err := build.StdoutPipe()
2016-08-23 12:42:01 +00:00
stderr, err := build.StderrPipe()
2016-08-04 19:57:19 +00:00
if err != nil {
log.Println(red.bold(err.Error()))
return
2016-08-04 19:57:19 +00:00
}
if err := build.Start(); err != nil {
log.Println(red.bold(err.Error()))
return
2016-08-04 19:57:19 +00:00
}
2016-08-17 22:24:06 +00:00
close(runner)
2016-08-04 19:57:19 +00:00
2016-09-17 19:07:22 +00:00
execOutput, execError := bufio.NewScanner(stdout), bufio.NewScanner(stderr)
stopOutput, stopError := make(chan bool, 1), make(chan bool, 1)
scanner := func(stop chan bool, output *bufio.Scanner, isError bool) {
for output.Scan() {
text := output.Text()
msg := fmt.Sprintln(p.pname(p.Name, 3), ":", blue.regular(text))
2017-08-08 21:56:19 +00:00
if isError && !isErrorText(text) {
out := BufferOut{Time: time.Now(), Text: text, Type: "Go Run"}
2017-08-28 08:49:38 +00:00
p.stamp("error", out, msg, "")
} else {
2017-08-07 23:33:31 +00:00
out := BufferOut{Time: time.Now(), Text: text, Type: "Go Run"}
2017-08-28 08:49:38 +00:00
p.stamp("out", out, msg, "")
2016-08-17 14:56:06 +00:00
}
}
2016-08-17 19:34:06 +00:00
close(stop)
}
go scanner(stopOutput, execOutput, false)
go scanner(stopError, execError, true)
2016-08-17 23:35:37 +00:00
for {
2016-08-04 19:57:19 +00:00
select {
case <-stop:
return
case <-stopOutput:
return
case <-stopError:
return
}
}
}
// Exec an additional command from a defined path if specified
func (p *Project) command(stop <-chan bool, cmd Command) (string, string) {
var stdout bytes.Buffer
var stderr bytes.Buffer
done := make(chan error)
args := strings.Split(strings.Replace(strings.Replace(cmd.Command, "'", "", -1), "\"", "", -1), " ")
2017-11-11 21:53:29 +00:00
ex := exec.Command(args[0], args[1:]...)
ex.Dir = p.Path
2017-10-22 18:50:51 +00:00
// make cmd path
if cmd.Path != "" {
2017-10-22 22:35:45 +00:00
if strings.Contains(cmd.Path, p.Path) {
2017-11-11 21:53:29 +00:00
ex.Dir = cmd.Path
} else {
2017-11-11 21:53:29 +00:00
ex.Dir = filepath.Join(p.Path, cmd.Path)
}
}
2017-11-11 21:53:29 +00:00
ex.Stdout = &stdout
ex.Stderr = &stderr
// Start command
2017-11-11 21:53:29 +00:00
ex.Start()
go func() { done <- ex.Wait() }()
// Wait a result
select {
case <-stop:
// Stop running command
2017-11-11 21:53:29 +00:00
ex.Process.Kill()
return "", ""
case err := <-done:
// Command completed
if err != nil {
return stderr.String(), stdout.String()
}
}
return "", stdout.String()
}
// GoTool is used for run go tools methods such as fmt, test, generate and so on
func (p *Project) goTool(wg *sync.WaitGroup, stop <-chan bool, result chan<- tool, path string, tool tool) {
defer wg.Done()
if tool.status {
2017-10-16 12:01:41 +00:00
if tool.dir && filepath.Ext(path) != "" {
2017-10-15 20:15:50 +00:00
path = filepath.Dir(path)
}
if strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "") {
if strings.HasSuffix(path, ".go") {
tool.options = append(tool.options, path)
2017-10-22 22:35:45 +00:00
path = p.Path
}
if s := ext(path); s == "" || s == "go" {
var out, stderr bytes.Buffer
done := make(chan error)
2017-10-16 13:21:34 +00:00
tool.cmd = append(tool.cmd, tool.options...)
cmd := exec.Command(tool.cmd[0], tool.cmd[1:]...)
cmd.Dir = path
cmd.Stdout = &out
cmd.Stderr = &stderr
// Start command
cmd.Start()
go func() { done <- cmd.Wait() }()
// Wait a result
select {
case <-stop:
// Stop running command
cmd.Process.Kill()
2017-11-12 21:42:21 +00:00
return
case err := <-done:
// Command completed
if err != nil {
tool.err = stderr.String() + out.String()
// send command result
result <- tool
2017-11-14 15:12:37 +00:00
} else {
tool.out = out.String()
}
2017-11-12 21:42:21 +00:00
return
}
}
2016-08-04 19:57:19 +00:00
}
}
2016-08-03 08:28:58 +00:00
}