| 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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } | |
| OLD | NEW |