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