Chromium Code Reviews| Index: tools/gn/header_checker.cc | 
| diff --git a/tools/gn/header_checker.cc b/tools/gn/header_checker.cc | 
| index ea6384440bddf9d0afd41fff850d87b70aa0e3e1..423929b0080aec5ef820ead271cc2f8779b9b8cf 100644 | 
| --- a/tools/gn/header_checker.cc | 
| +++ b/tools/gn/header_checker.cc | 
| @@ -115,6 +115,13 @@ std::string GetDependencyChainPublicError( | 
| return ret; | 
| } | 
| +// Returns true if the two targets have the same label not counting the | 
| +// toolchain. | 
| +bool TargetLabelsMatchExceptToolchain(const Target* a, const Target* b) { | 
| + return a->label().dir() == b->label().dir() && | 
| 
 
scottmg
2015/02/20 22:41:40
maybe GetWithNoToolchain() and == would be clearer
 
brettw
2015/02/20 22:50:03
Slightly but that's constructing extra strings for
 
 | 
| + a->label().name() == b->label().name(); | 
| +} | 
| + | 
| } // namespace | 
| HeaderChecker::HeaderChecker(const BuildSettings* build_settings, | 
| @@ -155,26 +162,20 @@ void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) { | 
| type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC) | 
| continue; | 
| - // Do a first pass to find if this should be skipped. All targets including | 
| - // this source file must exclude it from checking, or any target | 
| - // must mark it as generated (for cases where one target generates a file, | 
| - // and another lists it as a source to compile it). | 
| - if (!force_check) { | 
| - bool check_includes = false; | 
| - bool is_generated = false; | 
| - for (const auto& vect_i : file.second) { | 
| - check_includes |= vect_i.target->check_includes(); | 
| - is_generated |= vect_i.is_generated; | 
| - } | 
| - if (!check_includes || is_generated) | 
| - continue; | 
| - } | 
| + // If any target marks it as generated, don't check it. | 
| + bool is_generated = false; | 
| + for (const auto& vect_i : file.second) | 
| + is_generated |= vect_i.is_generated; | 
| + if (is_generated) | 
| + continue; | 
| for (const auto& vect_i : file.second) { | 
| - pool->PostWorkerTaskWithShutdownBehavior( | 
| - FROM_HERE, | 
| - base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first), | 
| - base::SequencedWorkerPool::BLOCK_SHUTDOWN); | 
| + if (vect_i.target->check_includes()) { | 
| + pool->PostWorkerTaskWithShutdownBehavior( | 
| + FROM_HERE, | 
| + base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first), | 
| + base::SequencedWorkerPool::BLOCK_SHUTDOWN); | 
| + } | 
| } | 
| } | 
| @@ -336,6 +337,18 @@ bool HeaderChecker::CheckInclude(const Target* from_target, | 
| if (to_target == from_target) | 
| return true; | 
| + // Additionally, allow a target to include files from that same target | 
| + // in other toolchains. This is a bit of a hack to account for the fact that | 
| + // the include finder doesn't understand the preprocessor. | 
| + // | 
| + // If a source file conditionally depends on a platform-specific include in | 
| + // the same target, and there is a cross-compile such that GN sees | 
| + // definitions of the target both with and without that include, it would | 
| + // give an error that the target needs to depend on itself in the other | 
| + // toolchain (where the platform-specific header is defined as a source). | 
| + if (TargetLabelsMatchExceptToolchain(to_target, from_target)) | 
| + return true; | 
| + | 
| bool is_permitted_chain = false; | 
| if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) { | 
| DCHECK(chain.size() >= 2); | 
| @@ -378,20 +391,7 @@ bool HeaderChecker::CheckInclude(const Target* from_target, | 
| if (!found_dependency) { | 
| DCHECK(!last_error.has_error()); | 
| - | 
| - std::string msg = "It is not in any dependency of " + | 
| - from_target->label().GetUserVisibleName(false); | 
| - msg += "\nThe include file is in the target(s):\n"; | 
| - for (const auto& target : targets) | 
| - msg += " " + target.target->label().GetUserVisibleName(false) + "\n"; | 
| - if (targets.size() > 1) | 
| - msg += "at least one of "; | 
| - msg += "which should somehow be reachable from " + | 
| - from_target->label().GetUserVisibleName(false); | 
| - | 
| - // Danger: must call CreatePersistentRange to put in Err. | 
| - *err = Err(CreatePersistentRange(source_file, range), | 
| - "Include not allowed.", msg); | 
| + *err = MakeUnreachableError(source_file, range, from_target, targets); | 
| return false; | 
| } | 
| if (last_error.has_error()) { | 
| @@ -508,3 +508,62 @@ bool HeaderChecker::IsDependencyOf(const Target* search_for, | 
| return false; | 
| } | 
| + | 
| +Err HeaderChecker::MakeUnreachableError( | 
| + const InputFile& source_file, | 
| + const LocationRange& range, | 
| + const Target* from_target, | 
| + const TargetVector& targets) { | 
| + // Normally the toolchains will all match, but when cross-compiling, we can | 
| + // get targets with more than one toolchain in the list of possibilities. | 
| + std::vector<const Target*> targets_with_matching_toolchains; | 
| + std::vector<const Target*> targets_with_other_toolchains; | 
| + for (const TargetInfo& candidate : targets) { | 
| + if (candidate.target->toolchain() == from_target->toolchain()) | 
| + targets_with_matching_toolchains.push_back(candidate.target); | 
| + else | 
| + targets_with_other_toolchains.push_back(candidate.target); | 
| + } | 
| + | 
| + // It's common when cross-compiling to have a target with the same file in | 
| + // more than one toolchain. We could output all of them, but this is | 
| + // generally confusing to people (most end-users won't understand toolchains | 
| + // well). | 
| + // | 
| + // So delete any candidates in other toolchains that also appear in the same | 
| + // toolchain as the from_target. | 
| + for (int other_index = 0; | 
| + other_index < static_cast<int>(targets_with_other_toolchains.size()); | 
| + other_index++) { | 
| + for (const Target* cur_matching : targets_with_matching_toolchains) { | 
| + if (TargetLabelsMatchExceptToolchain( | 
| + cur_matching, targets_with_other_toolchains[other_index])) { | 
| + // Found a duplicate, erase it. | 
| + targets_with_other_toolchains.erase( | 
| + targets_with_other_toolchains.begin() + other_index); | 
| + other_index--; | 
| + break; | 
| + } | 
| + } | 
| + } | 
| + | 
| + // Only display toolchains on labels if they don't all match. | 
| + bool include_toolchain = !targets_with_other_toolchains.empty(); | 
| + | 
| + std::string msg = "It is not in any dependency of\n " + | 
| + from_target->label().GetUserVisibleName(include_toolchain); | 
| + msg += "\nThe include file is in the target(s):\n"; | 
| + for (const auto& target : targets_with_matching_toolchains) | 
| + msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n"; | 
| + for (const auto& target : targets_with_other_toolchains) | 
| + msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n"; | 
| + if (targets_with_other_toolchains.size() + | 
| + targets_with_matching_toolchains.size() > 1) | 
| + msg += "at least one of "; | 
| + msg += "which should somehow be reachable."; | 
| + | 
| + // Danger: must call CreatePersistentRange to put in Err. | 
| + return Err(CreatePersistentRange(source_file, range), | 
| + "Include not allowed.", msg); | 
| +} | 
| + |