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

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
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 desination target.\n"
jamesr 2014/09/16 22:14:24 s/desination/destination/
82 } 82 "In some cases, the detination target is considered a subcomponent\n"
jamesr 2014/09/16 22:14:24 s/detination/destination/
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. Dependency chain (there may be others):\n";
jamesr 2014/09/16 22:14:24 \n before 'Dependency chain' ?
83 86
84 // Returns true if the given target has any direct dependent configs with 87 for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
85 // compiler settings in it. 88 ret.append(" " + chain[i]->label().GetUserVisibleName(false));
86 bool HasDirectDependentCompilerSettings(const Target* target) { 89 if (i != 0)
87 const UniqueVector<LabelConfigPair>& direct = 90 ret.append(" ->");
88 target->direct_dependent_configs(); 91 ret.append("\n");
89 for (size_t i = 0; i < direct.size(); i++) { 92 }
90 if (ConfigHasCompilerSettings(direct[i].ptr))
91 return true;
92 } 93 }
93 return false; 94 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 } 95 }
137 96
138 } // namespace 97 } // namespace
139 98
140 HeaderChecker::HeaderChecker(const BuildSettings* build_settings, 99 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
141 const std::vector<const Target*>& targets) 100 const std::vector<const Target*>& targets)
142 : main_loop_(base::MessageLoop::current()), 101 : main_loop_(base::MessageLoop::current()),
143 build_settings_(build_settings) { 102 build_settings_(build_settings) {
144 for (size_t i = 0; i < targets.size(); i++) 103 for (size_t i = 0; i < targets.size(); i++)
145 AddTargetToFileMap(targets[i], &file_map_); 104 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 292 // 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 293 // 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 294 // 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. 295 // not unusual for the buildfiles to not specify that header at all.
337 FileMap::const_iterator found = file_map_.find(include_file); 296 FileMap::const_iterator found = file_map_.find(include_file);
338 if (found == file_map_.end()) 297 if (found == file_map_.end())
339 return true; 298 return true;
340 299
341 const TargetVector& targets = found->second; 300 const TargetVector& targets = found->second;
342 std::vector<const Target*> chain; // Prevent reallocating in the loop. 301 std::vector<const Target*> chain; // Prevent reallocating in the loop.
343 bool direct_dependent_configs_apply = false;
344 302
345 // For all targets containing this file, we require that at least one be 303 // 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 304 // a dependency of the current target, and all targets that are dependencies
347 // must have the file listed in the public section. 305 // must have the file listed in the public section.
348 bool found_dependency = false; 306 bool found_dependency = false;
349 for (size_t i = 0; i < targets.size(); i++) { 307 for (size_t i = 0; i < targets.size(); i++) {
350 // We always allow source files in a target to include headers also in that 308 // We always allow source files in a target to include headers also in that
351 // target. 309 // target.
352 const Target* to_target = targets[i].target; 310 const Target* to_target = targets[i].target;
353 if (to_target == from_target) 311 if (to_target == from_target)
354 return true; 312 return true;
355 313
356 bool has_direct_dependent_compiler_settings = 314 bool is_public_dep_chain = false;
357 HasDirectDependentCompilerSettings(to_target); 315 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); 316 DCHECK(chain.size() >= 2);
364 DCHECK(chain[0] == to_target); 317 DCHECK(chain[0] == to_target);
365 DCHECK(chain[chain.size() - 1] == from_target); 318 DCHECK(chain[chain.size() - 1] == from_target);
366 319
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. 320 // The file must be public in the target.
383 if (!targets[i].is_public) { 321 if (!targets[i].is_public) {
384 // Danger: must call CreatePersistentRange to put in Err. 322 // Danger: must call CreatePersistentRange to put in Err.
385 *err = Err(CreatePersistentRange(source_file, range), 323 *err = Err(CreatePersistentRange(source_file, range),
386 "Including a private header.", 324 "Including a private header.",
387 "This file is private to the target " + 325 "This file is private to the target " +
388 targets[i].target->label().GetUserVisibleName(false)); 326 targets[i].target->label().GetUserVisibleName(false));
389 return false; 327 return false;
390 } 328 }
391 329
392 // If the to_target has direct_dependent_configs, they must apply to the 330 // The dependency chain must be public.
393 // from_target. 331 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), 332 *err = Err(CreatePersistentRange(source_file, range),
398 "Can't include this header from here.", 333 "Can't include this header from here.",
399 GetDependentConfigChainError(chain, problematic_index)); 334 GetDependencyChainPublicError(chain));
400 return false; 335 return false;
401 } 336 }
402 337
403 found_dependency = true; 338 found_dependency = true;
404 } else if ( 339 } else if (
405 to_target->allow_circular_includes_from().find(from_target->label()) != 340 to_target->allow_circular_includes_from().find(from_target->label()) !=
406 to_target->allow_circular_includes_from().end()) { 341 to_target->allow_circular_includes_from().end()) {
407 // Not a dependency, but this include is whitelisted from the destination. 342 // Not a dependency, but this include is whitelisted from the destination.
408 found_dependency = true; 343 found_dependency = true;
409 } 344 }
(...skipping 12 matching lines...) Expand all
422 357
423 // Danger: must call CreatePersistentRange to put in Err. 358 // Danger: must call CreatePersistentRange to put in Err.
424 *err = Err(CreatePersistentRange(source_file, range), 359 *err = Err(CreatePersistentRange(source_file, range),
425 "Include not allowed.", msg); 360 "Include not allowed.", msg);
426 return false; 361 return false;
427 } 362 }
428 363
429 // One thing we didn't check for is targets that expose their dependents 364 // One thing we didn't check for is targets that expose their dependents
430 // headers in their own public headers. 365 // headers in their own public headers.
431 // 366 //
432 // Say we have A -> B -> C. If C has direct_dependent_configs, everybody 367 // 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 368 // 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 369 // 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. 370 // header from B that in turn includes a header from C.
436 // 371 //
437 // There are two ways to solve this: 372 // There are two ways to solve this:
438 // - If a public header in B includes C, force B to forward C's direct 373 // - 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 374 // dependent configs. This is possible to check, but might be super
440 // annoying because most targets (especially large leaf-node targets) 375 // annoying because most targets (especially large leaf-node targets)
441 // don't declare public/private headers and you'll get lots of false 376 // don't declare public/private headers and you'll get lots of false
442 // positives. 377 // positives.
443 // 378 //
444 // - Save the includes found in each file and actually compute the graph of 379 // - 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 380 // 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. 381 // have the annoying false positive problem, but is complex to write.
447 382
448 return true; 383 return true;
449 } 384 }
450 385
451 bool HeaderChecker::IsDependencyOf(const Target* search_for, 386 bool HeaderChecker::IsDependencyOf(const Target* search_for,
452 const Target* search_from, 387 const Target* search_from,
453 bool prefer_direct_dependent_configs,
454 std::vector<const Target*>* chain, 388 std::vector<const Target*>* chain,
455 bool* direct_dependent_configs_apply) const { 389 bool* is_public) const {
456 if (search_for == search_from) 390 if (search_for == search_from) {
391 // A target is always visible from itself.
392 *is_public = true;
457 return false; 393 return false;
394 }
458 395
459 // Find the shortest chain that forwards dependent configs, if one exists. 396 // Find the shortest public dependency chain.
460 if (prefer_direct_dependent_configs && 397 if (IsDependencyOf(search_for, search_from, true, chain)) {
461 IsDependencyOf(search_for, search_from, true, chain)) { 398 *is_public = true;
462 if (direct_dependent_configs_apply)
463 *direct_dependent_configs_apply = true;
464 return true; 399 return true;
465 } 400 }
466 401
467 // If not, try to find any dependency chain at all. 402 // If not, try to find any dependency chain at all.
468 if (IsDependencyOf(search_for, search_from, false, chain)) { 403 if (IsDependencyOf(search_for, search_from, false, chain)) {
469 if (direct_dependent_configs_apply) 404 *is_public = false;
470 *direct_dependent_configs_apply = false;
471 return true; 405 return true;
472 } 406 }
473 407
408 *is_public = false;
474 return false; 409 return false;
475 } 410 }
476 411
477 bool HeaderChecker::IsDependencyOf(const Target* search_for, 412 bool HeaderChecker::IsDependencyOf(const Target* search_for,
478 const Target* search_from, 413 const Target* search_from,
479 bool requires_dependent_configs, 414 bool require_public,
480 std::vector<const Target*>* chain) const { 415 std::vector<const Target*>* chain) const {
481 // This method conducts a breadth-first search through the dependency graph 416 // This method conducts a breadth-first search through the dependency graph
482 // to find a shortest chain from search_from to search_for. 417 // to find a shortest chain from search_from to search_for.
483 // 418 //
484 // work_queue maintains a queue of targets which need to be considered as 419 // 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. 420 // part of this chain, in the order they were first traversed.
486 // 421 //
487 // Each time a new transitive dependency of search_from is discovered for 422 // 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, 423 // 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. 424 // indicating which target it was reached from when first discovered.
(...skipping 14 matching lines...) Expand all
504 // Found it! Reconstruct the chain. 439 // Found it! Reconstruct the chain.
505 chain->clear(); 440 chain->clear();
506 while (target != search_from) { 441 while (target != search_from) {
507 chain->push_back(target); 442 chain->push_back(target);
508 target = breadcrumbs[target]; 443 target = breadcrumbs[target];
509 } 444 }
510 chain->push_back(search_from); 445 chain->push_back(search_from);
511 return true; 446 return true;
512 } 447 }
513 448
514 // If the callee requires direct-dependent configs be forwarded, then 449 // Always consider public dependencies as possibilities.
515 // only targets for which they will be forwarded should be explored. 450 const LabelTargetVector& public_deps = target->public_deps();
516 // Groups implicitly forward direct-dependent configs of all of their deps. 451 for (size_t i = 0; i < public_deps.size(); i++) {
517 bool uses_all_deps = !requires_dependent_configs || target == search_from || 452 if (breadcrumbs.insert(std::make_pair(public_deps[i].ptr, target)).second)
518 target->output_type() == Target::GROUP; 453 work_queue.push(public_deps[i].ptr);
454 }
519 455
520 const LabelTargetVector& deps = 456 if (!require_public) {
521 uses_all_deps ? target->deps() 457 // Consider all dependencies since all target paths are allowed, so add
522 : target->forward_dependent_configs().vector(); 458 // in private ones.
523 for (size_t i = 0; i < deps.size(); i++) { 459 const LabelTargetVector& private_deps = target->private_deps();
524 bool seeing_for_first_time = 460 for (size_t i = 0; i < private_deps.size(); i++) {
525 breadcrumbs.insert(std::make_pair(deps[i].ptr, target)).second; 461 if (breadcrumbs.insert(std::make_pair(private_deps[i].ptr, target))
526 if (seeing_for_first_time) 462 .second)
527 work_queue.push(deps[i].ptr); 463 work_queue.push(private_deps[i].ptr);
464 }
528 } 465 }
529 } 466 }
530 467
531 return false; 468 return false;
532 } 469 }
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

Powered by Google App Engine
This is Rietveld 408576698