// 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 }