/
main.go
131 lines (115 loc) · 2.07 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package main
import (
"context"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"sync"
"time"
"github.com/midbel/toml"
"golang.org/x/sync/semaphore"
)
type writer struct {
inner io.Writer
mu sync.Mutex
}
func (w *writer) Write(b []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
return w.inner.Write(b)
}
func Writer(w io.Writer) io.Writer {
return &writer{inner: w}
}
var (
Out = Writer(os.Stdout)
Err = Writer(os.Stderr)
)
type Variable struct {
Name string
Value string
}
type Cmd struct {
Path string
File string
Args []string
Silent bool
Env []Variable
Pre []Cmd `toml:"pre"`
Post []Cmd `toml:"post"`
}
func (c Cmd) Exec() error {
if err := execCmd(c.Pre...); err != nil {
return err
}
if err := execCmd(c); err != nil {
return err
}
return execCmd(c.Post...)
}
func (c Cmd) Run() error {
var (
args = append(c.Args, c.File)
cmd = exec.Command(c.Path, args...)
)
for i := range c.Env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s"), c.Env[i].Name, c.Env[i].Value)
}
if !c.Silent {
cmd.Stdout = Out
cmd.Stderr = Err
} else {
cmd.Stdout = ioutil.Discard
cmd.Stderr = ioutil.Discard
}
return cmd.Run()
}
func execCmd(cs ...Cmd) error {
var err error
for _, c := range cs {
err = c.Run()
if err != nil {
break
}
}
return err
}
func main() {
flag.Parse()
c := struct {
Task int64 `toml:"parallel"`
Commands []Cmd `toml:"command"`
Env []Variable
}{}
if err := toml.DecodeFile(flag.Arg(0), &c); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if c.Task == 0 {
c.Task = int64(len(c.Commands))
}
var (
ctx = context.TODO()
sema = semaphore.NewWeighted(c.Task)
)
for _, c := range c.Commands {
if err := sema.Acquire(ctx, 1); err != nil {
os.Exit(2)
}
go func(c Cmd) {
defer func(n time.Time) {
sema.Release(1)
log.Printf("done: %s %s (%s)", c.Path, c.File, time.Since(n))
}(time.Now())
log.Printf("start: %s %s", c.Path, c.File)
if err := c.Exec(); err != nil {
log.Printf("error: %s", err)
}
}(c)
}
sema.Acquire(ctx, c.Task)
}