Ninja
missing_deps.cc
Go to the documentation of this file.
1 // Copyright 2019 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 "missing_deps.h"
16 
17 #include <string.h>
18 
19 #include <iostream>
20 
21 #include "depfile_parser.h"
22 #include "deps_log.h"
23 #include "disk_interface.h"
24 #include "graph.h"
25 #include "state.h"
26 #include "util.h"
27 
28 namespace {
29 
30 /// ImplicitDepLoader variant that stores dep nodes into the given output
31 /// without updating graph deps like the base loader does.
32 struct NodeStoringImplicitDepLoader : public ImplicitDepLoader {
33  NodeStoringImplicitDepLoader(
34  State* state, DepsLog* deps_log, DiskInterface* disk_interface,
35  DepfileParserOptions const* depfile_parser_options,
36  Explanations* explanations, std::vector<Node*>* dep_nodes_output)
37  : ImplicitDepLoader(state, deps_log, disk_interface,
38  depfile_parser_options, explanations),
39  dep_nodes_output_(dep_nodes_output) {}
40 
41  protected:
42  virtual bool ProcessDepfileDeps(Edge* edge,
43  std::vector<StringPiece>* depfile_ins,
44  std::string* err);
45 
46  private:
47  std::vector<Node*>* dep_nodes_output_;
48 };
49 
50 bool NodeStoringImplicitDepLoader::ProcessDepfileDeps(
51  Edge* edge, std::vector<StringPiece>* depfile_ins, std::string* err) {
52  for (std::vector<StringPiece>::iterator i = depfile_ins->begin();
53  i != depfile_ins->end(); ++i) {
54  uint64_t slash_bits;
55  CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits);
56  Node* node = state_->GetNode(*i, slash_bits);
57  dep_nodes_output_->push_back(node);
58  }
59  return true;
60 }
61 
62 } // namespace
63 
65 
66 void MissingDependencyPrinter::OnMissingDep(Node* node, const std::string& path,
67  const Rule& generator) {
68  std::cout << "Missing dep: " << node->path() << " uses " << path
69  << " (generated by " << generator.name() << ")\n";
70 }
71 
73  MissingDependencyScannerDelegate* delegate, DepsLog* deps_log, State* state,
74  DiskInterface* disk_interface)
75  : delegate_(delegate), deps_log_(deps_log), state_(state),
76  disk_interface_(disk_interface), missing_dep_path_count_(0) {}
77 
79  if (!node)
80  return;
81  Edge* edge = node->in_edge();
82  if (!edge)
83  return;
84  if (!seen_.insert(node).second)
85  return;
86 
87  for (std::vector<Node*>::iterator in = edge->inputs_.begin();
88  in != edge->inputs_.end(); ++in) {
89  ProcessNode(*in);
90  }
91 
92  std::string deps_type = edge->GetBinding("deps");
93  if (!deps_type.empty()) {
94  DepsLog::Deps* deps = deps_log_->GetDeps(node);
95  if (deps)
96  ProcessNodeDeps(node, deps->nodes, deps->node_count);
97  } else {
98  DepfileParserOptions parser_opts;
99  std::vector<Node*> depfile_deps;
100  NodeStoringImplicitDepLoader dep_loader(state_, deps_log_, disk_interface_,
101  &parser_opts, nullptr,
102  &depfile_deps);
103  std::string err;
104  dep_loader.LoadDeps(edge, &err);
105  if (!depfile_deps.empty())
106  ProcessNodeDeps(node, &depfile_deps[0],
107  static_cast<int>(depfile_deps.size()));
108  }
109 }
110 
112  int dep_nodes_count) {
113  Edge* edge = node->in_edge();
114  std::set<Edge*> deplog_edges;
115  for (int i = 0; i < dep_nodes_count; ++i) {
116  Node* deplog_node = dep_nodes[i];
117  // Special exception: A dep on build.ninja can be used to mean "always
118  // rebuild this target when the build is reconfigured", but build.ninja is
119  // often generated by a configuration tool like cmake or gn. The rest of
120  // the build "implicitly" depends on the entire build being reconfigured,
121  // so a missing dep path to build.ninja is not an actual missing dependency
122  // problem.
123  if (deplog_node->path() == "build.ninja")
124  return;
125  Edge* deplog_edge = deplog_node->in_edge();
126  if (deplog_edge) {
127  deplog_edges.insert(deplog_edge);
128  }
129  }
130  std::vector<Edge*> missing_deps;
131  for (std::set<Edge*>::iterator de = deplog_edges.begin();
132  de != deplog_edges.end(); ++de) {
133  if (!PathExistsBetween(*de, edge)) {
134  missing_deps.push_back(*de);
135  }
136  }
137 
138  if (!missing_deps.empty()) {
139  std::set<std::string> missing_deps_rule_names;
140  for (std::vector<Edge*>::iterator ne = missing_deps.begin();
141  ne != missing_deps.end(); ++ne) {
142  for (int i = 0; i < dep_nodes_count; ++i) {
143  if (dep_nodes[i]->in_edge() == *ne) {
144  generated_nodes_.insert(dep_nodes[i]);
145  generator_rules_.insert(&(*ne)->rule());
146  missing_deps_rule_names.insert((*ne)->rule().name());
147  delegate_->OnMissingDep(node, dep_nodes[i]->path(), (*ne)->rule());
148  }
149  }
150  }
151  missing_dep_path_count_ += missing_deps_rule_names.size();
152  nodes_missing_deps_.insert(node);
153  }
154 }
155 
157  std::cout << "Processed " << seen_.size() << " nodes.\n";
158  if (HadMissingDeps()) {
159  std::cout << "Error: There are " << missing_dep_path_count_
160  << " missing dependency paths.\n";
161  std::cout << nodes_missing_deps_.size()
162  << " targets had depfile dependencies on "
163  << generated_nodes_.size() << " distinct generated inputs "
164  << "(from " << generator_rules_.size() << " rules) "
165  << " without a non-depfile dep path to the generator.\n";
166  std::cout << "There might be build flakiness if any of the targets listed "
167  "above are built alone, or not late enough, in a clean output "
168  "directory.\n";
169  } else {
170  std::cout << "No missing dependencies on generated files found.\n";
171  }
172 }
173 
175  AdjacencyMap::iterator it = adjacency_map_.find(from);
176  if (it != adjacency_map_.end()) {
177  InnerAdjacencyMap::iterator inner_it = it->second.find(to);
178  if (inner_it != it->second.end()) {
179  return inner_it->second;
180  }
181  } else {
182  it = adjacency_map_.insert(std::make_pair(from, InnerAdjacencyMap())).first;
183  }
184  bool found = false;
185  for (size_t i = 0; i < to->inputs_.size(); ++i) {
186  Edge* e = to->inputs_[i]->in_edge();
187  if (e && (e == from || PathExistsBetween(from, e))) {
188  found = true;
189  break;
190  }
191  }
192  it->second.insert(std::make_pair(to, found));
193  return found;
194 }
void OnMissingDep(Node *node, const std::string &path, const Rule &generator)
Definition: missing_deps.cc:66
virtual void OnMissingDep(Node *node, const std::string &path, const Rule &generator)=0
Node ** nodes
Definition: deps_log.h:86
int node_count
Definition: deps_log.h:85
As build commands run they can output extra dependency information (e.g.
Definition: deps_log.h:68
Deps * GetDeps(Node *node)
Definition: deps_log.cc:305
Interface for accessing the disk.
An edge in the dependency graph; links between Nodes using Rules.
Definition: graph.h:175
std::string GetBinding(const std::string &key) const
Returns the shell-escaped value of |key|.
Definition: graph.cc:511
std::vector< Node * > inputs_
Definition: graph.h:216
A class used to record a list of explanation strings associated with a given 'item' pointer.
Definition: explanations.h:27
ImplicitDepLoader loads implicit dependencies, as referenced via the "depfile" attribute in build fil...
Definition: graph.h:285
virtual bool ProcessDepfileDeps(Edge *edge, std::vector< StringPiece > *depfile_ins, std::string *err)
Process loaded implicit dependencies for edge and update the graph.
Definition: graph.cc:707
MissingDependencyScanner(MissingDependencyScannerDelegate *delegate, DepsLog *deps_log, State *state, DiskInterface *disk_interface)
Definition: missing_deps.cc:72
std::set< const Rule * > generator_rules_
Definition: missing_deps.h:65
bool PathExistsBetween(Edge *from, Edge *to)
MissingDependencyScannerDelegate * delegate_
Definition: missing_deps.h:58
std::set< Node * > generated_nodes_
Definition: missing_deps.h:64
void ProcessNode(Node *node)
Definition: missing_deps.cc:78
std::set< Node * > seen_
Definition: missing_deps.h:62
std::unordered_map< Edge *, bool > InnerAdjacencyMap
Definition: missing_deps.h:69
void ProcessNodeDeps(Node *node, Node **dep_nodes, int dep_nodes_count)
DiskInterface * disk_interface_
Definition: missing_deps.h:61
std::set< Node * > nodes_missing_deps_
Definition: missing_deps.h:63
AdjacencyMap adjacency_map_
Definition: missing_deps.h:71
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition: graph.h:42
const std::string & path() const
Definition: graph.h:82
Edge * in_edge() const
Definition: graph.h:100
An invocable build command and associated metadata (description, etc.).
Definition: eval_env.h:66
const std::string & name() const
Definition: eval_env.h:73
Global state (file status) for a single run.
Definition: state.h:95
void CanonicalizePath(string *path, uint64_t *slash_bits)
Definition: util.cc:124
unsigned long long uint64_t
Definition: win32port.h:29