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"
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"
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
);
38 if (RecursiveFindCycle(look_for
, cur
, cycle
)) {
39 // Found a cycle inside this one, record our path and return.
40 cycle
->push_back(cur
);
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())
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
,
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
))
85 // No more pending deps.
86 MarkItemResolvedLocked(node
);
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
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();
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) +
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())
163 return Err(Location(), "Unresolved dependencies.", depstring
);
166 void ItemTree::MarkItemResolvedLocked(ItemNode
* node
) {
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) +