Index: tools/gn/header_checker.cc |
diff --git a/tools/gn/header_checker.cc b/tools/gn/header_checker.cc |
index f2c9db51df76ff3ab70e737655aa06bb952cb3e5..2193030bf2a9f9c3a18de51bd1705758971d2c8e 100644 |
--- a/tools/gn/header_checker.cc |
+++ b/tools/gn/header_checker.cc |
@@ -275,6 +275,7 @@ bool HeaderChecker::CheckInclude(const Target* from_target, |
const TargetVector& targets = found->second; |
std::vector<const Target*> chain; // Prevent reallocating in the loop. |
+ bool direct_dependent_configs_apply = false; |
// For all targets containing this file, we require that at least one be |
// a dependency of the current target, and all targets that are dependencies |
@@ -287,7 +288,13 @@ bool HeaderChecker::CheckInclude(const Target* from_target, |
if (to_target == from_target) |
return true; |
- if (IsDependencyOf(to_target, from_target, &chain)) { |
+ bool has_direct_dependent_compiler_settings = |
+ HasDirectDependentCompilerSettings(to_target); |
+ if (IsDependencyOf(to_target, |
+ from_target, |
+ has_direct_dependent_compiler_settings, |
+ &chain, |
+ &direct_dependent_configs_apply)) { |
DCHECK(chain.size() >= 2); |
DCHECK(chain[0] == to_target); |
DCHECK(chain[chain.size() - 1] == from_target); |
@@ -319,14 +326,13 @@ bool HeaderChecker::CheckInclude(const Target* from_target, |
// If the to_target has direct_dependent_configs, they must apply to the |
// from_target. |
- if (HasDirectDependentCompilerSettings(to_target)) { |
- size_t problematic_index; |
- if (!DoDirectDependentConfigsApply(chain, &problematic_index)) { |
- *err = Err(CreatePersistentRange(source_file, range), |
- "Can't include this header from here.", |
- GetDependentConfigChainError(chain, problematic_index)); |
- return false; |
- } |
+ if (has_direct_dependent_compiler_settings && |
+ !direct_dependent_configs_apply) { |
+ size_t problematic_index = GetDependentConfigChainProblemIndex(chain); |
+ *err = Err(CreatePersistentRange(source_file, range), |
+ "Can't include this header from here.", |
+ GetDependentConfigChainError(chain, problematic_index)); |
+ return false; |
} |
found_dependency = true; |
@@ -374,33 +380,81 @@ bool HeaderChecker::CheckInclude(const Target* from_target, |
bool HeaderChecker::IsDependencyOf(const Target* search_for, |
const Target* search_from, |
- std::vector<const Target*>* chain) const { |
- std::set<const Target*> checked; |
- return IsDependencyOf(search_for, search_from, chain, &checked); |
+ bool prefer_direct_dependent_configs, |
+ std::vector<const Target*>* chain, |
+ bool* direct_dependent_configs_apply) const { |
+ if (search_for == search_from) |
+ return false; |
+ |
+ // Find the shortest chain that forwards dependent configs, if one exists. |
+ if (prefer_direct_dependent_configs && |
+ IsDependencyOf(search_for, search_from, true, chain)) { |
+ if (direct_dependent_configs_apply) |
+ *direct_dependent_configs_apply = true; |
+ return true; |
+ } |
+ |
+ // If not, try to find any dependency chain at all. |
+ if (IsDependencyOf(search_for, search_from, false, chain)) { |
+ if (direct_dependent_configs_apply) |
+ *direct_dependent_configs_apply = false; |
+ return true; |
+ } |
+ |
+ return false; |
} |
bool HeaderChecker::IsDependencyOf(const Target* search_for, |
const Target* search_from, |
- std::vector<const Target*>* chain, |
- std::set<const Target*>* checked) const { |
- if (checked->find(search_for) != checked->end()) |
- return false; // Already checked this subtree. |
- |
- const LabelTargetVector& deps = search_from->deps(); |
- for (size_t i = 0; i < deps.size(); i++) { |
- if (deps[i].ptr == search_for) { |
- // Found it. |
+ bool requires_dependent_configs, |
+ std::vector<const Target*>* chain) const { |
+ // This method conducts a breadth-first search through the dependency graph |
+ // to find a shortest chain from search_from to search_for. |
+ // |
+ // work_queue maintains a queue of targets which need to be considered as |
+ // part of this chain, in the order they were first traversed. |
+ // |
+ // Each time a new transitive dependency of search_from is discovered for |
+ // the first time, it is added to work_queue and a "breadcrumb" is added, |
+ // indicating which target it was reached from when first discovered. |
+ // |
+ // Once this search finds search_for, the breadcrumbs are used to reconstruct |
+ // a shortest dependency chain (in reverse order) from search_from to |
+ // search_for. |
+ |
+ std::map<const Target*, const Target*> breadcrumbs; |
+ std::queue<const Target*> work_queue; |
+ work_queue.push(search_from); |
+ |
+ while (!work_queue.empty()) { |
+ const Target* target = work_queue.front(); |
+ work_queue.pop(); |
+ |
+ if (target == search_for) { |
+ // Found it! Reconstruct the chain. |
chain->clear(); |
- chain->push_back(deps[i].ptr); |
+ while (target != search_from) { |
+ chain->push_back(target); |
+ target = breadcrumbs[target]; |
+ } |
chain->push_back(search_from); |
return true; |
} |
- // Recursive search. |
- checked->insert(deps[i].ptr); |
- if (IsDependencyOf(search_for, deps[i].ptr, chain, checked)) { |
- chain->push_back(search_from); |
- return true; |
+ // If the callee requires direct-dependent configs be forwarded, then |
+ // only targets for which they will be forwarded should be explored. |
+ // Groups implicitly forward direct-dependent configs of all of their deps. |
+ bool uses_all_deps = !requires_dependent_configs || target == search_from || |
+ target->output_type() == Target::GROUP; |
+ |
+ const LabelTargetVector& deps = |
+ uses_all_deps ? target->deps() |
+ : target->forward_dependent_configs().vector(); |
+ for (size_t i = 0; i < deps.size(); i++) { |
+ bool seeing_for_first_time = |
+ breadcrumbs.insert(std::make_pair(deps[i].ptr, target)).second; |
+ if (seeing_for_first_time) |
+ work_queue.push(deps[i].ptr); |
} |
} |
@@ -408,24 +462,17 @@ bool HeaderChecker::IsDependencyOf(const Target* search_for, |
} |
// static |
-bool HeaderChecker::DoDirectDependentConfigsApply( |
- const std::vector<const Target*>& chain, |
- size_t* problematic_index) { |
+size_t HeaderChecker::GetDependentConfigChainProblemIndex( |
+ const std::vector<const Target*>& chain) { |
// Direct dependent configs go up the chain one level with the following |
// exceptions: |
// - Skip over groups |
// - Skip anything that explicitly forwards it |
- // All chains should be at least two (or it wouldn't be a chain). |
- DCHECK(chain.size() >= 2); |
- |
- // A chain of length 2 is always OK as far as direct dependent configs are |
- // concerned since the targets are direct dependents. |
- if (chain.size() == 2) |
- return true; |
+ // Chains of length less than three have no problems. |
+ // These should have been filtered out earlier. |
+ DCHECK(chain.size() >= 3); |
- // Check the middle configs to make sure they're either groups or configs |
- // are forwarded. |
for (size_t i = 1; i < chain.size() - 1; i++) { |
if (chain[i]->output_type() == Target::GROUP) |
continue; // This one is OK, skip to next one. |
@@ -436,11 +483,10 @@ bool HeaderChecker::DoDirectDependentConfigsApply( |
chain[i]->forward_dependent_configs(); |
if (std::find_if(forwarded.begin(), forwarded.end(), |
LabelPtrPtrEquals<Target>(chain[i - 1])) == |
- forwarded.end()) { |
- *problematic_index = i; |
- return false; |
- } |
+ forwarded.end()) |
+ return i; |
} |
- return true; |
+ CHECK(false) << "Unable to diagnose dependent config chain problem."; |
+ return 0; |
} |