blob: a6761710190337d8944ea6f87d2710a4150c6d27 [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 {
24 out_vars map[string]string
25 out_rules []*Rule
26 refs map[string]bool
27 vars map[string]string
28 cur_rule *Rule
29}
30
31func newEvaluator() *Evaluator {
32 return &Evaluator{
33 out_vars: make(map[string]string),
34 refs: make(map[string]bool),
35 vars: make(map[string]string),
36 }
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 }
60 re, err := regexp.Compile("\\s")
61 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 '@':
84 buf.WriteString(ev.cur_rule.output)
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
104 value = ev.out_vars[varname]
105 }
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)
127 ev.out_vars[lhs] = rhs
128}
129
130func (ev *Evaluator) evalRule(ast *RuleAST) {
131 ev.cur_rule = &Rule{}
132 lhs := ev.evalExpr(ast.lhs)
133 ev.cur_rule.output = lhs
134 rhs := ev.evalExpr(ast.rhs)
135 if rhs != "" {
136 ev.cur_rule.inputs = strings.Split(rhs, " ")
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)
143 ev.cur_rule.cmds = cmds
144 ev.out_rules = append(ev.out_rules, ev.cur_rule)
145 ev.cur_rule = nil
146}
147
148func (ev *Evaluator) eval(ast AST) {
149 switch ast.typ() {
150 case AST_ASSIGN:
151 ev.evalAssign(ast.(*AssignAST))
152 case AST_RULE:
153 ev.evalRule(ast.(*RuleAST))
154 }
155}
156
157func Eval(mk Makefile) *EvalResult {
158 ev := newEvaluator()
159 for _, stmt := range mk.stmts {
160 ev.eval(stmt)
161 }
162 return &EvalResult{
163 vars: ev.out_vars,
164 rules: ev.out_rules,
165 refs: ev.refs,
166 }
167}