blob: 2d1de66dc404e8e69ee9527bbfd6aa596ee0ba01 [file] [log] [blame]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09001package main
2
3import (
4 "bytes"
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +09005 "fmt"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09006 "os/exec"
7 "path/filepath"
8 "regexp"
9 "strings"
10)
11
12type Rule struct {
13 output string
14 inputs []string
15 cmds []string
16}
17
18type EvalResult struct {
19 vars map[string]string
20 rules []*Rule
21 refs map[string]bool
22}
23
24type Evaluator struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090025 outVars map[string]string
26 outRules []*Rule
27 refs map[string]bool
28 vars map[string]string
29 curRule *Rule
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090030}
31
32func newEvaluator() *Evaluator {
33 return &Evaluator{
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090034 outVars: make(map[string]string),
35 refs: make(map[string]bool),
36 vars: make(map[string]string),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090037 }
38}
39
40func (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 Ukaicf2b0382015-03-30 17:48:54 +090061 re, err := regexp.Compile(`\s`)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090062 if err != nil {
63 panic(err)
64 }
65 return string(re.ReplaceAllString(string(out), " ")), true
66 }
67 return "", false
68}
69
70func (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 Ukaicf2b0382015-03-30 17:48:54 +090085 buf.WriteString(ev.curRule.output)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090086 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 Ukaicf2b0382015-03-30 17:48:54 +0900105 value = ev.outVars[varname]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900106 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900107 buf.WriteString(ev.evalExpr(value))
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900108
109 default:
110 buf.WriteByte(ch)
111 }
112 }
113 return buf.String(), i
114}
115
116func (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
124func (ev *Evaluator) evalAssign(ast *AssignAST) {
125 lhs := ev.evalExpr(ast.lhs)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900126 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 Ukai119dc912015-03-30 16:52:41 +0900135 Log("ASSIGN: %s=%s", lhs, rhs)
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900136 ev.outVars[lhs] = rhs
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900137}
138
139func (ev *Evaluator) evalRule(ast *RuleAST) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900140 ev.curRule = &Rule{}
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900141 lhs := ev.evalExpr(ast.lhs)
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900142 ev.curRule.output = lhs
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900143 rhs := ev.evalExpr(ast.rhs)
144 if rhs != "" {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900145 ev.curRule.inputs = strings.Split(rhs, " ")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900146 }
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 Ukaicf2b0382015-03-30 17:48:54 +0900152 ev.curRule.cmds = cmds
153 ev.outRules = append(ev.outRules, ev.curRule)
154 ev.curRule = nil
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900155}
156
157func (ev *Evaluator) eval(ast AST) {
Fumitoshi Ukaie34c1792015-03-30 17:53:47 +0900158 ast.eval(ev)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900159}
160
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900161func Eval(mk Makefile) (er *EvalResult, err error) {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900162 ev := newEvaluator()
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900163 defer func() {
164 if r := recover(); r != nil {
165 err = fmt.Errorf("panic: %v", r)
166 }
167 }()
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900168 for _, stmt := range mk.stmts {
169 ev.eval(stmt)
170 }
171 return &EvalResult{
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900172 vars: ev.outVars,
173 rules: ev.outRules,
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900174 refs: ev.refs,
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900175 }, nil
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900176}