// 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" "time" ) // 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, _ := existsInVPATH(ex.ctx.ev, n.Output) if neededBy != nil { logf("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 { logf("%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 { // need lock for ex.ctx.ev? if _, ok := existsInVPATH(ex.ctx.ev, d.Output); ok { j.numDeps-- continue } deps = append(deps, d) } logf("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 !LogFlag && !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(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) if err != nil { return nil, err } ctx := newExecContext(vars, false) ex := &Executor{ rules: make(map[string]*rule), suffixRules: make(map[string][]*rule), done: make(map[string]*job), wm: wm, ctx: ctx, } return ex, nil } // Exec executes to build roots. func (ex *Executor) Exec(roots []*DepNode) error { startTime := time.Now() for _, root := range roots { err := ex.makeJobs(root, nil) if err != nil { break } } err := ex.wm.Wait() logStats("exec time: %q", time.Since(startTime)) return err }