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. 4 5 #include "tools/gn/item_tree.h" 6 7 #include <algorithm> 8 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" 13 14 namespace { 15 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. 19 // 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. 22 // 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; 36 } 37 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; 42 } 43 } 44 return false; 45 } 46 47 } // namespace 48 49 ItemTree::ItemTree() { 50 } 51 52 ItemTree::~ItemTree() { 53 STLDeleteContainerPairSecondPointers(items_.begin(), items_.end()); 54 } 55 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; 62 } 63 64 void ItemTree::AddNodeLocked(ItemNode* node) { 65 lock_.AssertAcquired(); 66 DCHECK(items_.find(node->item()->label()) == items_.end()); 67 items_[node->item()->label()] = node; 68 } 69 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()); 75 76 ItemNode* node = items_[label]; 77 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; 83 } 84 85 // No more pending deps. 86 MarkItemResolvedLocked(node); 87 return true; 88 } 89 90 void ItemTree::GetAllItemsLocked(std::vector<const Item*>* 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->item()); 96 } 97 } 98 99 Err ItemTree::CheckForBadItems() const { 100 base::AutoLock lock(lock_); 101 102 // Look for errors where we find a GENERATED node that refers to a REFERENCED 103 // one. There may be other nodes depending on the GENERATED one, but listing 104 // all of those isn't helpful, we want to find the broken link. 105 // 106 // This finds normal "missing dependency" errors but does not find circular 107 // dependencies because in this case all items in the cycle will be GENERATED 108 // but none will be resolved. If this happens, we'll check explicitly for 109 // that below. 110 std::vector<const ItemNode*> bad_nodes; 111 std::string depstring; 112 for (StringToNodeHash::const_iterator i = items_.begin(); 113 i != items_.end(); ++i) { 114 const ItemNode* src = i->second; 115 if (!src->should_generate()) 116 continue; // Skip ungenerated nodes. 117 118 if (src->state() == ItemNode::DEFINED || 119 src->state() == ItemNode::PENDING_DEPS) { 120 bad_nodes.push_back(src); 121 122 // Check dependencies. 123 for (ItemNode::ItemNodeMap::const_iterator dest = 124 src->unresolved_dependencies().begin(); 125 dest != src->unresolved_dependencies().end(); 126 ++dest) { 127 const ItemNode* dest_node = dest->first; 128 if (dest_node->state() == ItemNode::REFERENCED) { 129 depstring += "\"" + src->item()->label().GetUserVisibleName(false) + 130 "\" needs " + dest_node->item()->GetItemTypeName() + 131 " \"" + dest_node->item()->label().GetUserVisibleName(false) + 132 "\"\n"; 133 } 134 } 135 } 136 } 137 138 if (!bad_nodes.empty() && depstring.empty()) { 139 // Our logic above found a bad node but didn't identify the problem. This 140 // normally means a circular dependency. 141 depstring = CheckForCircularDependenciesLocked(bad_nodes); 142 if (depstring.empty()) { 143 // Something's very wrong, just dump out the bad nodes. 144 depstring = "I have no idea what went wrong, but these are unresolved, " 145 "possible due to an\ninternal error:"; 146 for (size_t i = 0; i < bad_nodes.size(); i++) { 147 depstring += "\n\"" + 148 bad_nodes[i]->item()->label().GetUserVisibleName(false) + "\""; 149 } 150 } 151 } 152 153 if (depstring.empty()) 154 return Err(); 155 return Err(Location(), "Unresolved dependencies.", depstring); 156 } 157 158 void ItemTree::MarkItemResolvedLocked(ItemNode* node) { 159 node->SetResolved(); 160 node->item()->OnResolved(); 161 162 // Now update our waiters, pushing the "resolved" bit. 163 ItemNode::ItemNodeMap waiting; 164 node->SwapOutWaitingDependencySet(&waiting); 165 for (ItemNode::ItemNodeMap::iterator i = waiting.begin(); 166 i != waiting.end(); ++i) { 167 ItemNode* waiter = i->first; 168 169 // Our node should be unresolved in the waiter. 170 DCHECK(waiter->unresolved_dependencies().find(node) != 171 waiter->unresolved_dependencies().end()); 172 waiter->MarkDirectDependencyResolved(node); 173 174 // Recursively mark nodes as resolved. 175 if ((waiter->state() == ItemNode::DEFINED || 176 waiter->state() == ItemNode::PENDING_DEPS) && 177 waiter->unresolved_dependencies().empty()) 178 MarkItemResolvedLocked(waiter); 179 } 180 } 181 182 std::string ItemTree::CheckForCircularDependenciesLocked( 183 const std::vector<const ItemNode*>& bad_nodes) const { 184 std::vector<const ItemNode*> cycle; 185 if (!RecursiveFindCycle(bad_nodes[0], bad_nodes[0], &cycle)) 186 return std::string(); // Didn't find a cycle, something else is wrong. 187 188 cycle.push_back(bad_nodes[0]); 189 std::string ret = "There is a dependency cycle:"; 190 191 // Walk backwards since the dependency arrows point in the reverse direction. 192 for (int i = static_cast<int>(cycle.size()) - 1; i >= 0; i--) { 193 ret += "\n \"" + cycle[i]->item()->label().GetUserVisibleName(false) + 194 "\""; 195 if (i != 0) 196 ret += " ->"; 197 } 198 199 return ret; 200 } 201