203 lines
4.4 KiB
Go
203 lines
4.4 KiB
Go
// 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
|
|
}
|