OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "tools/gn/header_checker.h" | 5 #include "tools/gn/header_checker.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
108 ret.append(" -->"); | 108 ret.append(" -->"); |
109 else | 109 else |
110 ret.append(" --[private]-->"); | 110 ret.append(" --[private]-->"); |
111 } | 111 } |
112 ret.append("\n"); | 112 ret.append("\n"); |
113 } | 113 } |
114 } | 114 } |
115 return ret; | 115 return ret; |
116 } | 116 } |
117 | 117 |
118 // Returns true if the two targets have the same label not counting the | |
119 // toolchain. | |
120 bool TargetLabelsMatchExceptToolchain(const Target* a, const Target* b) { | |
121 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
| |
122 a->label().name() == b->label().name(); | |
123 } | |
124 | |
118 } // namespace | 125 } // namespace |
119 | 126 |
120 HeaderChecker::HeaderChecker(const BuildSettings* build_settings, | 127 HeaderChecker::HeaderChecker(const BuildSettings* build_settings, |
121 const std::vector<const Target*>& targets) | 128 const std::vector<const Target*>& targets) |
122 : main_loop_(base::MessageLoop::current()), | 129 : main_loop_(base::MessageLoop::current()), |
123 build_settings_(build_settings) { | 130 build_settings_(build_settings) { |
124 for (const auto& target : targets) | 131 for (const auto& target : targets) |
125 AddTargetToFileMap(target, &file_map_); | 132 AddTargetToFileMap(target, &file_map_); |
126 } | 133 } |
127 | 134 |
(...skipping 20 matching lines...) Expand all Loading... | |
148 | 155 |
149 scoped_refptr<base::SequencedWorkerPool> pool( | 156 scoped_refptr<base::SequencedWorkerPool> pool( |
150 new base::SequencedWorkerPool(16, "HeaderChecker")); | 157 new base::SequencedWorkerPool(16, "HeaderChecker")); |
151 for (const auto& file : files) { | 158 for (const auto& file : files) { |
152 // Only check C-like source files (RC files also have includes). | 159 // Only check C-like source files (RC files also have includes). |
153 SourceFileType type = GetSourceFileType(file.first); | 160 SourceFileType type = GetSourceFileType(file.first); |
154 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C && | 161 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C && |
155 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC) | 162 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC) |
156 continue; | 163 continue; |
157 | 164 |
158 // Do a first pass to find if this should be skipped. All targets including | 165 // If any target marks it as generated, don't check it. |
159 // this source file must exclude it from checking, or any target | 166 bool is_generated = false; |
160 // must mark it as generated (for cases where one target generates a file, | 167 for (const auto& vect_i : file.second) |
161 // and another lists it as a source to compile it). | 168 is_generated |= vect_i.is_generated; |
162 if (!force_check) { | 169 if (is_generated) |
163 bool check_includes = false; | 170 continue; |
164 bool is_generated = false; | |
165 for (const auto& vect_i : file.second) { | |
166 check_includes |= vect_i.target->check_includes(); | |
167 is_generated |= vect_i.is_generated; | |
168 } | |
169 if (!check_includes || is_generated) | |
170 continue; | |
171 } | |
172 | 171 |
173 for (const auto& vect_i : file.second) { | 172 for (const auto& vect_i : file.second) { |
174 pool->PostWorkerTaskWithShutdownBehavior( | 173 if (vect_i.target->check_includes()) { |
175 FROM_HERE, | 174 pool->PostWorkerTaskWithShutdownBehavior( |
176 base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first), | 175 FROM_HERE, |
177 base::SequencedWorkerPool::BLOCK_SHUTDOWN); | 176 base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first), |
177 base::SequencedWorkerPool::BLOCK_SHUTDOWN); | |
178 } | |
178 } | 179 } |
179 } | 180 } |
180 | 181 |
181 // After this call we're single-threaded again. | 182 // After this call we're single-threaded again. |
182 pool->Shutdown(); | 183 pool->Shutdown(); |
183 } | 184 } |
184 | 185 |
185 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) { | 186 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) { |
186 Err err; | 187 Err err; |
187 if (!CheckFile(target, file, &err)) { | 188 if (!CheckFile(target, file, &err)) { |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
329 Err last_error; | 330 Err last_error; |
330 | 331 |
331 bool found_dependency = false; | 332 bool found_dependency = false; |
332 for (size_t i = 0; i < targets.size(); i++) { | 333 for (size_t i = 0; i < targets.size(); i++) { |
333 // We always allow source files in a target to include headers also in that | 334 // We always allow source files in a target to include headers also in that |
334 // target. | 335 // target. |
335 const Target* to_target = targets[i].target; | 336 const Target* to_target = targets[i].target; |
336 if (to_target == from_target) | 337 if (to_target == from_target) |
337 return true; | 338 return true; |
338 | 339 |
340 // Additionally, allow a target to include files from that same target | |
341 // in other toolchains. This is a bit of a hack to account for the fact that | |
342 // the include finder doesn't understand the preprocessor. | |
343 // | |
344 // If a source file conditionally depends on a platform-specific include in | |
345 // the same target, and there is a cross-compile such that GN sees | |
346 // definitions of the target both with and without that include, it would | |
347 // give an error that the target needs to depend on itself in the other | |
348 // toolchain (where the platform-specific header is defined as a source). | |
349 if (TargetLabelsMatchExceptToolchain(to_target, from_target)) | |
350 return true; | |
351 | |
339 bool is_permitted_chain = false; | 352 bool is_permitted_chain = false; |
340 if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) { | 353 if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) { |
341 DCHECK(chain.size() >= 2); | 354 DCHECK(chain.size() >= 2); |
342 DCHECK(chain[0].target == to_target); | 355 DCHECK(chain[0].target == to_target); |
343 DCHECK(chain[chain.size() - 1].target == from_target); | 356 DCHECK(chain[chain.size() - 1].target == from_target); |
344 | 357 |
345 found_dependency = true; | 358 found_dependency = true; |
346 | 359 |
347 if (targets[i].is_public && is_permitted_chain) { | 360 if (targets[i].is_public && is_permitted_chain) { |
348 // This one is OK, we're done. | 361 // This one is OK, we're done. |
(...skipping 22 matching lines...) Expand all Loading... | |
371 to_target->allow_circular_includes_from().end()) { | 384 to_target->allow_circular_includes_from().end()) { |
372 // Not a dependency, but this include is whitelisted from the destination. | 385 // Not a dependency, but this include is whitelisted from the destination. |
373 found_dependency = true; | 386 found_dependency = true; |
374 last_error = Err(); | 387 last_error = Err(); |
375 break; | 388 break; |
376 } | 389 } |
377 } | 390 } |
378 | 391 |
379 if (!found_dependency) { | 392 if (!found_dependency) { |
380 DCHECK(!last_error.has_error()); | 393 DCHECK(!last_error.has_error()); |
381 | 394 *err = MakeUnreachableError(source_file, range, from_target, targets); |
382 std::string msg = "It is not in any dependency of " + | |
383 from_target->label().GetUserVisibleName(false); | |
384 msg += "\nThe include file is in the target(s):\n"; | |
385 for (const auto& target : targets) | |
386 msg += " " + target.target->label().GetUserVisibleName(false) + "\n"; | |
387 if (targets.size() > 1) | |
388 msg += "at least one of "; | |
389 msg += "which should somehow be reachable from " + | |
390 from_target->label().GetUserVisibleName(false); | |
391 | |
392 // Danger: must call CreatePersistentRange to put in Err. | |
393 *err = Err(CreatePersistentRange(source_file, range), | |
394 "Include not allowed.", msg); | |
395 return false; | 395 return false; |
396 } | 396 } |
397 if (last_error.has_error()) { | 397 if (last_error.has_error()) { |
398 // Found at least one dependency chain above, but it had an error. | 398 // Found at least one dependency chain above, but it had an error. |
399 *err = last_error; | 399 *err = last_error; |
400 return false; | 400 return false; |
401 } | 401 } |
402 | 402 |
403 // One thing we didn't check for is targets that expose their dependents | 403 // One thing we didn't check for is targets that expose their dependents |
404 // headers in their own public headers. | 404 // headers in their own public headers. |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
501 first_time = false; | 501 first_time = false; |
502 for (const auto& dep : target->private_deps()) { | 502 for (const auto& dep : target->private_deps()) { |
503 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second) | 503 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second) |
504 work_queue.push(ChainLink(dep.ptr, false)); | 504 work_queue.push(ChainLink(dep.ptr, false)); |
505 } | 505 } |
506 } | 506 } |
507 } | 507 } |
508 | 508 |
509 return false; | 509 return false; |
510 } | 510 } |
511 | |
512 Err HeaderChecker::MakeUnreachableError( | |
513 const InputFile& source_file, | |
514 const LocationRange& range, | |
515 const Target* from_target, | |
516 const TargetVector& targets) { | |
517 // Normally the toolchains will all match, but when cross-compiling, we can | |
518 // get targets with more than one toolchain in the list of possibilities. | |
519 std::vector<const Target*> targets_with_matching_toolchains; | |
520 std::vector<const Target*> targets_with_other_toolchains; | |
521 for (const TargetInfo& candidate : targets) { | |
522 if (candidate.target->toolchain() == from_target->toolchain()) | |
523 targets_with_matching_toolchains.push_back(candidate.target); | |
524 else | |
525 targets_with_other_toolchains.push_back(candidate.target); | |
526 } | |
527 | |
528 // It's common when cross-compiling to have a target with the same file in | |
529 // more than one toolchain. We could output all of them, but this is | |
530 // generally confusing to people (most end-users won't understand toolchains | |
531 // well). | |
532 // | |
533 // So delete any candidates in other toolchains that also appear in the same | |
534 // toolchain as the from_target. | |
535 for (int other_index = 0; | |
536 other_index < static_cast<int>(targets_with_other_toolchains.size()); | |
537 other_index++) { | |
538 for (const Target* cur_matching : targets_with_matching_toolchains) { | |
539 if (TargetLabelsMatchExceptToolchain( | |
540 cur_matching, targets_with_other_toolchains[other_index])) { | |
541 // Found a duplicate, erase it. | |
542 targets_with_other_toolchains.erase( | |
543 targets_with_other_toolchains.begin() + other_index); | |
544 other_index--; | |
545 break; | |
546 } | |
547 } | |
548 } | |
549 | |
550 // Only display toolchains on labels if they don't all match. | |
551 bool include_toolchain = !targets_with_other_toolchains.empty(); | |
552 | |
553 std::string msg = "It is not in any dependency of\n " + | |
554 from_target->label().GetUserVisibleName(include_toolchain); | |
555 msg += "\nThe include file is in the target(s):\n"; | |
556 for (const auto& target : targets_with_matching_toolchains) | |
557 msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n"; | |
558 for (const auto& target : targets_with_other_toolchains) | |
559 msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n"; | |
560 if (targets_with_other_toolchains.size() + | |
561 targets_with_matching_toolchains.size() > 1) | |
562 msg += "at least one of "; | |
563 msg += "which should somehow be reachable."; | |
564 | |
565 // Danger: must call CreatePersistentRange to put in Err. | |
566 return Err(CreatePersistentRange(source_file, range), | |
567 "Include not allowed.", msg); | |
568 } | |
569 | |
OLD | NEW |