Ninja
graph.cc
Go to the documentation of this file.
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 <assert.h>
18 #include <stdio.h>
19 
20 #include "build_log.h"
21 #include "debug_flags.h"
22 #include "depfile_parser.h"
23 #include "deps_log.h"
24 #include "disk_interface.h"
25 #include "manifest_parser.h"
26 #include "metrics.h"
27 #include "state.h"
28 #include "util.h"
29 
30 bool Node::Stat(DiskInterface* disk_interface, string* err) {
31  METRIC_RECORD("node stat");
32  return (mtime_ = disk_interface->Stat(path_, err)) != -1;
33 }
34 
35 bool DependencyScan::RecomputeDirty(Edge* edge, string* err) {
36  bool dirty = false;
37  edge->outputs_ready_ = true;
38  edge->deps_missing_ = false;
39 
40  // RecomputeDirty() recursively walks the graph following the input nodes
41  // of |edge| and the in_edges of these nodes. It uses the stat state of each
42  // node to mark nodes as visited and doesn't traverse across nodes that have
43  // been visited already. To make sure that every edge is visited only once
44  // (important because an edge's deps are loaded every time it's visited), mark
45  // all outputs of |edge| visited as a first step. This ensures that edges
46  // with multiple inputs and outputs are visited only once, even in cyclic
47  // graphs.
48  for (vector<Node*>::iterator o = edge->outputs_.begin();
49  o != edge->outputs_.end(); ++o) {
50  if (!(*o)->StatIfNecessary(disk_interface_, err))
51  return false;
52  }
53 
54  if (!dep_loader_.LoadDeps(edge, err)) {
55  if (!err->empty())
56  return false;
57  // Failed to load dependency info: rebuild to regenerate it.
58  // LoadDeps() did EXPLAIN() already, no need to do it here.
59  dirty = edge->deps_missing_ = true;
60  }
61 
62  // Visit all inputs; we're dirty if any of the inputs are dirty.
63  Node* most_recent_input = NULL;
64  for (vector<Node*>::iterator i = edge->inputs_.begin();
65  i != edge->inputs_.end(); ++i) {
66  if (!(*i)->status_known()) {
67  if (!(*i)->StatIfNecessary(disk_interface_, err))
68  return false;
69  if (Edge* in_edge = (*i)->in_edge()) {
70  if (!RecomputeDirty(in_edge, err))
71  return false;
72  } else {
73  // This input has no in-edge; it is dirty if it is missing.
74  if (!(*i)->exists())
75  EXPLAIN("%s has no in-edge and is missing", (*i)->path().c_str());
76  (*i)->set_dirty(!(*i)->exists());
77  }
78  }
79 
80  // If an input is not ready, neither are our outputs.
81  if (Edge* in_edge = (*i)->in_edge()) {
82  if (!in_edge->outputs_ready_)
83  edge->outputs_ready_ = false;
84  }
85 
86  if (!edge->is_order_only(i - edge->inputs_.begin())) {
87  // If a regular input is dirty (or missing), we're dirty.
88  // Otherwise consider mtime.
89  if ((*i)->dirty()) {
90  EXPLAIN("%s is dirty", (*i)->path().c_str());
91  dirty = true;
92  } else {
93  if (!most_recent_input || (*i)->mtime() > most_recent_input->mtime()) {
94  most_recent_input = *i;
95  }
96  }
97  }
98  }
99 
100  // We may also be dirty due to output state: missing outputs, out of
101  // date outputs, etc. Visit all outputs and determine whether they're dirty.
102  if (!dirty)
103  if (!RecomputeOutputsDirty(edge, most_recent_input, &dirty, err))
104  return false;
105 
106  // Finally, visit each output and update their dirty state if necessary.
107  for (vector<Node*>::iterator o = edge->outputs_.begin();
108  o != edge->outputs_.end(); ++o) {
109  if (dirty)
110  (*o)->MarkDirty();
111  }
112 
113  // If an edge is dirty, its outputs are normally not ready. (It's
114  // possible to be clean but still not be ready in the presence of
115  // order-only inputs.)
116  // But phony edges with no inputs have nothing to do, so are always
117  // ready.
118  if (dirty && !(edge->is_phony() && edge->inputs_.empty()))
119  edge->outputs_ready_ = false;
120 
121  return true;
122 }
123 
124 bool DependencyScan::RecomputeOutputsDirty(Edge* edge, Node* most_recent_input,
125  bool* outputs_dirty, string* err) {
126  string command = edge->EvaluateCommand(/*incl_rsp_file=*/true);
127  for (vector<Node*>::iterator o = edge->outputs_.begin();
128  o != edge->outputs_.end(); ++o) {
129  if (!(*o)->StatIfNecessary(disk_interface_, err))
130  return false;
131  if (RecomputeOutputDirty(edge, most_recent_input, command, *o)) {
132  *outputs_dirty = true;
133  return true;
134  }
135  }
136  return true;
137 }
138 
140  Node* most_recent_input,
141  const string& command,
142  Node* output) {
143  if (edge->is_phony()) {
144  // Phony edges don't write any output. Outputs are only dirty if
145  // there are no inputs and we're missing the output.
146  if (edge->inputs_.empty() && !output->exists()) {
147  EXPLAIN("output %s of phony edge with no inputs doesn't exist",
148  output->path().c_str());
149  return true;
150  }
151  return false;
152  }
153 
154  BuildLog::LogEntry* entry = 0;
155 
156  // Dirty if we're missing the output.
157  if (!output->exists()) {
158  EXPLAIN("output %s doesn't exist", output->path().c_str());
159  return true;
160  }
161 
162  // Dirty if the output is older than the input.
163  if (most_recent_input && output->mtime() < most_recent_input->mtime()) {
164  TimeStamp output_mtime = output->mtime();
165 
166  // If this is a restat rule, we may have cleaned the output with a restat
167  // rule in a previous run and stored the most recent input mtime in the
168  // build log. Use that mtime instead, so that the file will only be
169  // considered dirty if an input was modified since the previous run.
170  bool used_restat = false;
171  if (edge->GetBindingBool("restat") && build_log() &&
172  (entry = build_log()->LookupByOutput(output->path()))) {
173  output_mtime = entry->restat_mtime;
174  used_restat = true;
175  }
176 
177  if (output_mtime < most_recent_input->mtime()) {
178  EXPLAIN("%soutput %s older than most recent input %s "
179  "(%d vs %d)",
180  used_restat ? "restat of " : "", output->path().c_str(),
181  most_recent_input->path().c_str(),
182  output_mtime, most_recent_input->mtime());
183  return true;
184  }
185  }
186 
187  // May also be dirty due to the command changing since the last build.
188  // But if this is a generator rule, the command changing does not make us
189  // dirty.
190  if (!edge->GetBindingBool("generator") && build_log()) {
191  if (entry || (entry = build_log()->LookupByOutput(output->path()))) {
192  if (BuildLog::LogEntry::HashCommand(command) != entry->command_hash) {
193  EXPLAIN("command line changed for %s", output->path().c_str());
194  return true;
195  }
196  }
197  if (!entry) {
198  EXPLAIN("command line not found in log for %s", output->path().c_str());
199  return true;
200  }
201  }
202 
203  return false;
204 }
205 
206 bool Edge::AllInputsReady() const {
207  for (vector<Node*>::const_iterator i = inputs_.begin();
208  i != inputs_.end(); ++i) {
209  if ((*i)->in_edge() && !(*i)->in_edge()->outputs_ready())
210  return false;
211  }
212  return true;
213 }
214 
215 /// An Env for an Edge, providing $in and $out.
216 struct EdgeEnv : public Env {
218 
219  EdgeEnv(Edge* edge, EscapeKind escape)
220  : edge_(edge), escape_in_out_(escape), recursive_(false) {}
221  virtual string LookupVariable(const string& var);
222 
223  /// Given a span of Nodes, construct a list of paths suitable for a command
224  /// line.
225  string MakePathList(vector<Node*>::iterator begin,
226  vector<Node*>::iterator end,
227  char sep);
228 
229  private:
230  vector<string> lookups_;
234 };
235 
236 string EdgeEnv::LookupVariable(const string& var) {
237  if (var == "in" || var == "in_newline") {
238  int explicit_deps_count = edge_->inputs_.size() - edge_->implicit_deps_ -
240  return MakePathList(edge_->inputs_.begin(),
241  edge_->inputs_.begin() + explicit_deps_count,
242  var == "in" ? ' ' : '\n');
243  } else if (var == "out") {
244  return MakePathList(edge_->outputs_.begin(),
245  edge_->outputs_.end(),
246  ' ');
247  }
248 
249  if (recursive_) {
250  vector<string>::const_iterator it;
251  if ((it = find(lookups_.begin(), lookups_.end(), var)) != lookups_.end()) {
252  string cycle;
253  for (; it != lookups_.end(); ++it)
254  cycle.append(*it + " -> ");
255  cycle.append(var);
256  Fatal(("cycle in rule variables: " + cycle).c_str());
257  }
258  }
259 
260  // See notes on BindingEnv::LookupWithFallback.
261  const EvalString* eval = edge_->rule_->GetBinding(var);
262  if (recursive_ && eval)
263  lookups_.push_back(var);
264 
265  // In practice, variables defined on rules never use another rule variable.
266  // For performance, only start checking for cycles after the first lookup.
267  recursive_ = true;
268  return edge_->env_->LookupWithFallback(var, eval, this);
269 }
270 
271 string EdgeEnv::MakePathList(vector<Node*>::iterator begin,
272  vector<Node*>::iterator end,
273  char sep) {
274  string result;
275  for (vector<Node*>::iterator i = begin; i != end; ++i) {
276  if (!result.empty())
277  result.push_back(sep);
278  const string& path = (*i)->PathDecanonicalized();
279  if (escape_in_out_ == kShellEscape) {
280 #if _WIN32
281  GetWin32EscapedString(path, &result);
282 #else
283  GetShellEscapedString(path, &result);
284 #endif
285  } else {
286  result.append(path);
287  }
288  }
289  return result;
290 }
291 
292 string Edge::EvaluateCommand(bool incl_rsp_file) {
293  string command = GetBinding("command");
294  if (incl_rsp_file) {
295  string rspfile_content = GetBinding("rspfile_content");
296  if (!rspfile_content.empty())
297  command += ";rspfile=" + rspfile_content;
298  }
299  return command;
300 }
301 
302 string Edge::GetBinding(const string& key) {
303  EdgeEnv env(this, EdgeEnv::kShellEscape);
304  return env.LookupVariable(key);
305 }
306 
307 bool Edge::GetBindingBool(const string& key) {
308  return !GetBinding(key).empty();
309 }
310 
312  EdgeEnv env(this, EdgeEnv::kDoNotEscape);
313  return env.LookupVariable("depfile");
314 }
315 
317  EdgeEnv env(this, EdgeEnv::kDoNotEscape);
318  return env.LookupVariable("rspfile");
319 }
320 
321 void Edge::Dump(const char* prefix) const {
322  printf("%s[ ", prefix);
323  for (vector<Node*>::const_iterator i = inputs_.begin();
324  i != inputs_.end() && *i != NULL; ++i) {
325  printf("%s ", (*i)->path().c_str());
326  }
327  printf("--%s-> ", rule_->name().c_str());
328  for (vector<Node*>::const_iterator i = outputs_.begin();
329  i != outputs_.end() && *i != NULL; ++i) {
330  printf("%s ", (*i)->path().c_str());
331  }
332  if (pool_) {
333  if (!pool_->name().empty()) {
334  printf("(in pool '%s')", pool_->name().c_str());
335  }
336  } else {
337  printf("(null pool?)");
338  }
339  printf("] 0x%p\n", this);
340 }
341 
342 bool Edge::is_phony() const {
343  return rule_ == &State::kPhonyRule;
344 }
345 
346 bool Edge::use_console() const {
347  return pool() == &State::kConsolePool;
348 }
349 
351  string result = path_;
352 #ifdef _WIN32
353  unsigned int mask = 1;
354  for (char* c = &result[0]; (c = strchr(c, '/')) != NULL;) {
355  if (slash_bits_ & mask)
356  *c = '\\';
357  c++;
358  mask <<= 1;
359  }
360 #endif
361  return result;
362 }
363 
364 void Node::Dump(const char* prefix) const {
365  printf("%s <%s 0x%p> mtime: %d%s, (:%s), ",
366  prefix, path().c_str(), this,
367  mtime(), mtime() ? "" : " (:missing)",
368  dirty() ? " dirty" : " clean");
369  if (in_edge()) {
370  in_edge()->Dump("in-edge: ");
371  } else {
372  printf("no in-edge\n");
373  }
374  printf(" out edges:\n");
375  for (vector<Edge*>::const_iterator e = out_edges().begin();
376  e != out_edges().end() && *e != NULL; ++e) {
377  (*e)->Dump(" +- ");
378  }
379 }
380 
381 bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) {
382  string deps_type = edge->GetBinding("deps");
383  if (!deps_type.empty())
384  return LoadDepsFromLog(edge, err);
385 
386  string depfile = edge->GetUnescapedDepfile();
387  if (!depfile.empty())
388  return LoadDepFile(edge, depfile, err);
389 
390  // No deps to load.
391  return true;
392 }
393 
394 bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path,
395  string* err) {
396  METRIC_RECORD("depfile load");
397  string content = disk_interface_->ReadFile(path, err);
398  if (!err->empty()) {
399  *err = "loading '" + path + "': " + *err;
400  return false;
401  }
402  // On a missing depfile: return false and empty *err.
403  if (content.empty()) {
404  EXPLAIN("depfile '%s' is missing", path.c_str());
405  return false;
406  }
407 
408  DepfileParser depfile;
409  string depfile_err;
410  if (!depfile.Parse(&content, &depfile_err)) {
411  *err = path + ": " + depfile_err;
412  return false;
413  }
414 
415  unsigned int unused;
416  if (!CanonicalizePath(const_cast<char*>(depfile.out_.str_),
417  &depfile.out_.len_, &unused, err))
418  return false;
419 
420  // Check that this depfile matches the edge's output, if not return false to
421  // mark the edge as dirty.
422  Node* first_output = edge->outputs_[0];
423  StringPiece opath = StringPiece(first_output->path());
424  if (opath != depfile.out_) {
425  EXPLAIN("expected depfile '%s' to mention '%s', got '%s'", path.c_str(),
426  first_output->path().c_str(), depfile.out_.AsString().c_str());
427  return false;
428  }
429 
430  // Preallocate space in edge->inputs_ to be filled in below.
431  vector<Node*>::iterator implicit_dep =
432  PreallocateSpace(edge, depfile.ins_.size());
433 
434  // Add all its in-edges.
435  for (vector<StringPiece>::iterator i = depfile.ins_.begin();
436  i != depfile.ins_.end(); ++i, ++implicit_dep) {
437  unsigned int slash_bits;
438  if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits,
439  err))
440  return false;
441 
442  Node* node = state_->GetNode(*i, slash_bits);
443  *implicit_dep = node;
444  node->AddOutEdge(edge);
445  CreatePhonyInEdge(node);
446  }
447 
448  return true;
449 }
450 
451 bool ImplicitDepLoader::LoadDepsFromLog(Edge* edge, string* err) {
452  // NOTE: deps are only supported for single-target edges.
453  Node* output = edge->outputs_[0];
454  DepsLog::Deps* deps = deps_log_->GetDeps(output);
455  if (!deps) {
456  EXPLAIN("deps for '%s' are missing", output->path().c_str());
457  return false;
458  }
459 
460  // Deps are invalid if the output is newer than the deps.
461  if (output->mtime() > deps->mtime) {
462  EXPLAIN("stored deps info out of date for '%s' (%d vs %d)",
463  output->path().c_str(), deps->mtime, output->mtime());
464  return false;
465  }
466 
467  vector<Node*>::iterator implicit_dep =
468  PreallocateSpace(edge, deps->node_count);
469  for (int i = 0; i < deps->node_count; ++i, ++implicit_dep) {
470  Node* node = deps->nodes[i];
471  *implicit_dep = node;
472  node->AddOutEdge(edge);
473  CreatePhonyInEdge(node);
474  }
475  return true;
476 }
477 
478 vector<Node*>::iterator ImplicitDepLoader::PreallocateSpace(Edge* edge,
479  int count) {
480  edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_,
481  (size_t)count, 0);
482  edge->implicit_deps_ += count;
483  return edge->inputs_.end() - edge->order_only_deps_ - count;
484 }
485 
487  if (node->in_edge())
488  return;
489 
490  Edge* phony_edge = state_->AddEdge(&State::kPhonyRule);
491  node->set_in_edge(phony_edge);
492  phony_edge->outputs_.push_back(node);
493 
494  // RecomputeDirty might not be called for phony_edge if a previous call
495  // to RecomputeDirty had caused the file to be stat'ed. Because previous
496  // invocations of RecomputeDirty would have seen this node without an
497  // input edge (and therefore ready), we have to set outputs_ready_ to true
498  // to avoid a potential stuck build. If we do call RecomputeDirty for
499  // this node, it will simply set outputs_ready_ to the correct value.
500  phony_edge->outputs_ready_ = true;
501 }
bool is_phony() const
Definition: graph.cc:342
An Env for an Edge, providing $in and $out.
Definition: graph.cc:216
void Dump(const char *prefix="") const
Definition: graph.cc:364
virtual string ReadFile(const string &path, string *err)=0
Read a file to a string. Fill in |err| on error.
string PathDecanonicalized() const
Get |path()| but use slash_bits to convert back to original slash styles.
Definition: graph.cc:350
const char * str_
Definition: string_piece.h:49
bool LoadDeps(Edge *edge, string *err)
Load implicit dependencies for edge.
Definition: graph.cc:381
int order_only_deps_
Definition: graph.h:170
TimeStamp restat_mtime
Definition: build_log.h:59
TimeStamp mtime() const
Definition: graph.h:77
int implicit_deps_
Definition: graph.h:169
Edge * edge_
Definition: graph.cc:231
bool RecomputeOutputDirty(Edge *edge, Node *most_recent_input, const string &command, Node *output)
Recompute whether a given single output should be marked dirty.
Definition: graph.cc:139
Parser for the dependency information emitted by gcc's -M flags.
void GetWin32EscapedString(const string &input, string *result)
Definition: util.cc:308
bool RecomputeDirty(Edge *edge, string *err)
Examine inputs, outputs, and command lines to judge whether an edge needs to be re-run, and update outputs_ready_ and each outputs' |dirty_| state accordingly.
Definition: graph.cc:35
StringPiece represents a slice of a string whose memory is managed externally.
Definition: string_piece.h:27
string GetUnescapedRspfile()
Like GetBinding("rspfile"), but without shell escaping.
Definition: graph.cc:316
Information about a node in the dependency graph: the file, whether it's dirty, mtime, etc.
Definition: graph.h:35
bool Parse(string *content, string *err)
Parse an input file.
void GetShellEscapedString(const string &input, string *result)
Appends |input| to |*result|, escaping according to the whims of either Bash, or Win32's CommandLineT...
Definition: util.cc:281
vector< Node * >::iterator PreallocateSpace(Edge *edge, int count)
Preallocate count spaces in the input array on edge, returning an iterator pointing at the first new ...
Definition: graph.cc:478
string MakePathList(vector< Node * >::iterator begin, vector< Node * >::iterator end, char sep)
Given a span of Nodes, construct a list of paths suitable for a command line.
Definition: graph.cc:271
virtual string LookupVariable(const string &var)
Definition: graph.cc:236
ImplicitDepLoader dep_loader_
Definition: graph.h:263
Interface for accessing the disk.
Edge * in_edge() const
Definition: graph.h:83
Node ** nodes
Definition: deps_log.h:83
bool GetBindingBool(const string &key)
Definition: graph.cc:307
string AsString() const
Convert the slice into a full-fledged std::string, copying the data into a new string.
Definition: string_piece.h:45
bool CanonicalizePath(string *path, unsigned int *slash_bits, string *err)
Canonicalize a path like "foo/../bar.h" into just "bar.h".
Definition: util.cc:91
void Dump(const char *prefix="") const
Definition: graph.cc:321
void AddOutEdge(Edge *edge)
Definition: graph.h:90
int TimeStamp
Definition: timestamp.h:22
Node * GetNode(StringPiece path, unsigned int slash_bits)
Definition: state.cc:103
Pool * pool() const
Definition: graph.h:157
An edge in the dependency graph; links between Nodes using Rules.
Definition: graph.h:124
string EvaluateCommand(bool incl_rsp_file=false)
Expand all variables in a command and return it as a string.
Definition: graph.cc:292
bool RecomputeOutputsDirty(Edge *edge, Node *most_recent_input, bool *dirty, string *err)
Recompute whether any output of the edge is dirty, if so sets |*dirty|.
Definition: graph.cc:124
bool is_order_only(size_t index)
Definition: graph.h:175
vector< string > lookups_
Definition: graph.cc:230
EscapeKind escape_in_out_
Definition: graph.cc:232
void MarkDirty()
Definition: graph.h:81
const EvalString * GetBinding(const string &key) const
Definition: eval_env.cc:57
EscapeKind
Definition: graph.cc:217
uint64_t command_hash
Definition: build_log.h:56
Edge * AddEdge(const Rule *rule)
Definition: state.cc:94
vector< Node * > inputs_
Definition: graph.h:150
bool outputs_ready_
Definition: graph.h:153
bool recursive_
Definition: graph.cc:233
BuildLog * build_log() const
Definition: graph.h:244
bool LoadDepFile(Edge *edge, const string &path, string *err)
Load implicit dependencies for edge from a depfile attribute.
Definition: graph.cc:394
DiskInterface * disk_interface_
Definition: graph.h:262
DepsLog * deps_log_
Definition: graph.h:220
BindingEnv * env_
Definition: graph.h:152
Deps * GetDeps(Node *node)
Definition: deps_log.cc:300
bool dirty() const
Definition: graph.h:79
bool exists() const
Definition: graph.h:65
int node_count
Definition: deps_log.h:82
bool use_console() const
Definition: graph.cc:346
static Pool kConsolePool
Definition: state.h:86
virtual TimeStamp Stat(const string &path, string *err) const =0
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
string LookupWithFallback(const string &var, const EvalString *eval, Env *env)
This is tricky.
Definition: eval_env.cc:81
void CreatePhonyInEdge(Node *node)
If we don't have a edge that generates this input already, create one; this makes us not abort if the...
Definition: graph.cc:486
vector< StringPiece > ins_
TimeStamp mtime_
Possible values of mtime_: -1: file hasn't been examined 0: we looked, and file doesn't exist >0: actu...
Definition: graph.h:105
#define METRIC_RECORD(name)
The primary interface to metrics.
Definition: metrics.h:85
const string & name() const
Definition: state.h:46
const string & path() const
Definition: graph.h:73
Pool * pool_
Definition: graph.h:149
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition: util.cc:55
string GetBinding(const string &key)
Returns the shell-escaped value of |key|.
Definition: graph.cc:302
static uint64_t HashCommand(StringPiece command)
Definition: build_log.cc:91
const Rule * rule_
Definition: graph.h:148
LogEntry * LookupByOutput(const string &path)
Lookup a previously-run command by its output path.
Definition: build_log.cc:341
bool LoadDepsFromLog(Edge *edge, string *err)
Load implicit dependencies for edge from the DepsLog.
Definition: graph.cc:451
unsigned int slash_bits_
Set bits starting from lowest for backslashes that were normalized to forward slashes by Canonicalize...
Definition: graph.h:99
bool Stat(DiskInterface *disk_interface, string *err)
Return false on error.
Definition: graph.cc:30
size_t len_
Definition: string_piece.h:50
State * state_
Definition: graph.h:218
void set_in_edge(Edge *edge)
Definition: graph.h:84
const string & name() const
Definition: eval_env.h:58
bool AllInputsReady() const
Return true if all inputs' in-edges are ready.
Definition: graph.cc:206
DiskInterface * disk_interface_
Definition: graph.h:219
string GetUnescapedDepfile()
Like GetBinding("depfile"), but without shell escaping.
Definition: graph.cc:311
string path_
Definition: graph.h:95
StringPiece out_
EdgeEnv(Edge *edge, EscapeKind escape)
Definition: graph.cc:219
A tokenized string that contains variable references.
Definition: eval_env.h:35
An interface for a scope for variable (e.g. "$foo") lookups.
Definition: eval_env.h:28
const vector< Edge * > & out_edges() const
Definition: graph.h:89
bool deps_missing_
Definition: graph.h:154
#define EXPLAIN(fmt,...)
Definition: debug_flags.h:20
static const Rule kPhonyRule
Definition: state.h:87
vector< Node * > outputs_
Definition: graph.h:151