blob: 7b8d723bd6c0128d3e87bc2bad9033d5694b0400 [file] [log] [blame]
Shinichiro Hamaji74a66002015-04-27 16:42:30 +09001package main
2
3import (
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +09004 "container/heap"
Shinichiro Hamaji74a66002015-04-27 16:42:30 +09005 "fmt"
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +09006 "os"
Shinichiro Hamaji74a66002015-04-27 16:42:30 +09007 "os/exec"
8 "strings"
9 "syscall"
10 "time"
11)
12
13type Job struct {
14 n *DepNode
15 ex *Executor
16 parents []*Job
17 outputTs int64
18 numDeps int
19 depsTs int64
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +090020 id int
Shinichiro Hamajia6808422015-05-13 18:00:50 +090021
22 runners []runner
Shinichiro Hamaji74a66002015-04-27 16:42:30 +090023}
24
25type runner struct {
26 output string
27 cmd string
28 echo bool
Shinichiro Hamaji74a66002015-04-27 16:42:30 +090029 ignoreError bool
30 shell string
31}
32
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +090033type JobResult struct {
34 j *Job
35 w *Worker
36}
37
Shinichiro Hamaji55c50bd2015-04-27 21:05:47 +090038type NewDep struct {
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +090039 j *Job
40 neededBy *Job
41}
42
43type Worker struct {
44 wm *WorkerManager
45 jobChan chan *Job
46 waitChan chan bool
47 doneChan chan bool
48}
49
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +090050type JobQueue []*Job
51
52func (jq JobQueue) Len() int { return len(jq) }
53
54func (jq JobQueue) Less(i, j int) bool {
55 // First come, first serve, for GNU make compatibility.
56 return jq[i].id < jq[j].id
57}
58
59func (jq JobQueue) Swap(i, j int) {
60 jq[i], jq[j] = jq[j], jq[i]
61}
62
63func (jq *JobQueue) Push(x interface{}) {
64 item := x.(*Job)
65 *jq = append(*jq, item)
66}
67
68func (jq *JobQueue) Pop() interface{} {
69 old := *jq
70 n := len(old)
71 item := old[n-1]
72 *jq = old[0 : n-1]
73 return item
74}
75
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +090076func NewWorker(wm *WorkerManager) *Worker {
77 w := &Worker{
78 wm: wm,
79 jobChan: make(chan *Job),
80 waitChan: make(chan bool),
81 doneChan: make(chan bool),
82 }
83 return w
84}
85
86func (w *Worker) Run() {
87 done := false
88 for !done {
89 select {
90 case j := <-w.jobChan:
91 j.build()
92 w.wm.ReportResult(w, j)
93 case done = <-w.waitChan:
94 }
95 }
96 w.doneChan <- true
97}
98
99func (w *Worker) PostJob(j *Job) {
100 w.jobChan <- j
101}
102
103func (w *Worker) Wait() {
104 w.waitChan <- true
105 <-w.doneChan
106}
107
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900108func evalCmd(ev *Evaluator, r runner, s string) []runner {
109 r = newRunner(r, s)
110 if strings.IndexByte(r.cmd, '$') < 0 {
111 // fast path
112 return []runner{r}
113 }
114 // TODO(ukai): parse once more earlier?
115 expr, _, err := parseExpr([]byte(r.cmd), nil)
116 if err != nil {
117 panic(fmt.Errorf("parse cmd %q: %v", r.cmd, err))
118 }
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900119 buf := newBuf()
120 expr.Eval(buf, ev)
121 cmds := buf.String()
122 freeBuf(buf)
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900123 var runners []runner
124 for _, cmd := range strings.Split(cmds, "\n") {
Shinichiro Hamaji212abfb2015-04-29 03:02:59 +0900125 if len(runners) > 0 && strings.HasSuffix(runners[len(runners)-1].cmd, "\\") {
126 runners[len(runners)-1].cmd += "\n"
127 runners[len(runners)-1].cmd += cmd
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900128 } else {
129 runners = append(runners, newRunner(r, cmd))
130 }
131 }
132 return runners
133}
134
135func newRunner(r runner, s string) runner {
136 for {
137 s = trimLeftSpace(s)
138 if s == "" {
139 return runner{}
140 }
141 switch s[0] {
142 case '@':
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900143 if !dryRunFlag {
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900144 r.echo = false
145 }
146 s = s[1:]
147 continue
148 case '-':
149 r.ignoreError = true
150 s = s[1:]
151 continue
152 }
153 break
154 }
155 r.cmd = s
156 return r
157}
158
159func (r runner) run(output string) error {
160 if r.echo || dryRunFlag {
161 fmt.Printf("%s\n", r.cmd)
162 }
163 if dryRunFlag {
164 return nil
165 }
166 args := []string{r.shell, "-c", r.cmd}
167 cmd := exec.Cmd{
168 Path: args[0],
169 Args: args,
170 }
171 out, err := cmd.CombinedOutput()
172 fmt.Printf("%s", out)
173 exit := exitStatus(err)
174 if r.ignoreError && exit != 0 {
175 fmt.Printf("[%s] Error %d (ignored)\n", output, exit)
176 err = nil
177 }
178 return err
179}
180
181func (j Job) createRunners() []runner {
Shinichiro Hamajib41fd502015-04-29 03:34:07 +0900182 runners, _ := j.ex.createRunners(j.n, false)
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900183 return runners
184}
185
Shinichiro Hamaji71fae4c2015-05-25 17:48:34 +0900186// TODO(ukai): use time.Time?
187func getTimestamp(filename string) int64 {
188 st, err := os.Stat(filename)
189 if err != nil {
190 return -2
191 }
192 return st.ModTime().Unix()
193}
194
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900195func (j Job) build() {
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900196 if j.n.IsPhony {
197 j.outputTs = -2 // trigger cmd even if all inputs don't exist.
198 } else {
199 j.outputTs = getTimestamp(j.n.Output)
200 }
201
202 if !j.n.HasRule {
203 if j.outputTs >= 0 || j.n.IsPhony {
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900204 return
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900205 }
206 if len(j.parents) == 0 {
207 ErrorNoLocation("*** No rule to make target %q.", j.n.Output)
208 } else {
209 ErrorNoLocation("*** No rule to make target %q, needed by %q.", j.n.Output, j.parents[0].n.Output)
210 }
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900211 ErrorNoLocation("no rule to make target %q", j.n.Output)
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900212 }
213
214 if j.outputTs >= j.depsTs {
215 // TODO: stats.
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900216 return
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900217 }
218
219 for _, r := range j.createRunners() {
220 err := r.run(j.n.Output)
221 if err != nil {
222 exit := exitStatus(err)
223 ErrorNoLocation("[%s] Error %d: %v", j.n.Output, exit, err)
224 }
225 }
226
227 if j.n.IsPhony {
228 j.outputTs = time.Now().Unix()
229 } else {
230 j.outputTs = getTimestamp(j.n.Output)
231 if j.outputTs < 0 {
232 j.outputTs = time.Now().Unix()
233 }
234 }
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900235}
236
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900237func (wm *WorkerManager) handleJobs() {
238 for {
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900239 if !useParaFlag && len(wm.freeWorkers) == 0 {
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900240 return
241 }
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900242 if wm.readyQueue.Len() == 0 {
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900243 return
244 }
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900245 j := heap.Pop(&wm.readyQueue).(*Job)
Shinichiro Hamaji93f2aa32015-05-14 23:23:27 +0900246 Log("run: %s", j.n.Output)
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900247
248 if useParaFlag {
Shinichiro Hamajia6808422015-05-13 18:00:50 +0900249 j.runners = j.createRunners()
250 if len(j.runners) == 0 {
251 wm.updateParents(j)
252 wm.finishCnt++
253 } else {
254 wm.runnings[j.n.Output] = j
255 wm.para.RunCommand(j.runners)
256 }
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900257 } else {
258 j.numDeps = -1 // Do not let other workers pick this.
259 w := wm.freeWorkers[0]
260 wm.freeWorkers = wm.freeWorkers[1:]
261 wm.busyWorkers[w] = true
262 w.jobChan <- j
263 }
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900264 }
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900265}
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900266
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900267func (wm *WorkerManager) updateParents(j *Job) {
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900268 for _, p := range j.parents {
269 p.numDeps--
Shinichiro Hamaji8e4dd9d2015-05-15 00:04:44 +0900270 Log("child: %s (%d)", p.n.Output, p.numDeps)
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900271 if p.depsTs < j.outputTs {
272 p.depsTs = j.outputTs
273 }
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900274 wm.maybePushToReadyQueue(p)
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900275 }
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900276}
277
278type WorkerManager struct {
Shinichiro Hamaji55c50bd2015-04-27 21:05:47 +0900279 jobs []*Job
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900280 readyQueue JobQueue
Shinichiro Hamaji55c50bd2015-04-27 21:05:47 +0900281 jobChan chan *Job
282 resultChan chan JobResult
283 newDepChan chan NewDep
284 waitChan chan bool
285 doneChan chan bool
286 freeWorkers []*Worker
287 busyWorkers map[*Worker]bool
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900288 ex *Executor
289 para *ParaWorker
290 paraChan chan *ParaResult
291 runnings map[string]*Job
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900292
293 finishCnt int
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900294}
295
296func NewWorkerManager() *WorkerManager {
297 wm := &WorkerManager{
Shinichiro Hamaji55c50bd2015-04-27 21:05:47 +0900298 jobChan: make(chan *Job),
299 resultChan: make(chan JobResult),
300 newDepChan: make(chan NewDep),
301 waitChan: make(chan bool),
302 doneChan: make(chan bool),
303 busyWorkers: make(map[*Worker]bool),
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900304 }
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900305
306 if useParaFlag {
307 wm.runnings = make(map[string]*Job)
308 wm.paraChan = make(chan *ParaResult)
309 wm.para = NewParaWorker(wm.paraChan)
310 go wm.para.Run()
311 } else {
312 wm.busyWorkers = make(map[*Worker]bool)
313 for i := 0; i < jobsFlag; i++ {
314 w := NewWorker(wm)
315 wm.freeWorkers = append(wm.freeWorkers, w)
316 go w.Run()
317 }
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900318 }
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900319 heap.Init(&wm.readyQueue)
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900320 go wm.Run()
321 return wm
322}
323
324func exitStatus(err error) int {
325 if err == nil {
326 return 0
327 }
328 exit := 1
329 if err, ok := err.(*exec.ExitError); ok {
330 if w, ok := err.ProcessState.Sys().(syscall.WaitStatus); ok {
331 return w.ExitStatus()
332 }
333 }
334 return exit
335}
336
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900337func (wm *WorkerManager) hasTodo() bool {
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900338 return wm.finishCnt != len(wm.jobs)
339}
340
341func (wm *WorkerManager) maybePushToReadyQueue(j *Job) {
342 if j.numDeps != 0 {
343 return
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900344 }
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900345 heap.Push(&wm.readyQueue, j)
Shinichiro Hamaji93f2aa32015-05-14 23:23:27 +0900346 Log("ready: %s", j.n.Output)
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900347}
348
Shinichiro Hamaji55c50bd2015-04-27 21:05:47 +0900349func (wm *WorkerManager) handleNewDep(j *Job, neededBy *Job) {
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900350 if j.numDeps < 0 {
351 neededBy.numDeps--
Shinichiro Hamaji5180c972015-04-28 20:14:38 +0900352 if neededBy.id > 0 {
353 panic("already in WM... can this happen?")
354 wm.maybePushToReadyQueue(neededBy)
355 }
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900356 } else {
357 j.parents = append(j.parents, neededBy)
358 }
359}
360
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900361func (wm *WorkerManager) Run() {
362 done := false
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900363 for wm.hasTodo() || len(wm.busyWorkers) > 0 || len(wm.runnings) > 0 || !done {
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900364 select {
365 case j := <-wm.jobChan:
Shinichiro Hamaji8e4dd9d2015-05-15 00:04:44 +0900366 Log("wait: %s (%d)", j.n.Output, j.numDeps)
Shinichiro Hamaji5180c972015-04-28 20:14:38 +0900367 j.id = len(wm.jobs) + 1
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900368 wm.jobs = append(wm.jobs, j)
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900369 wm.maybePushToReadyQueue(j)
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900370 case jr := <-wm.resultChan:
Shinichiro Hamaji93f2aa32015-05-14 23:23:27 +0900371 Log("done: %s", jr.j.n.Output)
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900372 delete(wm.busyWorkers, jr.w)
373 wm.freeWorkers = append(wm.freeWorkers, jr.w)
374 wm.updateParents(jr.j)
Shinichiro Hamajidbc6c132015-04-28 18:26:36 +0900375 wm.finishCnt++
Shinichiro Hamaji55c50bd2015-04-27 21:05:47 +0900376 case af := <-wm.newDepChan:
377 wm.handleNewDep(af.j, af.neededBy)
Shinichiro Hamaji8e4dd9d2015-05-15 00:04:44 +0900378 Log("dep: %s (%d) %s", af.neededBy.n.Output, af.neededBy.numDeps, af.j.n.Output)
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900379 case pr := <-wm.paraChan:
Shinichiro Hamajia6808422015-05-13 18:00:50 +0900380 if pr.status < 0 && pr.signal < 0 {
381 j := wm.runnings[pr.output]
382 for _, r := range j.runners {
383 if r.echo || dryRunFlag {
384 fmt.Printf("%s\n", r.cmd)
385 }
386 }
387 } else {
388 os.Stdout.Write([]byte(pr.stdout))
389 os.Stderr.Write([]byte(pr.stderr))
390 j := wm.runnings[pr.output]
391 wm.updateParents(j)
392 delete(wm.runnings, pr.output)
393 wm.finishCnt++
394 }
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900395 case done = <-wm.waitChan:
396 }
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900397 wm.handleJobs()
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900398
399 if useParaFlag {
400 numBusy := len(wm.runnings)
401 if numBusy > jobsFlag {
402 numBusy = jobsFlag
403 }
404 Log("job=%d ready=%d free=%d busy=%d", len(wm.jobs)-wm.finishCnt, wm.readyQueue.Len(), jobsFlag-numBusy, numBusy)
405 } else {
406 Log("job=%d ready=%d free=%d busy=%d", len(wm.jobs)-wm.finishCnt, wm.readyQueue.Len(), len(wm.freeWorkers), len(wm.busyWorkers))
407 }
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900408 }
409
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900410 if useParaFlag {
411 Log("Wait for para to finish")
412 wm.para.Wait()
413 } else {
414 for _, w := range wm.freeWorkers {
415 w.Wait()
416 }
417 for w := range wm.busyWorkers {
418 w.Wait()
419 }
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900420 }
421 wm.doneChan <- true
422}
423
424func (wm *WorkerManager) PostJob(j *Job) {
425 wm.jobChan <- j
426}
427
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900428func (wm *WorkerManager) ReportResult(w *Worker, j *Job) {
429 wm.resultChan <- JobResult{w: w, j: j}
430}
431
Shinichiro Hamaji55c50bd2015-04-27 21:05:47 +0900432func (wm *WorkerManager) ReportNewDep(j *Job, neededBy *Job) {
433 wm.newDepChan <- NewDep{j: j, neededBy: neededBy}
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +0900434}
435
Shinichiro Hamaji74a66002015-04-27 16:42:30 +0900436func (wm *WorkerManager) Wait() {
437 wm.waitChan <- true
438 <-wm.doneChan
439}