aboutsummaryrefslogtreecommitdiffstats
path: root/golang/kati/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'golang/kati/exec.go')
-rw-r--r--golang/kati/exec.go203
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
+}