diff options
Diffstat (limited to 'golang/kati/exec.go')
| -rw-r--r-- | golang/kati/exec.go | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/golang/kati/exec.go b/golang/kati/exec.go new file mode 100644 index 0000000..fbcf41e --- /dev/null +++ b/golang/kati/exec.go @@ -0,0 +1,203 @@ +// Copyright 2015 Google Inc. All rights reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kati + +import ( + "fmt" + "os" + "time" + + "github.com/golang/glog" +) + +// Executor manages execution of makefile rules. +type Executor struct { + rules map[string]*rule + implicitRules []*rule + suffixRules map[string][]*rule + firstRule *rule + // target -> Job, nil means the target is currently being processed. + done map[string]*job + + wm *workerManager + + ctx *execContext + + trace []string + buildCnt int + alreadyDoneCnt int + noRuleCnt int + upToDateCnt int + runCommandCnt int +} + +func (ex *Executor) makeJobs(n *DepNode, neededBy *job) error { + output, _ := ex.ctx.vpaths.exists(n.Output) + if neededBy != nil { + glog.V(1).Infof("MakeJob: %s for %s", output, neededBy.n.Output) + } + n.Output = output + ex.buildCnt++ + if ex.buildCnt%100 == 0 { + ex.reportStats() + } + + j, present := ex.done[output] + + if present { + if j == nil { + if !n.IsPhony { + fmt.Printf("Circular %s <- %s dependency dropped.\n", neededBy.n.Output, n.Output) + } + if neededBy != nil { + neededBy.numDeps-- + } + } else { + glog.Infof("%s already done: %d", j.n.Output, j.outputTs) + if neededBy != nil { + ex.wm.ReportNewDep(j, neededBy) + } + } + return nil + } + + j = &job{ + n: n, + ex: ex, + numDeps: len(n.Deps) + len(n.OrderOnlys), + depsTs: int64(-1), + } + if neededBy != nil { + j.parents = append(j.parents, neededBy) + } + + ex.done[output] = nil + // We iterate n.Deps twice. In the first run, we may modify + // numDeps. There will be a race if we do so after the first + // ex.makeJobs(d, j). + var deps []*DepNode + for _, d := range n.Deps { + deps = append(deps, d) + } + for _, d := range n.OrderOnlys { + if _, ok := ex.ctx.vpaths.exists(d.Output); ok { + j.numDeps-- + continue + } + deps = append(deps, d) + } + glog.V(1).Infof("new: %s (%d)", j.n.Output, j.numDeps) + + for _, d := range deps { + ex.trace = append(ex.trace, d.Output) + err := ex.makeJobs(d, j) + ex.trace = ex.trace[0 : len(ex.trace)-1] + if err != nil { + return err + } + } + + ex.done[output] = j + return ex.wm.PostJob(j) +} + +func (ex *Executor) reportStats() { + if !PeriodicStatsFlag { + return + } + + logStats("build=%d alreadyDone=%d noRule=%d, upToDate=%d runCommand=%d", + ex.buildCnt, ex.alreadyDoneCnt, ex.noRuleCnt, ex.upToDateCnt, ex.runCommandCnt) + if len(ex.trace) > 1 { + logStats("trace=%q", ex.trace) + } +} + +// ExecutorOpt is an option for Executor. +type ExecutorOpt struct { + NumJobs int +} + +// NewExecutor creates new Executor. +func NewExecutor(opt *ExecutorOpt) (*Executor, error) { + if opt == nil { + opt = &ExecutorOpt{NumJobs: 1} + } + if opt.NumJobs < 1 { + opt.NumJobs = 1 + } + wm, err := newWorkerManager(opt.NumJobs) + if err != nil { + return nil, err + } + ex := &Executor{ + rules: make(map[string]*rule), + suffixRules: make(map[string][]*rule), + done: make(map[string]*job), + wm: wm, + } + return ex, nil +} + +// Exec executes to build targets, or first target in DepGraph. +func (ex *Executor) Exec(g *DepGraph, targets []string) error { + ex.ctx = newExecContext(g.vars, g.vpaths, false) + + // TODO: Handle target specific variables. + for name, export := range g.exports { + if export { + v, err := ex.ctx.ev.EvaluateVar(name) + if err != nil { + return err + } + os.Setenv(name, v) + } else { + os.Unsetenv(name) + } + } + + startTime := time.Now() + var nodes []*DepNode + if len(targets) == 0 { + if len(g.nodes) > 0 { + nodes = append(nodes, g.nodes[0]) + } + } else { + m := make(map[string]*DepNode) + for _, n := range g.nodes { + m[n.Output] = n + } + for _, t := range targets { + n := m[t] + if n != nil { + nodes = append(nodes, n) + } + } + } + for _, root := range nodes { + err := ex.makeJobs(root, nil) + if err != nil { + break + } + } + n, err := ex.wm.Wait() + logStats("exec time: %q", time.Since(startTime)) + if n == 0 { + for _, root := range nodes { + fmt.Printf("kati: Nothing to be done for `%s'.\n", root.Output) + } + } + return err +} |
