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 15package kati 16 17import ( 18 "fmt" 19 "os" 20 "time" 21 22 "github.com/golang/glog" 23) 24 25// Executor manages execution of makefile rules. 26type 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 46func (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 116func (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. 129type ExecutorOpt struct { 130 NumJobs int 131} 132 133// NewExecutor creates new Executor. 134func 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. 155func (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