blob: 37f94b8e71e86d141b71129d4cc292fec015d2b2 [file] [log] [blame]
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +09001// Copyright 2015 Google Inc. All rights reserved
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package kati
16
17import (
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090018 "fmt"
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090019 "os/exec"
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090020 "strings"
21 "sync"
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090022
23 "github.com/golang/glog"
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090024)
25
26type execContext struct {
27 shell string
28
29 mu sync.Mutex
30 ev *Evaluator
Fumitoshi Ukai09fcd522015-07-15 14:31:50 +090031 vpaths searchPaths
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090032 output string
33 inputs []string
34}
35
Fumitoshi Ukai09fcd522015-07-15 14:31:50 +090036func newExecContext(vars Vars, vpaths searchPaths, avoidIO bool) *execContext {
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090037 ev := NewEvaluator(vars)
38 ev.avoidIO = avoidIO
39
40 ctx := &execContext{
Fumitoshi Ukai09fcd522015-07-15 14:31:50 +090041 ev: ev,
42 vpaths: vpaths,
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090043 }
44 av := autoVar{ctx: ctx}
45 for k, v := range map[string]Var{
46 "@": autoAtVar{autoVar: av},
47 "<": autoLessVar{autoVar: av},
48 "^": autoHatVar{autoVar: av},
49 "+": autoPlusVar{autoVar: av},
50 "*": autoStarVar{autoVar: av},
51 } {
52 ev.vars[k] = v
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090053 // $<k>D = $(patsubst %/,%,$(dir $<k>))
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +090054 ev.vars[k+"D"] = suffixDVar(k)
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090055 // $<k>F = $(notdir $<k>)
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +090056 ev.vars[k+"F"] = suffixFVar(k)
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +090057 }
58
59 // TODO: We should move this to somewhere around evalCmd so that
60 // we can handle SHELL in target specific variables.
61 shell, err := ev.EvaluateVar("SHELL")
62 if err != nil {
63 shell = "/bin/sh"
64 }
65 ctx.shell = shell
66 return ctx
67}
68
69func (ec *execContext) uniqueInputs() []string {
70 var uniqueInputs []string
71 seen := make(map[string]bool)
72 for _, input := range ec.inputs {
73 if !seen[input] {
74 seen[input] = true
75 uniqueInputs = append(uniqueInputs, input)
76 }
77 }
78 return uniqueInputs
79}
80
81type autoVar struct{ ctx *execContext }
82
83func (v autoVar) Flavor() string { return "undefined" }
84func (v autoVar) Origin() string { return "automatic" }
85func (v autoVar) IsDefined() bool { return true }
86func (v autoVar) Append(*Evaluator, string) (Var, error) {
87 return nil, fmt.Errorf("cannot append to autovar")
88}
89func (v autoVar) AppendVar(*Evaluator, Value) (Var, error) {
90 return nil, fmt.Errorf("cannot append to autovar")
91}
92func (v autoVar) serialize() serializableVar {
93 return serializableVar{Type: ""}
94}
95func (v autoVar) dump(d *dumpbuf) {
96 d.err = fmt.Errorf("cannot dump auto var: %v", v)
97}
98
99type autoAtVar struct{ autoVar }
100
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900101func (v autoAtVar) Eval(w evalWriter, ev *Evaluator) error {
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900102 fmt.Fprint(w, v.String())
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900103 return nil
104}
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900105func (v autoAtVar) String() string { return v.ctx.output }
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900106
107type autoLessVar struct{ autoVar }
108
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900109func (v autoLessVar) Eval(w evalWriter, ev *Evaluator) error {
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900110 fmt.Fprint(w, v.String())
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900111 return nil
112}
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900113func (v autoLessVar) String() string {
114 if len(v.ctx.inputs) > 0 {
115 return v.ctx.inputs[0]
116 }
117 return ""
118}
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900119
120type autoHatVar struct{ autoVar }
121
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900122func (v autoHatVar) Eval(w evalWriter, ev *Evaluator) error {
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900123 fmt.Fprint(w, v.String())
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900124 return nil
125}
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900126func (v autoHatVar) String() string {
127 return strings.Join(v.ctx.uniqueInputs(), " ")
128}
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900129
130type autoPlusVar struct{ autoVar }
131
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900132func (v autoPlusVar) Eval(w evalWriter, ev *Evaluator) error {
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900133 fmt.Fprint(w, v.String())
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900134 return nil
135}
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900136func (v autoPlusVar) String() string { return strings.Join(v.ctx.inputs, " ") }
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900137
138type autoStarVar struct{ autoVar }
139
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900140func (v autoStarVar) Eval(w evalWriter, ev *Evaluator) error {
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900141 fmt.Fprint(w, v.String())
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900142 return nil
143}
144
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900145// TODO: Use currentStem. See auto_stem_var.mk
146func (v autoStarVar) String() string { return stripExt(v.ctx.output) }
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900147
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900148func suffixDVar(k string) Var {
149 return &recursiveVar{
150 expr: expr{
151 &funcPatsubst{
152 fclosure: fclosure{
153 args: []Value{
154 literal("(patsubst"),
155 literal("%/"),
156 literal("%"),
157 &funcDir{
158 fclosure: fclosure{
159 args: []Value{
160 literal("(dir"),
161 &varref{
162 varname: literal(k),
163 },
164 },
165 },
166 },
167 },
168 },
169 },
170 },
171 origin: "automatic",
172 }
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900173}
174
Fumitoshi Ukaibbb0db52015-06-30 16:51:27 +0900175func suffixFVar(k string) Var {
176 return &recursiveVar{
177 expr: expr{
178 &funcNotdir{
179 fclosure: fclosure{
180 args: []Value{
181 literal("(notdir"),
182 &varref{varname: literal(k)},
183 },
184 },
185 },
186 },
187 origin: "automatic",
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900188 }
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900189}
190
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900191// runner is a single shell command invocation.
192type runner struct {
193 output string
194 cmd string
195 echo bool
196 ignoreError bool
197 shell string
198}
199
200func (r runner) String() string {
201 cmd := r.cmd
202 if !r.echo {
203 cmd = "@" + cmd
204 }
205 if r.ignoreError {
206 cmd = "-" + cmd
207 }
208 return cmd
209}
210
211func (r runner) forCmd(s string) runner {
212 for {
213 s = trimLeftSpace(s)
214 if s == "" {
215 return runner{}
216 }
217 switch s[0] {
218 case '@':
219 if !DryRunFlag {
220 r.echo = false
221 }
222 s = s[1:]
223 continue
224 case '-':
225 r.ignoreError = true
226 s = s[1:]
227 continue
228 }
229 break
230 }
231 r.cmd = s
232 return r
233}
234
235func (r runner) eval(ev *Evaluator, s string) ([]runner, error) {
236 r = r.forCmd(s)
237 if strings.IndexByte(r.cmd, '$') < 0 {
238 // fast path
239 return []runner{r}, nil
240 }
241 // TODO(ukai): parse once more earlier?
Fumitoshi Ukaie9aa3802015-07-03 11:33:23 +0900242 expr, _, err := parseExpr([]byte(r.cmd), nil, parseOp{})
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900243 if err != nil {
244 return nil, ev.errorf("parse cmd %q: %v", r.cmd, err)
245 }
Fumitoshi Ukaia4a02252015-07-09 14:25:18 +0900246 buf := newEbuf()
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900247 err = expr.Eval(buf, ev)
248 if err != nil {
249 return nil, err
250 }
251 cmds := buf.String()
Fumitoshi Ukaia4a02252015-07-09 14:25:18 +0900252 buf.release()
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900253 glog.V(1).Infof("evalcmd: %q => %q", r.cmd, cmds)
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900254 var runners []runner
255 for _, cmd := range strings.Split(cmds, "\n") {
256 if len(runners) > 0 && strings.HasSuffix(runners[len(runners)-1].cmd, "\\") {
257 runners[len(runners)-1].cmd += "\n"
258 runners[len(runners)-1].cmd += cmd
259 continue
260 }
261 runners = append(runners, r.forCmd(cmd))
262 }
263 return runners, nil
264}
265
266func (r runner) run(output string) error {
267 if r.echo || DryRunFlag {
268 fmt.Printf("%s\n", r.cmd)
269 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900270 s := cmdline(r.cmd)
271 glog.Infof("sh:%q", s)
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900272 if DryRunFlag {
273 return nil
274 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900275 args := []string{r.shell, "-c", s}
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900276 cmd := exec.Cmd{
277 Path: args[0],
278 Args: args,
279 }
280 out, err := cmd.CombinedOutput()
281 fmt.Printf("%s", out)
282 exit := exitStatus(err)
283 if r.ignoreError && exit != 0 {
284 fmt.Printf("[%s] Error %d (ignored)\n", output, exit)
285 err = nil
286 }
287 return err
288}
289
290func createRunners(ctx *execContext, n *DepNode) ([]runner, bool, error) {
291 var runners []runner
292 if len(n.Cmds) == 0 {
293 return runners, false, nil
294 }
295
296 ctx.mu.Lock()
297 defer ctx.mu.Unlock()
298 // For automatic variables.
299 ctx.output = n.Output
300 ctx.inputs = n.ActualInputs
301 for k, v := range n.TargetSpecificVars {
302 restore := ctx.ev.vars.save(k)
303 defer restore()
304 ctx.ev.vars[k] = v
Fumitoshi Ukai7fd162b2015-07-15 12:53:57 +0900305 if glog.V(1) {
306 glog.Infof("set tsv: %s=%s", k, v)
307 }
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900308 }
309
310 ctx.ev.filename = n.Filename
311 ctx.ev.lineno = n.Lineno
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900312 glog.Infof("Building: %s cmds:%q", n.Output, n.Cmds)
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900313 r := runner{
314 output: n.Output,
315 echo: true,
316 shell: ctx.shell,
317 }
318 for _, cmd := range n.Cmds {
319 rr, err := r.eval(ctx.ev, cmd)
320 if err != nil {
321 return nil, false, err
322 }
323 for _, r := range rr {
324 if len(r.cmd) != 0 {
325 runners = append(runners, r)
326 }
327 }
328 }
Fumitoshi Ukaie5bf7582015-07-28 16:07:51 +0900329 if len(ctx.ev.delayedOutputs) > 0 {
330 var nrunners []runner
331 r := runner{
332 output: n.Output,
333 shell: ctx.shell,
334 }
335 for _, o := range ctx.ev.delayedOutputs {
336 nrunners = append(nrunners, r.forCmd(o))
337 }
338 nrunners = append(nrunners, runners...)
339 runners = nrunners
340 ctx.ev.delayedOutputs = nil
341 }
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900342 return runners, ctx.ev.hasIO, nil
343}
344
345func evalCommands(nodes []*DepNode, vars Vars) error {
346 ioCnt := 0
Fumitoshi Ukai09fcd522015-07-15 14:31:50 +0900347 ectx := newExecContext(vars, searchPaths{}, true)
Fumitoshi Ukaia70b4ea2015-06-30 15:31:49 +0900348 for i, n := range nodes {
349 runners, hasIO, err := createRunners(ectx, n)
350 if err != nil {
351 return err
352 }
353 if hasIO {
354 ioCnt++
355 if ioCnt%100 == 0 {
356 logStats("%d/%d rules have IO", ioCnt, i+1)
357 }
358 continue
359 }
360
361 n.Cmds = []string{}
362 n.TargetSpecificVars = make(Vars)
363 for _, r := range runners {
364 n.Cmds = append(n.Cmds, r.String())
365 }
366 }
367 logStats("%d/%d rules have IO", ioCnt, len(nodes))
368 return nil
369}