Chromium Code Reviews| Index: tools/gn/header_checker.cc |
| diff --git a/tools/gn/header_checker.cc b/tools/gn/header_checker.cc |
| index 62a0f44d580baa41a03d2938fd6f36dc0b7d6ba7..29e0d485495e84e5fde51a4030e9b9ecc60c3189 100644 |
| --- a/tools/gn/header_checker.cc |
| +++ b/tools/gn/header_checker.cc |
| @@ -274,6 +274,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 |
| @@ -286,7 +287,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); |
| @@ -318,9 +325,10 @@ 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)) { |
| + if (has_direct_dependent_compiler_settings && |
| + !direct_dependent_configs_apply) { |
| + size_t problematic_index = GetDependentConfigChainProblemIndex(chain); |
| + if (!direct_dependent_configs_apply) { |
| *err = Err(CreatePersistentRange(source_file, range), |
| "Can't include this header from here.", |
| GetDependentConfigChainError(chain, problematic_index)); |
| @@ -373,33 +381,67 @@ 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 path 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 path 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 maps targets to the target which receives dependent configs from it. |
|
brettw
2014/07/31 20:50:49
I'm a bit confused about this function. Rather tha
jbroman
2014/07/31 22:05:37
It maps targets to their predecessor in a shortest
|
| + std::map<const Target*, const Target*> breadcrumbs; |
| + std::queue<const Target*> work_queue; |
| + breadcrumbs.insert(std::make_pair( |
| + search_from, static_cast<const Target*>(NULL))); |
|
brettw
2014/07/31 20:50:49
I see you add this to make the reconstruction step
jbroman
2014/07/31 22:05:37
It's unnecessarily clever. I can just make getting
|
| + 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); |
| - chain->push_back(search_from); |
| + while (target) { |
| + chain->push_back(target); |
| + target = breadcrumbs[target]; |
| + } |
| 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; |
| + // All deps of a group implicitly forward dependent configs. |
| + 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(); |
| + 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); |
| } |
| } |
| @@ -407,24 +449,18 @@ 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. |
| @@ -434,11 +470,10 @@ bool HeaderChecker::DoDirectDependentConfigsApply( |
| const LabelTargetVector& forwarded = 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; |
| } |