Index: tools/gn/target.cc |
diff --git a/tools/gn/target.cc b/tools/gn/target.cc |
index 905ee4f3c65cc81eea8460543493a7b5011f605e..7dd192181ea891e96aa6a9762470fc63af02496e 100644 |
--- a/tools/gn/target.cc |
+++ b/tools/gn/target.cc |
@@ -129,6 +129,61 @@ bool EnsureFileIsGeneratedByDependency(const Target* target, |
return false; |
} |
+// check_this indicates if the given target should be matched against the |
+// patterns. It should be set to false for the first call since assert_no_deps |
+// shouldn't match the target itself. |
+// |
+// visited should point to an empty set, this will be used to prevent |
+// multiple visits. |
+// |
+// *failure_path_str will be filled with a string describing the path of the |
+// dependency failure, and failure_pattern will indicate the pattern in |
+// assert_no that matched the target. |
+// |
+// Returns true if everything is OK. failure_path_str and failure_pattern_index |
+// will be unchanged in this case. |
+bool RecursiveCheckAssertNoDeps(const Target* target, |
+ bool check_this, |
+ const std::vector<LabelPattern>& assert_no, |
+ std::set<const Target*>* visited, |
+ std::string* failure_path_str, |
+ const LabelPattern** failure_pattern) { |
+ static const char kIndentPath[] = " "; |
+ |
+ if (visited->find(target) != visited->end()) |
+ return true; // Already checked this target. |
+ visited->insert(target); |
+ |
+ if (check_this) { |
+ // Check this target against the given list of patterns. |
+ for (const LabelPattern& pattern : assert_no) { |
+ if (pattern.Matches(target->label())) { |
+ // Found a match. |
+ *failure_pattern = &pattern; |
+ *failure_path_str = |
+ kIndentPath + target->label().GetUserVisibleName(false); |
+ return false; |
+ } |
+ } |
+ } |
+ |
+ // Recursively check dependencies. |
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) { |
+ if (pair.ptr->output_type() == Target::EXECUTABLE) |
+ continue; |
+ if (!RecursiveCheckAssertNoDeps(pair.ptr, true, assert_no, visited, |
+ failure_path_str, failure_pattern)) { |
+ // To reconstruct the path, prepend the current target to the error. |
+ std::string prepend_path = |
+ kIndentPath + target->label().GetUserVisibleName(false) + " ->\n"; |
+ failure_path_str->insert(0, prepend_path); |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+} |
+ |
} // namespace |
Target::Target(const Settings* settings, const Label& label) |
@@ -234,6 +289,8 @@ bool Target::OnResolved(Err* err) { |
return false; |
if (!CheckNoNestedStaticLibs(err)) |
return false; |
+ if (!CheckAssertNoDeps(err)) |
+ return false; |
CheckSourcesGenerated(); |
} |
@@ -604,6 +661,27 @@ bool Target::CheckNoNestedStaticLibs(Err* err) const { |
return true; |
} |
+bool Target::CheckAssertNoDeps(Err* err) const { |
+ if (assert_no_deps_.empty()) |
+ return true; |
+ |
+ std::set<const Target*> visited; |
+ std::string failure_path_str; |
+ const LabelPattern* failure_pattern = nullptr; |
+ |
+ if (!RecursiveCheckAssertNoDeps(this, false, assert_no_deps_, &visited, |
+ &failure_path_str, &failure_pattern)) { |
+ *err = Err(defined_from(), "assert_no_deps failed.", |
+ label().GetUserVisibleName(false) + |
+ " has an assert_no_deps entry:\n " + |
+ failure_pattern->Describe() + |
+ "\nwhich fails for the dependency path:\n" + |
+ failure_path_str); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
void Target::CheckSourcesGenerated() const { |
// Checks that any inputs or sources to this target that are in the build |
// directory are generated by a target that this one transitively depends on |