Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "os/exec" |
| 6 | "path/filepath" |
| 7 | "regexp" |
| 8 | "strings" |
| 9 | ) |
| 10 | |
| 11 | type Rule struct { |
| 12 | output string |
| 13 | inputs []string |
| 14 | cmds []string |
| 15 | } |
| 16 | |
| 17 | type EvalResult struct { |
| 18 | vars map[string]string |
| 19 | rules []*Rule |
| 20 | refs map[string]bool |
| 21 | } |
| 22 | |
| 23 | type Evaluator struct { |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 24 | outVars map[string]string |
| 25 | outRules []*Rule |
| 26 | refs map[string]bool |
| 27 | vars map[string]string |
| 28 | curRule *Rule |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 29 | } |
| 30 | |
| 31 | func newEvaluator() *Evaluator { |
| 32 | return &Evaluator{ |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 33 | outVars: make(map[string]string), |
| 34 | refs: make(map[string]bool), |
| 35 | vars: make(map[string]string), |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 36 | } |
| 37 | } |
| 38 | |
| 39 | func (ev *Evaluator) evalFunction(ex string) (string, bool) { |
| 40 | if strings.HasPrefix(ex, "wildcard ") { |
| 41 | arg := ex[len("wildcard "):] |
| 42 | |
| 43 | files, err := filepath.Glob(arg) |
| 44 | if err != nil { |
| 45 | panic(err) |
| 46 | } |
| 47 | return strings.Join(files, " "), true |
| 48 | } else if strings.HasPrefix(ex, "shell ") { |
| 49 | arg := ex[len("shell "):] |
| 50 | |
| 51 | args := []string{"/bin/sh", "-c", arg} |
| 52 | cmd := exec.Cmd{ |
| 53 | Path: args[0], |
| 54 | Args: args, |
| 55 | } |
| 56 | out, err := cmd.CombinedOutput() |
| 57 | if err != nil { |
| 58 | panic(err) |
| 59 | } |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 60 | re, err := regexp.Compile(`\s`) |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 61 | if err != nil { |
| 62 | panic(err) |
| 63 | } |
| 64 | return string(re.ReplaceAllString(string(out), " ")), true |
| 65 | } |
| 66 | return "", false |
| 67 | } |
| 68 | |
| 69 | func (ev *Evaluator) evalExprSlice(ex string, term byte) (string, int) { |
| 70 | var buf bytes.Buffer |
| 71 | i := 0 |
| 72 | for i < len(ex) && ex[i] != term { |
| 73 | ch := ex[i] |
| 74 | i++ |
| 75 | switch ch { |
| 76 | case '$': |
| 77 | if i >= len(ex) || ex[i] == term { |
| 78 | continue |
| 79 | } |
| 80 | |
| 81 | var varname string |
| 82 | switch ex[i] { |
| 83 | case '@': |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 84 | buf.WriteString(ev.curRule.output) |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 85 | i++ |
| 86 | continue |
| 87 | case '(': |
| 88 | v, j := ev.evalExprSlice(ex[i+1:], ')') |
| 89 | i += j + 2 |
| 90 | if r, done := ev.evalFunction(v); done { |
| 91 | buf.WriteString(r) |
| 92 | continue |
| 93 | } |
| 94 | |
| 95 | varname = v |
| 96 | default: |
| 97 | varname = string(ex[i]) |
| 98 | i++ |
| 99 | } |
| 100 | |
| 101 | value, present := ev.vars[varname] |
| 102 | if !present { |
| 103 | ev.refs[varname] = true |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 104 | value = ev.outVars[varname] |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 105 | } |
| 106 | buf.WriteString(value) |
| 107 | |
| 108 | default: |
| 109 | buf.WriteByte(ch) |
| 110 | } |
| 111 | } |
| 112 | return buf.String(), i |
| 113 | } |
| 114 | |
| 115 | func (ev *Evaluator) evalExpr(ex string) string { |
| 116 | r, i := ev.evalExprSlice(ex, 0) |
| 117 | if len(ex) != i { |
| 118 | panic("Had a null character?") |
| 119 | } |
| 120 | return r |
| 121 | } |
| 122 | |
| 123 | func (ev *Evaluator) evalAssign(ast *AssignAST) { |
| 124 | lhs := ev.evalExpr(ast.lhs) |
| 125 | rhs := ev.evalExpr(ast.rhs) |
| 126 | Log("ASSIGN: %s=%s", lhs, rhs) |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 127 | ev.outVars[lhs] = rhs |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | func (ev *Evaluator) evalRule(ast *RuleAST) { |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 131 | ev.curRule = &Rule{} |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 132 | lhs := ev.evalExpr(ast.lhs) |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 133 | ev.curRule.output = lhs |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 134 | rhs := ev.evalExpr(ast.rhs) |
| 135 | if rhs != "" { |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 136 | ev.curRule.inputs = strings.Split(rhs, " ") |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 137 | } |
| 138 | var cmds []string |
| 139 | for _, cmd := range ast.cmds { |
| 140 | cmds = append(cmds, ev.evalExpr(cmd)) |
| 141 | } |
| 142 | Log("RULE: %s=%s", lhs, rhs) |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 143 | ev.curRule.cmds = cmds |
| 144 | ev.outRules = append(ev.outRules, ev.curRule) |
| 145 | ev.curRule = nil |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 146 | } |
| 147 | |
| 148 | func (ev *Evaluator) eval(ast AST) { |
Fumitoshi Ukai | e34c179 | 2015-03-30 17:53:47 +0900 | [diff] [blame^] | 149 | ast.eval(ev) |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | func Eval(mk Makefile) *EvalResult { |
| 153 | ev := newEvaluator() |
| 154 | for _, stmt := range mk.stmts { |
| 155 | ev.eval(stmt) |
| 156 | } |
| 157 | return &EvalResult{ |
Fumitoshi Ukai | cf2b038 | 2015-03-30 17:48:54 +0900 | [diff] [blame] | 158 | vars: ev.outVars, |
| 159 | rules: ev.outRules, |
Fumitoshi Ukai | 119dc91 | 2015-03-30 16:52:41 +0900 | [diff] [blame] | 160 | refs: ev.refs, |
| 161 | } |
| 162 | } |