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