xref: /third_party/ninja/src/missing_deps.cc (revision 695b41ee)
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
28namespace {
29
30/// ImplicitDepLoader variant that stores dep nodes into the given output
31/// without updating graph deps like the base loader does.
32struct NodeStoringImplicitDepLoader : public ImplicitDepLoader {
33  NodeStoringImplicitDepLoader(
34      State* state, DepsLog* deps_log, DiskInterface* disk_interface,
35      DepfileParserOptions const* depfile_parser_options,
36      std::vector<Node*>* dep_nodes_output)
37      : ImplicitDepLoader(state, deps_log, disk_interface,
38                          depfile_parser_options),
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
50bool 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
64MissingDependencyScannerDelegate::~MissingDependencyScannerDelegate() {}
65
66void 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
72MissingDependencyScanner::MissingDependencyScanner(
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
78void MissingDependencyScanner::ProcessNode(Node* node) {
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, &depfile_deps);
102    std::string err;
103    dep_loader.LoadDeps(edge, &err);
104    if (!depfile_deps.empty())
105      ProcessNodeDeps(node, &depfile_deps[0], depfile_deps.size());
106  }
107}
108
109void MissingDependencyScanner::ProcessNodeDeps(Node* node, Node** dep_nodes,
110                                               int dep_nodes_count) {
111  Edge* edge = node->in_edge();
112  std::set<Edge*> deplog_edges;
113  for (int i = 0; i < dep_nodes_count; ++i) {
114    Node* deplog_node = dep_nodes[i];
115    // Special exception: A dep on build.ninja can be used to mean "always
116    // rebuild this target when the build is reconfigured", but build.ninja is
117    // often generated by a configuration tool like cmake or gn. The rest of
118    // the build "implicitly" depends on the entire build being reconfigured,
119    // so a missing dep path to build.ninja is not an actual missing dependency
120    // problem.
121    if (deplog_node->path() == "build.ninja")
122      return;
123    Edge* deplog_edge = deplog_node->in_edge();
124    if (deplog_edge) {
125      deplog_edges.insert(deplog_edge);
126    }
127  }
128  std::vector<Edge*> missing_deps;
129  for (std::set<Edge*>::iterator de = deplog_edges.begin();
130       de != deplog_edges.end(); ++de) {
131    if (!PathExistsBetween(*de, edge)) {
132      missing_deps.push_back(*de);
133    }
134  }
135
136  if (!missing_deps.empty()) {
137    std::set<std::string> missing_deps_rule_names;
138    for (std::vector<Edge*>::iterator ne = missing_deps.begin();
139         ne != missing_deps.end(); ++ne) {
140      for (int i = 0; i < dep_nodes_count; ++i) {
141        if (dep_nodes[i]->in_edge() == *ne) {
142          generated_nodes_.insert(dep_nodes[i]);
143          generator_rules_.insert(&(*ne)->rule());
144          missing_deps_rule_names.insert((*ne)->rule().name());
145          delegate_->OnMissingDep(node, dep_nodes[i]->path(), (*ne)->rule());
146        }
147      }
148    }
149    missing_dep_path_count_ += missing_deps_rule_names.size();
150    nodes_missing_deps_.insert(node);
151  }
152}
153
154void MissingDependencyScanner::PrintStats() {
155  std::cout << "Processed " << seen_.size() << " nodes.\n";
156  if (HadMissingDeps()) {
157    std::cout << "Error: There are " << missing_dep_path_count_
158              << " missing dependency paths.\n";
159    std::cout << nodes_missing_deps_.size()
160              << " targets had depfile dependencies on "
161              << generated_nodes_.size() << " distinct generated inputs "
162              << "(from " << generator_rules_.size() << " rules) "
163              << " without a non-depfile dep path to the generator.\n";
164    std::cout << "There might be build flakiness if any of the targets listed "
165                 "above are built alone, or not late enough, in a clean output "
166                 "directory.\n";
167  } else {
168    std::cout << "No missing dependencies on generated files found.\n";
169  }
170}
171
172bool MissingDependencyScanner::PathExistsBetween(Edge* from, Edge* to) {
173  AdjacencyMap::iterator it = adjacency_map_.find(from);
174  if (it != adjacency_map_.end()) {
175    InnerAdjacencyMap::iterator inner_it = it->second.find(to);
176    if (inner_it != it->second.end()) {
177      return inner_it->second;
178    }
179  } else {
180    it = adjacency_map_.insert(std::make_pair(from, InnerAdjacencyMap())).first;
181  }
182  bool found = false;
183  for (size_t i = 0; i < to->inputs_.size(); ++i) {
184    Edge* e = to->inputs_[i]->in_edge();
185    if (e && (e == from || PathExistsBetween(from, e))) {
186      found = true;
187      break;
188    }
189  }
190  it->second.insert(std::make_pair(to, found));
191  return found;
192}
193