Split exec.go into exec.go and dep.go
Now the dependency graph is constructed by dep.go
diff --git a/dep.go b/dep.go
new file mode 100644
index 0000000..a6bcba3
--- /dev/null
+++ b/dep.go
@@ -0,0 +1,436 @@
+package main
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+)
+
+type DepNode struct {
+ Output string
+ Cmds []string
+ Deps []*DepNode
+ HasRule bool
+ IsOrderOnly bool
+ ActualInputs []string
+ TargetSpecificVars Vars
+ Filename string
+ Lineno int
+}
+
+func (n *DepNode) String() string {
+ return fmt.Sprintf("Dep{output=%s cmds=%d deps=%d hasRule=%t orderOnly=%t, filename=%s lineno=%d}",
+ n.Output, len(n.Cmds), len(n.Deps), n.HasRule, n.IsOrderOnly, n.Filename, n.Lineno)
+}
+
+type DepBuilder struct {
+ rules map[string]*Rule
+ ruleVars map[string]Vars
+ implicitRules []*Rule
+ suffixRules map[string][]*Rule
+ firstRule *Rule
+ vars Vars
+ done map[string]*DepNode
+
+ trace []string
+ nodeCnt int
+ pickExplicitRuleCnt int
+ pickImplicitRuleCnt int
+ pickSuffixRuleCnt int
+ pickExplicitRuleWithoutCmdCnt int
+}
+
+func replaceSuffix(s string, newsuf string) string {
+ // TODO: Factor out the logic around suffix rules and use
+ // it from substitution references.
+ // http://www.gnu.org/software/make/manual/make.html#Substitution-Refs
+ return fmt.Sprintf("%s.%s", stripExt(s), newsuf)
+}
+
+func (db *DepBuilder) exists(target string) bool {
+ _, present := db.rules[target]
+ if present {
+ return true
+ }
+ rule, present := db.rules[".PHONY"]
+ if present {
+ for _, input := range rule.inputs {
+ if target == input {
+ return true
+ }
+ }
+ }
+ return exists(target)
+}
+
+func (db *DepBuilder) canPickImplicitRule(rule *Rule, output string) bool {
+ outputPattern := rule.outputPatterns[0]
+ if !matchPattern(outputPattern, output) {
+ return false
+ }
+ for _, input := range rule.inputs {
+ input = substPattern(outputPattern, input, output)
+ if !db.exists(input) {
+ return false
+ }
+ }
+ return true
+}
+
+func (db *DepBuilder) mergeImplicitRuleVars(outputs []string, vars Vars) Vars {
+ if len(outputs) != 1 {
+ panic(fmt.Sprintf("Implicit rule should have only one output but %q", outputs))
+ }
+ Log("merge? %q", db.ruleVars)
+ Log("merge? %q", outputs[0])
+ ivars, present := db.ruleVars[outputs[0]]
+ if !present {
+ return vars
+ }
+ if vars == nil {
+ return ivars
+ }
+ Log("merge!")
+ v := make(Vars)
+ v.Merge(ivars)
+ v.Merge(vars)
+ return v
+}
+
+func (db *DepBuilder) pickRule(output string) (*Rule, Vars, bool) {
+ rule, present := db.rules[output]
+ vars := db.ruleVars[output]
+ if present {
+ db.pickExplicitRuleCnt++
+ if len(rule.cmds) > 0 {
+ return rule, vars, true
+ }
+ // If none of the explicit rules for a target has commands,
+ // then `make' searches for an applicable implicit rule to
+ // find some commands.
+ db.pickExplicitRuleWithoutCmdCnt++
+ }
+
+ for _, irule := range db.implicitRules {
+ if !db.canPickImplicitRule(irule, output) {
+ continue
+ }
+ db.pickImplicitRuleCnt++
+ if rule != nil {
+ r := &Rule{}
+ *r = *rule
+ r.outputPatterns = irule.outputPatterns
+ // implicit rule's prerequisites will be used for $<
+ r.inputs = append(irule.inputs, r.inputs...)
+ r.cmds = irule.cmds
+ // TODO(ukai): filename, lineno?
+ r.cmdLineno = irule.cmdLineno
+ return r, vars, true
+ }
+ if vars != nil {
+ vars = db.mergeImplicitRuleVars(irule.outputPatterns, vars)
+ }
+ // TODO(ukai): check len(irule.cmd) ?
+ return irule, vars, true
+ }
+
+ outputSuffix := filepath.Ext(output)
+ if !strings.HasPrefix(outputSuffix, ".") {
+ return rule, vars, rule != nil
+ }
+ rules, present := db.suffixRules[outputSuffix[1:]]
+ if !present {
+ return rule, vars, rule != nil
+ }
+ for _, irule := range rules {
+ if len(irule.inputs) != 1 {
+ panic(fmt.Sprintf("unexpected number of input for a suffix rule (%d)", len(irule.inputs)))
+ }
+ if !db.exists(replaceSuffix(output, irule.inputs[0])) {
+ continue
+ }
+ db.pickSuffixRuleCnt++
+ if rule != nil {
+ r := &Rule{}
+ *r = *rule
+ // TODO(ukai): input order is correct?
+ r.inputs = append([]string{replaceSuffix(output, irule.inputs[0])}, r.inputs...)
+ r.cmds = irule.cmds
+ // TODO(ukai): filename, lineno?
+ r.cmdLineno = irule.cmdLineno
+ return r, vars, true
+ }
+ if vars != nil {
+ vars = db.mergeImplicitRuleVars(irule.outputs, vars)
+ }
+ // TODO(ukai): check len(irule.cmd) ?
+ return irule, vars, true
+ }
+ return rule, vars, rule != nil
+}
+
+func (db *DepBuilder) buildPlan(output string, neededBy string, tsvs Vars) (*DepNode, error) {
+ Log("Evaluating command: %s", output)
+ db.nodeCnt++
+ if db.nodeCnt%100 == 0 {
+ db.reportStats()
+ }
+
+ if n, present := db.done[output]; present {
+ return n, nil
+ }
+ n := &DepNode{Output: output}
+ db.done[output] = n
+
+ rule, vars, present := db.pickRule(output)
+ if !present {
+ return n, nil
+ }
+
+ var restores []func()
+ if vars != nil {
+ for name, v := range vars {
+ // TODO: Consider not updating db.vars.
+ tsv := v.(TargetSpecificVar)
+ restores = append(restores, db.vars.save(name))
+ restores = append(restores, tsvs.save(name))
+ switch tsv.op {
+ case ":=", "=":
+ db.vars[name] = tsv
+ tsvs[name] = v
+ case "+=":
+ oldVar, present := db.vars[name]
+ if !present || oldVar.String() == "" {
+ db.vars[name] = tsv
+ } else {
+ v = oldVar.AppendVar(newEvaluator(db.vars), tsv)
+ db.vars[name] = v
+ }
+ tsvs[name] = v
+ case "?=":
+ if _, present := db.vars[name]; !present {
+ db.vars[name] = tsv
+ tsvs[name] = v
+ }
+ }
+ }
+ defer func() {
+ for _, restore := range restores {
+ restore()
+ }
+ }()
+ }
+
+ var children []*DepNode
+ var actualInputs []string
+ Log("Evaluating command: %s inputs:%q", output, rule.inputs)
+ for _, input := range rule.inputs {
+ if len(rule.outputPatterns) > 0 {
+ if len(rule.outputPatterns) > 1 {
+ panic("TODO: multiple output pattern is not supported yet")
+ }
+ input = substPattern(rule.outputPatterns[0], input, output)
+ } else if rule.isSuffixRule {
+ input = replaceSuffix(output, input)
+ }
+ actualInputs = append(actualInputs, input)
+
+ db.trace = append(db.trace, input)
+ n, err := db.buildPlan(input, output, tsvs)
+ db.trace = db.trace[0 : len(db.trace)-1]
+ if err != nil {
+ return nil, err
+ }
+ if n != nil {
+ children = append(children, n)
+ }
+ }
+
+ for _, input := range rule.orderOnlyInputs {
+ db.trace = append(db.trace, input)
+ n, err := db.buildPlan(input, output, tsvs)
+ db.trace = db.trace[0 : len(db.trace)-1]
+ if err != nil {
+ return nil, err
+ }
+ if n != nil {
+ n.IsOrderOnly = true
+ children = append(children, n)
+ }
+ }
+
+ n.Deps = children
+ n.HasRule = true
+ n.Cmds = rule.cmds
+ n.ActualInputs = actualInputs
+ n.TargetSpecificVars = make(Vars)
+ for k, v := range tsvs {
+ n.TargetSpecificVars[k] = v
+ }
+ n.Filename = rule.filename
+ if len(rule.cmds) > 0 {
+ n.Lineno = rule.cmdLineno
+ }
+ return n, nil
+}
+
+func (db *DepBuilder) populateSuffixRule(rule *Rule, output string) bool {
+ if len(output) == 0 || output[0] != '.' {
+ return false
+ }
+ rest := output[1:]
+ dotIndex := strings.IndexByte(rest, '.')
+ // If there is only a single dot or the third dot, this is not a
+ // suffix rule.
+ if dotIndex < 0 || strings.IndexByte(rest[dotIndex+1:], '.') >= 0 {
+ return false
+ }
+
+ // This is a suffix rule.
+ inputSuffix := rest[:dotIndex]
+ outputSuffix := rest[dotIndex+1:]
+ r := &Rule{}
+ *r = *rule
+ r.inputs = []string{inputSuffix}
+ r.isSuffixRule = true
+ db.suffixRules[outputSuffix] = append([]*Rule{r}, db.suffixRules[outputSuffix]...)
+ return true
+}
+
+func mergeRules(oldRule, rule *Rule, output string, isSuffixRule bool) *Rule {
+ if oldRule.isDoubleColon != rule.isDoubleColon {
+ Error(rule.filename, rule.lineno, "*** target file %q has both : and :: entries.", output)
+ }
+ if len(oldRule.cmds) > 0 && len(rule.cmds) > 0 && !isSuffixRule && !rule.isDoubleColon {
+ Warn(rule.filename, rule.cmdLineno, "overriding commands for target %q", output)
+ Warn(oldRule.filename, oldRule.cmdLineno, "ignoring old commands for target %q", output)
+ }
+
+ r := &Rule{}
+ *r = *rule
+ if rule.isDoubleColon {
+ r.cmds = append(oldRule.cmds, r.cmds...)
+ } else if len(oldRule.cmds) > 0 && len(rule.cmds) == 0 {
+ r.cmds = oldRule.cmds
+ }
+ // If the latter rule has a command (regardless of the
+ // commands in oldRule), inputs in the latter rule has a
+ // priority.
+ if len(rule.cmds) > 0 {
+ r.inputs = append(r.inputs, oldRule.inputs...)
+ r.orderOnlyInputs = append(r.orderOnlyInputs, oldRule.orderOnlyInputs...)
+ } else {
+ r.inputs = append(oldRule.inputs, r.inputs...)
+ r.orderOnlyInputs = append(oldRule.orderOnlyInputs, r.orderOnlyInputs...)
+ }
+ r.outputPatterns = append(r.outputPatterns, oldRule.outputPatterns...)
+ return r
+}
+
+func (db *DepBuilder) populateExplicitRule(rule *Rule) {
+ // It seems rules with no outputs are siliently ignored.
+ if len(rule.outputs) == 0 {
+ return
+ }
+ for _, output := range rule.outputs {
+ output = filepath.Clean(output)
+
+ isSuffixRule := db.populateSuffixRule(rule, output)
+
+ if oldRule, present := db.rules[output]; present {
+ r := mergeRules(oldRule, rule, output, isSuffixRule)
+ db.rules[output] = r
+ } else {
+ db.rules[output] = rule
+ if db.firstRule == nil && !strings.HasPrefix(output, ".") {
+ db.firstRule = rule
+ }
+ }
+ }
+}
+
+func (db *DepBuilder) populateImplicitRule(rule *Rule) {
+ for _, outputPattern := range rule.outputPatterns {
+ r := &Rule{}
+ *r = *rule
+ r.outputPatterns = []string{outputPattern}
+ db.implicitRules = append(db.implicitRules, r)
+ }
+}
+
+func (db *DepBuilder) populateRules(er *EvalResult) {
+ for _, rule := range er.rules {
+ for i, input := range rule.inputs {
+ rule.inputs[i] = filepath.Clean(input)
+ }
+ for i, orderOnlyInput := range rule.orderOnlyInputs {
+ rule.orderOnlyInputs[i] = filepath.Clean(orderOnlyInput)
+ }
+ db.populateExplicitRule(rule)
+
+ if len(rule.outputs) == 0 {
+ db.populateImplicitRule(rule)
+ }
+ }
+
+ // Reverse the implicit rule for easier lookup.
+ for i, r := range db.implicitRules {
+ if i >= len(db.implicitRules)/2 {
+ break
+ }
+ j := len(db.implicitRules) - i - 1
+ db.implicitRules[i] = db.implicitRules[j]
+ db.implicitRules[j] = r
+ }
+}
+
+func (db *DepBuilder) reportStats() {
+ if !katiLogFlag && !katiStatsFlag {
+ return
+ }
+
+ LogStats("node=%d explicit=%d implicit=%d suffix=%d explicitWOCmd=%d",
+ db.nodeCnt, db.pickExplicitRuleCnt, db.pickImplicitRuleCnt, db.pickSuffixRuleCnt, db.pickExplicitRuleWithoutCmdCnt)
+ if len(db.trace) > 1 {
+ LogStats("trace=%q", db.trace)
+ }
+}
+
+func NewDepBuilder(er *EvalResult, vars Vars) *DepBuilder {
+ db := &DepBuilder{
+ rules: make(map[string]*Rule),
+ ruleVars: er.ruleVars,
+ suffixRules: make(map[string][]*Rule),
+ vars: vars,
+ done: make(map[string]*DepNode),
+ }
+
+ db.populateRules(er)
+ return db
+}
+
+func (db *DepBuilder) Eval(targets []string) ([]*DepNode, error) {
+ if len(targets) == 0 {
+ if db.firstRule == nil {
+ ErrorNoLocation("*** No targets.")
+ }
+ targets = append(targets, db.firstRule.outputs[0])
+ }
+
+ LogStats("%d variables", len(db.vars))
+ LogStats("%d explicit rules", len(db.rules))
+ LogStats("%d implicit rules", len(db.implicitRules))
+ LogStats("%d suffix rules", len(db.suffixRules))
+
+ var nodes []*DepNode
+ for _, target := range targets {
+ db.trace = []string{target}
+ n, err := db.buildPlan(target, "", make(Vars))
+ if err != nil {
+ return nil, err
+ }
+ nodes = append(nodes, n)
+ }
+ db.reportStats()
+ return nodes, nil
+}
diff --git a/exec.go b/exec.go
index 07378f4..3c0afba 100644
--- a/exec.go
+++ b/exec.go
@@ -14,7 +14,6 @@
type Executor struct {
rules map[string]*Rule
- ruleVars map[string]Vars
implicitRules []*Rule
suffixRules map[string][]*Rule
firstRule *Rule
@@ -28,16 +27,12 @@
currentInputs []string
currentStem string
- trace []string
- buildCnt int
- alreadyDoneCnt int
- noRuleCnt int
- upToDateCnt int
- runCommandCnt int
- pickExplicitRuleCnt int
- pickImplicitRuleCnt int
- pickSuffixRuleCnt int
- pickExplicitRuleWithoutCmdCnt int
+ trace []string
+ buildCnt int
+ alreadyDoneCnt int
+ noRuleCnt int
+ upToDateCnt int
+ runCommandCnt int
}
type AutoVar struct{ ex *Executor }
@@ -126,30 +121,6 @@
}
}
-func newExecutor(vars Vars, ruleVars map[string]Vars) *Executor {
- ex := &Executor{
- rules: make(map[string]*Rule),
- ruleVars: ruleVars,
- suffixRules: make(map[string][]*Rule),
- done: make(map[string]int64),
- vars: vars,
- }
-
- for k, v := range map[string]Var{
- "@": AutoAtVar{AutoVar: AutoVar{ex: ex}},
- "<": AutoLessVar{AutoVar: AutoVar{ex: ex}},
- "^": AutoHatVar{AutoVar: AutoVar{ex: ex}},
- "+": AutoPlusVar{AutoVar: AutoVar{ex: ex}},
- "*": AutoStarVar{AutoVar: AutoVar{ex: ex}},
- } {
- ex.vars[k] = v
- ex.vars[k+"D"] = AutoSuffixDVar{v: v}
- ex.vars[k+"F"] = AutoSuffixFVar{v: v}
- }
-
- return ex
-}
-
// TODO(ukai): use time.Time?
func getTimestamp(filename string) int64 {
st, err := os.Stat(filename)
@@ -159,22 +130,6 @@
return st.ModTime().Unix()
}
-func (ex *Executor) exists(target string) bool {
- _, present := ex.rules[target]
- if present {
- return true
- }
- rule, present := ex.rules[".PHONY"]
- if present {
- for _, input := range rule.inputs {
- if target == input {
- return true
- }
- }
- }
- return exists(target)
-}
-
type runner struct {
output string
cmd string
@@ -232,11 +187,11 @@
return r
}
-func (r runner) run() error {
- if r.echo {
+func (r runner) run(output string) error {
+ if r.echo || dryRunFlag {
fmt.Printf("%s\n", r.cmd)
}
- if r.dryRun {
+ if dryRunFlag {
return nil
}
args := []string{r.shell, "-c", r.cmd}
@@ -248,7 +203,7 @@
fmt.Printf("%s", out)
exit := exitStatus(err)
if r.ignoreError && exit != 0 {
- fmt.Printf("[%s] Error %d (ignored)\n", r.output, exit)
+ fmt.Printf("[%s] Error %d (ignored)\n", output, exit)
err = nil
}
return err
@@ -267,120 +222,8 @@
return exit
}
-func replaceSuffix(s string, newsuf string) string {
- // TODO: Factor out the logic around suffix rules and use
- // it from substitution references.
- // http://www.gnu.org/software/make/manual/make.html#Substitution-Refs
- return fmt.Sprintf("%s.%s", stripExt(s), newsuf)
-}
-
-func (ex *Executor) canPickImplicitRule(rule *Rule, output string) bool {
- outputPattern := rule.outputPatterns[0]
- if !matchPattern(outputPattern, output) {
- return false
- }
- for _, input := range rule.inputs {
- input = substPattern(outputPattern, input, output)
- if !ex.exists(input) {
- return false
- }
- }
- return true
-}
-
-func (ex *Executor) mergeImplicitRuleVars(outputs []string, vars Vars) Vars {
- if len(outputs) != 1 {
- panic(fmt.Sprintf("Implicit rule should have only one output but %q", outputs))
- }
- Log("merge? %q", ex.ruleVars)
- Log("merge? %q", outputs[0])
- ivars, present := ex.ruleVars[outputs[0]]
- if !present {
- return vars
- }
- if vars == nil {
- return ivars
- }
- Log("merge!")
- v := make(Vars)
- v.Merge(ivars)
- v.Merge(vars)
- return v
-}
-
-func (ex *Executor) pickRule(output string) (*Rule, Vars, bool) {
- rule, present := ex.rules[output]
- vars := ex.ruleVars[output]
- if present {
- ex.pickExplicitRuleCnt++
- if len(rule.cmds) > 0 {
- return rule, vars, true
- }
- // If none of the explicit rules for a target has commands,
- // then `make' searches for an applicable implicit rule to
- // find some commands.
- ex.pickExplicitRuleWithoutCmdCnt++
- }
-
- for _, irule := range ex.implicitRules {
- if !ex.canPickImplicitRule(irule, output) {
- continue
- }
- ex.pickImplicitRuleCnt++
- if rule != nil {
- r := &Rule{}
- *r = *rule
- r.outputPatterns = irule.outputPatterns
- // implicit rule's prerequisites will be used for $<
- r.inputs = append(irule.inputs, r.inputs...)
- r.cmds = irule.cmds
- // TODO(ukai): filename, lineno?
- r.cmdLineno = irule.cmdLineno
- return r, vars, true
- }
- if vars != nil {
- vars = ex.mergeImplicitRuleVars(irule.outputPatterns, vars)
- }
- // TODO(ukai): check len(irule.cmd) ?
- return irule, vars, true
- }
-
- outputSuffix := filepath.Ext(output)
- if !strings.HasPrefix(outputSuffix, ".") {
- return rule, vars, rule != nil
- }
- rules, present := ex.suffixRules[outputSuffix[1:]]
- if !present {
- return rule, vars, rule != nil
- }
- for _, irule := range rules {
- if len(irule.inputs) != 1 {
- panic(fmt.Sprintf("unexpected number of input for a suffix rule (%d)", len(irule.inputs)))
- }
- if !ex.exists(replaceSuffix(output, irule.inputs[0])) {
- continue
- }
- ex.pickSuffixRuleCnt++
- if rule != nil {
- r := &Rule{}
- *r = *rule
- // TODO(ukai): input order is correct?
- r.inputs = append([]string{replaceSuffix(output, irule.inputs[0])}, r.inputs...)
- r.cmds = irule.cmds
- // TODO(ukai): filename, lineno?
- r.cmdLineno = irule.cmdLineno
- return r, vars, true
- }
- if vars != nil {
- vars = ex.mergeImplicitRuleVars(irule.outputs, vars)
- }
- // TODO(ukai): check len(irule.cmd) ?
- return irule, vars, true
- }
- return rule, vars, rule != nil
-}
-
-func (ex *Executor) build(output string, neededBy string) (int64, error) {
+func (ex *Executor) build(n *DepNode, neededBy string) (int64, error) {
+ output := n.Output
Log("Building: %s", output)
ex.buildCnt++
if ex.buildCnt%100 == 0 {
@@ -399,8 +242,7 @@
ex.done[output] = -1
outputTs = getTimestamp(output)
- rule, vars, present := ex.pickRule(output)
- if !present {
+ if !n.HasRule {
if outputTs >= 0 {
ex.done[output] = outputTs
ex.noRuleCnt++
@@ -414,66 +256,15 @@
return outputTs, fmt.Errorf("no rule to make target %q", output)
}
- var restores []func()
- if vars != nil {
- for name, v := range vars {
- tsv := v.(TargetSpecificVar)
- restores = append(restores, ex.vars.save(name))
- switch tsv.op {
- case ":=", "=":
- ex.vars[name] = tsv
- case "+=":
- oldVar, present := ex.vars[name]
- if !present || oldVar.String() == "" {
- ex.vars[name] = tsv
- } else {
- ex.vars[name] = oldVar.AppendVar(newEvaluator(ex.vars), tsv)
- }
- case "?=":
- if _, present := ex.vars[name]; !present {
- ex.vars[name] = tsv
- }
- }
- }
- defer func() {
- for _, restore := range restores {
- restore()
- }
- }()
- }
-
latest := int64(-1)
- var actualInputs []string
- Log("Building: %s inputs:%q", output, rule.inputs)
- for _, input := range rule.inputs {
- if len(rule.outputPatterns) > 0 {
- if len(rule.outputPatterns) > 1 {
- panic("TODO: multiple output pattern is not supported yet")
- }
- input = substPattern(rule.outputPatterns[0], input, output)
- } else if rule.isSuffixRule {
- input = replaceSuffix(output, input)
- }
- actualInputs = append(actualInputs, input)
-
- ex.trace = append(ex.trace, input)
- ts, err := ex.build(input, output)
- ex.trace = ex.trace[0 : len(ex.trace)-1]
- if err != nil {
- return outputTs, err
- }
- if latest < ts {
- latest = ts
- }
- }
-
- for _, input := range rule.orderOnlyInputs {
- if exists(input) {
+ Log("Building: %s inputs:%q", output, n.Deps)
+ for _, d := range n.Deps {
+ if d.IsOrderOnly && exists(d.Output) {
continue
}
- ex.trace = append(ex.trace, input)
- ts, err := ex.build(input, output)
+ ex.trace = append(ex.trace, d.Output)
+ ts, err := ex.build(d, output)
ex.trace = ex.trace[0 : len(ex.trace)-1]
if err != nil {
return outputTs, err
@@ -491,31 +282,43 @@
// For automatic variables.
ex.currentOutput = output
- ex.currentInputs = actualInputs
+ ex.currentInputs = n.ActualInputs
+
+ var restores []func()
+ for k, v := range n.TargetSpecificVars {
+ restores = append(restores, ex.vars.save(k))
+ ex.vars[k] = v
+ }
+ defer func() {
+ for _, restore := range restores {
+ restore()
+ }
+ }()
ev := newEvaluator(ex.vars)
- ev.filename = rule.filename
- ev.lineno = rule.cmdLineno
+ ev.filename = n.Filename
+ ev.lineno = n.Lineno
var runners []runner
- Log("Building: %s cmds:%q", output, rule.cmds)
+ Log("Building: %s cmds:%q", output, n.Cmds)
r := runner{
output: output,
echo: true,
dryRun: dryRunFlag,
shell: ex.shell,
}
- for _, cmd := range rule.cmds {
+ for _, cmd := range n.Cmds {
for _, r := range evalCmd(ev, r, cmd) {
if len(r.cmd) != 0 {
runners = append(runners, r)
}
}
}
+
for _, r := range runners {
- err := r.run()
+ err := r.run(output)
if err != nil {
exit := exitStatus(err)
- fmt.Printf("[%s] Error %d: %v\n", r.output, exit, err)
+ fmt.Printf("[%s] Error %d: %v\n", output, exit, err)
return outputTs, err
}
}
@@ -530,116 +333,6 @@
return outputTs, nil
}
-func (ex *Executor) populateSuffixRule(rule *Rule, output string) bool {
- if len(output) == 0 || output[0] != '.' {
- return false
- }
- rest := output[1:]
- dotIndex := strings.IndexByte(rest, '.')
- // If there is only a single dot or the third dot, this is not a
- // suffix rule.
- if dotIndex < 0 || strings.IndexByte(rest[dotIndex+1:], '.') >= 0 {
- return false
- }
-
- // This is a suffix rule.
- inputSuffix := rest[:dotIndex]
- outputSuffix := rest[dotIndex+1:]
- r := &Rule{}
- *r = *rule
- r.inputs = []string{inputSuffix}
- r.isSuffixRule = true
- ex.suffixRules[outputSuffix] = append([]*Rule{r}, ex.suffixRules[outputSuffix]...)
- return true
-}
-
-func mergeRules(oldRule, rule *Rule, output string, isSuffixRule bool) *Rule {
- if oldRule.isDoubleColon != rule.isDoubleColon {
- Error(rule.filename, rule.lineno, "*** target file %q has both : and :: entries.", output)
- }
- if len(oldRule.cmds) > 0 && len(rule.cmds) > 0 && !isSuffixRule && !rule.isDoubleColon {
- Warn(rule.filename, rule.cmdLineno, "overriding commands for target %q", output)
- Warn(oldRule.filename, oldRule.cmdLineno, "ignoring old commands for target %q", output)
- }
-
- r := &Rule{}
- *r = *rule
- if rule.isDoubleColon {
- r.cmds = append(oldRule.cmds, r.cmds...)
- } else if len(oldRule.cmds) > 0 && len(rule.cmds) == 0 {
- r.cmds = oldRule.cmds
- }
- // If the latter rule has a command (regardless of the
- // commands in oldRule), inputs in the latter rule has a
- // priority.
- if len(rule.cmds) > 0 {
- r.inputs = append(r.inputs, oldRule.inputs...)
- r.orderOnlyInputs = append(r.orderOnlyInputs, oldRule.orderOnlyInputs...)
- } else {
- r.inputs = append(oldRule.inputs, r.inputs...)
- r.orderOnlyInputs = append(oldRule.orderOnlyInputs, r.orderOnlyInputs...)
- }
- r.outputPatterns = append(r.outputPatterns, oldRule.outputPatterns...)
- return r
-}
-
-func (ex *Executor) populateExplicitRule(rule *Rule) {
- // It seems rules with no outputs are siliently ignored.
- if len(rule.outputs) == 0 {
- return
- }
- for _, output := range rule.outputs {
- output = filepath.Clean(output)
-
- isSuffixRule := ex.populateSuffixRule(rule, output)
-
- if oldRule, present := ex.rules[output]; present {
- r := mergeRules(oldRule, rule, output, isSuffixRule)
- ex.rules[output] = r
- } else {
- ex.rules[output] = rule
- if ex.firstRule == nil && !strings.HasPrefix(output, ".") {
- ex.firstRule = rule
- }
- }
- }
-}
-
-func (ex *Executor) populateImplicitRule(rule *Rule) {
- for _, outputPattern := range rule.outputPatterns {
- r := &Rule{}
- *r = *rule
- r.outputPatterns = []string{outputPattern}
- ex.implicitRules = append(ex.implicitRules, r)
- }
-}
-
-func (ex *Executor) populateRules(er *EvalResult) {
- for _, rule := range er.rules {
- for i, input := range rule.inputs {
- rule.inputs[i] = filepath.Clean(input)
- }
- for i, orderOnlyInput := range rule.orderOnlyInputs {
- rule.orderOnlyInputs[i] = filepath.Clean(orderOnlyInput)
- }
- ex.populateExplicitRule(rule)
-
- if len(rule.outputs) == 0 {
- ex.populateImplicitRule(rule)
- }
- }
-
- // Reverse the implicit rule for easier lookup.
- for i, r := range ex.implicitRules {
- if i >= len(ex.implicitRules)/2 {
- break
- }
- j := len(ex.implicitRules) - i - 1
- ex.implicitRules[i] = ex.implicitRules[j]
- ex.implicitRules[j] = r
- }
-}
-
func (ex *Executor) reportStats() {
if !katiLogFlag && !katiStatsFlag {
return
@@ -647,44 +340,39 @@
LogStats("build=%d alreadyDone=%d noRule=%d, upToDate=%d runCommand=%d",
ex.buildCnt, ex.alreadyDoneCnt, ex.noRuleCnt, ex.upToDateCnt, ex.runCommandCnt)
- LogStats("explicit=%d implicit=%d suffix=%d explicitWOCmd=%d",
- ex.pickExplicitRuleCnt, ex.pickImplicitRuleCnt, ex.pickSuffixRuleCnt, ex.pickExplicitRuleWithoutCmdCnt)
if len(ex.trace) > 1 {
LogStats("trace=%q", ex.trace)
}
}
-func NewExecutor(er *EvalResult, vars Vars) *Executor {
- ex := newExecutor(vars, er.ruleVars)
+func NewExecutor(vars Vars) *Executor {
+ ex := &Executor{
+ rules: make(map[string]*Rule),
+ suffixRules: make(map[string][]*Rule),
+ done: make(map[string]int64),
+ vars: vars,
+ }
// TODO: We should move this to somewhere around evalCmd so that
// we can handle SHELL in target specific variables.
shellVar := ex.vars.Lookup("SHELL")
ex.shell = shellVar.String()
-
- ex.populateRules(er)
+ for k, v := range map[string]Var{
+ "@": AutoAtVar{AutoVar: AutoVar{ex: ex}},
+ "<": AutoLessVar{AutoVar: AutoVar{ex: ex}},
+ "^": AutoHatVar{AutoVar: AutoVar{ex: ex}},
+ "+": AutoPlusVar{AutoVar: AutoVar{ex: ex}},
+ "*": AutoStarVar{AutoVar: AutoVar{ex: ex}},
+ } {
+ ex.vars[k] = v
+ ex.vars[k+"D"] = AutoSuffixDVar{v: v}
+ ex.vars[k+"F"] = AutoSuffixFVar{v: v}
+ }
return ex
}
-func (ex *Executor) Exec(targets []string) error {
- if len(targets) == 0 {
- if ex.firstRule == nil {
- ErrorNoLocation("*** No targets.")
- }
- targets = append(targets, ex.firstRule.outputs[0])
+func (ex *Executor) Exec(roots []*DepNode) error {
+ for _, root := range roots {
+ ex.build(root, "")
}
-
- LogStats("%d variables", len(ex.vars))
- LogStats("%d explicit rules", len(ex.rules))
- LogStats("%d implicit rules", len(ex.implicitRules))
- LogStats("%d suffix rules", len(ex.suffixRules))
-
- for _, target := range targets {
- ex.trace = []string{target}
- _, err := ex.build(target, "")
- if err != nil {
- return err
- }
- }
- ex.reportStats()
return nil
}
diff --git a/main.go b/main.go
index 5f81dc1..bfcd7f7 100644
--- a/main.go
+++ b/main.go
@@ -152,11 +152,19 @@
LogStats("eval time: %q", time.Now().Sub(startTime))
startTime = time.Now()
- ex := NewExecutor(er, vars)
- LogStats("exec prepare time: %q", time.Now().Sub(startTime))
+ db := NewDepBuilder(er, vars)
+ LogStats("eval command prepare time: %q", time.Now().Sub(startTime))
startTime = time.Now()
- err = ex.Exec(targets)
+ nodes, err2 := db.Eval(targets)
+ if err2 != nil {
+ panic(err2)
+ }
+ LogStats("eval command time: %q", time.Now().Sub(startTime))
+
+ startTime = time.Now()
+ ex := NewExecutor(vars)
+ err = ex.Exec(nodes)
if err != nil {
panic(err)
}