Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "os" |
| 6 | "os/exec" |
| 7 | "time" |
| 8 | ) |
| 9 | |
| 10 | type Executor struct { |
| 11 | rules map[string]*Rule |
| 12 | } |
| 13 | |
| 14 | func newExecutor() *Executor { |
| 15 | return &Executor{ |
| 16 | rules: make(map[string]*Rule), |
| 17 | } |
| 18 | } |
| 19 | |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame^] | 20 | // TODO(ukai): use time.Time? |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 21 | func getTimestamp(filename string) int64 { |
| 22 | st, err := os.Stat(filename) |
| 23 | if err != nil { |
| 24 | return -2 |
| 25 | } |
| 26 | return st.ModTime().Unix() |
| 27 | } |
| 28 | |
| 29 | func (ex *Executor) runCommands(cmds []string) { |
| 30 | for _, cmd := range cmds { |
| 31 | fmt.Printf("%s\n", cmd) |
| 32 | |
| 33 | args := []string{"/bin/sh", "-c", cmd} |
| 34 | cmd := exec.Cmd{ |
| 35 | Path: args[0], |
| 36 | Args: args, |
| 37 | } |
| 38 | out, err := cmd.CombinedOutput() |
| 39 | if err != nil { |
| 40 | panic(err) |
| 41 | } |
| 42 | success := false |
| 43 | if cmd.ProcessState != nil { |
| 44 | success = cmd.ProcessState.Success() |
| 45 | } |
| 46 | |
| 47 | fmt.Printf("%s", out) |
| 48 | if !success { |
| 49 | panic("Command failed") |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | func (ex *Executor) build(output string) int64 { |
| 55 | Log("Building: %s", output) |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame^] | 56 | outputTs := getTimestamp(output) |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 57 | |
| 58 | rule, present := ex.rules[output] |
| 59 | if !present { |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame^] | 60 | if outputTs >= 0 { |
| 61 | return outputTs |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 62 | } |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame^] | 63 | Error("No rule to make target %q", output) |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | latest := int64(-1) |
| 67 | for _, input := range rule.inputs { |
| 68 | ts := ex.build(input) |
| 69 | if latest < ts { |
| 70 | latest = ts |
| 71 | } |
| 72 | } |
| 73 | |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame^] | 74 | if outputTs >= latest { |
| 75 | return outputTs |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | ex.runCommands(rule.cmds) |
| 79 | |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame^] | 80 | outputTs = getTimestamp(output) |
| 81 | if outputTs < 0 { |
| 82 | outputTs = time.Now().Unix() |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 83 | } |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame^] | 84 | return outputTs |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 85 | } |
| 86 | |
| 87 | func (ex *Executor) exec(er *EvalResult) { |
| 88 | if len(er.rules) == 0 { |
| 89 | panic("No targets.") |
| 90 | } |
| 91 | |
| 92 | for _, rule := range er.rules { |
| 93 | if _, present := ex.rules[rule.output]; present { |
| 94 | Warn("overiding recipie for target '%s'", rule.output) |
| 95 | } |
| 96 | ex.rules[rule.output] = rule |
| 97 | } |
| 98 | |
| 99 | ex.build(er.rules[0].output) |
| 100 | } |
| 101 | |
| 102 | func Exec(er *EvalResult) { |
| 103 | ex := newExecutor() |
| 104 | ex.exec(er) |
| 105 | } |