aboutsummaryrefslogtreecommitdiff
path: root/exec.go
diff options
context:
space:
mode:
authorFumitoshi Ukai <fumitoshi.ukai@gmail.com>2015-06-26 21:32:50 +0900
committerFumitoshi Ukai <fumitoshi.ukai@gmail.com>2015-06-26 23:08:01 +0900
commit65c7233c02ba0f62c38ef96e4bb59dcb273542e9 (patch)
tree9b02ced4ebe2cff26d772431cc81b4ecfc2425ff /exec.go
parent480bca50f6f74c0021910540a6e0ea8761eb5f61 (diff)
downloadkati-65c7233c02ba0f62c38ef96e4bb59dcb273542e9.tar.gz
fix panic based error reporting
Diffstat (limited to 'exec.go')
-rw-r--r--exec.go106
1 files changed, 75 insertions, 31 deletions
diff --git a/exec.go b/exec.go
index 160c0b7..2e6c346 100644
--- a/exec.go
+++ b/exec.go
@@ -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
}