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/file_util.h" | 10 #include "base/file_util.h" |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 // check it. It would be nice if we could give an error if this happens, but | 268 // check it. It would be nice if we could give an error if this happens, but |
269 // our include finder is too primitive and returns all includes, even if | 269 // our include finder is too primitive and returns all includes, even if |
270 // they're in a #if not executed in the current build. In that case, it's | 270 // they're in a #if not executed in the current build. In that case, it's |
271 // not unusual for the buildfiles to not specify that header at all. | 271 // not unusual for the buildfiles to not specify that header at all. |
272 FileMap::const_iterator found = file_map_.find(include_file); | 272 FileMap::const_iterator found = file_map_.find(include_file); |
273 if (found == file_map_.end()) | 273 if (found == file_map_.end()) |
274 return true; | 274 return true; |
275 | 275 |
276 const TargetVector& targets = found->second; | 276 const TargetVector& targets = found->second; |
277 std::vector<const Target*> chain; // Prevent reallocating in the loop. | 277 std::vector<const Target*> chain; // Prevent reallocating in the loop. |
| 278 bool direct_dependent_configs_apply = false; |
278 | 279 |
279 // For all targets containing this file, we require that at least one be | 280 // For all targets containing this file, we require that at least one be |
280 // a dependency of the current target, and all targets that are dependencies | 281 // a dependency of the current target, and all targets that are dependencies |
281 // must have the file listed in the public section. | 282 // must have the file listed in the public section. |
282 bool found_dependency = false; | 283 bool found_dependency = false; |
283 for (size_t i = 0; i < targets.size(); i++) { | 284 for (size_t i = 0; i < targets.size(); i++) { |
284 // We always allow source files in a target to include headers also in that | 285 // We always allow source files in a target to include headers also in that |
285 // target. | 286 // target. |
286 const Target* to_target = targets[i].target; | 287 const Target* to_target = targets[i].target; |
287 if (to_target == from_target) | 288 if (to_target == from_target) |
288 return true; | 289 return true; |
289 | 290 |
290 if (IsDependencyOf(to_target, from_target, &chain)) { | 291 bool has_direct_dependent_compiler_settings = |
| 292 HasDirectDependentCompilerSettings(to_target); |
| 293 if (IsDependencyOf(to_target, |
| 294 from_target, |
| 295 has_direct_dependent_compiler_settings, |
| 296 &chain, |
| 297 &direct_dependent_configs_apply)) { |
291 DCHECK(chain.size() >= 2); | 298 DCHECK(chain.size() >= 2); |
292 DCHECK(chain[0] == to_target); | 299 DCHECK(chain[0] == to_target); |
293 DCHECK(chain[chain.size() - 1] == from_target); | 300 DCHECK(chain[chain.size() - 1] == from_target); |
294 | 301 |
295 // The include is in a target that's a proper dependency. Verify that | 302 // The include is in a target that's a proper dependency. Verify that |
296 // the including target has visibility. | 303 // the including target has visibility. |
297 if (!to_target->visibility().CanSeeMe(from_target->label())) { | 304 if (!to_target->visibility().CanSeeMe(from_target->label())) { |
298 std::string msg = "The included file is in " + | 305 std::string msg = "The included file is in " + |
299 to_target->label().GetUserVisibleName(false) + | 306 to_target->label().GetUserVisibleName(false) + |
300 "\nwhich is not visible from " + | 307 "\nwhich is not visible from " + |
(...skipping 11 matching lines...) Expand all Loading... |
312 // Danger: must call CreatePersistentRange to put in Err. | 319 // Danger: must call CreatePersistentRange to put in Err. |
313 *err = Err(CreatePersistentRange(source_file, range), | 320 *err = Err(CreatePersistentRange(source_file, range), |
314 "Including a private header.", | 321 "Including a private header.", |
315 "This file is private to the target " + | 322 "This file is private to the target " + |
316 targets[i].target->label().GetUserVisibleName(false)); | 323 targets[i].target->label().GetUserVisibleName(false)); |
317 return false; | 324 return false; |
318 } | 325 } |
319 | 326 |
320 // If the to_target has direct_dependent_configs, they must apply to the | 327 // If the to_target has direct_dependent_configs, they must apply to the |
321 // from_target. | 328 // from_target. |
322 if (HasDirectDependentCompilerSettings(to_target)) { | 329 if (has_direct_dependent_compiler_settings && |
323 size_t problematic_index; | 330 !direct_dependent_configs_apply) { |
324 if (!DoDirectDependentConfigsApply(chain, &problematic_index)) { | 331 size_t problematic_index = GetDependentConfigChainProblemIndex(chain); |
325 *err = Err(CreatePersistentRange(source_file, range), | 332 *err = Err(CreatePersistentRange(source_file, range), |
326 "Can't include this header from here.", | 333 "Can't include this header from here.", |
327 GetDependentConfigChainError(chain, problematic_index)); | 334 GetDependentConfigChainError(chain, problematic_index)); |
328 return false; | 335 return false; |
329 } | |
330 } | 336 } |
331 | 337 |
332 found_dependency = true; | 338 found_dependency = true; |
333 } | 339 } |
334 } | 340 } |
335 | 341 |
336 if (!found_dependency) { | 342 if (!found_dependency) { |
337 std::string msg = "It is not in any dependency of " + | 343 std::string msg = "It is not in any dependency of " + |
338 from_target->label().GetUserVisibleName(false); | 344 from_target->label().GetUserVisibleName(false); |
339 msg += "\nThe include file is in the target(s):\n"; | 345 msg += "\nThe include file is in the target(s):\n"; |
(...skipping 27 matching lines...) Expand all Loading... |
367 // | 373 // |
368 // - Save the includes found in each file and actually compute the graph of | 374 // - Save the includes found in each file and actually compute the graph of |
369 // includes to detect when A implicitly includes C's header. This will not | 375 // includes to detect when A implicitly includes C's header. This will not |
370 // have the annoying false positive problem, but is complex to write. | 376 // have the annoying false positive problem, but is complex to write. |
371 | 377 |
372 return true; | 378 return true; |
373 } | 379 } |
374 | 380 |
375 bool HeaderChecker::IsDependencyOf(const Target* search_for, | 381 bool HeaderChecker::IsDependencyOf(const Target* search_for, |
376 const Target* search_from, | 382 const Target* search_from, |
377 std::vector<const Target*>* chain) const { | 383 bool prefer_direct_dependent_configs, |
378 std::set<const Target*> checked; | 384 std::vector<const Target*>* chain, |
379 return IsDependencyOf(search_for, search_from, chain, &checked); | 385 bool* direct_dependent_configs_apply) const { |
| 386 if (search_for == search_from) |
| 387 return false; |
| 388 |
| 389 // Find the shortest chain that forwards dependent configs, if one exists. |
| 390 if (prefer_direct_dependent_configs && |
| 391 IsDependencyOf(search_for, search_from, true, chain)) { |
| 392 if (direct_dependent_configs_apply) |
| 393 *direct_dependent_configs_apply = true; |
| 394 return true; |
| 395 } |
| 396 |
| 397 // If not, try to find any dependency chain at all. |
| 398 if (IsDependencyOf(search_for, search_from, false, chain)) { |
| 399 if (direct_dependent_configs_apply) |
| 400 *direct_dependent_configs_apply = false; |
| 401 return true; |
| 402 } |
| 403 |
| 404 return false; |
380 } | 405 } |
381 | 406 |
382 bool HeaderChecker::IsDependencyOf(const Target* search_for, | 407 bool HeaderChecker::IsDependencyOf(const Target* search_for, |
383 const Target* search_from, | 408 const Target* search_from, |
384 std::vector<const Target*>* chain, | 409 bool requires_dependent_configs, |
385 std::set<const Target*>* checked) const { | 410 std::vector<const Target*>* chain) const { |
386 if (checked->find(search_for) != checked->end()) | 411 // This method conducts a breadth-first search through the dependency graph |
387 return false; // Already checked this subtree. | 412 // to find a shortest chain from search_from to search_for. |
| 413 // |
| 414 // work_queue maintains a queue of targets which need to be considered as |
| 415 // part of this chain, in the order they were first traversed. |
| 416 // |
| 417 // Each time a new transitive dependency of search_from is discovered for |
| 418 // the first time, it is added to work_queue and a "breadcrumb" is added, |
| 419 // indicating which target it was reached from when first discovered. |
| 420 // |
| 421 // Once this search finds search_for, the breadcrumbs are used to reconstruct |
| 422 // a shortest dependency chain (in reverse order) from search_from to |
| 423 // search_for. |
388 | 424 |
389 const LabelTargetVector& deps = search_from->deps(); | 425 std::map<const Target*, const Target*> breadcrumbs; |
390 for (size_t i = 0; i < deps.size(); i++) { | 426 std::queue<const Target*> work_queue; |
391 if (deps[i].ptr == search_for) { | 427 work_queue.push(search_from); |
392 // Found it. | 428 |
| 429 while (!work_queue.empty()) { |
| 430 const Target* target = work_queue.front(); |
| 431 work_queue.pop(); |
| 432 |
| 433 if (target == search_for) { |
| 434 // Found it! Reconstruct the chain. |
393 chain->clear(); | 435 chain->clear(); |
394 chain->push_back(deps[i].ptr); | 436 while (target != search_from) { |
| 437 chain->push_back(target); |
| 438 target = breadcrumbs[target]; |
| 439 } |
395 chain->push_back(search_from); | 440 chain->push_back(search_from); |
396 return true; | 441 return true; |
397 } | 442 } |
398 | 443 |
399 // Recursive search. | 444 // If the callee requires direct-dependent configs be forwarded, then |
400 checked->insert(deps[i].ptr); | 445 // only targets for which they will be forwarded should be explored. |
401 if (IsDependencyOf(search_for, deps[i].ptr, chain, checked)) { | 446 // Groups implicitly forward direct-dependent configs of all of their deps. |
402 chain->push_back(search_from); | 447 bool uses_all_deps = !requires_dependent_configs || target == search_from || |
403 return true; | 448 target->output_type() == Target::GROUP; |
| 449 |
| 450 const LabelTargetVector& deps = |
| 451 uses_all_deps ? target->deps() |
| 452 : target->forward_dependent_configs().vector(); |
| 453 for (size_t i = 0; i < deps.size(); i++) { |
| 454 bool seeing_for_first_time = |
| 455 breadcrumbs.insert(std::make_pair(deps[i].ptr, target)).second; |
| 456 if (seeing_for_first_time) |
| 457 work_queue.push(deps[i].ptr); |
404 } | 458 } |
405 } | 459 } |
406 | 460 |
407 return false; | 461 return false; |
408 } | 462 } |
409 | 463 |
410 // static | 464 // static |
411 bool HeaderChecker::DoDirectDependentConfigsApply( | 465 size_t HeaderChecker::GetDependentConfigChainProblemIndex( |
412 const std::vector<const Target*>& chain, | 466 const std::vector<const Target*>& chain) { |
413 size_t* problematic_index) { | |
414 // Direct dependent configs go up the chain one level with the following | 467 // Direct dependent configs go up the chain one level with the following |
415 // exceptions: | 468 // exceptions: |
416 // - Skip over groups | 469 // - Skip over groups |
417 // - Skip anything that explicitly forwards it | 470 // - Skip anything that explicitly forwards it |
418 | 471 |
419 // All chains should be at least two (or it wouldn't be a chain). | 472 // Chains of length less than three have no problems. |
420 DCHECK(chain.size() >= 2); | 473 // These should have been filtered out earlier. |
| 474 DCHECK(chain.size() >= 3); |
421 | 475 |
422 // A chain of length 2 is always OK as far as direct dependent configs are | |
423 // concerned since the targets are direct dependents. | |
424 if (chain.size() == 2) | |
425 return true; | |
426 | |
427 // Check the middle configs to make sure they're either groups or configs | |
428 // are forwarded. | |
429 for (size_t i = 1; i < chain.size() - 1; i++) { | 476 for (size_t i = 1; i < chain.size() - 1; i++) { |
430 if (chain[i]->output_type() == Target::GROUP) | 477 if (chain[i]->output_type() == Target::GROUP) |
431 continue; // This one is OK, skip to next one. | 478 continue; // This one is OK, skip to next one. |
432 | 479 |
433 // The forward list on this target should have contained in it the target | 480 // The forward list on this target should have contained in it the target |
434 // at the next lower level. | 481 // at the next lower level. |
435 const UniqueVector<LabelTargetPair>& forwarded = | 482 const UniqueVector<LabelTargetPair>& forwarded = |
436 chain[i]->forward_dependent_configs(); | 483 chain[i]->forward_dependent_configs(); |
437 if (std::find_if(forwarded.begin(), forwarded.end(), | 484 if (std::find_if(forwarded.begin(), forwarded.end(), |
438 LabelPtrPtrEquals<Target>(chain[i - 1])) == | 485 LabelPtrPtrEquals<Target>(chain[i - 1])) == |
439 forwarded.end()) { | 486 forwarded.end()) |
440 *problematic_index = i; | 487 return i; |
441 return false; | |
442 } | |
443 } | 488 } |
444 | 489 |
445 return true; | 490 CHECK(false) << "Unable to diagnose dependent config chain problem."; |
| 491 return 0; |
446 } | 492 } |
OLD | NEW |