Home | History | Annotate | Download | only in kati
      1 // Copyright 2015 Google Inc. All rights reserved
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package kati
     16 
     17 import (
     18 	"fmt"
     19 	"os"
     20 	"time"
     21 
     22 	"github.com/golang/glog"
     23 )
     24 
     25 // Executor manages execution of makefile rules.
     26 type Executor struct {
     27 	rules         map[string]*rule
     28 	implicitRules []*rule
     29 	suffixRules   map[string][]*rule
     30 	firstRule     *rule
     31 	// target -> Job, nil means the target is currently being processed.
     32 	done map[string]*job
     33 
     34 	wm *workerManager
     35 
     36 	ctx *execContext
     37 
     38 	trace          []string
     39 	buildCnt       int
     40 	alreadyDoneCnt int
     41 	noRuleCnt      int
     42 	upToDateCnt    int
     43 	runCommandCnt  int
     44 }
     45 
     46 func (ex *Executor) makeJobs(n *DepNode, neededBy *job) error {
     47 	output, _ := ex.ctx.vpaths.exists(n.Output)
     48 	if neededBy != nil {
     49 		glog.V(1).Infof("MakeJob: %s for %s", output, neededBy.n.Output)
     50 	}
     51 	n.Output = output
     52 	ex.buildCnt++
     53 	if ex.buildCnt%100 == 0 {
     54 		ex.reportStats()
     55 	}
     56 
     57 	j, present := ex.done[output]
     58 
     59 	if present {
     60 		if j == nil {
     61 			if !n.IsPhony {
     62 				fmt.Printf("Circular %s <- %s dependency dropped.\n", neededBy.n.Output, n.Output)
     63 			}
     64 			if neededBy != nil {
     65 				neededBy.numDeps--
     66 			}
     67 		} else {
     68 			glog.Infof("%s already done: %d", j.n.Output, j.outputTs)
     69 			if neededBy != nil {
     70 				ex.wm.ReportNewDep(j, neededBy)
     71 			}
     72 		}
     73 		return nil
     74 	}
     75 
     76 	j = &job{
     77 		n:       n,
     78 		ex:      ex,
     79 		numDeps: len(n.Deps) + len(n.OrderOnlys),
     80 		depsTs:  int64(-1),
     81 	}
     82 	if neededBy != nil {
     83 		j.parents = append(j.parents, neededBy)
     84 	}
     85 
     86 	ex.done[output] = nil
     87 	// We iterate n.Deps twice. In the first run, we may modify
     88 	// numDeps. There will be a race if we do so after the first
     89 	// ex.makeJobs(d, j).
     90 	var deps []*DepNode
     91 	for _, d := range n.Deps {
     92 		deps = append(deps, d)
     93 	}
     94 	for _, d := range n.OrderOnlys {
     95 		if _, ok := ex.ctx.vpaths.exists(d.Output); ok {
     96 			j.numDeps--
     97 			continue
     98 		}
     99 		deps = append(deps, d)
    100 	}
    101 	glog.V(1).Infof("new: %s (%d)", j.n.Output, j.numDeps)
    102 
    103 	for _, d := range deps {
    104 		ex.trace = append(ex.trace, d.Output)
    105 		err := ex.makeJobs(d, j)
    106 		ex.trace = ex.trace[0 : len(ex.trace)-1]
    107 		if err != nil {
    108 			return err
    109 		}
    110 	}
    111 
    112 	ex.done[output] = j
    113 	return ex.wm.PostJob(j)
    114 }
    115 
    116 func (ex *Executor) reportStats() {
    117 	if !PeriodicStatsFlag {
    118 		return
    119 	}
    120 
    121 	logStats("build=%d alreadyDone=%d noRule=%d, upToDate=%d runCommand=%d",
    122 		ex.buildCnt, ex.alreadyDoneCnt, ex.noRuleCnt, ex.upToDateCnt, ex.runCommandCnt)
    123 	if len(ex.trace) > 1 {
    124 		logStats("trace=%q", ex.trace)
    125 	}
    126 }
    127 
    128 // ExecutorOpt is an option for Executor.
    129 type ExecutorOpt struct {
    130 	NumJobs int
    131 }
    132 
    133 // NewExecutor creates new Executor.
    134 func NewExecutor(opt *ExecutorOpt) (*Executor, error) {
    135 	if opt == nil {
    136 		opt = &ExecutorOpt{NumJobs: 1}
    137 	}
    138 	if opt.NumJobs < 1 {
    139 		opt.NumJobs = 1
    140 	}
    141 	wm, err := newWorkerManager(opt.NumJobs)
    142 	if err != nil {
    143 		return nil, err
    144 	}
    145 	ex := &Executor{
    146 		rules:       make(map[string]*rule),
    147 		suffixRules: make(map[string][]*rule),
    148 		done:        make(map[string]*job),
    149 		wm:          wm,
    150 	}
    151 	return ex, nil
    152 }
    153 
    154 // Exec executes to build targets, or first target in DepGraph.
    155 func (ex *Executor) Exec(g *DepGraph, targets []string) error {
    156 	ex.ctx = newExecContext(g.vars, g.vpaths, false)
    157 
    158 	// TODO: Handle target specific variables.
    159 	for name, export := range g.exports {
    160 		if export {
    161 			v, err := ex.ctx.ev.EvaluateVar(name)
    162 			if err != nil {
    163 				return err
    164 			}
    165 			os.Setenv(name, v)
    166 		} else {
    167 			os.Unsetenv(name)
    168 		}
    169 	}
    170 
    171 	startTime := time.Now()
    172 	var nodes []*DepNode
    173 	if len(targets) == 0 {
    174 		if len(g.nodes) > 0 {
    175 			nodes = append(nodes, g.nodes[0])
    176 		}
    177 	} else {
    178 		m := make(map[string]*DepNode)
    179 		for _, n := range g.nodes {
    180 			m[n.Output] = n
    181 		}
    182 		for _, t := range targets {
    183 			n := m[t]
    184 			if n != nil {
    185 				nodes = append(nodes, n)
    186 			}
    187 		}
    188 	}
    189 	for _, root := range nodes {
    190 		err := ex.makeJobs(root, nil)
    191 		if err != nil {
    192 			break
    193 		}
    194 	}
    195 	n, err := ex.wm.Wait()
    196 	logStats("exec time: %q", time.Since(startTime))
    197 	if n == 0 {
    198 		for _, root := range nodes {
    199 			fmt.Printf("kati: Nothing to be done for `%s'.\n", root.Output)
    200 		}
    201 	}
    202 	return err
    203 }
    204