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