diff options
author | Fumitoshi Ukai <fumitoshi.ukai@gmail.com> | 2015-06-26 21:32:50 +0900 |
---|---|---|
committer | Fumitoshi Ukai <fumitoshi.ukai@gmail.com> | 2015-06-26 23:08:01 +0900 |
commit | 65c7233c02ba0f62c38ef96e4bb59dcb273542e9 (patch) | |
tree | 9b02ced4ebe2cff26d772431cc81b4ecfc2425ff /exec.go | |
parent | 480bca50f6f74c0021910540a6e0ea8761eb5f61 (diff) | |
download | kati-65c7233c02ba0f62c38ef96e4bb59dcb273542e9.tar.gz |
fix panic based error reporting
Diffstat (limited to 'exec.go')
-rw-r--r-- | exec.go | 106 |
1 files changed, 75 insertions, 31 deletions
@@ -24,6 +24,7 @@ import ( "time" ) +// Executor manages execution of makefile rules. type Executor struct { rules map[string]*rule implicitRules []*rule @@ -53,38 +54,41 @@ type autoVar struct{ ex *Executor } func (v autoVar) Flavor() string { return "undefined" } func (v autoVar) Origin() string { return "automatic" } -func (v autoVar) IsDefined() bool { panic("not implemented") } -func (v autoVar) String() string { panic("not implemented") } -func (v autoVar) Append(*Evaluator, string) Var { - panic("must not be called") +func (v autoVar) IsDefined() bool { return true } +func (v autoVar) Append(*Evaluator, string) (Var, error) { + return nil, fmt.Errorf("cannot append to autovar") } -func (v autoVar) AppendVar(*Evaluator, Value) Var { - panic("must not be called") +func (v autoVar) AppendVar(*Evaluator, Value) (Var, error) { + return nil, fmt.Errorf("cannot append to autovar") } func (v autoVar) serialize() serializableVar { - panic(fmt.Sprintf("cannot serialize auto var: %q", v)) + return serializableVar{Type: ""} } -func (v autoVar) dump(w io.Writer) { - panic(fmt.Sprintf("cannot dump auto var: %q", v)) +func (v autoVar) dump(d *dumpbuf) { + d.err = fmt.Errorf("cannot dump auto var: %v", v) } type autoAtVar struct{ autoVar } -func (v autoAtVar) Eval(w io.Writer, ev *Evaluator) { +func (v autoAtVar) Eval(w io.Writer, ev *Evaluator) error { fmt.Fprint(w, v.ex.currentOutput) + return nil } +func (v autoAtVar) String() string { return "$*" } type autoLessVar struct{ autoVar } -func (v autoLessVar) Eval(w io.Writer, ev *Evaluator) { +func (v autoLessVar) Eval(w io.Writer, ev *Evaluator) error { if len(v.ex.currentInputs) > 0 { fmt.Fprint(w, v.ex.currentInputs[0]) } + return nil } +func (v autoLessVar) String() string { return "$<" } type autoHatVar struct{ autoVar } -func (v autoHatVar) Eval(w io.Writer, ev *Evaluator) { +func (v autoHatVar) Eval(w io.Writer, ev *Evaluator) error { var uniqueInputs []string seen := make(map[string]bool) for _, input := range v.ex.currentInputs { @@ -94,51 +98,69 @@ func (v autoHatVar) Eval(w io.Writer, ev *Evaluator) { } } fmt.Fprint(w, strings.Join(uniqueInputs, " ")) + return nil } +func (v autoHatVar) String() string { return "$^" } type autoPlusVar struct{ autoVar } -func (v autoPlusVar) Eval(w io.Writer, ev *Evaluator) { +func (v autoPlusVar) Eval(w io.Writer, ev *Evaluator) error { fmt.Fprint(w, strings.Join(v.ex.currentInputs, " ")) + return nil } +func (v autoPlusVar) String() string { return "$+" } type autoStarVar struct{ autoVar } -func (v autoStarVar) Eval(w io.Writer, ev *Evaluator) { +func (v autoStarVar) Eval(w io.Writer, ev *Evaluator) error { // TODO: Use currentStem. See auto_stem_var.mk fmt.Fprint(w, stripExt(v.ex.currentOutput)) + return nil } +func (v autoStarVar) String() string { return "$*" } type autoSuffixDVar struct { autoVar v Var } -func (v autoSuffixDVar) Eval(w io.Writer, ev *Evaluator) { +func (v autoSuffixDVar) Eval(w io.Writer, ev *Evaluator) error { var buf bytes.Buffer - v.v.Eval(&buf, ev) + err := v.v.Eval(&buf, ev) + if err != nil { + return err + } ws := newWordScanner(buf.Bytes()) sw := ssvWriter{w: w} for ws.Scan() { sw.WriteString(filepath.Dir(string(ws.Bytes()))) } + return nil } +func (v autoSuffixDVar) String() string { return v.v.String() + "D" } + type autoSuffixFVar struct { autoVar v Var } -func (v autoSuffixFVar) Eval(w io.Writer, ev *Evaluator) { +func (v autoSuffixFVar) Eval(w io.Writer, ev *Evaluator) error { var buf bytes.Buffer - v.v.Eval(&buf, ev) + err := v.v.Eval(&buf, ev) + if err != nil { + return err + } ws := newWordScanner(buf.Bytes()) sw := ssvWriter{w: w} for ws.Scan() { sw.WriteString(filepath.Base(string(ws.Bytes()))) } + return nil } +func (v autoSuffixFVar) String() string { return v.v.String() + "F" } + func (ex *Executor) makeJobs(n *DepNode, neededBy *job) error { output := n.Output if neededBy != nil { @@ -219,29 +241,38 @@ func (ex *Executor) reportStats() { } } +// ExecutorOpt is an option for Executor. type ExecutorOpt struct { NumJobs int ParaPath string } -func NewExecutor(vars Vars, opt *ExecutorOpt) *Executor { +// NewExecutor creates new Executor. +func NewExecutor(vars Vars, opt *ExecutorOpt) (*Executor, error) { if opt == nil { opt = &ExecutorOpt{NumJobs: 1} } if opt.NumJobs < 1 { opt.NumJobs = 1 } + wm, err := newWorkerManager(opt.NumJobs, opt.ParaPath) + if err != nil { + return nil, err + } ex := &Executor{ rules: make(map[string]*rule), suffixRules: make(map[string][]*rule), done: make(map[string]*job), vars: vars, - wm: newWorkerManager(opt.NumJobs, opt.ParaPath), + wm: wm, } // TODO: We should move this to somewhere around evalCmd so that // we can handle SHELL in target specific variables. ev := NewEvaluator(ex.vars) - ex.shell = ev.EvaluateVar("SHELL") + ex.shell, err = ev.EvaluateVar("SHELL") + if err != nil { + ex.shell = "/bin/sh" + } for k, v := range map[string]Var{ "@": autoAtVar{autoVar: autoVar{ex: ex}}, "<": autoLessVar{autoVar: autoVar{ex: ex}}, @@ -253,23 +284,24 @@ func NewExecutor(vars Vars, opt *ExecutorOpt) *Executor { ex.vars[k+"D"] = autoSuffixDVar{v: v} ex.vars[k+"F"] = autoSuffixFVar{v: v} } - return ex + return ex, nil } +// Exec executes to build roots. func (ex *Executor) Exec(roots []*DepNode) error { startTime := time.Now() for _, root := range roots { ex.makeJobs(root, nil) } - ex.wm.Wait() + err := ex.wm.Wait() logStats("exec time: %q", time.Since(startTime)) - return nil + return err } -func (ex *Executor) createRunners(n *DepNode, avoidIO bool) ([]runner, bool) { +func (ex *Executor) createRunners(n *DepNode, avoidIO bool) ([]runner, bool, error) { var runners []runner if len(n.Cmds) == 0 { - return runners, false + return runners, false, nil } var restores []func() @@ -287,6 +319,7 @@ func (ex *Executor) createRunners(n *DepNode, avoidIO bool) ([]runner, bool) { for k, v := range n.TargetSpecificVars { restores = append(restores, ex.vars.save(k)) ex.vars[k] = v + logf("tsv: %s=%s", k, v) } ev := NewEvaluator(ex.vars) @@ -300,20 +333,30 @@ func (ex *Executor) createRunners(n *DepNode, avoidIO bool) ([]runner, bool) { shell: ex.shell, } for _, cmd := range n.Cmds { - for _, r := range evalCmd(ev, r, cmd) { + rr, err := evalCmd(ev, r, cmd) + if err != nil { + return nil, false, err + } + for _, r := range rr { if len(r.cmd) != 0 { runners = append(runners, r) } } } - return runners, ev.hasIO + return runners, ev.hasIO, nil } -func evalCommands(nodes []*DepNode, vars Vars) { +func evalCommands(nodes []*DepNode, vars Vars) error { ioCnt := 0 - ex := NewExecutor(vars, nil) + ex, err := NewExecutor(vars, nil) + if err != nil { + return err + } for i, n := range nodes { - runners, hasIO := ex.createRunners(n, true) + runners, hasIO, err := ex.createRunners(n, true) + if err != nil { + return err + } if hasIO { ioCnt++ if ioCnt%100 == 0 { @@ -338,4 +381,5 @@ func evalCommands(nodes []*DepNode, vars Vars) { } logStats("%d/%d rules have IO", ioCnt, len(nodes)) + return nil } |