Correct blacklist entry message
[chromium-blink-merge.git] / tools / gn / item_tree.cc
blob42e005b5990e53cfbb46a711a3194df347ee5560
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "tools/gn/item_tree.h"
7 #include <algorithm>
9 #include "base/stl_util.h"
10 #include "tools/gn/err.h"
11 #include "tools/gn/item.h"
12 #include "tools/gn/item_node.h"
14 namespace {
16 // Recursively looks in the tree for a given node, returning true if it
17 // was found in the dependecy graph. This is used to see if a given node
18 // participates in a cycle.
20 // Note that "look_for" and "search_in" will be the same node when starting the
21 // search, so we don't want to return true in that case.
23 // If a cycle is found, the return value will be true and the cycle vector will
24 // be filled with the path (in reverse order).
25 bool RecursiveFindCycle(const ItemNode* look_for,
26 const ItemNode* search_in,
27 std::vector<const ItemNode*>* cycle) {
28 const ItemNode::ItemNodeMap& unresolved =
29 search_in->unresolved_dependencies();
30 for (ItemNode::ItemNodeMap::const_iterator i = unresolved.begin();
31 i != unresolved.end(); ++i) {
32 ItemNode* cur = i->first;
33 if (cur == look_for) {
34 cycle->push_back(cur);
35 return true;
38 if (RecursiveFindCycle(look_for, cur, cycle)) {
39 // Found a cycle inside this one, record our path and return.
40 cycle->push_back(cur);
41 return true;
44 return false;
47 } // namespace
49 ItemTree::ItemTree() {
52 ItemTree::~ItemTree() {
53 STLDeleteContainerPairSecondPointers(items_.begin(), items_.end());
56 ItemNode* ItemTree::GetExistingNodeLocked(const Label& label) {
57 lock_.AssertAcquired();
58 StringToNodeHash::iterator found = items_.find(label);
59 if (found == items_.end())
60 return NULL;
61 return found->second;
64 void ItemTree::AddNodeLocked(ItemNode* node) {
65 lock_.AssertAcquired();
66 DCHECK(items_.find(node->item()->label()) == items_.end());
67 items_[node->item()->label()] = node;
70 bool ItemTree::MarkItemDefinedLocked(const BuildSettings* build_settings,
71 const Label& label,
72 Err* err) {
73 lock_.AssertAcquired();
74 DCHECK(items_.find(label) != items_.end());
76 ItemNode* node = items_[label];
78 if (!node->unresolved_dependencies().empty()) {
79 // Still some pending dependencies, wait for those to be resolved.
80 if (!node->SetDefined(build_settings, err))
81 return false;
82 return true;
85 // No more pending deps.
86 MarkItemResolvedLocked(node);
87 return true;
90 void ItemTree::GetAllItemNodesLocked(std::vector<const ItemNode*>* dest) const {
91 lock_.AssertAcquired();
92 dest->reserve(items_.size());
93 for (StringToNodeHash::const_iterator i = items_.begin();
94 i != items_.end(); ++i)
95 dest->push_back(i->second);
98 void ItemTree::GetAllItemsLocked(std::vector<const Item*>* dest) const {
99 lock_.AssertAcquired();
100 dest->reserve(items_.size());
101 for (StringToNodeHash::const_iterator i = items_.begin();
102 i != items_.end(); ++i) {
103 dest->push_back(i->second->item());
107 Err ItemTree::CheckForBadItems() const {
108 base::AutoLock lock(lock_);
110 // Look for errors where we find a GENERATED node that refers to a REFERENCED
111 // one. There may be other nodes depending on the GENERATED one, but listing
112 // all of those isn't helpful, we want to find the broken link.
114 // This finds normal "missing dependency" errors but does not find circular
115 // dependencies because in this case all items in the cycle will be GENERATED
116 // but none will be resolved. If this happens, we'll check explicitly for
117 // that below.
118 std::vector<const ItemNode*> bad_nodes;
119 std::string depstring;
120 for (StringToNodeHash::const_iterator i = items_.begin();
121 i != items_.end(); ++i) {
122 const ItemNode* src = i->second;
123 if (!src->should_generate())
124 continue; // Skip ungenerated nodes.
126 if (src->state() == ItemNode::DEFINED ||
127 src->state() == ItemNode::PENDING_DEPS) {
128 bad_nodes.push_back(src);
130 // Check dependencies.
131 for (ItemNode::ItemNodeMap::const_iterator dest =
132 src->unresolved_dependencies().begin();
133 dest != src->unresolved_dependencies().end();
134 ++dest) {
135 const ItemNode* dest_node = dest->first;
136 if (dest_node->state() == ItemNode::REFERENCED) {
137 depstring += "\"" + src->item()->label().GetUserVisibleName(false) +
138 "\" needs " + dest_node->item()->GetItemTypeName() +
139 " \"" + dest_node->item()->label().GetUserVisibleName(false) +
140 "\"\n";
146 if (!bad_nodes.empty() && depstring.empty()) {
147 // Our logic above found a bad node but didn't identify the problem. This
148 // normally means a circular dependency.
149 depstring = CheckForCircularDependenciesLocked(bad_nodes);
150 if (depstring.empty()) {
151 // Something's very wrong, just dump out the bad nodes.
152 depstring = "I have no idea what went wrong, but these are unresolved, "
153 "possible due to an\ninternal error:";
154 for (size_t i = 0; i < bad_nodes.size(); i++) {
155 depstring += "\n\"" +
156 bad_nodes[i]->item()->label().GetUserVisibleName(false) + "\"";
161 if (depstring.empty())
162 return Err();
163 return Err(Location(), "Unresolved dependencies.", depstring);
166 void ItemTree::MarkItemResolvedLocked(ItemNode* node) {
167 node->SetResolved();
168 node->item()->OnResolved();
170 // Now update our waiters, pushing the "resolved" bit.
171 ItemNode::ItemNodeMap waiting;
172 node->SwapOutWaitingDependencySet(&waiting);
173 for (ItemNode::ItemNodeMap::iterator i = waiting.begin();
174 i != waiting.end(); ++i) {
175 ItemNode* waiter = i->first;
177 // Our node should be unresolved in the waiter.
178 DCHECK(waiter->unresolved_dependencies().find(node) !=
179 waiter->unresolved_dependencies().end());
180 waiter->MarkDirectDependencyResolved(node);
182 // Recursively mark nodes as resolved.
183 if ((waiter->state() == ItemNode::DEFINED ||
184 waiter->state() == ItemNode::PENDING_DEPS) &&
185 waiter->unresolved_dependencies().empty())
186 MarkItemResolvedLocked(waiter);
190 std::string ItemTree::CheckForCircularDependenciesLocked(
191 const std::vector<const ItemNode*>& bad_nodes) const {
192 std::vector<const ItemNode*> cycle;
193 if (!RecursiveFindCycle(bad_nodes[0], bad_nodes[0], &cycle))
194 return std::string(); // Didn't find a cycle, something else is wrong.
196 cycle.push_back(bad_nodes[0]);
197 std::string ret = "There is a dependency cycle:";
199 // Walk backwards since the dependency arrows point in the reverse direction.
200 for (int i = static_cast<int>(cycle.size()) - 1; i >= 0; i--) {
201 ret += "\n \"" + cycle[i]->item()->label().GetUserVisibleName(false) +
202 "\"";
203 if (i != 0)
204 ret += " ->";
207 return ret;