blob: 2092cb3d6751a7793a3990d3e19ef8ca268d4ddb [file] [log] [blame]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09001package main
2
3import (
4 "fmt"
5 "os"
6 "os/exec"
Fumitoshi Ukai8edcb792015-04-02 11:23:23 +09007 "path/filepath"
Fumitoshi Ukai3e5161e2015-04-01 22:42:10 +09008 "strings"
Fumitoshi Ukai772fe1a2015-04-01 23:28:26 +09009 "syscall"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090010 "time"
11)
12
13type Executor struct {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +090014 rules map[string]*Rule
15 implicitRules []*Rule
Shinichiro Hamaji20a43762015-04-02 02:42:25 +090016 suffixRules map[string][]*Rule
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +090017 firstRule *Rule
Fumitoshi Ukai4b82f192015-04-07 11:36:42 +090018
19 // target -> timestamp
20 done map[string]int64
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090021}
22
23func newExecutor() *Executor {
24 return &Executor{
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +090025 rules: make(map[string]*Rule),
Shinichiro Hamaji20a43762015-04-02 02:42:25 +090026 suffixRules: make(map[string][]*Rule),
Fumitoshi Ukai4b82f192015-04-07 11:36:42 +090027 done: make(map[string]int64),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090028 }
29}
30
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090031// TODO(ukai): use time.Time?
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090032func getTimestamp(filename string) int64 {
33 st, err := os.Stat(filename)
34 if err != nil {
35 return -2
36 }
37 return st.ModTime().Unix()
38}
39
Fumitoshi Ukai6b378832015-04-06 16:54:37 +090040func (ex *Executor) exists(target string) bool {
Fumitoshi Ukaif04610d2015-04-07 00:03:03 +090041 _, present := ex.rules[target]
42 if present {
43 return true
44 }
Fumitoshi Ukai6b378832015-04-06 16:54:37 +090045 rule, present := ex.rules[".PHONY"]
46 if present {
47 for _, input := range rule.inputs {
48 if target == input {
49 return true
50 }
51 }
52 }
53 return exists(target)
54}
55
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +090056type runner struct {
57 output string
58 cmd string
59 echo bool
60 dryRun bool
61 ignoreError bool
62}
63
64func evalCmd(ev *Evaluator, r runner, s string) []runner {
65 r = newRunner(r, s)
66 if strings.IndexByte(r.cmd, '$') < 0 {
67 // fast path
68 return []runner{r}
69 }
70 cmds := ev.evalExpr(r.cmd)
71 var runners []runner
72 for _, cmd := range strings.Split(cmds, "\n") {
73 runners = append(runners, newRunner(r, cmd))
74 }
75 return runners
76}
77
78func newRunner(r runner, s string) runner {
79 for {
80 s = strings.TrimLeft(s, " \t")
81 if s == "" {
82 return runner{}
Fumitoshi Ukai772fe1a2015-04-01 23:28:26 +090083 }
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +090084 switch s[0] {
85 case '@':
86 if !r.dryRun {
87 r.echo = false
88 }
89 s = s[1:]
90 continue
91 case '-':
92 r.ignoreError = true
93 s = s[1:]
Shinichiro Hamaji07e146b2015-04-02 16:17:24 +090094 continue
95 }
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +090096 break
97 }
98 r.cmd = s
99 return r
100}
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900101
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +0900102func (r runner) run() error {
103 if r.echo {
104 fmt.Printf("%s\n", r.cmd)
105 }
106 if r.dryRun {
107 return nil
108 }
109 args := []string{"/bin/sh", "-c", r.cmd}
110 cmd := exec.Cmd{
111 Path: args[0],
112 Args: args,
113 }
114 out, err := cmd.CombinedOutput()
115 fmt.Printf("%s", out)
116 exit := exitStatus(err)
117 if r.ignoreError && exit != 0 {
118 fmt.Printf("[%s] Error %d (ignored)\n", r.output, exit)
119 err = nil
120 }
121 return err
122}
123
124func exitStatus(err error) int {
125 if err == nil {
126 return 0
127 }
128 exit := 1
129 if err, ok := err.(*exec.ExitError); ok {
130 if w, ok := err.ProcessState.Sys().(syscall.WaitStatus); ok {
131 return w.ExitStatus()
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900132 }
133 }
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +0900134 return exit
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900135}
136
Shinichiro Hamaji20a43762015-04-02 02:42:25 +0900137func replaceSuffix(s string, newsuf string) string {
138 // TODO: Factor out the logic around suffix rules and use
139 // it from substitution references.
140 // http://www.gnu.org/software/make/manual/make.html#Substitution-Refs
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +0900141 return fmt.Sprintf("%s.%s", stripExt(s), newsuf)
Shinichiro Hamaji20a43762015-04-02 02:42:25 +0900142}
143
Shinichiro Hamaji0d0af5b2015-04-02 05:17:23 +0900144func (ex *Executor) canPickImplicitRule(rule *Rule, output string) bool {
145 outputPattern := rule.outputPatterns[0]
146 if !matchPattern(outputPattern, output) {
147 return false
148 }
149 for _, input := range rule.inputs {
150 input = substPattern(outputPattern, input, output)
Fumitoshi Ukai6b378832015-04-06 16:54:37 +0900151 if !ex.exists(input) {
Shinichiro Hamaji0d0af5b2015-04-02 05:17:23 +0900152 return false
153 }
154 }
155 return true
156}
157
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900158func (ex *Executor) pickRule(output string) (*Rule, bool) {
159 rule, present := ex.rules[output]
160 if present {
Fumitoshi Ukaid92c4872015-04-06 16:17:31 +0900161 if len(rule.cmds) > 0 {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900162 return rule, true
163 }
Fumitoshi Ukaid92c4872015-04-06 16:17:31 +0900164 // If none of the explicit rules for a target has commands,
165 // then `make' searches for an applicable implicit rule to
166 // find some commands.
167 }
168
169 for _, irule := range ex.implicitRules {
170 if !ex.canPickImplicitRule(irule, output) {
171 continue
172 }
173 if rule != nil {
174 r := &Rule{}
175 *r = *rule
176 r.outputPatterns = irule.outputPatterns
177 // implicit rule's prerequisites will be used for $<
178 r.inputs = append(irule.inputs, r.inputs...)
179 if irule.vars != nil {
180 r.vars = NewVarTab(rule.vars)
181 for k, v := range irule.vars.m {
182 r.vars.Assign(k, v)
183 }
184 }
185 r.cmds = irule.cmds
186 // TODO(ukai): filename, lineno?
187 r.cmdLineno = irule.cmdLineno
188 return r, true
189 }
190 // TODO(ukai): check len(irule.cmd) ?
191 return irule, true
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900192 }
193
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900194 outputSuffix := filepath.Ext(output)
Fumitoshi Ukaid92c4872015-04-06 16:17:31 +0900195 if !strings.HasPrefix(outputSuffix, ".") {
196 return rule, rule != nil
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900197 }
Fumitoshi Ukaid92c4872015-04-06 16:17:31 +0900198 rules, present := ex.suffixRules[outputSuffix[1:]]
199 if !present {
200 return rule, rule != nil
201 }
202 for _, irule := range rules {
203 if len(irule.inputs) != 1 {
204 panic(fmt.Sprintf("unexpected number of input for a suffix rule (%d)", len(irule.inputs)))
205 }
Fumitoshi Ukai6b378832015-04-06 16:54:37 +0900206 if !ex.exists(replaceSuffix(output, irule.inputs[0])) {
Fumitoshi Ukaid92c4872015-04-06 16:17:31 +0900207 continue
208 }
209 if rule != nil {
210 r := &Rule{}
211 *r = *rule
212 // TODO(ukai): input order is correct?
213 r.inputs = append([]string{replaceSuffix(output, irule.inputs[0])}, r.inputs...)
214 r.vars = NewVarTab(rule.vars)
Fumitoshi Ukai6b378832015-04-06 16:54:37 +0900215 if irule.vars != nil {
216 for k, v := range irule.vars.m {
217 r.vars.Assign(k, v)
218 }
Fumitoshi Ukaid92c4872015-04-06 16:17:31 +0900219 }
220 r.cmds = irule.cmds
221 // TODO(ukai): filename, lineno?
222 r.cmdLineno = irule.cmdLineno
223 return r, true
224 }
225 // TODO(ukai): check len(irule.cmd) ?
226 return irule, true
227 }
228 return rule, rule != nil
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900229}
230
Fumitoshi Ukai8edcb792015-04-02 11:23:23 +0900231func (ex *Executor) build(vars *VarTab, output string) (int64, error) {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900232 Log("Building: %s", output)
Fumitoshi Ukai4b82f192015-04-07 11:36:42 +0900233 outputTs, ok := ex.done[output]
234 if ok {
Fumitoshi Ukaibac3ccf2015-04-08 13:41:22 +0900235 Log("Building: %s already done: %d", output, outputTs)
Fumitoshi Ukai4b82f192015-04-07 11:36:42 +0900236 return outputTs, nil
237 }
238 outputTs = getTimestamp(output)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900239
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900240 rule, present := ex.pickRule(output)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900241 if !present {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900242 if outputTs >= 0 {
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900243 return outputTs, nil
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900244 }
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900245 return outputTs, fmt.Errorf("no rule to make target %q", output)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900246 }
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900247 if rule.vars != nil {
248 vars = NewVarTab(vars)
249 for k, v := range rule.vars.m {
250 vars.Assign(k, v)
251 }
252 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900253
254 latest := int64(-1)
Shinichiro Hamaji57aff982015-04-02 02:15:38 +0900255 var actualInputs []string
Fumitoshi Ukaid92c4872015-04-06 16:17:31 +0900256 Log("Building: %s inputs:%q", output, rule.inputs)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900257 for _, input := range rule.inputs {
Shinichiro Hamaji7b203b22015-04-01 03:44:57 +0900258 if len(rule.outputPatterns) > 0 {
259 if len(rule.outputPatterns) > 1 {
260 panic("TODO: multiple output pattern is not supported yet")
261 }
262 input = substPattern(rule.outputPatterns[0], input, output)
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900263 } else if rule.isSuffixRule {
Shinichiro Hamaji20a43762015-04-02 02:42:25 +0900264 input = replaceSuffix(output, input)
Shinichiro Hamaji7b203b22015-04-01 03:44:57 +0900265 }
Shinichiro Hamaji57aff982015-04-02 02:15:38 +0900266 actualInputs = append(actualInputs, input)
Shinichiro Hamaji7b203b22015-04-01 03:44:57 +0900267
Fumitoshi Ukai3e5161e2015-04-01 22:42:10 +0900268 ts, err := ex.build(vars, input)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900269 if err != nil {
270 return outputTs, err
271 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900272 if latest < ts {
273 latest = ts
274 }
275 }
276
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900277 for _, input := range rule.orderOnlyInputs {
Fumitoshi Ukaif04610d2015-04-07 00:03:03 +0900278 if exists(input) {
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900279 continue
280 }
281 ts, err := ex.build(vars, input)
282 if err != nil {
283 return outputTs, err
284 }
285 if latest < ts {
286 latest = ts
287 }
288 }
289
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900290 if outputTs >= latest {
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900291 return outputTs, nil
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900292 }
293
Fumitoshi Ukai8edcb792015-04-02 11:23:23 +0900294 localVars := NewVarTab(vars)
Fumitoshi Ukai3e5161e2015-04-01 22:42:10 +0900295 // automatic variables.
Fumitoshi Ukai8edcb792015-04-02 11:23:23 +0900296 localVars.Assign("@", SimpleVar{value: output, origin: "automatic"})
Shinichiro Hamaji57aff982015-04-02 02:15:38 +0900297 if len(actualInputs) > 0 {
Fumitoshi Ukai8edcb792015-04-02 11:23:23 +0900298 localVars.Assign("<", SimpleVar{
299 value: actualInputs[0],
300 origin: "automatic",
301 })
302 localVars.Assign("^", SimpleVar{
303 value: strings.Join(actualInputs, " "),
304 origin: "automatic",
305 })
Shinichiro Hamaji57aff982015-04-02 02:15:38 +0900306 }
Shinichiro Hamajicc919ae2015-04-09 17:23:30 +0900307 ev := newEvaluator(localVars.Vars())
Shinichiro Hamajie708a9d2015-04-03 14:34:35 +0900308 ev.filename = rule.filename
309 ev.lineno = rule.cmdLineno
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +0900310 var runners []runner
Fumitoshi Ukaif04610d2015-04-07 00:03:03 +0900311 Log("Building: %s cmds:%q", output, rule.cmds)
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +0900312 r := runner{
313 output: output,
314 echo: true,
315 dryRun: dryRunFlag,
Fumitoshi Ukai3e5161e2015-04-01 22:42:10 +0900316 }
Fumitoshi Ukai457e7ba2015-04-08 11:24:57 +0900317 for _, cmd := range rule.cmds {
318 runners = append(runners, evalCmd(ev, r, cmd)...)
319 }
320 for _, r := range runners {
321 err := r.run()
322 if err != nil {
323 exit := exitStatus(err)
324 fmt.Printf("[%s] Error %d: %v\n", r.output, exit, err)
325 return outputTs, err
326 }
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900327 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900328
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900329 outputTs = getTimestamp(output)
330 if outputTs < 0 {
331 outputTs = time.Now().Unix()
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900332 }
Fumitoshi Ukai4b82f192015-04-07 11:36:42 +0900333 ex.done[output] = outputTs
334 Log("Building: %s done %d", output, outputTs)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900335 return outputTs, nil
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900336}
337
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +0900338func (ex *Executor) populateSuffixRule(rule *Rule, output string) bool {
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900339 if len(output) == 0 || output[0] != '.' {
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +0900340 return false
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900341 }
342 rest := output[1:]
343 dotIndex := strings.IndexByte(rest, '.')
344 // If there is only a single dot or the third dot, this is not a
345 // suffix rule.
346 if dotIndex < 0 || strings.IndexByte(rest[dotIndex+1:], '.') >= 0 {
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +0900347 return false
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900348 }
349
350 // This is a suffix rule.
351 inputSuffix := rest[:dotIndex]
352 outputSuffix := rest[dotIndex+1:]
353 r := &Rule{}
354 *r = *rule
355 r.inputs = []string{inputSuffix}
356 r.isSuffixRule = true
Shinichiro Hamaji20a43762015-04-02 02:42:25 +0900357 ex.suffixRules[outputSuffix] = append([]*Rule{r}, ex.suffixRules[outputSuffix]...)
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +0900358 return true
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900359}
360
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900361func (ex *Executor) populateExplicitRule(rule *Rule) {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900362 // It seems rules with no outputs are siliently ignored.
363 if len(rule.outputs) == 0 {
364 return
365 }
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900366 for _, output := range rule.outputs {
Fumitoshi Ukaie1e34442015-04-08 13:10:05 +0900367 output = filepath.Clean(output)
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900368
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +0900369 isSuffixRule := ex.populateSuffixRule(rule, output)
Shinichiro Hamajiabc80d22015-04-02 01:56:32 +0900370
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900371 if oldRule, present := ex.rules[output]; present {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900372 if oldRule.vars != nil || rule.vars != nil {
373 oldRule.isDoubleColon = rule.isDoubleColon
374 switch {
375 case rule.vars == nil && oldRule.vars != nil:
376 rule.vars = oldRule.vars
377 case rule.vars != nil && oldRule.vars == nil:
378 case rule.vars != nil && oldRule.vars != nil:
379 // parent would be the same vars?
380 for k, v := range rule.vars.m {
381 oldRule.vars.m[k] = v
382 }
383 rule.vars = oldRule.vars
384 }
385 }
Shinichiro Hamajicc3abc32015-04-02 04:46:49 +0900386 if oldRule.isDoubleColon != rule.isDoubleColon {
387 Error(rule.filename, rule.lineno, "*** target file %q has both : and :: entries.", output)
388 }
389 if len(oldRule.cmds) > 0 && len(rule.cmds) > 0 && !isSuffixRule && !rule.isDoubleColon {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900390 Warn(rule.filename, rule.cmdLineno, "overriding commands for target %q", output)
391 Warn(oldRule.filename, oldRule.cmdLineno, "ignoring old commands for target %q", output)
392 }
393 r := &Rule{}
394 *r = *rule
Shinichiro Hamajicc3abc32015-04-02 04:46:49 +0900395 if rule.isDoubleColon {
396 r.cmds = append(oldRule.cmds, r.cmds...)
Fumitoshi Ukaic97f49c2015-04-07 00:16:38 +0900397 } else if len(oldRule.cmds) > 0 && len(rule.cmds) == 0 {
398 r.cmds = oldRule.cmds
Shinichiro Hamajicc3abc32015-04-02 04:46:49 +0900399 }
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900400 r.inputs = append(r.inputs, oldRule.inputs...)
Fumitoshi Ukai79fcf972015-04-07 11:10:29 +0900401 r.orderOnlyInputs = append(r.orderOnlyInputs, oldRule.orderOnlyInputs...)
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900402 ex.rules[output] = r
403 } else {
404 ex.rules[output] = rule
Shinichiro Hamaji4a1cfc22015-04-11 13:54:46 +0900405 if ex.firstRule == nil && !strings.HasPrefix(output, ".") {
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +0900406 ex.firstRule = rule
407 }
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900408 }
409 }
410}
411
412func (ex *Executor) populateImplicitRule(rule *Rule) {
413 for _, outputPattern := range rule.outputPatterns {
414 r := &Rule{}
415 *r = *rule
416 r.outputPatterns = []string{outputPattern}
417 ex.implicitRules = append(ex.implicitRules, r)
418 }
419}
420
421func (ex *Executor) populateRules(er *EvalResult) {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900422 for _, rule := range er.rules {
Fumitoshi Ukaibac3ccf2015-04-08 13:41:22 +0900423 for i, input := range rule.inputs {
424 rule.inputs[i] = filepath.Clean(input)
425 }
426 for i, orderOnlyInput := range rule.orderOnlyInputs {
427 rule.orderOnlyInputs[i] = filepath.Clean(orderOnlyInput)
428 }
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900429 ex.populateExplicitRule(rule)
430
431 if len(rule.outputs) == 0 {
432 ex.populateImplicitRule(rule)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900433 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900434 }
435
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900436 // Reverse the implicit rule for easier lookup.
437 for i, r := range ex.implicitRules {
Fumitoshi Ukai8edcb792015-04-02 11:23:23 +0900438 if i >= len(ex.implicitRules)/2 {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900439 break
440 }
Fumitoshi Ukai8edcb792015-04-02 11:23:23 +0900441 j := len(ex.implicitRules) - i - 1
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900442 ex.implicitRules[i] = ex.implicitRules[j]
443 ex.implicitRules[j] = r
444 }
445}
446
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900447func (ex *Executor) exec(er *EvalResult, targets []string, vars *VarTab) error {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900448 ex.populateRules(er)
449
Shinichiro Hamajib13f3d52015-03-30 19:29:44 +0900450 if len(targets) == 0 {
Shinichiro Hamaji26fe9572015-04-02 02:31:32 +0900451 if ex.firstRule == nil {
452 ErrorNoLocation("*** No targets.")
453 }
454 targets = append(targets, ex.firstRule.outputs[0])
Shinichiro Hamajib13f3d52015-03-30 19:29:44 +0900455 }
456
457 for _, target := range targets {
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900458 _, err := ex.build(vars, target)
Shinichiro Hamajib13f3d52015-03-30 19:29:44 +0900459 if err != nil {
460 return err
461 }
462 }
463 return nil
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900464}
465
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900466func Exec(er *EvalResult, targets []string, vars *VarTab) error {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900467 ex := newExecutor()
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900468 return ex.exec(er, targets, vars)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900469}