Shinichiro Hamaji | b69bf8a | 2015-06-10 14:52:06 +0900 | [diff] [blame] | 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 | |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 15 | package kati |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 16 | |
| 17 | import ( |
| 18 | "fmt" |
| 19 | "path/filepath" |
Fumitoshi Ukai | b79b290 | 2015-07-16 16:40:09 +0900 | [diff] [blame] | 20 | "sort" |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 21 | "strings" |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 22 | |
| 23 | "github.com/golang/glog" |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 24 | ) |
| 25 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 26 | // DepNode represents a makefile rule for an output. |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 27 | type DepNode struct { |
| 28 | Output string |
| 29 | Cmds []string |
| 30 | Deps []*DepNode |
Fumitoshi Ukai | c916ea2 | 2015-06-30 09:52:13 +0900 | [diff] [blame] | 31 | OrderOnlys []*DepNode |
Shinichiro Hamaji | 744b139 | 2015-05-14 23:29:14 +0900 | [diff] [blame] | 32 | Parents []*DepNode |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 33 | HasRule bool |
Fumitoshi Ukai | ab431e2 | 2015-04-22 23:27:18 +0900 | [diff] [blame] | 34 | IsPhony bool |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 35 | ActualInputs []string |
| 36 | TargetSpecificVars Vars |
| 37 | Filename string |
| 38 | Lineno int |
| 39 | } |
| 40 | |
| 41 | func (n *DepNode) String() string { |
Fumitoshi Ukai | c916ea2 | 2015-06-30 09:52:13 +0900 | [diff] [blame] | 42 | return fmt.Sprintf("Dep{output=%s cmds=%d deps=%d orders=%d hasRule=%t phony=%t filename=%s lineno=%d}", |
| 43 | n.Output, len(n.Cmds), len(n.Deps), len(n.OrderOnlys), n.HasRule, n.IsPhony, n.Filename, n.Lineno) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 44 | } |
| 45 | |
Fumitoshi Ukai | c9ff91b | 2015-06-25 15:58:36 +0900 | [diff] [blame] | 46 | type depBuilder struct { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 47 | rules map[string]*rule |
Fumitoshi Ukai | 72508a7 | 2015-05-08 13:41:31 +0900 | [diff] [blame] | 48 | ruleVars map[string]Vars |
| 49 | |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 50 | implicitRules *ruleTrie |
Fumitoshi Ukai | 72508a7 | 2015-05-08 13:41:31 +0900 | [diff] [blame] | 51 | |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 52 | suffixRules map[string][]*rule |
| 53 | firstRule *rule |
Fumitoshi Ukai | 72508a7 | 2015-05-08 13:41:31 +0900 | [diff] [blame] | 54 | vars Vars |
Fumitoshi Ukai | dd058ff | 2015-07-03 14:29:41 +0900 | [diff] [blame] | 55 | ev *Evaluator |
Fumitoshi Ukai | 09fcd52 | 2015-07-15 14:31:50 +0900 | [diff] [blame] | 56 | vpaths searchPaths |
Fumitoshi Ukai | 72508a7 | 2015-05-08 13:41:31 +0900 | [diff] [blame] | 57 | done map[string]*DepNode |
| 58 | phony map[string]bool |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 59 | |
| 60 | trace []string |
| 61 | nodeCnt int |
| 62 | pickExplicitRuleCnt int |
| 63 | pickImplicitRuleCnt int |
| 64 | pickSuffixRuleCnt int |
| 65 | pickExplicitRuleWithoutCmdCnt int |
| 66 | } |
| 67 | |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 68 | type ruleTrieEntry struct { |
| 69 | rule *rule |
| 70 | suffix string |
| 71 | } |
| 72 | |
| 73 | type ruleTrie struct { |
| 74 | rules []ruleTrieEntry |
| 75 | children map[byte]*ruleTrie |
| 76 | } |
| 77 | |
| 78 | func newRuleTrie() *ruleTrie { |
| 79 | return &ruleTrie{ |
| 80 | children: make(map[byte]*ruleTrie), |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | func (rt *ruleTrie) add(name string, r *rule) { |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 85 | glog.V(1).Infof("rule trie: add %q %v %s", name, r.outputPatterns[0], r) |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 86 | if name == "" || name[0] == '%' { |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 87 | glog.V(1).Infof("rule trie: add entry %q %v %s", name, r.outputPatterns[0], r) |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 88 | rt.rules = append(rt.rules, ruleTrieEntry{ |
| 89 | rule: r, |
| 90 | suffix: name, |
| 91 | }) |
| 92 | return |
| 93 | } |
| 94 | c, found := rt.children[name[0]] |
| 95 | if !found { |
| 96 | c = newRuleTrie() |
| 97 | rt.children[name[0]] = c |
| 98 | } |
| 99 | c.add(name[1:], r) |
| 100 | } |
| 101 | |
| 102 | func (rt *ruleTrie) lookup(name string) []*rule { |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 103 | glog.V(1).Infof("rule trie: lookup %q", name) |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 104 | if rt == nil { |
| 105 | return nil |
| 106 | } |
| 107 | var rules []*rule |
| 108 | for _, entry := range rt.rules { |
| 109 | if (entry.suffix == "" && name == "") || strings.HasSuffix(name, entry.suffix[1:]) { |
| 110 | rules = append(rules, entry.rule) |
| 111 | } |
| 112 | } |
| 113 | if name == "" { |
| 114 | return rules |
| 115 | } |
| 116 | rules = append(rules, rt.children[name[0]].lookup(name[1:])...) |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 117 | glog.V(1).Infof("rule trie: lookup %q => %v", name, rules) |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 118 | return rules |
| 119 | } |
| 120 | |
| 121 | func (rt *ruleTrie) size() int { |
| 122 | if rt == nil { |
| 123 | return 0 |
| 124 | } |
| 125 | size := len(rt.rules) |
| 126 | for _, c := range rt.children { |
| 127 | size += c.size() |
| 128 | } |
| 129 | return size |
| 130 | } |
| 131 | |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 132 | func replaceSuffix(s string, newsuf string) string { |
| 133 | // TODO: Factor out the logic around suffix rules and use |
| 134 | // it from substitution references. |
| 135 | // http://www.gnu.org/software/make/manual/make.html#Substitution-Refs |
| 136 | return fmt.Sprintf("%s.%s", stripExt(s), newsuf) |
| 137 | } |
| 138 | |
Fumitoshi Ukai | c9ff91b | 2015-06-25 15:58:36 +0900 | [diff] [blame] | 139 | func (db *depBuilder) exists(target string) bool { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 140 | _, present := db.rules[target] |
| 141 | if present { |
| 142 | return true |
| 143 | } |
Fumitoshi Ukai | ab431e2 | 2015-04-22 23:27:18 +0900 | [diff] [blame] | 144 | if db.phony[target] { |
| 145 | return true |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 146 | } |
Fumitoshi Ukai | 09fcd52 | 2015-07-15 14:31:50 +0900 | [diff] [blame] | 147 | _, ok := db.vpaths.exists(target) |
Fumitoshi Ukai | dd058ff | 2015-07-03 14:29:41 +0900 | [diff] [blame] | 148 | return ok |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 149 | } |
| 150 | |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 151 | func (db *depBuilder) canPickImplicitRule(r *rule, output string) bool { |
| 152 | outputPattern := r.outputPatterns[0] |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 153 | if !outputPattern.match(output) { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 154 | return false |
| 155 | } |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 156 | for _, input := range r.inputs { |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 157 | input = outputPattern.subst(input, output) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 158 | if !db.exists(input) { |
| 159 | return false |
| 160 | } |
| 161 | } |
| 162 | return true |
| 163 | } |
| 164 | |
Fumitoshi Ukai | c9ff91b | 2015-06-25 15:58:36 +0900 | [diff] [blame] | 165 | func (db *depBuilder) mergeImplicitRuleVars(outputs []string, vars Vars) Vars { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 166 | if len(outputs) != 1 { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 167 | // TODO(ukai): should return error? |
| 168 | panic(fmt.Sprintf("FIXME: Implicit rule should have only one output but %q", outputs)) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 169 | } |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 170 | glog.V(1).Infof("merge? %q", db.ruleVars) |
| 171 | glog.V(1).Infof("merge? %q", outputs[0]) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 172 | ivars, present := db.ruleVars[outputs[0]] |
| 173 | if !present { |
| 174 | return vars |
| 175 | } |
| 176 | if vars == nil { |
| 177 | return ivars |
| 178 | } |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 179 | glog.V(1).Info("merge!") |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 180 | v := make(Vars) |
| 181 | v.Merge(ivars) |
| 182 | v.Merge(vars) |
| 183 | return v |
| 184 | } |
| 185 | |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 186 | func (db *depBuilder) pickRule(output string) (*rule, Vars, bool) { |
| 187 | r, present := db.rules[output] |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 188 | vars := db.ruleVars[output] |
| 189 | if present { |
| 190 | db.pickExplicitRuleCnt++ |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 191 | if len(r.cmds) > 0 { |
| 192 | return r, vars, true |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 193 | } |
| 194 | // If none of the explicit rules for a target has commands, |
| 195 | // then `make' searches for an applicable implicit rule to |
| 196 | // find some commands. |
| 197 | db.pickExplicitRuleWithoutCmdCnt++ |
| 198 | } |
| 199 | |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 200 | irules := db.implicitRules.lookup(output) |
| 201 | for i := len(irules) - 1; i >= 0; i-- { |
| 202 | irule := irules[i] |
| 203 | if !db.canPickImplicitRule(irule, output) { |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 204 | glog.Infof("ignore implicit rule %q %s", output, irule) |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 205 | continue |
| 206 | } |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 207 | glog.Infof("pick implicit rule %q => %q %s", output, irule.outputPatterns, irule) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 208 | db.pickImplicitRuleCnt++ |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 209 | if r != nil { |
| 210 | ir := &rule{} |
| 211 | *ir = *r |
| 212 | ir.outputPatterns = irule.outputPatterns |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 213 | // implicit rule's prerequisites will be used for $< |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 214 | ir.inputs = append(irule.inputs, ir.inputs...) |
| 215 | ir.cmds = irule.cmds |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 216 | // TODO(ukai): filename, lineno? |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 217 | ir.cmdLineno = irule.cmdLineno |
| 218 | return ir, vars, true |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 219 | } |
| 220 | if vars != nil { |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 221 | var outputs []string |
| 222 | for _, op := range irule.outputPatterns { |
| 223 | outputs = append(outputs, op.String()) |
| 224 | } |
| 225 | vars = db.mergeImplicitRuleVars(outputs, vars) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 226 | } |
| 227 | // TODO(ukai): check len(irule.cmd) ? |
| 228 | return irule, vars, true |
| 229 | } |
| 230 | |
| 231 | outputSuffix := filepath.Ext(output) |
| 232 | if !strings.HasPrefix(outputSuffix, ".") { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 233 | return r, vars, r != nil |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 234 | } |
| 235 | rules, present := db.suffixRules[outputSuffix[1:]] |
| 236 | if !present { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 237 | return r, vars, r != nil |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 238 | } |
| 239 | for _, irule := range rules { |
| 240 | if len(irule.inputs) != 1 { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 241 | // TODO(ukai): should return error? |
| 242 | panic(fmt.Sprintf("FIXME: unexpected number of input for a suffix rule (%d)", len(irule.inputs))) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 243 | } |
| 244 | if !db.exists(replaceSuffix(output, irule.inputs[0])) { |
| 245 | continue |
| 246 | } |
| 247 | db.pickSuffixRuleCnt++ |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 248 | if r != nil { |
| 249 | sr := &rule{} |
| 250 | *sr = *r |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 251 | // TODO(ukai): input order is correct? |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 252 | sr.inputs = append([]string{replaceSuffix(output, irule.inputs[0])}, r.inputs...) |
| 253 | sr.cmds = irule.cmds |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 254 | // TODO(ukai): filename, lineno? |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 255 | sr.cmdLineno = irule.cmdLineno |
| 256 | return sr, vars, true |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 257 | } |
| 258 | if vars != nil { |
| 259 | vars = db.mergeImplicitRuleVars(irule.outputs, vars) |
| 260 | } |
| 261 | // TODO(ukai): check len(irule.cmd) ? |
| 262 | return irule, vars, true |
| 263 | } |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 264 | return r, vars, r != nil |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 265 | } |
| 266 | |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 267 | func expandInputs(rule *rule, output string) []string { |
| 268 | var inputs []string |
| 269 | for _, input := range rule.inputs { |
| 270 | if len(rule.outputPatterns) > 0 { |
| 271 | if len(rule.outputPatterns) != 1 { |
| 272 | panic(fmt.Sprintf("FIXME: multiple output pattern is not supported yet")) |
| 273 | } |
| 274 | input = intern(rule.outputPatterns[0].subst(input, output)) |
| 275 | } else if rule.isSuffixRule { |
| 276 | input = intern(replaceSuffix(output, input)) |
| 277 | } |
| 278 | inputs = append(inputs, input) |
| 279 | } |
| 280 | return inputs |
| 281 | } |
| 282 | |
Fumitoshi Ukai | c9ff91b | 2015-06-25 15:58:36 +0900 | [diff] [blame] | 283 | func (db *depBuilder) buildPlan(output string, neededBy string, tsvs Vars) (*DepNode, error) { |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 284 | glog.V(1).Infof("Evaluating command: %s", output) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 285 | db.nodeCnt++ |
| 286 | if db.nodeCnt%100 == 0 { |
| 287 | db.reportStats() |
| 288 | } |
| 289 | |
| 290 | if n, present := db.done[output]; present { |
| 291 | return n, nil |
| 292 | } |
Fumitoshi Ukai | ab431e2 | 2015-04-22 23:27:18 +0900 | [diff] [blame] | 293 | |
| 294 | n := &DepNode{Output: output, IsPhony: db.phony[output]} |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 295 | db.done[output] = n |
| 296 | |
Fumitoshi Ukai | ab431e2 | 2015-04-22 23:27:18 +0900 | [diff] [blame] | 297 | // create depnode for phony targets? |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 298 | rule, vars, present := db.pickRule(output) |
| 299 | if !present { |
| 300 | return n, nil |
| 301 | } |
| 302 | |
| 303 | var restores []func() |
| 304 | if vars != nil { |
| 305 | for name, v := range vars { |
| 306 | // TODO: Consider not updating db.vars. |
Fumitoshi Ukai | 7bf992d | 2015-06-25 12:42:19 +0900 | [diff] [blame] | 307 | tsv := v.(*targetSpecificVar) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 308 | restores = append(restores, db.vars.save(name)) |
| 309 | restores = append(restores, tsvs.save(name)) |
| 310 | switch tsv.op { |
| 311 | case ":=", "=": |
| 312 | db.vars[name] = tsv |
| 313 | tsvs[name] = v |
| 314 | case "+=": |
| 315 | oldVar, present := db.vars[name] |
| 316 | if !present || oldVar.String() == "" { |
| 317 | db.vars[name] = tsv |
| 318 | } else { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 319 | var err error |
Fumitoshi Ukai | dd058ff | 2015-07-03 14:29:41 +0900 | [diff] [blame] | 320 | v, err = oldVar.AppendVar(db.ev, tsv) |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 321 | if err != nil { |
| 322 | return nil, err |
| 323 | } |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 324 | db.vars[name] = v |
| 325 | } |
| 326 | tsvs[name] = v |
| 327 | case "?=": |
| 328 | if _, present := db.vars[name]; !present { |
| 329 | db.vars[name] = tsv |
| 330 | tsvs[name] = v |
| 331 | } |
| 332 | } |
| 333 | } |
| 334 | defer func() { |
| 335 | for _, restore := range restores { |
| 336 | restore() |
| 337 | } |
| 338 | }() |
| 339 | } |
| 340 | |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 341 | inputs := expandInputs(rule, output) |
| 342 | glog.Infof("Evaluating command: %s inputs:%q => %q", output, rule.inputs, inputs) |
| 343 | for _, input := range inputs { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 344 | db.trace = append(db.trace, input) |
Fumitoshi Ukai | c916ea2 | 2015-06-30 09:52:13 +0900 | [diff] [blame] | 345 | ni, err := db.buildPlan(input, output, tsvs) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 346 | db.trace = db.trace[0 : len(db.trace)-1] |
| 347 | if err != nil { |
| 348 | return nil, err |
| 349 | } |
Fumitoshi Ukai | c916ea2 | 2015-06-30 09:52:13 +0900 | [diff] [blame] | 350 | if ni != nil { |
| 351 | n.Deps = append(n.Deps, ni) |
| 352 | ni.Parents = append(ni.Parents, n) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 353 | } |
| 354 | } |
| 355 | |
| 356 | for _, input := range rule.orderOnlyInputs { |
| 357 | db.trace = append(db.trace, input) |
Fumitoshi Ukai | c916ea2 | 2015-06-30 09:52:13 +0900 | [diff] [blame] | 358 | ni, err := db.buildPlan(input, output, tsvs) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 359 | db.trace = db.trace[0 : len(db.trace)-1] |
| 360 | if err != nil { |
| 361 | return nil, err |
| 362 | } |
| 363 | if n != nil { |
Fumitoshi Ukai | c916ea2 | 2015-06-30 09:52:13 +0900 | [diff] [blame] | 364 | n.OrderOnlys = append(n.OrderOnlys, ni) |
| 365 | ni.Parents = append(ni.Parents, n) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 366 | } |
| 367 | } |
| 368 | |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 369 | n.HasRule = true |
| 370 | n.Cmds = rule.cmds |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 371 | n.ActualInputs = inputs |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 372 | n.TargetSpecificVars = make(Vars) |
| 373 | for k, v := range tsvs { |
Fumitoshi Ukai | 7fd162b | 2015-07-15 12:53:57 +0900 | [diff] [blame] | 374 | if glog.V(1) { |
| 375 | glog.Infof("output=%s tsv %s=%s", output, k, v) |
| 376 | } |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 377 | n.TargetSpecificVars[k] = v |
| 378 | } |
| 379 | n.Filename = rule.filename |
| 380 | if len(rule.cmds) > 0 { |
Shinichiro Hamaji | 0483e64 | 2015-04-27 21:45:30 +0900 | [diff] [blame] | 381 | if rule.cmdLineno > 0 { |
| 382 | n.Lineno = rule.cmdLineno |
Shinichiro Hamaji | 5fa1b1e | 2015-04-27 22:51:52 +0900 | [diff] [blame] | 383 | } else { |
| 384 | n.Lineno = rule.lineno |
Shinichiro Hamaji | 0483e64 | 2015-04-27 21:45:30 +0900 | [diff] [blame] | 385 | } |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 386 | } |
| 387 | return n, nil |
| 388 | } |
| 389 | |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 390 | func (db *depBuilder) populateSuffixRule(r *rule, output string) bool { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 391 | if len(output) == 0 || output[0] != '.' { |
| 392 | return false |
| 393 | } |
| 394 | rest := output[1:] |
| 395 | dotIndex := strings.IndexByte(rest, '.') |
| 396 | // If there is only a single dot or the third dot, this is not a |
| 397 | // suffix rule. |
| 398 | if dotIndex < 0 || strings.IndexByte(rest[dotIndex+1:], '.') >= 0 { |
| 399 | return false |
| 400 | } |
| 401 | |
| 402 | // This is a suffix rule. |
| 403 | inputSuffix := rest[:dotIndex] |
| 404 | outputSuffix := rest[dotIndex+1:] |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 405 | sr := &rule{} |
| 406 | *sr = *r |
| 407 | sr.inputs = []string{inputSuffix} |
| 408 | sr.isSuffixRule = true |
| 409 | db.suffixRules[outputSuffix] = append([]*rule{sr}, db.suffixRules[outputSuffix]...) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 410 | return true |
| 411 | } |
| 412 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 413 | func mergeRules(oldRule, r *rule, output string, isSuffixRule bool) (*rule, error) { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 414 | if oldRule.isDoubleColon != r.isDoubleColon { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 415 | return nil, r.errorf("*** target file %q has both : and :: entries.", output) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 416 | } |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 417 | if len(oldRule.cmds) > 0 && len(r.cmds) > 0 && !isSuffixRule && !r.isDoubleColon { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 418 | warn(r.cmdpos(), "overriding commands for target %q", output) |
| 419 | warn(oldRule.cmdpos(), "ignoring old commands for target %q", output) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 420 | } |
| 421 | |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 422 | mr := &rule{} |
| 423 | *mr = *r |
| 424 | if r.isDoubleColon { |
| 425 | mr.cmds = append(oldRule.cmds, mr.cmds...) |
| 426 | } else if len(oldRule.cmds) > 0 && len(r.cmds) == 0 { |
| 427 | mr.cmds = oldRule.cmds |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 428 | } |
| 429 | // If the latter rule has a command (regardless of the |
| 430 | // commands in oldRule), inputs in the latter rule has a |
| 431 | // priority. |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 432 | if len(r.cmds) > 0 { |
| 433 | mr.inputs = append(mr.inputs, oldRule.inputs...) |
| 434 | mr.orderOnlyInputs = append(mr.orderOnlyInputs, oldRule.orderOnlyInputs...) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 435 | } else { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 436 | mr.inputs = append(oldRule.inputs, mr.inputs...) |
| 437 | mr.orderOnlyInputs = append(oldRule.orderOnlyInputs, mr.orderOnlyInputs...) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 438 | } |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 439 | mr.outputPatterns = append(mr.outputPatterns, oldRule.outputPatterns...) |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 440 | return mr, nil |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 441 | } |
| 442 | |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 443 | // expandPattern expands static pattern (target: target-pattern: prereq-pattern). |
| 444 | |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 445 | func expandPattern(r *rule) []*rule { |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 446 | if len(r.outputs) == 0 { |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 447 | return []*rule{r} |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 448 | } |
| 449 | if len(r.outputPatterns) != 1 { |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 450 | return []*rule{r} |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 451 | } |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 452 | var rules []*rule |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 453 | pat := r.outputPatterns[0] |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 454 | for _, output := range r.outputs { |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 455 | nr := new(rule) |
| 456 | *nr = *r |
| 457 | nr.outputs = []string{output} |
| 458 | nr.outputPatterns = nil |
| 459 | nr.inputs = nil |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 460 | for _, input := range r.inputs { |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 461 | nr.inputs = append(nr.inputs, intern(pat.subst(input, output))) |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 462 | } |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 463 | rules = append(rules, nr) |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 464 | } |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 465 | glog.V(1).Infof("expand static pattern: outputs=%q inputs=%q -> %q", r.outputs, r.inputs, rules) |
| 466 | return rules |
Fumitoshi Ukai | 4ed0b06 | 2015-07-17 22:03:46 +0900 | [diff] [blame] | 467 | } |
| 468 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 469 | func (db *depBuilder) populateExplicitRule(r *rule) error { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 470 | // It seems rules with no outputs are siliently ignored. |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 471 | if len(r.outputs) == 0 { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 472 | return nil |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 473 | } |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 474 | for _, output := range r.outputs { |
Shinichiro Hamaji | dad8053 | 2015-04-21 17:55:50 +0900 | [diff] [blame] | 475 | output = trimLeadingCurdir(output) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 476 | |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 477 | isSuffixRule := db.populateSuffixRule(r, output) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 478 | |
| 479 | if oldRule, present := db.rules[output]; present { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 480 | mr, err := mergeRules(oldRule, r, output, isSuffixRule) |
| 481 | if err != nil { |
| 482 | return err |
| 483 | } |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 484 | db.rules[output] = mr |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 485 | } else { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 486 | db.rules[output] = r |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 487 | if db.firstRule == nil && !strings.HasPrefix(output, ".") { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 488 | db.firstRule = r |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 489 | } |
| 490 | } |
| 491 | } |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 492 | return nil |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 493 | } |
| 494 | |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 495 | func (db *depBuilder) populateImplicitRule(r *rule) { |
| 496 | for _, outputPattern := range r.outputPatterns { |
| 497 | ir := &rule{} |
| 498 | *ir = *r |
| 499 | ir.outputPatterns = []pattern{outputPattern} |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 500 | db.implicitRules.add(outputPattern.String(), ir) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 501 | } |
| 502 | } |
| 503 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 504 | func (db *depBuilder) populateRules(er *evalResult) error { |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 505 | for _, r := range er.rules { |
| 506 | for i, input := range r.inputs { |
| 507 | r.inputs[i] = trimLeadingCurdir(input) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 508 | } |
Fumitoshi Ukai | adc1444 | 2015-06-25 16:10:30 +0900 | [diff] [blame] | 509 | for i, orderOnlyInput := range r.orderOnlyInputs { |
| 510 | r.orderOnlyInputs[i] = trimLeadingCurdir(orderOnlyInput) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 511 | } |
Fumitoshi Ukai | b8b8050 | 2015-07-21 14:28:26 +0900 | [diff] [blame] | 512 | for _, r := range expandPattern(r) { |
| 513 | err := db.populateExplicitRule(r) |
| 514 | if err != nil { |
| 515 | return err |
| 516 | } |
| 517 | if len(r.outputs) == 0 { |
| 518 | db.populateImplicitRule(r) |
| 519 | } |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 520 | } |
| 521 | } |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 522 | return nil |
Fumitoshi Ukai | 72508a7 | 2015-05-08 13:41:31 +0900 | [diff] [blame] | 523 | } |
| 524 | |
Fumitoshi Ukai | c9ff91b | 2015-06-25 15:58:36 +0900 | [diff] [blame] | 525 | func (db *depBuilder) reportStats() { |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 526 | if !PeriodicStatsFlag { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 527 | return |
| 528 | } |
| 529 | |
Fumitoshi Ukai | 49599e5 | 2015-06-26 10:10:24 +0900 | [diff] [blame] | 530 | logStats("node=%d explicit=%d implicit=%d suffix=%d explicitWOCmd=%d", |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 531 | db.nodeCnt, db.pickExplicitRuleCnt, db.pickImplicitRuleCnt, db.pickSuffixRuleCnt, db.pickExplicitRuleWithoutCmdCnt) |
| 532 | if len(db.trace) > 1 { |
Fumitoshi Ukai | 49599e5 | 2015-06-26 10:10:24 +0900 | [diff] [blame] | 533 | logStats("trace=%q", db.trace) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 534 | } |
| 535 | } |
| 536 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 537 | func newDepBuilder(er *evalResult, vars Vars) (*depBuilder, error) { |
Fumitoshi Ukai | c9ff91b | 2015-06-25 15:58:36 +0900 | [diff] [blame] | 538 | db := &depBuilder{ |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 539 | rules: make(map[string]*rule), |
| 540 | ruleVars: er.ruleVars, |
| 541 | implicitRules: newRuleTrie(), |
| 542 | suffixRules: make(map[string][]*rule), |
| 543 | vars: vars, |
Fumitoshi Ukai | dd058ff | 2015-07-03 14:29:41 +0900 | [diff] [blame] | 544 | ev: NewEvaluator(vars), |
Fumitoshi Ukai | 09fcd52 | 2015-07-15 14:31:50 +0900 | [diff] [blame] | 545 | vpaths: er.vpaths, |
Fumitoshi Ukai | 914fed3 | 2015-07-02 15:50:16 +0900 | [diff] [blame] | 546 | done: make(map[string]*DepNode), |
| 547 | phony: make(map[string]bool), |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 548 | } |
| 549 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 550 | err := db.populateRules(er) |
| 551 | if err != nil { |
| 552 | return nil, err |
| 553 | } |
Fumitoshi Ukai | ab431e2 | 2015-04-22 23:27:18 +0900 | [diff] [blame] | 554 | rule, present := db.rules[".PHONY"] |
| 555 | if present { |
| 556 | for _, input := range rule.inputs { |
| 557 | db.phony[input] = true |
| 558 | } |
| 559 | } |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 560 | return db, nil |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 561 | } |
| 562 | |
Fumitoshi Ukai | c9ff91b | 2015-06-25 15:58:36 +0900 | [diff] [blame] | 563 | func (db *depBuilder) Eval(targets []string) ([]*DepNode, error) { |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 564 | if len(targets) == 0 { |
| 565 | if db.firstRule == nil { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 566 | return nil, fmt.Errorf("*** No targets.") |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 567 | } |
| 568 | targets = append(targets, db.firstRule.outputs[0]) |
Fumitoshi Ukai | b79b290 | 2015-07-16 16:40:09 +0900 | [diff] [blame] | 569 | var phonys []string |
| 570 | for t := range db.phony { |
| 571 | phonys = append(phonys, t) |
| 572 | } |
| 573 | sort.Strings(phonys) |
| 574 | targets = append(targets, phonys...) |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 575 | } |
| 576 | |
Fumitoshi Ukai | d81f9b9 | 2015-07-21 17:07:08 +0900 | [diff] [blame] | 577 | if StatsFlag { |
| 578 | logStats("%d variables", len(db.vars)) |
| 579 | logStats("%d explicit rules", len(db.rules)) |
| 580 | logStats("%d implicit rules", db.implicitRules.size()) |
| 581 | logStats("%d suffix rules", len(db.suffixRules)) |
Fumitoshi Ukai | 0547db6 | 2015-07-29 16:20:59 +0900 | [diff] [blame] | 582 | logStats("%d dirs %d files", fsCache.dirs(), fsCache.files()) |
Fumitoshi Ukai | d81f9b9 | 2015-07-21 17:07:08 +0900 | [diff] [blame] | 583 | } |
Shinichiro Hamaji | 17a8a6e | 2015-04-20 21:44:42 +0900 | [diff] [blame] | 584 | |
| 585 | var nodes []*DepNode |
| 586 | for _, target := range targets { |
| 587 | db.trace = []string{target} |
| 588 | n, err := db.buildPlan(target, "", make(Vars)) |
| 589 | if err != nil { |
| 590 | return nil, err |
| 591 | } |
| 592 | nodes = append(nodes, n) |
| 593 | } |
| 594 | db.reportStats() |
| 595 | return nodes, nil |
| 596 | } |