blob: de76620ac8314e145b80d8306546af262caca351 [file] [log] [blame]
// Copyright 2019 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "missing_deps.h"
#include <string.h>
#include <iostream>
#include "depfile_parser.h"
#include "deps_log.h"
#include "disk_interface.h"
#include "graph.h"
#include "state.h"
#include "util.h"
namespace {
/// ImplicitDepLoader variant that stores dep nodes into the given output
/// without updating graph deps like the base loader does.
struct NodeStoringImplicitDepLoader : public ImplicitDepLoader {
NodeStoringImplicitDepLoader(
State* state, DepsLog* deps_log, DiskInterface* disk_interface,
DepfileParserOptions const* depfile_parser_options,
std::vector<Node*>* dep_nodes_output)
: ImplicitDepLoader(state, deps_log, disk_interface,
depfile_parser_options),
dep_nodes_output_(dep_nodes_output) {}
protected:
virtual bool ProcessDepfileDeps(Edge* edge,
std::vector<StringPiece>* depfile_ins,
std::string* err);
private:
std::vector<Node*>* dep_nodes_output_;
};
bool NodeStoringImplicitDepLoader::ProcessDepfileDeps(
Edge* edge, std::vector<StringPiece>* depfile_ins, std::string* err) {
for (std::vector<StringPiece>::iterator i = depfile_ins->begin();
i != depfile_ins->end(); ++i) {
uint64_t slash_bits;
CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits);
Node* node = state_->GetNode(*i, slash_bits);
dep_nodes_output_->push_back(node);
}
return true;
}
} // namespace
MissingDependencyScannerDelegate::~MissingDependencyScannerDelegate() {}
void MissingDependencyPrinter::OnMissingDep(Node* node, const std::string& path,
const Rule& generator) {
std::cout << "Missing dep: " << node->path() << " uses " << path
<< " (generated by " << generator.name() << ")\n";
}
MissingDependencyScanner::MissingDependencyScanner(
MissingDependencyScannerDelegate* delegate, DepsLog* deps_log, State* state,
DiskInterface* disk_interface)
: delegate_(delegate), deps_log_(deps_log), state_(state),
disk_interface_(disk_interface), missing_dep_path_count_(0) {}
void MissingDependencyScanner::ProcessNode(Node* node) {
if (!node)
return;
Edge* edge = node->in_edge();
if (!edge)
return;
if (!seen_.insert(node).second)
return;
for (std::vector<Node*>::iterator in = edge->inputs_.begin();
in != edge->inputs_.end(); ++in) {
ProcessNode(*in);
}
std::string deps_type = edge->GetBinding("deps");
if (!deps_type.empty()) {
DepsLog::Deps* deps = deps_log_->GetDeps(node);
if (deps)
ProcessNodeDeps(node, deps->nodes, deps->node_count);
} else {
DepfileParserOptions parser_opts;
std::vector<Node*> depfile_deps;
NodeStoringImplicitDepLoader dep_loader(state_, deps_log_, disk_interface_,
&parser_opts, &depfile_deps);
std::string err;
dep_loader.LoadDeps(edge, &err);
if (!depfile_deps.empty())
ProcessNodeDeps(node, &depfile_deps[0], depfile_deps.size());
}
}
void MissingDependencyScanner::ProcessNodeDeps(Node* node, Node** dep_nodes,
int dep_nodes_count) {
Edge* edge = node->in_edge();
std::set<Edge*> deplog_edges;
for (int i = 0; i < dep_nodes_count; ++i) {
Node* deplog_node = dep_nodes[i];
// Special exception: A dep on build.ninja can be used to mean "always
// rebuild this target when the build is reconfigured", but build.ninja is
// often generated by a configuration tool like cmake or gn. The rest of
// the build "implicitly" depends on the entire build being reconfigured,
// so a missing dep path to build.ninja is not an actual missing dependency
// problem.
if (deplog_node->path() == "build.ninja")
return;
Edge* deplog_edge = deplog_node->in_edge();
if (deplog_edge) {
deplog_edges.insert(deplog_edge);
}
}
std::vector<Edge*> missing_deps;
for (std::set<Edge*>::iterator de = deplog_edges.begin();
de != deplog_edges.end(); ++de) {
if (!PathExistsBetween(*de, edge)) {
missing_deps.push_back(*de);
}
}
if (!missing_deps.empty()) {
std::set<std::string> missing_deps_rule_names;
for (std::vector<Edge*>::iterator ne = missing_deps.begin();
ne != missing_deps.end(); ++ne) {
for (int i = 0; i < dep_nodes_count; ++i) {
if (dep_nodes[i]->in_edge() == *ne) {
generated_nodes_.insert(dep_nodes[i]);
generator_rules_.insert(&(*ne)->rule());
missing_deps_rule_names.insert((*ne)->rule().name());
delegate_->OnMissingDep(node, dep_nodes[i]->path(), (*ne)->rule());
}
}
}
missing_dep_path_count_ += missing_deps_rule_names.size();
nodes_missing_deps_.insert(node);
}
}
void MissingDependencyScanner::PrintStats() {
std::cout << "Processed " << seen_.size() << " nodes.\n";
if (HadMissingDeps()) {
std::cout << "Error: There are " << missing_dep_path_count_
<< " missing dependency paths.\n";
std::cout << nodes_missing_deps_.size()
<< " targets had depfile dependencies on "
<< generated_nodes_.size() << " distinct generated inputs "
<< "(from " << generator_rules_.size() << " rules) "
<< " without a non-depfile dep path to the generator.\n";
std::cout << "There might be build flakiness if any of the targets listed "
"above are built alone, or not late enough, in a clean output "
"directory.\n";
} else {
std::cout << "No missing dependencies on generated files found.\n";
}
}
bool MissingDependencyScanner::PathExistsBetween(Edge* from, Edge* to) {
AdjacencyMap::iterator it = adjacency_map_.find(from);
if (it != adjacency_map_.end()) {
InnerAdjacencyMap::iterator inner_it = it->second.find(to);
if (inner_it != it->second.end()) {
return inner_it->second;
}
} else {
it = adjacency_map_.insert(std::make_pair(from, InnerAdjacencyMap())).first;
}
bool found = false;
for (size_t i = 0; i < to->inputs_.size(); ++i) {
Edge* e = to->inputs_[i]->in_edge();
if (e && (e == from || PathExistsBetween(from, e))) {
found = true;
break;
}
}
it->second.insert(std::make_pair(to, found));
return found;
}