36 mtime_ = disk_interface->
Stat(path_, err);
40 exists_ = (mtime_ != 0) ? ExistenceStatusExists : ExistenceStatusMissing;
46 mtime_ = std::max(mtime_, mtime);
51 std::vector<Node*>* validation_nodes,
53 std::vector<Node*> stack;
54 std::vector<Node*> new_validation_nodes;
56 std::deque<Node*> nodes(1, initial_node);
60 while (!nodes.empty()) {
61 Node* node = nodes.front();
65 new_validation_nodes.clear();
67 if (!RecomputeNodeDirty(node, &stack, &new_validation_nodes, err))
69 nodes.insert(nodes.end(), new_validation_nodes.begin(),
70 new_validation_nodes.end());
71 if (!new_validation_nodes.empty()) {
72 assert(validation_nodes &&
73 "validations require RecomputeDirty to be called with validation_nodes");
74 validation_nodes->insert(validation_nodes->end(),
75 new_validation_nodes.begin(),
76 new_validation_nodes.end());
84 std::vector<Node*>* validation_nodes,
95 EXPLAIN(
"%s has no in-edge and is missing", node->
path().c_str());
105 if (!VerifyDAG(node, stack, err))
110 stack->push_back(node);
129 if (!RecomputeNodeDirty(edge->
dyndep_, stack, validation_nodes, err))
135 if (!LoadDyndeps(edge->
dyndep_, err))
142 for (vector<Node*>::iterator o = edge->
outputs_.begin();
144 if (!(*o)->StatIfNecessary(disk_interface_, err))
151 if (!dep_loader_.LoadDeps(edge, err)) {
165 validation_nodes->insert(validation_nodes->end(),
169 Node* most_recent_input = NULL;
170 for (vector<Node*>::iterator i = edge->
inputs_.begin();
171 i != edge->
inputs_.end(); ++i) {
173 if (!RecomputeNodeDirty(*i, stack, validation_nodes, err))
177 if (
Edge* in_edge = (*i)->in_edge()) {
178 if (!in_edge->outputs_ready_)
186 EXPLAIN(
"%s is dirty", (*i)->path().c_str());
189 if (!most_recent_input || (*i)->
mtime() > most_recent_input->
mtime()) {
190 most_recent_input = *i;
199 if (!RecomputeOutputsDirty(edge, most_recent_input, &dirty, err))
203 for (vector<Node*>::iterator o = edge->
outputs_.begin();
220 assert(stack->back() == node);
228 assert(edge != NULL);
235 vector<Node*>::iterator start = stack->begin();
236 while (start != stack->end() && (*start)->in_edge() != edge)
238 assert(start != stack->end());
249 *err =
"dependency cycle: ";
250 for (vector<Node*>::const_iterator i = start; i != stack->end(); ++i) {
251 err->append((*i)->path());
254 err->append((*start)->path());
259 err->append(
" [-w phonycycle=err]");
266 bool* outputs_dirty,
string* err) {
268 for (vector<Node*>::iterator o = edge->
outputs_.begin();
270 if (RecomputeOutputDirty(edge, most_recent_input, command, *o)) {
271 *outputs_dirty =
true;
279 const Node* most_recent_input,
280 const string& command,
286 EXPLAIN(
"output %s of phony edge with no inputs doesn't exist",
287 output->
path().c_str());
293 if (most_recent_input) {
305 EXPLAIN(
"output %s doesn't exist", output->
path().c_str());
310 if (most_recent_input && output->
mtime() < most_recent_input->
mtime()) {
317 bool used_restat =
false;
319 (entry = build_log()->LookupByOutput(output->
path()))) {
320 output_mtime = entry->
mtime;
324 if (output_mtime < most_recent_input->mtime()) {
325 EXPLAIN(
"%soutput %s older than most recent input %s " 327 used_restat ?
"restat of " :
"", output->
path().c_str(),
328 most_recent_input->
path().c_str(),
329 output_mtime, most_recent_input->
mtime());
336 if (entry || (entry = build_log()->LookupByOutput(output->
path()))) {
342 EXPLAIN(
"command line changed for %s", output->
path().c_str());
345 if (most_recent_input && entry->
mtime < most_recent_input->
mtime()) {
350 EXPLAIN(
"recorded mtime of %s older than most recent input %s (%" PRId64 " vs %" PRId64 ")",
351 output->
path().c_str(), most_recent_input->
path().c_str(),
356 if (!entry && !generator) {
357 EXPLAIN(
"command line not found in log for %s", output->
path().c_str());
366 return dyndep_loader_.LoadDyndeps(node, err);
371 return dyndep_loader_.LoadDyndeps(node, ddf, err);
375 for (vector<Node*>::const_iterator i = inputs_.begin();
376 i != inputs_.end(); ++i) {
377 if ((*i)->in_edge() && !(*i)->in_edge()->outputs_ready())
388 : edge_(edge), escape_in_out_(escape), recursive_(false) {}
389 virtual string LookupVariable(
const string& var);
393 std::string MakePathList(
const Node*
const* span,
size_t size,
char sep)
const;
403 if (var ==
"in" || var ==
"in_newline") {
404 int explicit_deps_count = edge_->inputs_.size() - edge_->implicit_deps_ -
405 edge_->order_only_deps_;
406 #if __cplusplus >= 201103L 407 return MakePathList(edge_->inputs_.data(), explicit_deps_count,
409 return MakePathList(&edge_->inputs_[0], explicit_deps_count,
411 var ==
"in" ?
' ' :
'\n');
412 }
else if (var ==
"out") {
413 int explicit_outs_count = edge_->outputs_.size() - edge_->implicit_outs_;
414 return MakePathList(&edge_->outputs_[0], explicit_outs_count,
' ');
418 vector<string>::const_iterator it;
419 if ((it = find(lookups_.begin(), lookups_.end(), var)) != lookups_.end()) {
421 for (; it != lookups_.end(); ++it)
422 cycle.append(*it +
" -> ");
424 Fatal((
"cycle in rule variables: " + cycle).c_str());
429 const EvalString* eval = edge_->rule_->GetBinding(var);
430 if (recursive_ && eval)
431 lookups_.push_back(var);
436 return edge_->env_->LookupWithFallback(var, eval,
this);
440 const size_t size,
const char sep)
const {
442 for (
const Node*
const* i = span; i != span + size; ++i) {
444 result.push_back(sep);
445 const string& path = (*i)->PathDecanonicalized();
446 if (escape_in_out_ == kShellEscape) {
460 std::vector<std::string>* out)
const {
461 for (std::vector<Node*>::const_iterator it = inputs_.begin();
462 it != inputs_.end(); ++it) {
463 std::string path = (*it)->PathDecanonicalized();
465 std::string unescaped;
466 unescaped.swap(path);
473 #if __cplusplus >= 201103L 474 out->push_back(std::move(path));
476 out->push_back(path);
482 string command = GetBinding(
"command");
484 string rspfile_content = GetBinding(
"rspfile_content");
485 if (!rspfile_content.empty())
486 command +=
";rspfile=" + rspfile_content;
497 return !GetBinding(key).empty();
516 printf(
"%s[ ", prefix);
517 for (vector<Node*>::const_iterator i = inputs_.begin();
518 i != inputs_.end() && *i != NULL; ++i) {
519 printf(
"%s ", (*i)->path().c_str());
521 printf(
"--%s-> ", rule_->name().c_str());
522 for (vector<Node*>::const_iterator i = outputs_.begin();
523 i != outputs_.end() && *i != NULL; ++i) {
524 printf(
"%s ", (*i)->path().c_str());
526 if (!validations_.empty()) {
527 printf(
" validations ");
528 for (std::vector<Node*>::const_iterator i = validations_.begin();
529 i != validations_.end() && *i != NULL; ++i) {
530 printf(
"%s ", (*i)->path().c_str());
534 if (!pool_->name().empty()) {
535 printf(
"(in pool '%s')", pool_->name().c_str());
538 printf(
"(null pool?)");
540 printf(
"] 0x%p\n",
this);
555 return is_phony() && outputs_.size() == 1 && implicit_outs_ == 0 &&
561 string result = path;
564 for (
char* c = &result[0]; (c = strchr(c,
'/')) != NULL;) {
565 if (slash_bits & mask)
575 printf(
"%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ",
576 prefix, path().c_str(),
this,
577 mtime(), exists() ?
"" :
" (:missing)",
578 dirty() ?
" dirty" :
" clean");
580 in_edge()->Dump(
"in-edge: ");
582 printf(
"no in-edge\n");
584 printf(
" out edges:\n");
585 for (vector<Edge*>::const_iterator e = out_edges().begin();
586 e != out_edges().end() && *e != NULL; ++e) {
589 if (!validation_out_edges().empty()) {
590 printf(
" validation out edges:\n");
591 for (std::vector<Edge*>::const_iterator e = validation_out_edges().begin();
592 e != validation_out_edges().end() && *e != NULL; ++e) {
600 if (!deps_type.empty())
601 return LoadDepsFromLog(edge, err);
604 if (!depfile.empty())
605 return LoadDepFile(edge, depfile, err);
612 explicit matches(std::vector<StringPiece>::iterator i) : i_(i) {}
619 std::vector<StringPiece>::iterator
i_;
627 switch (disk_interface_->ReadFile(path, &content, err)) {
634 *err =
"loading '" + path +
"': " + *err;
638 if (content.empty()) {
639 EXPLAIN(
"depfile '%s' is missing", path.c_str());
644 ? *depfile_parser_options_
647 if (!depfile.
Parse(&content, &depfile_err)) {
648 *err = path +
": " + depfile_err;
652 if (depfile.
outs_.empty()) {
653 *err = path +
": no outputs declared";
658 std::vector<StringPiece>::iterator primary_out = depfile.
outs_.begin();
666 if (opath != *primary_out) {
667 EXPLAIN(
"expected depfile '%s' to mention '%s', got '%s'", path.c_str(),
668 first_output->
path().c_str(), primary_out->AsString().c_str());
673 for (std::vector<StringPiece>::iterator o = depfile.
outs_.begin();
674 o != depfile.
outs_.end(); ++o) {
677 *err = path +
": depfile mentions '" + o->AsString() +
"' as an output, but no such output was declared";
682 return ProcessDepfileDeps(edge, &depfile.
ins_, err);
686 Edge* edge, std::vector<StringPiece>* depfile_ins, std::string* err) {
688 vector<Node*>::iterator implicit_dep =
689 PreallocateSpace(edge, depfile_ins->size());
692 for (std::vector<StringPiece>::iterator i = depfile_ins->begin();
693 i != depfile_ins->end(); ++i, ++implicit_dep) {
696 Node* node = state_->GetNode(*i, slash_bits);
697 *implicit_dep = node;
699 CreatePhonyInEdge(node);
708 DepsLog::Deps* deps = deps_log_ ? deps_log_->GetDeps(output) : NULL;
710 EXPLAIN(
"deps for '%s' are missing", output->
path().c_str());
721 vector<Node*>::iterator implicit_dep =
723 for (
int i = 0; i < deps->
node_count; ++i, ++implicit_dep) {
725 *implicit_dep = node;
727 CreatePhonyInEdge(node);
747 phony_edge->
outputs_.push_back(node);
bool dyndep_pending() const
bool RecomputeDirty(Node *node, std::vector< Node *> *validation_nodes, std::string *err)
Update the |dirty_| state of the given nodes by transitively inspecting their input edges...
An Env for an Edge, providing $in and $out.
bool Stat(DiskInterface *disk_interface, std::string *err)
Return false on error.
std::string MakePathList(const Node *const *span, size_t size, char sep) const
Given a span of Nodes, construct a list of paths suitable for a command line.
bool RecomputeOutputDirty(const Edge *edge, const Node *most_recent_input, const std::string &command, Node *output)
Recompute whether a given single output should be marked dirty.
void Dump(const char *prefix="") const
bool generated_by_dep_loader_
Store data loaded from one dyndep file.
void Dump(const char *prefix="") const
Parser for the dependency information emitted by gcc's -M flags.
std::vector< Node * > validations_
void GetWin32EscapedString(const string &input, string *result)
bool RecomputeOutputsDirty(Edge *edge, Node *most_recent_input, bool *dirty, std::string *err)
Recompute whether any output of the edge is dirty, if so sets |*dirty|.
StringPiece represents a slice of a string whose memory is managed externally.
void set_dirty(bool dirty)
Information about a node in the dependency graph: the file, whether it's dirty, mtime, etc.
bool Parse(std::string *content, std::string *err)
Parse an input file.
virtual bool ProcessDepfileDeps(Edge *edge, std::vector< StringPiece > *depfile_ins, std::string *err)
Process loaded implicit dependencies for edge and update the graph.
void GetShellEscapedString(const string &input, string *result)
bool outputs_ready() const
std::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 ...
virtual string LookupVariable(const string &var)
Interface for accessing the disk.
bool StatIfNecessary(DiskInterface *disk_interface, std::string *err)
Return false on error.
bool LoadDyndeps(Node *node, std::string *err) const
Load a dyndep file from the given node's path and update the build graph with the new information...
std::vector< Node * > outputs_
void AddOutEdge(Edge *edge)
bool LoadDepsFromLog(Edge *edge, std::string *err)
Load implicit dependencies for edge from the DepsLog.
An edge in the dependency graph; links between Nodes using Rules.
bool LoadDepFile(Edge *edge, const std::string &path, std::string *err)
Load implicit dependencies for edge from a depfile attribute.
std::string PathDecanonicalized() const
Get |path()| but use slash_bits to convert back to original slash styles.
std::vector< StringPiece > outs_
bool is_order_only(size_t index)
const std::string & path() const
vector< string > lookups_
EscapeKind escape_in_out_
bool RecomputeNodeDirty(Node *node, std::vector< Node *> *stack, std::vector< Node *> *validation_nodes, std::string *err)
void UpdatePhonyMtime(TimeStamp mtime)
If the file doesn't exist, set the mtime_ from its dependencies.
std::vector< StringPiece > ins_
std::string GetUnescapedRspfile() const
Like GetBinding("rspfile"), but without shell escaping.
bool VerifyDAG(Node *node, std::vector< Node *> *stack, std::string *err)
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...
bool LoadDeps(Edge *edge, std::string *err)
Load implicit dependencies for edge.
#define METRIC_RECORD(name)
The primary interface to metrics.
std::vector< Node * > inputs_
std::string GetUnescapedDepfile() const
Like GetBinding("depfile"), but without shell escaping.
bool status_known() const
std::vector< StringPiece >::iterator i_
EdgeEnv(const Edge *const edge, const EscapeKind escape)
std::string GetBinding(const std::string &key) const
Returns the shell-escaped value of |key|.
void Fatal(const char *msg,...)
Log a fatal message and exit.
static uint64_t HashCommand(StringPiece command)
bool operator()(const Node *node) const
std::string EvaluateCommand(bool incl_rsp_file=false) const
Expand all variables in a command and return it as a string.
bool AllInputsReady() const
Return true if all inputs' in-edges are ready.
bool maybe_phonycycle_diagnostic() const
void CanonicalizePath(string *path, uint64_t *slash_bits)
void set_in_edge(Edge *edge)
unsigned long long uint64_t
matches(std::vector< StringPiece >::iterator i)
virtual TimeStamp Stat(const std::string &path, std::string *err) const =0
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
bool GetBindingBool(const std::string &key) const
void CollectInputs(bool shell_escape, std::vector< std::string > *out) const
A tokenized string that contains variable references.
An interface for a scope for variable (e.g. "$foo") lookups.
std::string GetUnescapedDyndep() const
Like GetBinding("dyndep"), but without shell escaping.
static const Rule kPhonyRule