Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(360)

Side by Side Diff: tools/gn/header_checker.cc

Issue 946043002: GN header checker enhancements. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@check3
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/gn/header_checker.h ('k') | tools/gn/label.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « tools/gn/header_checker.h ('k') | tools/gn/label.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698