1 // Copyright 2011 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 #include "graph.h"
16
17 #include <algorithm>
18 #include <assert.h>
19 #include <stdio.h>
20
21 #include "build_log.h"
22 #include "debug_flags.h"
23 #include "depfile_parser.h"
24 #include "deps_log.h"
25 #include "disk_interface.h"
26 #include "manifest_parser.h"
27 #include "metrics.h"
28 #include "state.h"
29 #include "util.h"
30
31 using namespace std;
32
Stat(DiskInterface * disk_interface,string * err)33 bool Node::Stat(DiskInterface* disk_interface, string* err) {
34 return (mtime_ = disk_interface->Stat(path_, err)) != -1;
35 }
36
RecomputeDirty(Node * node,string * err)37 bool DependencyScan::RecomputeDirty(Node* node, string* err) {
38 vector<Node*> stack;
39 return RecomputeDirty(node, &stack, err);
40 }
41
RecomputeDirty(Node * node,vector<Node * > * stack,string * err)42 bool DependencyScan::RecomputeDirty(Node* node, vector<Node*>* stack,
43 string* err) {
44 Edge* edge = node->in_edge();
45 if (!edge) {
46 // If we already visited this leaf node then we are done.
47 if (node->status_known())
48 return true;
49 // This node has no in-edge; it is dirty if it is missing.
50 if (!node->StatIfNecessary(disk_interface_, err))
51 return false;
52 if (!node->exists())
53 EXPLAIN("%s has no in-edge and is missing", node->path().c_str());
54 node->set_dirty(!node->exists());
55 return true;
56 }
57
58 // If we already finished this edge then we are done.
59 if (edge->mark_ == Edge::VisitDone)
60 return true;
61
62 // If we encountered this edge earlier in the call stack we have a cycle.
63 if (!VerifyDAG(node, stack, err))
64 return false;
65
66 // Mark the edge temporarily while in the call stack.
67 edge->mark_ = Edge::VisitInStack;
68 stack->push_back(node);
69
70 bool dirty = false;
71 edge->outputs_ready_ = true;
72 edge->deps_missing_ = false;
73
74 if (!edge->deps_loaded_) {
75 // This is our first encounter with this edge.
76 // If there is a pending dyndep file, visit it now:
77 // * If the dyndep file is ready then load it now to get any
78 // additional inputs and outputs for this and other edges.
79 // Once the dyndep file is loaded it will no longer be pending
80 // if any other edges encounter it, but they will already have
81 // been updated.
82 // * If the dyndep file is not ready then since is known to be an
83 // input to this edge, the edge will not be considered ready below.
84 // Later during the build the dyndep file will become ready and be
85 // loaded to update this edge before it can possibly be scheduled.
86 if (edge->dyndep_ && edge->dyndep_->dyndep_pending()) {
87 if (!RecomputeDirty(edge->dyndep_, stack, err))
88 return false;
89
90 if (!edge->dyndep_->in_edge() ||
91 edge->dyndep_->in_edge()->outputs_ready()) {
92 // The dyndep file is ready, so load it now.
93 if (!LoadDyndeps(edge->dyndep_, err))
94 return false;
95 }
96 }
97 }
98
99 // Load output mtimes so we can compare them to the most recent input below.
100 for (vector<Node*>::iterator o = edge->outputs_.begin();
101 o != edge->outputs_.end(); ++o) {
102 if (!(*o)->StatIfNecessary(disk_interface_, err))
103 return false;
104 }
105
106 if (!edge->deps_loaded_) {
107 // This is our first encounter with this edge. Load discovered deps.
108 edge->deps_loaded_ = true;
109 if (!dep_loader_.LoadDeps(edge, err)) {
110 if (!err->empty())
111 return false;
112 // Failed to load dependency info: rebuild to regenerate it.
113 // LoadDeps() did EXPLAIN() already, no need to do it here.
114 dirty = edge->deps_missing_ = true;
115 }
116 }
117
118 // Visit all inputs; we're dirty if any of the inputs are dirty.
119 Node* most_recent_input = NULL;
120 for (vector<Node*>::iterator i = edge->inputs_.begin();
121 i != edge->inputs_.end(); ++i) {
122 // Visit this input.
123 if (!RecomputeDirty(*i, stack, err))
124 return false;
125
126 // If an input is not ready, neither are our outputs.
127 if (Edge* in_edge = (*i)->in_edge()) {
128 if (!in_edge->outputs_ready_)
129 edge->outputs_ready_ = false;
130 }
131
132 if (!edge->is_order_only(i - edge->inputs_.begin())) {
133 // If a regular input is dirty (or missing), we're dirty.
134 // Otherwise consider mtime.
135 if ((*i)->dirty()) {
136 EXPLAIN("%s is dirty", (*i)->path().c_str());
137 dirty = true;
138 } else {
139 if (!most_recent_input || (*i)->mtime() > most_recent_input->mtime()) {
140 most_recent_input = *i;
141 }
142 }
143 }
144 }
145
146 // We may also be dirty due to output state: missing outputs, out of
147 // date outputs, etc. Visit all outputs and determine whether they're dirty.
148 if (!dirty)
149 if (!RecomputeOutputsDirty(edge, most_recent_input, &dirty, err))
150 return false;
151
152 // Finally, visit each output and update their dirty state if necessary.
153 for (vector<Node*>::iterator o = edge->outputs_.begin();
154 o != edge->outputs_.end(); ++o) {
155 if (dirty)
156 (*o)->MarkDirty();
157 }
158
159 // If an edge is dirty, its outputs are normally not ready. (It's
160 // possible to be clean but still not be ready in the presence of
161 // order-only inputs.)
162 // But phony edges with no inputs have nothing to do, so are always
163 // ready.
164 if (dirty && !(edge->is_phony() && edge->inputs_.empty()))
165 edge->outputs_ready_ = false;
166
167 // Mark the edge as finished during this walk now that it will no longer
168 // be in the call stack.
169 edge->mark_ = Edge::VisitDone;
170 assert(stack->back() == node);
171 stack->pop_back();
172
173 return true;
174 }
175
VerifyDAG(Node * node,vector<Node * > * stack,string * err)176 bool DependencyScan::VerifyDAG(Node* node, vector<Node*>* stack, string* err) {
177 Edge* edge = node->in_edge();
178 assert(edge != NULL);
179
180 // If we have no temporary mark on the edge then we do not yet have a cycle.
181 if (edge->mark_ != Edge::VisitInStack)
182 return true;
183
184 // We have this edge earlier in the call stack. Find it.
185 vector<Node*>::iterator start = stack->begin();
186 while (start != stack->end() && (*start)->in_edge() != edge)
187 ++start;
188 assert(start != stack->end());
189
190 // Make the cycle clear by reporting its start as the node at its end
191 // instead of some other output of the starting edge. For example,
192 // running 'ninja b' on
193 // build a b: cat c
194 // build c: cat a
195 // should report a -> c -> a instead of b -> c -> a.
196 *start = node;
197
198 // Construct the error message rejecting the cycle.
199 *err = "dependency cycle: ";
200 for (vector<Node*>::const_iterator i = start; i != stack->end(); ++i) {
201 err->append((*i)->path());
202 err->append(" -> ");
203 }
204 err->append((*start)->path());
205
206 if ((start + 1) == stack->end() && edge->maybe_phonycycle_diagnostic()) {
207 // The manifest parser would have filtered out the self-referencing
208 // input if it were not configured to allow the error.
209 err->append(" [-w phonycycle=err]");
210 }
211
212 return false;
213 }
214
RecomputeOutputsDirty(Edge * edge,Node * most_recent_input,bool * outputs_dirty,string * err)215 bool DependencyScan::RecomputeOutputsDirty(Edge* edge, Node* most_recent_input,
216 bool* outputs_dirty, string* err) {
217 string command = edge->EvaluateCommand(/*incl_rsp_file=*/true);
218 for (vector<Node*>::iterator o = edge->outputs_.begin();
219 o != edge->outputs_.end(); ++o) {
220 if (RecomputeOutputDirty(edge, most_recent_input, command, *o)) {
221 *outputs_dirty = true;
222 return true;
223 }
224 }
225 return true;
226 }
227
RecomputeOutputDirty(const Edge * edge,const Node * most_recent_input,const string & command,Node * output)228 bool DependencyScan::RecomputeOutputDirty(const Edge* edge,
229 const Node* most_recent_input,
230 const string& command,
231 Node* output) {
232 if (edge->is_phony()) {
233 // Phony edges don't write any output. Outputs are only dirty if
234 // there are no inputs and we're missing the output.
235 if (edge->inputs_.empty() && !output->exists()) {
236 EXPLAIN("output %s of phony edge with no inputs doesn't exist",
237 output->path().c_str());
238 return true;
239 }
240 return false;
241 }
242
243 BuildLog::LogEntry* entry = 0;
244
245 // Dirty if we're missing the output.
246 if (!output->exists()) {
247 EXPLAIN("output %s doesn't exist", output->path().c_str());
248 return true;
249 }
250
251 // Dirty if the output is older than the input.
252 if (most_recent_input && output->mtime() < most_recent_input->mtime()) {
253 TimeStamp output_mtime = output->mtime();
254
255 // If this is a restat rule, we may have cleaned the output with a restat
256 // rule in a previous run and stored the most recent input mtime in the
257 // build log. Use that mtime instead, so that the file will only be
258 // considered dirty if an input was modified since the previous run.
259 bool used_restat = false;
260 if (edge->GetBindingBool("restat") && build_log() &&
261 (entry = build_log()->LookupByOutput(output->path()))) {
262 output_mtime = entry->mtime;
263 used_restat = true;
264 }
265
266 if (output_mtime < most_recent_input->mtime()) {
267 EXPLAIN("%soutput %s older than most recent input %s "
268 "(%" PRId64 " vs %" PRId64 ")",
269 used_restat ? "restat of " : "", output->path().c_str(),
270 most_recent_input->path().c_str(),
271 output_mtime, most_recent_input->mtime());
272 return true;
273 }
274 }
275
276 if (build_log()) {
277 bool generator = edge->GetBindingBool("generator");
278 if (entry || (entry = build_log()->LookupByOutput(output->path()))) {
279 if (!generator &&
280 BuildLog::LogEntry::HashCommand(command) != entry->command_hash) {
281 // May also be dirty due to the command changing since the last build.
282 // But if this is a generator rule, the command changing does not make us
283 // dirty.
284 EXPLAIN("command line changed for %s", output->path().c_str());
285 return true;
286 }
287 if (most_recent_input && entry->mtime < most_recent_input->mtime()) {
288 // May also be dirty due to the mtime in the log being older than the
289 // mtime of the most recent input. This can occur even when the mtime
290 // on disk is newer if a previous run wrote to the output file but
291 // exited with an error or was interrupted.
292 EXPLAIN("recorded mtime of %s older than most recent input %s (%" PRId64 " vs %" PRId64 ")",
293 output->path().c_str(), most_recent_input->path().c_str(),
294 entry->mtime, most_recent_input->mtime());
295 return true;
296 }
297 }
298 if (!entry && !generator) {
299 EXPLAIN("command line not found in log for %s", output->path().c_str());
300 return true;
301 }
302 }
303
304 return false;
305 }
306
LoadDyndeps(Node * node,string * err) const307 bool DependencyScan::LoadDyndeps(Node* node, string* err) const {
308 return dyndep_loader_.LoadDyndeps(node, err);
309 }
310
LoadDyndeps(Node * node,DyndepFile * ddf,string * err) const311 bool DependencyScan::LoadDyndeps(Node* node, DyndepFile* ddf,
312 string* err) const {
313 return dyndep_loader_.LoadDyndeps(node, ddf, err);
314 }
315
AllInputsReady() const316 bool Edge::AllInputsReady() const {
317 for (vector<Node*>::const_iterator i = inputs_.begin();
318 i != inputs_.end(); ++i) {
319 if ((*i)->in_edge() && !(*i)->in_edge()->outputs_ready())
320 return false;
321 }
322 return true;
323 }
324
325 /// An Env for an Edge, providing $in and $out.
326 struct EdgeEnv : public Env {
327 enum EscapeKind { kShellEscape, kDoNotEscape };
328
EdgeEnvEdgeEnv329 EdgeEnv(const Edge* const edge, const EscapeKind escape)
330 : edge_(edge), escape_in_out_(escape), recursive_(false) {}
331 virtual string LookupVariable(const string& var);
332
333 /// Given a span of Nodes, construct a list of paths suitable for a command
334 /// line.
335 std::string MakePathList(const Node* const* span, size_t size, char sep) const;
336
337 private:
338 vector<string> lookups_;
339 const Edge* const edge_;
340 EscapeKind escape_in_out_;
341 bool recursive_;
342 };
343
LookupVariable(const string & var)344 string EdgeEnv::LookupVariable(const string& var) {
345 if (var == "in" || var == "in_newline") {
346 int explicit_deps_count = edge_->inputs_.size() - edge_->implicit_deps_ -
347 edge_->order_only_deps_;
348 #if __cplusplus >= 201103L
349 return MakePathList(edge_->inputs_.data(), explicit_deps_count,
350 #else
351 return MakePathList(&edge_->inputs_[0], explicit_deps_count,
352 #endif
353 var == "in" ? ' ' : '\n');
354 } else if (var == "out") {
355 int explicit_outs_count = edge_->outputs_.size() - edge_->implicit_outs_;
356 return MakePathList(&edge_->outputs_[0], explicit_outs_count, ' ');
357 }
358
359 if (recursive_) {
360 vector<string>::const_iterator it;
361 if ((it = find(lookups_.begin(), lookups_.end(), var)) != lookups_.end()) {
362 string cycle;
363 for (; it != lookups_.end(); ++it)
364 cycle.append(*it + " -> ");
365 cycle.append(var);
366 Fatal(("cycle in rule variables: " + cycle).c_str());
367 }
368 }
369
370 // See notes on BindingEnv::LookupWithFallback.
371 const EvalString* eval = edge_->rule_->GetBinding(var);
372 if (recursive_ && eval)
373 lookups_.push_back(var);
374
375 // In practice, variables defined on rules never use another rule variable.
376 // For performance, only start checking for cycles after the first lookup.
377 recursive_ = true;
378 return edge_->env_->LookupWithFallback(var, eval, this);
379 }
380
MakePathList(const Node * const * const span,const size_t size,const char sep) const381 std::string EdgeEnv::MakePathList(const Node* const* const span,
382 const size_t size, const char sep) const {
383 string result;
384 for (const Node* const* i = span; i != span + size; ++i) {
385 if (!result.empty())
386 result.push_back(sep);
387 const string& path = (*i)->PathDecanonicalized();
388 if (escape_in_out_ == kShellEscape) {
389 #ifdef _WIN32
390 GetWin32EscapedString(path, &result);
391 #else
392 GetShellEscapedString(path, &result);
393 #endif
394 } else {
395 result.append(path);
396 }
397 }
398 return result;
399 }
400
EvaluateCommand(const bool incl_rsp_file) const401 std::string Edge::EvaluateCommand(const bool incl_rsp_file) const {
402 string command = GetBinding("command");
403 if (incl_rsp_file) {
404 string rspfile_content = GetBinding("rspfile_content");
405 if (!rspfile_content.empty())
406 command += ";rspfile=" + rspfile_content;
407 }
408 return command;
409 }
410
GetBinding(const std::string & key) const411 std::string Edge::GetBinding(const std::string& key) const {
412 EdgeEnv env(this, EdgeEnv::kShellEscape);
413 return env.LookupVariable(key);
414 }
415
GetBindingBool(const string & key) const416 bool Edge::GetBindingBool(const string& key) const {
417 return !GetBinding(key).empty();
418 }
419
GetUnescapedDepfile() const420 string Edge::GetUnescapedDepfile() const {
421 EdgeEnv env(this, EdgeEnv::kDoNotEscape);
422 return env.LookupVariable("depfile");
423 }
424
GetUnescapedDyndep() const425 string Edge::GetUnescapedDyndep() const {
426 EdgeEnv env(this, EdgeEnv::kDoNotEscape);
427 return env.LookupVariable("dyndep");
428 }
429
GetUnescapedRspfile() const430 std::string Edge::GetUnescapedRspfile() const {
431 EdgeEnv env(this, EdgeEnv::kDoNotEscape);
432 return env.LookupVariable("rspfile");
433 }
434
Dump(const char * prefix) const435 void Edge::Dump(const char* prefix) const {
436 printf("%s[ ", prefix);
437 for (vector<Node*>::const_iterator i = inputs_.begin();
438 i != inputs_.end() && *i != NULL; ++i) {
439 printf("%s ", (*i)->path().c_str());
440 }
441 printf("--%s-> ", rule_->name().c_str());
442 for (vector<Node*>::const_iterator i = outputs_.begin();
443 i != outputs_.end() && *i != NULL; ++i) {
444 printf("%s ", (*i)->path().c_str());
445 }
446 if (pool_) {
447 if (!pool_->name().empty()) {
448 printf("(in pool '%s')", pool_->name().c_str());
449 }
450 } else {
451 printf("(null pool?)");
452 }
453 printf("] 0x%p\n", this);
454 }
455
is_phony() const456 bool Edge::is_phony() const {
457 return rule_ == &State::kPhonyRule;
458 }
459
use_console() const460 bool Edge::use_console() const {
461 return pool() == &State::kConsolePool;
462 }
463
maybe_phonycycle_diagnostic() const464 bool Edge::maybe_phonycycle_diagnostic() const {
465 // CMake 2.8.12.x and 3.0.x produced self-referencing phony rules
466 // of the form "build a: phony ... a ...". Restrict our
467 // "phonycycle" diagnostic option to the form it used.
468 return is_phony() && outputs_.size() == 1 && implicit_outs_ == 0 &&
469 implicit_deps_ == 0;
470 }
471
472 // static
PathDecanonicalized(const string & path,uint64_t slash_bits)473 string Node::PathDecanonicalized(const string& path, uint64_t slash_bits) {
474 string result = path;
475 #ifdef _WIN32
476 uint64_t mask = 1;
477 for (char* c = &result[0]; (c = strchr(c, '/')) != NULL;) {
478 if (slash_bits & mask)
479 *c = '\\';
480 c++;
481 mask <<= 1;
482 }
483 #endif
484 return result;
485 }
486
Dump(const char * prefix) const487 void Node::Dump(const char* prefix) const {
488 printf("%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ",
489 prefix, path().c_str(), this,
490 mtime(), mtime() ? "" : " (:missing)",
491 dirty() ? " dirty" : " clean");
492 if (in_edge()) {
493 in_edge()->Dump("in-edge: ");
494 } else {
495 printf("no in-edge\n");
496 }
497 printf(" out edges:\n");
498 for (vector<Edge*>::const_iterator e = out_edges().begin();
499 e != out_edges().end() && *e != NULL; ++e) {
500 (*e)->Dump(" +- ");
501 }
502 }
503
LoadDeps(Edge * edge,string * err)504 bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) {
505 string deps_type = edge->GetBinding("deps");
506 if (!deps_type.empty())
507 return LoadDepsFromLog(edge, err);
508
509 string depfile = edge->GetUnescapedDepfile();
510 if (!depfile.empty())
511 return LoadDepFile(edge, depfile, err);
512
513 // No deps to load.
514 return true;
515 }
516
517 struct matches {
matchesmatches518 matches(std::vector<StringPiece>::iterator i) : i_(i) {}
519
operator ()matches520 bool operator()(const Node* node) const {
521 StringPiece opath = StringPiece(node->path());
522 return *i_ == opath;
523 }
524
525 std::vector<StringPiece>::iterator i_;
526 };
527
LoadDepFile(Edge * edge,const string & path,string * err)528 bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path,
529 string* err) {
530 METRIC_RECORD("depfile load");
531 // Read depfile content. Treat a missing depfile as empty.
532 string content;
533 switch (disk_interface_->ReadFile(path, &content, err)) {
534 case DiskInterface::Okay:
535 break;
536 case DiskInterface::NotFound:
537 err->clear();
538 break;
539 case DiskInterface::OtherError:
540 *err = "loading '" + path + "': " + *err;
541 return false;
542 }
543 // On a missing depfile: return false and empty *err.
544 if (content.empty()) {
545 EXPLAIN("depfile '%s' is missing", path.c_str());
546 return false;
547 }
548
549 DepfileParser depfile(depfile_parser_options_
550 ? *depfile_parser_options_
551 : DepfileParserOptions());
552 string depfile_err;
553 if (!depfile.Parse(&content, &depfile_err)) {
554 *err = path + ": " + depfile_err;
555 return false;
556 }
557
558 if (depfile.outs_.empty()) {
559 *err = path + ": no outputs declared";
560 return false;
561 }
562
563 uint64_t unused;
564 std::vector<StringPiece>::iterator primary_out = depfile.outs_.begin();
565 if (!CanonicalizePath(const_cast<char*>(primary_out->str_),
566 &primary_out->len_, &unused, err)) {
567 *err = path + ": " + *err;
568 return false;
569 }
570
571 // Check that this depfile matches the edge's output, if not return false to
572 // mark the edge as dirty.
573 Node* first_output = edge->outputs_[0];
574 StringPiece opath = StringPiece(first_output->path());
575 if (opath != *primary_out) {
576 EXPLAIN("expected depfile '%s' to mention '%s', got '%s'", path.c_str(),
577 first_output->path().c_str(), primary_out->AsString().c_str());
578 return false;
579 }
580
581 // Ensure that all mentioned outputs are outputs of the edge.
582 for (std::vector<StringPiece>::iterator o = depfile.outs_.begin();
583 o != depfile.outs_.end(); ++o) {
584 matches m(o);
585 if (std::find_if(edge->outputs_.begin(), edge->outputs_.end(), m) == edge->outputs_.end()) {
586 *err = path + ": depfile mentions '" + o->AsString() + "' as an output, but no such output was declared";
587 return false;
588 }
589 }
590
591 // Preallocate space in edge->inputs_ to be filled in below.
592 vector<Node*>::iterator implicit_dep =
593 PreallocateSpace(edge, depfile.ins_.size());
594
595 // Add all its in-edges.
596 for (vector<StringPiece>::iterator i = depfile.ins_.begin();
597 i != depfile.ins_.end(); ++i, ++implicit_dep) {
598 uint64_t slash_bits;
599 if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits,
600 err))
601 return false;
602
603 Node* node = state_->GetNode(*i, slash_bits);
604 *implicit_dep = node;
605 node->AddOutEdge(edge);
606 CreatePhonyInEdge(node);
607 }
608
609 return true;
610 }
611
LoadDepsFromLog(Edge * edge,string * err)612 bool ImplicitDepLoader::LoadDepsFromLog(Edge* edge, string* err) {
613 // NOTE: deps are only supported for single-target edges.
614 Node* output = edge->outputs_[0];
615 DepsLog::Deps* deps = deps_log_ ? deps_log_->GetDeps(output) : NULL;
616 if (!deps) {
617 EXPLAIN("deps for '%s' are missing", output->path().c_str());
618 return false;
619 }
620
621 // Deps are invalid if the output is newer than the deps.
622 if (output->mtime() > deps->mtime) {
623 EXPLAIN("stored deps info out of date for '%s' (%" PRId64 " vs %" PRId64 ")",
624 output->path().c_str(), deps->mtime, output->mtime());
625 return false;
626 }
627
628 vector<Node*>::iterator implicit_dep =
629 PreallocateSpace(edge, deps->node_count);
630 for (int i = 0; i < deps->node_count; ++i, ++implicit_dep) {
631 Node* node = deps->nodes[i];
632 *implicit_dep = node;
633 node->AddOutEdge(edge);
634 CreatePhonyInEdge(node);
635 }
636 return true;
637 }
638
PreallocateSpace(Edge * edge,int count)639 vector<Node*>::iterator ImplicitDepLoader::PreallocateSpace(Edge* edge,
640 int count) {
641 edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_,
642 (size_t)count, 0);
643 edge->implicit_deps_ += count;
644 return edge->inputs_.end() - edge->order_only_deps_ - count;
645 }
646
CreatePhonyInEdge(Node * node)647 void ImplicitDepLoader::CreatePhonyInEdge(Node* node) {
648 if (node->in_edge())
649 return;
650
651 Edge* phony_edge = state_->AddEdge(&State::kPhonyRule);
652 node->set_in_edge(phony_edge);
653 phony_edge->outputs_.push_back(node);
654
655 // RecomputeDirty might not be called for phony_edge if a previous call
656 // to RecomputeDirty had caused the file to be stat'ed. Because previous
657 // invocations of RecomputeDirty would have seen this node without an
658 // input edge (and therefore ready), we have to set outputs_ready_ to true
659 // to avoid a potential stuck build. If we do call RecomputeDirty for
660 // this node, it will simply set outputs_ready_ to the correct value.
661 phony_edge->outputs_ready_ = true;
662 }
663