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

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

Issue 561273003: Add public deps to GN (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: merge Created 6 years, 3 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/header_checker_unittest.cc » ('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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 input_file.name(), &clone_input_file, &tokens, &parse_root); 60 input_file.name(), &clone_input_file, &tokens, &parse_root);
61 clone_input_file->SetContents(input_file.contents()); 61 clone_input_file->SetContents(input_file.contents());
62 62
63 return LocationRange( 63 return LocationRange(
64 Location(clone_input_file, range.begin().line_number(), 64 Location(clone_input_file, range.begin().line_number(),
65 range.begin().char_offset()), 65 range.begin().char_offset()),
66 Location(clone_input_file, range.end().line_number(), 66 Location(clone_input_file, range.end().line_number(),
67 range.end().char_offset())); 67 range.end().char_offset()));
68 } 68 }
69 69
70 // Returns true if the given config could affect how the compiler runs (rather 70 // Given a reverse dependency chain where the target chain[0]'s includes are
71 // than being empty or just affecting linker flags). 71 // being used by chain[end] and not all deps are public, returns the string
72 bool ConfigHasCompilerSettings(const Config* config) { 72 // describing the error.
73 const ConfigValues& values = config->config_values(); 73 std::string GetDependencyChainPublicError(
74 return 74 const std::vector<const Target*>& chain) {
75 !values.cflags().empty() || 75 std::string ret = "The target " +
76 !values.cflags_c().empty() || 76 chain[chain.size() - 1]->label().GetUserVisibleName(false) + "\n" +
77 !values.cflags_cc().empty() || 77 "is including a file in " + chain[0]->label().GetUserVisibleName(false) +
78 !values.cflags_objc().empty() || 78 "\n";
79 !values.cflags_objcc().empty() || 79 if (chain.size() > 2) {
80 !values.defines().empty() || 80 // Indirect dependency chain, print the chain.
81 !values.include_dirs().empty(); 81 ret += "\nIt's usually best to depend directly on the destination target.\n"
82 } 82 "In some cases, the destination target is considered a subcomponent\n"
83 "of an intermediate target. In this case, the intermediate target\n"
84 "should depend publicly on the destination to forward the ability\n"
85 "to include headers.\n"
86 "\n"
87 "Dependency chain (there may be others):\n";
83 88
84 // Returns true if the given target has any direct dependent configs with 89 for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
85 // compiler settings in it. 90 ret.append(" " + chain[i]->label().GetUserVisibleName(false));
86 bool HasDirectDependentCompilerSettings(const Target* target) { 91 if (i != 0)
87 const UniqueVector<LabelConfigPair>& direct = 92 ret.append(" ->");
88 target->direct_dependent_configs(); 93 ret.append("\n");
89 for (size_t i = 0; i < direct.size(); i++) { 94 }
90 if (ConfigHasCompilerSettings(direct[i].ptr))
91 return true;
92 } 95 }
93 return false; 96 return ret;
94 }
95
96 // Given a reverse dependency chain where the target chain[0]'s dependent
97 // configs don't apply to chain[end], returns the string describing the error.
98 // The problematic index is the target where the dependent configs were lost.
99 std::string GetDependentConfigChainError(
100 const std::vector<const Target*>& chain,
101 size_t problematic_index) {
102 // Erroneous dependent config chains are always at least three long, since
103 // dependent configs would apply if it was length two.
104 DCHECK(chain.size() >= 3);
105
106 std::string from_label =
107 chain[chain.size() - 1]->label().GetUserVisibleName(false);
108 std::string to_label =
109 chain[0]->label().GetUserVisibleName(false);
110 std::string problematic_label =
111 chain[problematic_index]->label().GetUserVisibleName(false);
112 std::string problematic_upstream_label =
113 chain[problematic_index - 1]->label().GetUserVisibleName(false);
114
115 return
116 "You have the dependency tree: SOURCE -> MID -> DEST\n"
117 "Where a file from:\n"
118 " SOURCE = " + from_label + "\n"
119 "is including a header from:\n"
120 " DEST = " + to_label + "\n"
121 "\n"
122 "DEST has direct_dependent_configs, and they don't apply to SOURCE "
123 "because\nSOURCE is more than one hop away. This means that DEST's "
124 "headers might not\nreceive the expected compiler flags.\n"
125 "\n"
126 "To fix this, make SOURCE depend directly on DEST.\n"
127 "\n"
128 "Alternatively, if the target:\n"
129 " MID = " + problematic_label + "\n"
130 "exposes DEST as part of its public API, you can declare this by "
131 "adding:\n"
132 " forward_dependent_configs_from = [\n"
133 " \"" + problematic_upstream_label + "\"\n"
134 " ]\n"
135 "to MID. This will apply DEST's direct dependent configs to SOURCE.\n";
136 } 97 }
137 98
138 } // namespace 99 } // namespace
139 100
140 HeaderChecker::HeaderChecker(const BuildSettings* build_settings, 101 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
141 const std::vector<const Target*>& targets) 102 const std::vector<const Target*>& targets)
142 : main_loop_(base::MessageLoop::current()), 103 : main_loop_(base::MessageLoop::current()),
143 build_settings_(build_settings) { 104 build_settings_(build_settings) {
144 for (size_t i = 0; i < targets.size(); i++) 105 for (size_t i = 0; i < targets.size(); i++)
145 AddTargetToFileMap(targets[i], &file_map_); 106 AddTargetToFileMap(targets[i], &file_map_);
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
333 // check it. It would be nice if we could give an error if this happens, but 294 // check it. It would be nice if we could give an error if this happens, but
334 // our include finder is too primitive and returns all includes, even if 295 // our include finder is too primitive and returns all includes, even if
335 // they're in a #if not executed in the current build. In that case, it's 296 // they're in a #if not executed in the current build. In that case, it's
336 // not unusual for the buildfiles to not specify that header at all. 297 // not unusual for the buildfiles to not specify that header at all.
337 FileMap::const_iterator found = file_map_.find(include_file); 298 FileMap::const_iterator found = file_map_.find(include_file);
338 if (found == file_map_.end()) 299 if (found == file_map_.end())
339 return true; 300 return true;
340 301
341 const TargetVector& targets = found->second; 302 const TargetVector& targets = found->second;
342 std::vector<const Target*> chain; // Prevent reallocating in the loop. 303 std::vector<const Target*> chain; // Prevent reallocating in the loop.
343 bool direct_dependent_configs_apply = false;
344 304
345 // For all targets containing this file, we require that at least one be 305 // For all targets containing this file, we require that at least one be
346 // a dependency of the current target, and all targets that are dependencies 306 // a dependency of the current target, and all targets that are dependencies
347 // must have the file listed in the public section. 307 // must have the file listed in the public section.
348 bool found_dependency = false; 308 bool found_dependency = false;
349 for (size_t i = 0; i < targets.size(); i++) { 309 for (size_t i = 0; i < targets.size(); i++) {
350 // We always allow source files in a target to include headers also in that 310 // We always allow source files in a target to include headers also in that
351 // target. 311 // target.
352 const Target* to_target = targets[i].target; 312 const Target* to_target = targets[i].target;
353 if (to_target == from_target) 313 if (to_target == from_target)
354 return true; 314 return true;
355 315
356 bool has_direct_dependent_compiler_settings = 316 bool is_public_dep_chain = false;
357 HasDirectDependentCompilerSettings(to_target); 317 if (IsDependencyOf(to_target, from_target, &chain, &is_public_dep_chain)) {
358 if (IsDependencyOf(to_target,
359 from_target,
360 has_direct_dependent_compiler_settings,
361 &chain,
362 &direct_dependent_configs_apply)) {
363 DCHECK(chain.size() >= 2); 318 DCHECK(chain.size() >= 2);
364 DCHECK(chain[0] == to_target); 319 DCHECK(chain[0] == to_target);
365 DCHECK(chain[chain.size() - 1] == from_target); 320 DCHECK(chain[chain.size() - 1] == from_target);
366 321
367 // The include is in a target that's a proper dependency. Verify that
368 // the including target has visibility.
369 if (!to_target->visibility().CanSeeMe(from_target->label())) {
370 std::string msg = "The included file is in " +
371 to_target->label().GetUserVisibleName(false) +
372 "\nwhich is not visible from " +
373 from_target->label().GetUserVisibleName(false) +
374 "\n(see \"gn help visibility\").";
375
376 // Danger: must call CreatePersistentRange to put in Err.
377 *err = Err(CreatePersistentRange(source_file, range),
378 "Including a header from non-visible target.", msg);
379 return false;
380 }
381
382 // The file must be public in the target. 322 // The file must be public in the target.
383 if (!targets[i].is_public) { 323 if (!targets[i].is_public) {
384 // Danger: must call CreatePersistentRange to put in Err. 324 // Danger: must call CreatePersistentRange to put in Err.
385 *err = Err(CreatePersistentRange(source_file, range), 325 *err = Err(CreatePersistentRange(source_file, range),
386 "Including a private header.", 326 "Including a private header.",
387 "This file is private to the target " + 327 "This file is private to the target " +
388 targets[i].target->label().GetUserVisibleName(false)); 328 targets[i].target->label().GetUserVisibleName(false));
389 return false; 329 return false;
390 } 330 }
391 331
392 // If the to_target has direct_dependent_configs, they must apply to the 332 // The dependency chain must be public.
393 // from_target. 333 if (!is_public_dep_chain) {
394 if (has_direct_dependent_compiler_settings &&
395 !direct_dependent_configs_apply) {
396 size_t problematic_index = GetDependentConfigChainProblemIndex(chain);
397 *err = Err(CreatePersistentRange(source_file, range), 334 *err = Err(CreatePersistentRange(source_file, range),
398 "Can't include this header from here.", 335 "Can't include this header from here.",
399 GetDependentConfigChainError(chain, problematic_index)); 336 GetDependencyChainPublicError(chain));
400 return false; 337 return false;
401 } 338 }
402 339
403 found_dependency = true; 340 found_dependency = true;
404 } else if ( 341 } else if (
405 to_target->allow_circular_includes_from().find(from_target->label()) != 342 to_target->allow_circular_includes_from().find(from_target->label()) !=
406 to_target->allow_circular_includes_from().end()) { 343 to_target->allow_circular_includes_from().end()) {
407 // Not a dependency, but this include is whitelisted from the destination. 344 // Not a dependency, but this include is whitelisted from the destination.
408 found_dependency = true; 345 found_dependency = true;
409 } 346 }
(...skipping 12 matching lines...) Expand all
422 359
423 // Danger: must call CreatePersistentRange to put in Err. 360 // Danger: must call CreatePersistentRange to put in Err.
424 *err = Err(CreatePersistentRange(source_file, range), 361 *err = Err(CreatePersistentRange(source_file, range),
425 "Include not allowed.", msg); 362 "Include not allowed.", msg);
426 return false; 363 return false;
427 } 364 }
428 365
429 // One thing we didn't check for is targets that expose their dependents 366 // One thing we didn't check for is targets that expose their dependents
430 // headers in their own public headers. 367 // headers in their own public headers.
431 // 368 //
432 // Say we have A -> B -> C. If C has direct_dependent_configs, everybody 369 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
433 // getting headers from C should get the configs also or things could be 370 // from C should get the configs also or things could be out-of-sync. Above,
434 // out-of-sync. Above, we check for A including C's headers directly, but A 371 // we check for A including C's headers directly, but A could also include a
435 // could also include a header from B that in turn includes a header from C. 372 // header from B that in turn includes a header from C.
436 // 373 //
437 // There are two ways to solve this: 374 // There are two ways to solve this:
438 // - If a public header in B includes C, force B to forward C's direct 375 // - If a public header in B includes C, force B to forward C's direct
439 // dependent configs. This is possible to check, but might be super 376 // dependent configs. This is possible to check, but might be super
440 // annoying because most targets (especially large leaf-node targets) 377 // annoying because most targets (especially large leaf-node targets)
441 // don't declare public/private headers and you'll get lots of false 378 // don't declare public/private headers and you'll get lots of false
442 // positives. 379 // positives.
443 // 380 //
444 // - Save the includes found in each file and actually compute the graph of 381 // - Save the includes found in each file and actually compute the graph of
445 // includes to detect when A implicitly includes C's header. This will not 382 // includes to detect when A implicitly includes C's header. This will not
446 // have the annoying false positive problem, but is complex to write. 383 // have the annoying false positive problem, but is complex to write.
447 384
448 return true; 385 return true;
449 } 386 }
450 387
451 bool HeaderChecker::IsDependencyOf(const Target* search_for, 388 bool HeaderChecker::IsDependencyOf(const Target* search_for,
452 const Target* search_from, 389 const Target* search_from,
453 bool prefer_direct_dependent_configs,
454 std::vector<const Target*>* chain, 390 std::vector<const Target*>* chain,
455 bool* direct_dependent_configs_apply) const { 391 bool* is_public) const {
456 if (search_for == search_from) 392 if (search_for == search_from) {
393 // A target is always visible from itself.
394 *is_public = true;
457 return false; 395 return false;
396 }
458 397
459 // Find the shortest chain that forwards dependent configs, if one exists. 398 // Find the shortest public dependency chain.
460 if (prefer_direct_dependent_configs && 399 if (IsDependencyOf(search_for, search_from, true, chain)) {
461 IsDependencyOf(search_for, search_from, true, chain)) { 400 *is_public = true;
462 if (direct_dependent_configs_apply)
463 *direct_dependent_configs_apply = true;
464 return true; 401 return true;
465 } 402 }
466 403
467 // If not, try to find any dependency chain at all. 404 // If not, try to find any dependency chain at all.
468 if (IsDependencyOf(search_for, search_from, false, chain)) { 405 if (IsDependencyOf(search_for, search_from, false, chain)) {
469 if (direct_dependent_configs_apply) 406 *is_public = false;
470 *direct_dependent_configs_apply = false;
471 return true; 407 return true;
472 } 408 }
473 409
410 *is_public = false;
474 return false; 411 return false;
475 } 412 }
476 413
477 bool HeaderChecker::IsDependencyOf(const Target* search_for, 414 bool HeaderChecker::IsDependencyOf(const Target* search_for,
478 const Target* search_from, 415 const Target* search_from,
479 bool requires_dependent_configs, 416 bool require_public,
480 std::vector<const Target*>* chain) const { 417 std::vector<const Target*>* chain) const {
481 // This method conducts a breadth-first search through the dependency graph 418 // This method conducts a breadth-first search through the dependency graph
482 // to find a shortest chain from search_from to search_for. 419 // to find a shortest chain from search_from to search_for.
483 // 420 //
484 // work_queue maintains a queue of targets which need to be considered as 421 // work_queue maintains a queue of targets which need to be considered as
485 // part of this chain, in the order they were first traversed. 422 // part of this chain, in the order they were first traversed.
486 // 423 //
487 // Each time a new transitive dependency of search_from is discovered for 424 // Each time a new transitive dependency of search_from is discovered for
488 // the first time, it is added to work_queue and a "breadcrumb" is added, 425 // the first time, it is added to work_queue and a "breadcrumb" is added,
489 // indicating which target it was reached from when first discovered. 426 // indicating which target it was reached from when first discovered.
(...skipping 14 matching lines...) Expand all
504 // Found it! Reconstruct the chain. 441 // Found it! Reconstruct the chain.
505 chain->clear(); 442 chain->clear();
506 while (target != search_from) { 443 while (target != search_from) {
507 chain->push_back(target); 444 chain->push_back(target);
508 target = breadcrumbs[target]; 445 target = breadcrumbs[target];
509 } 446 }
510 chain->push_back(search_from); 447 chain->push_back(search_from);
511 return true; 448 return true;
512 } 449 }
513 450
514 // If the callee requires direct-dependent configs be forwarded, then 451 // Always consider public dependencies as possibilities.
515 // only targets for which they will be forwarded should be explored. 452 const LabelTargetVector& public_deps = target->public_deps();
516 // Groups implicitly forward direct-dependent configs of all of their deps. 453 for (size_t i = 0; i < public_deps.size(); i++) {
517 bool uses_all_deps = !requires_dependent_configs || target == search_from || 454 if (breadcrumbs.insert(std::make_pair(public_deps[i].ptr, target)).second)
518 target->output_type() == Target::GROUP; 455 work_queue.push(public_deps[i].ptr);
456 }
519 457
520 const LabelTargetVector& deps = 458 if (!require_public) {
521 uses_all_deps ? target->deps() 459 // Consider all dependencies since all target paths are allowed, so add
522 : target->forward_dependent_configs().vector(); 460 // in private ones.
523 for (size_t i = 0; i < deps.size(); i++) { 461 const LabelTargetVector& private_deps = target->private_deps();
524 bool seeing_for_first_time = 462 for (size_t i = 0; i < private_deps.size(); i++) {
525 breadcrumbs.insert(std::make_pair(deps[i].ptr, target)).second; 463 if (breadcrumbs.insert(std::make_pair(private_deps[i].ptr, target))
526 if (seeing_for_first_time) 464 .second)
527 work_queue.push(deps[i].ptr); 465 work_queue.push(private_deps[i].ptr);
466 }
528 } 467 }
529 } 468 }
530 469
531 return false; 470 return false;
532 } 471 }
533
534 // static
535 size_t HeaderChecker::GetDependentConfigChainProblemIndex(
536 const std::vector<const Target*>& chain) {
537 // Direct dependent configs go up the chain one level with the following
538 // exceptions:
539 // - Skip over groups
540 // - Skip anything that explicitly forwards it
541
542 // Chains of length less than three have no problems.
543 // These should have been filtered out earlier.
544 DCHECK(chain.size() >= 3);
545
546 for (size_t i = 1; i < chain.size() - 1; i++) {
547 if (chain[i]->output_type() == Target::GROUP)
548 continue; // This one is OK, skip to next one.
549
550 // The forward list on this target should have contained in it the target
551 // at the next lower level.
552 const UniqueVector<LabelTargetPair>& forwarded =
553 chain[i]->forward_dependent_configs();
554 if (std::find_if(forwarded.begin(), forwarded.end(),
555 LabelPtrPtrEquals<Target>(chain[i - 1])) ==
556 forwarded.end())
557 return i;
558 }
559
560 CHECK(false) << "Unable to diagnose dependent config chain problem.";
561 return 0;
562 }
OLDNEW
« no previous file with comments | « tools/gn/header_checker.h ('k') | tools/gn/header_checker_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698