blob: 4381774c3c27cb4047bf5c57b3242875f2b2f5ce [file] [log] [blame]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09001package main
2
3import (
4 "bytes"
5 "os/exec"
6 "path/filepath"
7 "regexp"
8 "strings"
9)
10
11type Rule struct {
12 output string
13 inputs []string
14 cmds []string
15}
16
17type EvalResult struct {
18 vars map[string]string
19 rules []*Rule
20 refs map[string]bool
21}
22
23type Evaluator struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090024 outVars map[string]string
25 outRules []*Rule
26 refs map[string]bool
27 vars map[string]string
28 curRule *Rule
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090029}
30
31func newEvaluator() *Evaluator {
32 return &Evaluator{
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090033 outVars: make(map[string]string),
34 refs: make(map[string]bool),
35 vars: make(map[string]string),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090036 }
37}
38
39func (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 Ukaicf2b0382015-03-30 17:48:54 +090060 re, err := regexp.Compile(`\s`)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090061 if err != nil {
62 panic(err)
63 }
64 return string(re.ReplaceAllString(string(out), " ")), true
65 }
66 return "", false
67}
68
69func (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 Ukaicf2b0382015-03-30 17:48:54 +090084 buf.WriteString(ev.curRule.output)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090085 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 Ukaicf2b0382015-03-30 17:48:54 +0900104 value = ev.outVars[varname]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900105 }
106 buf.WriteString(value)
107
108 default:
109 buf.WriteByte(ch)
110 }
111 }
112 return buf.String(), i
113}
114
115func (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
123func (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 Ukaicf2b0382015-03-30 17:48:54 +0900127 ev.outVars[lhs] = rhs
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900128}
129
130func (ev *Evaluator) evalRule(ast *RuleAST) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900131 ev.curRule = &Rule{}
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900132 lhs := ev.evalExpr(ast.lhs)
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900133 ev.curRule.output = lhs
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900134 rhs := ev.evalExpr(ast.rhs)
135 if rhs != "" {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900136 ev.curRule.inputs = strings.Split(rhs, " ")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900137 }
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 Ukaicf2b0382015-03-30 17:48:54 +0900143 ev.curRule.cmds = cmds
144 ev.outRules = append(ev.outRules, ev.curRule)
145 ev.curRule = nil
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900146}
147
148func (ev *Evaluator) eval(ast AST) {
Fumitoshi Ukaie34c1792015-03-30 17:53:47 +0900149 ast.eval(ev)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900150}
151
152func Eval(mk Makefile) *EvalResult {
153 ev := newEvaluator()
154 for _, stmt := range mk.stmts {
155 ev.eval(stmt)
156 }
157 return &EvalResult{
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900158 vars: ev.outVars,
159 rules: ev.outRules,
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900160 refs: ev.refs,
161 }
162}