35 mtime_ = disk_interface->
Stat(path_, err);
39 exists_ = (mtime_ != 0) ? ExistenceStatusExists : ExistenceStatusMissing;
45 mtime_ = std::max(mtime_, mtime);
50 std::vector<Node*>* validation_nodes,
52 std::vector<Node*> stack;
53 std::vector<Node*> new_validation_nodes;
55 std::deque<Node*> nodes(1, initial_node);
59 while (!nodes.empty()) {
60 Node* node = nodes.front();
64 new_validation_nodes.clear();
66 if (!RecomputeNodeDirty(node, &stack, &new_validation_nodes, err))
68 nodes.insert(nodes.end(), new_validation_nodes.begin(),
69 new_validation_nodes.end());
70 if (!new_validation_nodes.empty()) {
71 assert(validation_nodes &&
72 "validations require RecomputeDirty to be called with validation_nodes");
73 validation_nodes->insert(validation_nodes->end(),
74 new_validation_nodes.begin(),
75 new_validation_nodes.end());
83 std::vector<Node*>* validation_nodes,
94 explanations_.Record(node,
"%s has no in-edge and is missing",
95 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 explanations_.Record(node,
"%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 explanations_.Record(
287 output,
"output %s of phony edge with no inputs doesn't exist",
288 output->
path().c_str());
294 if (most_recent_input) {
304 explanations_.Record(output,
"output %s doesn't exist",
305 output->
path().c_str());
317 bool used_restat =
false;
319 (entry = build_log()->LookupByOutput(output->
path()))) {
324 if (!used_restat && most_recent_input && output->
mtime() < most_recent_input->
mtime()) {
325 explanations_.Record(output,
326 "output %s older than most recent input %s "
328 output->
path().c_str(),
329 most_recent_input->
path().c_str(), output->
mtime(),
330 most_recent_input->
mtime());
336 if (entry || (entry = build_log()->LookupByOutput(output->
path()))) {
342 explanations_.Record(output,
"command line changed for %s",
343 output->
path().c_str());
346 if (most_recent_input && entry->
mtime < most_recent_input->
mtime()) {
353 explanations_.Record(
355 "recorded mtime of %s older than most recent input %s (%" PRId64
357 output->
path().c_str(), most_recent_input->
path().c_str(),
362 if (!entry && !generator) {
363 explanations_.Record(output,
"command line not found in log for %s",
364 output->
path().c_str());
373 return dyndep_loader_.LoadDyndeps(node, err);
378 return dyndep_loader_.LoadDyndeps(node, ddf, err);
382 for (vector<Node*>::const_iterator i = inputs_.begin();
383 i != inputs_.end(); ++i) {
384 if ((*i)->in_edge() && !(*i)->in_edge()->outputs_ready())
395 : edge_(edge), escape_in_out_(escape), recursive_(false) {}
396 virtual string LookupVariable(
const string& var);
400 std::string MakePathList(
const Node*
const* span,
size_t size,
char sep)
const;
410 if (var ==
"in" || var ==
"in_newline") {
411 int explicit_deps_count =
412 static_cast<int>(edge_->inputs_.size() - edge_->implicit_deps_ -
413 edge_->order_only_deps_);
414 return MakePathList(edge_->inputs_.data(), explicit_deps_count,
415 var ==
"in" ?
' ' :
'\n');
416 }
else if (var ==
"out") {
417 int explicit_outs_count =
418 static_cast<int>(edge_->outputs_.size() - edge_->implicit_outs_);
419 return MakePathList(&edge_->outputs_[0], explicit_outs_count,
' ');
456 auto it = std::find(lookups_.begin(), lookups_.end(), var);
457 if (it != lookups_.end()) {
459 for (; it != lookups_.end(); ++it)
460 cycle.append(*it +
" -> ");
462 Fatal((
"cycle in rule variables: " + cycle).c_str());
467 const EvalString* eval = edge_->rule_->GetBinding(var);
468 bool record_varname = recursive_ && eval;
470 lookups_.push_back(var);
475 std::string result = edge_->env_->LookupWithFallback(var, eval,
this);
482 const size_t size,
const char sep)
const {
484 for (
const Node*
const* i = span; i != span + size; ++i) {
486 result.push_back(sep);
487 const string& path = (*i)->PathDecanonicalized();
488 if (escape_in_out_ == kShellEscape) {
502 string command = GetBinding(
"command");
504 string rspfile_content = GetBinding(
"rspfile_content");
505 if (!rspfile_content.empty())
506 command +=
";rspfile=" + rspfile_content;
517 return !GetBinding(key).empty();
536 printf(
"%s[ ", prefix);
537 for (vector<Node*>::const_iterator i = inputs_.begin();
538 i != inputs_.end() && *i != NULL; ++i) {
539 printf(
"%s ", (*i)->path().c_str());
541 printf(
"--%s-> ", rule_->name().c_str());
542 for (vector<Node*>::const_iterator i = outputs_.begin();
543 i != outputs_.end() && *i != NULL; ++i) {
544 printf(
"%s ", (*i)->path().c_str());
546 if (!validations_.empty()) {
547 printf(
" validations ");
548 for (std::vector<Node*>::const_iterator i = validations_.begin();
549 i != validations_.end() && *i != NULL; ++i) {
550 printf(
"%s ", (*i)->path().c_str());
554 if (!pool_->name().empty()) {
555 printf(
"(in pool '%s')", pool_->name().c_str());
558 printf(
"(null pool?)");
560 printf(
"] 0x%p\n",
this);
564 return rule_->IsPhony();
575 return is_phony() && outputs_.size() == 1 && implicit_outs_ == 0 &&
581 string result = path;
584 for (
char* c = &result[0]; (c = strchr(c,
'/')) != NULL;) {
585 if (slash_bits & mask)
595 printf(
"%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ",
596 prefix, path().c_str(),
this,
597 mtime(), exists() ?
"" :
" (:missing)",
598 dirty() ?
" dirty" :
" clean");
600 in_edge()->Dump(
"in-edge: ");
602 printf(
"no in-edge\n");
604 printf(
" out edges:\n");
605 for (vector<Edge*>::const_iterator e = out_edges().begin();
606 e != out_edges().end() && *e != NULL; ++e) {
609 if (!validation_out_edges().empty()) {
610 printf(
" validation out edges:\n");
611 for (std::vector<Edge*>::const_iterator e = validation_out_edges().begin();
612 e != validation_out_edges().end() && *e != NULL; ++e) {
620 if (!deps_type.empty())
621 return LoadDepsFromLog(edge, err);
624 if (!depfile.empty())
625 return LoadDepFile(edge, depfile, err);
632 explicit matches(std::vector<StringPiece>::iterator i) : i_(i) {}
639 std::vector<StringPiece>::iterator
i_;
647 switch (disk_interface_->ReadFile(path, &content, err)) {
654 *err =
"loading '" + path +
"': " + *err;
659 if (content.empty()) {
660 explanations_.Record(first_output,
"depfile '%s' is missing", path.c_str());
665 ? *depfile_parser_options_
668 if (!depfile.
Parse(&content, &depfile_err)) {
669 *err = path +
": " + depfile_err;
673 if (depfile.
outs_.empty()) {
674 *err = path +
": no outputs declared";
679 std::vector<StringPiece>::iterator primary_out = depfile.
outs_.begin();
686 if (opath != *primary_out) {
687 explanations_.Record(first_output,
688 "expected depfile '%s' to mention '%s', got '%s'",
689 path.c_str(), first_output->
path().c_str(),
690 primary_out->AsString().c_str());
695 for (std::vector<StringPiece>::iterator o = depfile.
outs_.begin();
696 o != depfile.
outs_.end(); ++o) {
699 *err = path +
": depfile mentions '" + o->AsString() +
"' as an output, but no such output was declared";
704 return ProcessDepfileDeps(edge, &depfile.
ins_, err);
708 Edge* edge, std::vector<StringPiece>* depfile_ins, std::string* err) {
710 vector<Node*>::iterator implicit_dep =
711 PreallocateSpace(edge,
static_cast<int>(depfile_ins->size()));
714 for (std::vector<StringPiece>::iterator i = depfile_ins->begin();
715 i != depfile_ins->end(); ++i, ++implicit_dep) {
718 Node* node = state_->GetNode(*i, slash_bits);
719 *implicit_dep = node;
729 DepsLog::Deps* deps = deps_log_ ? deps_log_->GetDeps(output) : NULL;
731 explanations_.Record(output,
"deps for '%s' are missing",
732 output->
path().c_str());
738 explanations_.Record(output,
739 "stored deps info out of date for '%s' (%" PRId64
748 nodes, nodes + node_count);
750 for (
size_t i = 0; i < node_count; ++i) {
774 if (!visited_nodes_.insert(input).second)
780 if (!(input_edge && input_edge->
is_phony())) {
781 inputs_.push_back(input);
787 bool shell_escape)
const {
788 std::vector<std::string> result;
789 result.reserve(inputs_.size());
791 for (
const Node* input : inputs_) {
792 std::string unescaped = input->PathDecanonicalized();
800 result.push_back(std::move(path));
802 result.push_back(std::move(unescaped));
#define METRIC_RECORD(name)
The primary interface to metrics.
static uint64_t HashCommand(StringPiece command)
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.
bool RecomputeNodeDirty(Node *node, std::vector< Node * > *stack, std::vector< Node * > *validation_nodes, std::string *err)
bool VerifyDAG(Node *node, std::vector< Node * > *stack, std::string *err)
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|.
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.
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.
Parser for the dependency information emitted by gcc's -M flags.
bool Parse(std::string *content, std::string *err)
Parse an input file.
std::vector< StringPiece > outs_
std::vector< StringPiece > ins_
Interface for accessing the disk.
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.
Store data loaded from one dyndep file.
An Env for an Edge, providing $in and $out.
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.
std::vector< std::string > lookups_
virtual string LookupVariable(const string &var)
EscapeKind escape_in_out_
EdgeEnv(const Edge *const edge, const EscapeKind escape)
An edge in the dependency graph; links between Nodes using Rules.
std::string GetBinding(const std::string &key) const
Returns the shell-escaped value of |key|.
bool maybe_phonycycle_diagnostic() const
std::string GetUnescapedDyndep() const
Like GetBinding("dyndep"), but without shell escaping.
std::vector< Node * > outputs_
bool outputs_ready() const
bool is_order_only(size_t index)
bool GetBindingBool(const std::string &key) const
std::string EvaluateCommand(bool incl_rsp_file=false) const
Expand all variables in a command and return it as a string.
void Dump(const char *prefix="") const
std::vector< Node * > validations_
std::vector< Node * > inputs_
std::string GetUnescapedRspfile() const
Like GetBinding("rspfile"), but without shell escaping.
std::string GetUnescapedDepfile() const
Like GetBinding("depfile"), but without shell escaping.
bool AllInputsReady() const
Return true if all inputs' in-edges are ready.
An interface for a scope for variable (e.g. "$foo") lookups.
A tokenized string that contains variable references.
bool LoadDepFile(Edge *edge, const std::string &path, std::string *err)
Load implicit dependencies for edge from a depfile attribute.
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 bool ProcessDepfileDeps(Edge *edge, std::vector< StringPiece > *depfile_ins, std::string *err)
Process loaded implicit dependencies for edge and update the graph.
bool LoadDeps(Edge *edge, std::string *err)
Load implicit dependencies for edge.
bool LoadDepsFromLog(Edge *edge, std::string *err)
Load implicit dependencies for edge from the DepsLog.
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
void set_dirty(bool dirty)
void UpdatePhonyMtime(TimeStamp mtime)
If the file doesn't exist, set the mtime_ from its dependencies.
void Dump(const char *prefix="") const
void AddOutEdge(Edge *edge)
const std::string & path() const
bool status_known() const
bool dyndep_pending() const
std::string PathDecanonicalized() const
Get |path()| but use slash_bits to convert back to original slash styles.
bool StatIfNecessary(DiskInterface *disk_interface, std::string *err)
Return false on error.
bool Stat(DiskInterface *disk_interface, std::string *err)
Return false on error.
StringPiece represents a slice of a string whose memory is managed externally.
std::vector< StringPiece >::iterator i_
bool operator()(const Node *node) const
matches(std::vector< StringPiece >::iterator i)
void GetWin32EscapedString(const string &input, string *result)
void GetShellEscapedString(const string &input, string *result)
void CanonicalizePath(string *path, uint64_t *slash_bits)
void Fatal(const char *msg,...)
Log a fatal message and exit.
unsigned long long uint64_t