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

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

Issue 424133002: GN: (HeaderChecker) Allow any chain to forward direct dependent configs. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge with UniqueVector Created 6 years, 4 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 | Annotate | Revision Log
« 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/file_util.h" 10 #include "base/file_util.h"
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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 }
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