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

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

Issue 584683002: Improve GN header checker, make //ui pass. (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
« 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/files/file_util.h" 10 #include "base/files/file_util.h"
(...skipping 19 matching lines...) Expand all
30 bool is_generated; 30 bool is_generated;
31 }; 31 };
32 32
33 // If the given file is in the "gen" folder, trims this so it treats the gen 33 // If the given file is in the "gen" folder, trims this so it treats the gen
34 // directory as the source root: 34 // directory as the source root:
35 // //out/Debug/gen/foo/bar.h -> //foo/bar.h 35 // //out/Debug/gen/foo/bar.h -> //foo/bar.h
36 // If the file isn't in the generated root, returns the input unchanged. 36 // If the file isn't in the generated root, returns the input unchanged.
37 SourceFile RemoveRootGenDirFromFile(const Target* target, 37 SourceFile RemoveRootGenDirFromFile(const Target* target,
38 const SourceFile& file) { 38 const SourceFile& file) {
39 const SourceDir& gen = target->settings()->toolchain_gen_dir(); 39 const SourceDir& gen = target->settings()->toolchain_gen_dir();
40 if (StartsWithASCII(file.value(), gen.value(), true)) 40 if (!gen.is_null() && StartsWithASCII(file.value(), gen.value(), true))
41 return SourceFile("//" + file.value().substr(gen.value().size())); 41 return SourceFile("//" + file.value().substr(gen.value().size()));
42 return file; 42 return file;
43 } 43 }
44 44
45 // This class makes InputFiles on the stack as it reads files to check. When 45 // This class makes InputFiles on the stack as it reads files to check. When
46 // we throw an error, the Err indicates a locatin which has a pointer to 46 // we throw an error, the Err indicates a locatin which has a pointer to
47 // an InputFile that must persist as long as the Err does. 47 // an InputFile that must persist as long as the Err does.
48 // 48 //
49 // To make this work, this function creates a clone of the InputFile managed 49 // To make this work, this function creates a clone of the InputFile managed
50 // by the InputFileManager so the error can refer to something that 50 // by the InputFileManager so the error can refer to something that
(...skipping 13 matching lines...) Expand all
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 // Given a reverse dependency chain where the target chain[0]'s includes are 70 // Given a reverse dependency chain where the target chain[0]'s includes are
71 // being used by chain[end] and not all deps are public, returns the string 71 // being used by chain[end] and not all deps are public, returns the string
72 // describing the error. 72 // describing the error.
73 std::string GetDependencyChainPublicError( 73 std::string GetDependencyChainPublicError(
74 const std::vector<const Target*>& chain) { 74 const HeaderChecker::Chain& chain) {
75 std::string ret = "The target " + 75 std::string ret = "The target:\n " +
76 chain[chain.size() - 1]->label().GetUserVisibleName(false) + "\n" + 76 chain[chain.size() - 1].target->label().GetUserVisibleName(false) +
77 "is including a file in " + chain[0]->label().GetUserVisibleName(false) + 77 "\nis including a file from the target:\n " +
78 chain[0].target->label().GetUserVisibleName(false) +
78 "\n"; 79 "\n";
79 if (chain.size() > 2) { 80
81 // Invalid chains should always be 0 (no chain) or more than two
82 // (intermediate private dependencies). 1 and 2 are impossible because a
83 // target can always include headers from itself and its direct dependents.
84 DCHECK(chain.size() != 1 && chain.size() != 2);
85 if (chain.empty()) {
86 ret += "There is no dependency chain between these targets.";
87 } else {
80 // Indirect dependency chain, print the chain. 88 // Indirect dependency chain, print the chain.
81 ret += "\nIt's usually best to depend directly on the destination target.\n" 89 ret += "\nIt's usually best to depend directly on the destination target.\n"
82 "In some cases, the destination target is considered a subcomponent\n" 90 "In some cases, the destination target is considered a subcomponent\n"
83 "of an intermediate target. In this case, the intermediate target\n" 91 "of an intermediate target. In this case, the intermediate target\n"
84 "should depend publicly on the destination to forward the ability\n" 92 "should depend publicly on the destination to forward the ability\n"
85 "to include headers.\n" 93 "to include headers.\n"
86 "\n" 94 "\n"
87 "Dependency chain (there may be others):\n"; 95 "Dependency chain (there may also be others):\n";
88 96
89 for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) { 97 for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
90 ret.append(" " + chain[i]->label().GetUserVisibleName(false)); 98 ret.append(" " + chain[i].target->label().GetUserVisibleName(false));
91 if (i != 0) 99 if (i != 0) {
92 ret.append(" ->"); 100 // Identify private dependencies so the user can see where in the
101 // dependency chain things went bad. Don't list this for the first link
102 // in the chain since direct dependencies are OK, and listing that as
103 // "private" may make people feel like they need to fix it.
104 if (i == static_cast<int>(chain.size()) - 1 || chain[i - 1].is_public)
105 ret.append(" -->");
106 else
107 ret.append(" --[private]-->");
108 }
93 ret.append("\n"); 109 ret.append("\n");
94 } 110 }
95 } 111 }
96 return ret; 112 return ret;
97 } 113 }
98 114
99 } // namespace 115 } // namespace
100 116
101 HeaderChecker::HeaderChecker(const BuildSettings* build_settings, 117 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
102 const std::vector<const Target*>& targets) 118 const std::vector<const Target*>& targets)
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 file_i != files.end(); ++file_i) { 155 file_i != files.end(); ++file_i) {
140 const TargetVector& vect = file_i->second; 156 const TargetVector& vect = file_i->second;
141 157
142 // Only check C-like source files (RC files also have includes). 158 // Only check C-like source files (RC files also have includes).
143 SourceFileType type = GetSourceFileType(file_i->first); 159 SourceFileType type = GetSourceFileType(file_i->first);
144 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C && 160 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C &&
145 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC) 161 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
146 continue; 162 continue;
147 163
148 // Do a first pass to find if this should be skipped. All targets including 164 // Do a first pass to find if this should be skipped. All targets including
149 // this source file must exclude it from checking, or it must be generated. 165 // this source file must exclude it from checking, or any target
166 // must mark it as generated (for cases where one target generates a file,
167 // and another lists it as a source to compile it).
150 if (!force_check) { 168 if (!force_check) {
151 bool check_includes = false; 169 bool check_includes = false;
170 bool is_generated = false;
152 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) { 171 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) {
153 check_includes |= 172 check_includes |= vect[vect_i].target->check_includes();
154 vect[vect_i].target->check_includes() && 173 is_generated |= vect[vect_i].is_generated;
155 !vect[vect_i].is_generated;
156 } 174 }
157 if (!check_includes) 175 if (!check_includes || is_generated)
158 continue; 176 continue;
159 } 177 }
160 178
161 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) { 179 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) {
162 pool->PostWorkerTaskWithShutdownBehavior( 180 pool->PostWorkerTaskWithShutdownBehavior(
163 FROM_HERE, 181 FROM_HERE,
164 base::Bind(&HeaderChecker::DoWork, this, 182 base::Bind(&HeaderChecker::DoWork, this,
165 vect[vect_i].target, file_i->first), 183 vect[vect_i].target, file_i->first),
166 base::SequencedWorkerPool::BLOCK_SHUTDOWN); 184 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
167 } 185 }
168 } 186 }
169 187
170 // After this call we're single-threaded again. 188 // After this call we're single-threaded again.
171 pool->Shutdown(); 189 pool->Shutdown();
172 } 190 }
173 191
174 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) { 192 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
175 Err err; 193 Err err;
176 if (!CheckFile(target, file, &err)) { 194 if (!CheckFile(target, file, &err)) {
177 base::AutoLock lock(lock_); 195 base::AutoLock lock(lock_);
178 errors_.push_back(err); 196 errors_.push_back(err);
179 } 197 }
180 } 198 }
181 199
182 // static 200 // static
183 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) { 201 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
184 // Files in the sources have this public bit by default. 202 // Files in the sources have this public bit by default.
185 bool default_public = target->all_headers_public(); 203 bool default_public = target->all_headers_public();
186 204
187 // First collect the normal files, they get the default visibility.
188 std::map<SourceFile, PublicGeneratedPair> files_to_public; 205 std::map<SourceFile, PublicGeneratedPair> files_to_public;
206
207 // First collect the normal files, they get the default visibility. Always
208 // trim the root gen dir if it exists. This will only exist on outputs of an
209 // action, but those are often then wired into the sources of a compiled
210 // target to actually compile generated code. If you depend on the compiled
211 // target, it should be enough to be able to include the header.
189 const Target::FileList& sources = target->sources(); 212 const Target::FileList& sources = target->sources();
190 for (size_t i = 0; i < sources.size(); i++) 213 for (size_t i = 0; i < sources.size(); i++) {
191 files_to_public[sources[i]].is_public = default_public; 214 SourceFile file = RemoveRootGenDirFromFile(target, sources[i]);
215 files_to_public[file].is_public = default_public;
216 }
192 217
193 // Add in the public files, forcing them to public. This may overwrite some 218 // Add in the public files, forcing them to public. This may overwrite some
194 // entries, and it may add new ones. 219 // entries, and it may add new ones.
195 const Target::FileList& public_list = target->public_headers(); 220 const Target::FileList& public_list = target->public_headers();
196 if (default_public) 221 if (default_public)
197 DCHECK(public_list.empty()); // List only used when default is not public. 222 DCHECK(public_list.empty()); // List only used when default is not public.
198 for (size_t i = 0; i < public_list.size(); i++) 223 for (size_t i = 0; i < public_list.size(); i++) {
199 files_to_public[public_list[i]].is_public = true; 224 SourceFile file = RemoveRootGenDirFromFile(target, public_list[i]);
225 files_to_public[file].is_public = true;
226 }
200 227
201 // Add in outputs from actions. These are treated as public (since if other 228 // Add in outputs from actions. These are treated as public (since if other
202 // targets can't use them, then there wouldn't be any point in outputting). 229 // targets can't use them, then there wouldn't be any point in outputting).
203 std::vector<SourceFile> outputs; 230 std::vector<SourceFile> outputs;
204 target->action_values().GetOutputsAsSourceFiles(target, &outputs); 231 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
205 for (size_t i = 0; i < outputs.size(); i++) { 232 for (size_t i = 0; i < outputs.size(); i++) {
206 // For generated files in the "gen" directory, add the filename to the 233 // For generated files in the "gen" directory, add the filename to the
207 // map assuming "gen" is the source root. This means that when files include 234 // map assuming "gen" is the source root. This means that when files include
208 // the generated header relative to there (the recommended practice), we'll 235 // the generated header relative to there (the recommended practice), we'll
209 // find the file. 236 // find the file.
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 // target. These won't exist at checking time. Since we require all generated 276 // target. These won't exist at checking time. Since we require all generated
250 // files to be somewhere in the output tree, we can just check the name to 277 // files to be somewhere in the output tree, we can just check the name to
251 // see if they should be skipped. 278 // see if they should be skipped.
252 if (IsFileInOuputDir(file)) 279 if (IsFileInOuputDir(file))
253 return true; 280 return true;
254 281
255 base::FilePath path = build_settings_->GetFullPath(file); 282 base::FilePath path = build_settings_->GetFullPath(file);
256 std::string contents; 283 std::string contents;
257 if (!base::ReadFileToString(path, &contents)) { 284 if (!base::ReadFileToString(path, &contents)) {
258 *err = Err(from_target->defined_from(), "Source file not found.", 285 *err = Err(from_target->defined_from(), "Source file not found.",
259 "This target includes as a source:\n " + file.value() + 286 "The target:\n " + from_target->label().GetUserVisibleName(false) +
287 "\nhas a source file:\n " + file.value() +
260 "\nwhich was not found."); 288 "\nwhich was not found.");
261 return false; 289 return false;
262 } 290 }
263 291
264 InputFile input_file(file); 292 InputFile input_file(file);
265 input_file.SetContents(contents); 293 input_file.SetContents(contents);
266 294
267 CIncludeIterator iter(&input_file); 295 CIncludeIterator iter(&input_file);
268 base::StringPiece current_include; 296 base::StringPiece current_include;
269 LocationRange range; 297 LocationRange range;
(...skipping 23 matching lines...) Expand all
293 // Assume if the file isn't declared in our sources that we don't need to 321 // Assume if the file isn't declared in our sources that we don't need to
294 // check it. It would be nice if we could give an error if this happens, but 322 // check it. It would be nice if we could give an error if this happens, but
295 // our include finder is too primitive and returns all includes, even if 323 // our include finder is too primitive and returns all includes, even if
296 // they're in a #if not executed in the current build. In that case, it's 324 // they're in a #if not executed in the current build. In that case, it's
297 // not unusual for the buildfiles to not specify that header at all. 325 // not unusual for the buildfiles to not specify that header at all.
298 FileMap::const_iterator found = file_map_.find(include_file); 326 FileMap::const_iterator found = file_map_.find(include_file);
299 if (found == file_map_.end()) 327 if (found == file_map_.end())
300 return true; 328 return true;
301 329
302 const TargetVector& targets = found->second; 330 const TargetVector& targets = found->second;
303 std::vector<const Target*> chain; // Prevent reallocating in the loop. 331 Chain chain; // Prevent reallocating in the loop.
304 332
305 // For all targets containing this file, we require that at least one be 333 // For all targets containing this file, we require that at least one be
306 // a dependency of the current target, and all targets that are dependencies 334 // a direct or public dependency of the current target, and that the header
307 // must have the file listed in the public section. 335 // is public within the target.
336 //
337 // If there is more than one target containing this header, we may encounter
338 // some error cases before finding a good one. This error stores the previous
339 // one encountered, which we may or may not throw away.
340 Err last_error;
341
308 bool found_dependency = false; 342 bool found_dependency = false;
309 for (size_t i = 0; i < targets.size(); i++) { 343 for (size_t i = 0; i < targets.size(); i++) {
310 // We always allow source files in a target to include headers also in that 344 // We always allow source files in a target to include headers also in that
311 // target. 345 // target.
312 const Target* to_target = targets[i].target; 346 const Target* to_target = targets[i].target;
313 if (to_target == from_target) 347 if (to_target == from_target)
314 return true; 348 return true;
315 349
316 bool is_public_dep_chain = false; 350 bool is_permitted_chain = false;
317 if (IsDependencyOf(to_target, from_target, &chain, &is_public_dep_chain)) { 351 if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
318 DCHECK(chain.size() >= 2); 352 DCHECK(chain.size() >= 2);
319 DCHECK(chain[0] == to_target); 353 DCHECK(chain[0].target == to_target);
320 DCHECK(chain[chain.size() - 1] == from_target); 354 DCHECK(chain[chain.size() - 1].target == from_target);
321 355
322 // The file must be public in the target. 356 found_dependency = true;
357
358 if (targets[i].is_public && is_permitted_chain) {
359 // This one is OK, we're done.
360 last_error = Err();
361 break;
362 }
363
364 // Diagnose the error.
323 if (!targets[i].is_public) { 365 if (!targets[i].is_public) {
324 // Danger: must call CreatePersistentRange to put in Err. 366 // Danger: must call CreatePersistentRange to put in Err.
325 *err = Err(CreatePersistentRange(source_file, range), 367 last_error = Err(
326 "Including a private header.", 368 CreatePersistentRange(source_file, range),
327 "This file is private to the target " + 369 "Including a private header.",
328 targets[i].target->label().GetUserVisibleName(false)); 370 "This file is private to the target " +
329 return false; 371 targets[i].target->label().GetUserVisibleName(false));
372 } else if (!is_permitted_chain) {
373 last_error = Err(
374 CreatePersistentRange(source_file, range),
375 "Can't include this header from here.",
376 GetDependencyChainPublicError(chain));
377 } else {
378 NOTREACHED();
330 } 379 }
331
332 // The dependency chain must be public.
333 if (!is_public_dep_chain) {
334 *err = Err(CreatePersistentRange(source_file, range),
335 "Can't include this header from here.",
336 GetDependencyChainPublicError(chain));
337 return false;
338 }
339
340 found_dependency = true;
341 } else if ( 380 } else if (
342 to_target->allow_circular_includes_from().find(from_target->label()) != 381 to_target->allow_circular_includes_from().find(from_target->label()) !=
343 to_target->allow_circular_includes_from().end()) { 382 to_target->allow_circular_includes_from().end()) {
344 // Not a dependency, but this include is whitelisted from the destination. 383 // Not a dependency, but this include is whitelisted from the destination.
345 found_dependency = true; 384 found_dependency = true;
385 last_error = Err();
386 break;
346 } 387 }
347 } 388 }
348 389
349 if (!found_dependency) { 390 if (!found_dependency) {
391 DCHECK(!last_error.has_error());
392
350 std::string msg = "It is not in any dependency of " + 393 std::string msg = "It is not in any dependency of " +
351 from_target->label().GetUserVisibleName(false); 394 from_target->label().GetUserVisibleName(false);
352 msg += "\nThe include file is in the target(s):\n"; 395 msg += "\nThe include file is in the target(s):\n";
353 for (size_t i = 0; i < targets.size(); i++) 396 for (size_t i = 0; i < targets.size(); i++)
354 msg += " " + targets[i].target->label().GetUserVisibleName(false) + "\n"; 397 msg += " " + targets[i].target->label().GetUserVisibleName(false) + "\n";
355 if (targets.size() > 1) 398 if (targets.size() > 1)
356 msg += "at least one of "; 399 msg += "at least one of ";
357 msg += "which should somehow be reachable from " + 400 msg += "which should somehow be reachable from " +
358 from_target->label().GetUserVisibleName(false); 401 from_target->label().GetUserVisibleName(false);
359 402
360 // Danger: must call CreatePersistentRange to put in Err. 403 // Danger: must call CreatePersistentRange to put in Err.
361 *err = Err(CreatePersistentRange(source_file, range), 404 *err = Err(CreatePersistentRange(source_file, range),
362 "Include not allowed.", msg); 405 "Include not allowed.", msg);
363 return false; 406 return false;
364 } 407 }
408 if (last_error.has_error()) {
409 // Found at least one dependency chain above, but it had an error.
410 *err = last_error;
411 return false;
412 }
365 413
366 // One thing we didn't check for is targets that expose their dependents 414 // One thing we didn't check for is targets that expose their dependents
367 // headers in their own public headers. 415 // headers in their own public headers.
368 // 416 //
369 // Say we have A -> B -> C. If C has public_configs, everybody getting headers 417 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
370 // from C should get the configs also or things could be out-of-sync. Above, 418 // from C should get the configs also or things could be out-of-sync. Above,
371 // we check for A including C's headers directly, but A could also include a 419 // we check for A including C's headers directly, but A could also include a
372 // header from B that in turn includes a header from C. 420 // header from B that in turn includes a header from C.
373 // 421 //
374 // There are two ways to solve this: 422 // There are two ways to solve this:
375 // - If a public header in B includes C, force B to forward C's direct 423 // - If a public header in B includes C, force B to publicly depend on C.
376 // dependent configs. This is possible to check, but might be super 424 // This is possible to check, but might be super annoying because most
377 // annoying because most targets (especially large leaf-node targets) 425 // targets (especially large leaf-node targets) don't declare
378 // don't declare public/private headers and you'll get lots of false 426 // public/private headers and you'll get lots of false positives.
379 // positives.
380 // 427 //
381 // - Save the includes found in each file and actually compute the graph of 428 // - Save the includes found in each file and actually compute the graph of
382 // includes to detect when A implicitly includes C's header. This will not 429 // includes to detect when A implicitly includes C's header. This will not
383 // have the annoying false positive problem, but is complex to write. 430 // have the annoying false positive problem, but is complex to write.
384 431
385 return true; 432 return true;
386 } 433 }
387 434
388 bool HeaderChecker::IsDependencyOf(const Target* search_for, 435 bool HeaderChecker::IsDependencyOf(const Target* search_for,
389 const Target* search_from, 436 const Target* search_from,
390 std::vector<const Target*>* chain, 437 Chain* chain,
391 bool* is_public) const { 438 bool* is_permitted) const {
392 if (search_for == search_from) { 439 if (search_for == search_from) {
393 // A target is always visible from itself. 440 // A target is always visible from itself.
394 *is_public = true; 441 *is_permitted = true;
395 return false; 442 return false;
396 } 443 }
397 444
398 // Find the shortest public dependency chain. 445 // Find the shortest public dependency chain.
399 if (IsDependencyOf(search_for, search_from, true, chain)) { 446 if (IsDependencyOf(search_for, search_from, true, chain)) {
400 *is_public = true; 447 *is_permitted = true;
401 return true; 448 return true;
402 } 449 }
403 450
404 // If not, try to find any dependency chain at all. 451 // If not, try to find any dependency chain at all.
405 if (IsDependencyOf(search_for, search_from, false, chain)) { 452 if (IsDependencyOf(search_for, search_from, false, chain)) {
406 *is_public = false; 453 *is_permitted = false;
407 return true; 454 return true;
408 } 455 }
409 456
410 *is_public = false; 457 *is_permitted = false;
411 return false; 458 return false;
412 } 459 }
413 460
414 bool HeaderChecker::IsDependencyOf(const Target* search_for, 461 bool HeaderChecker::IsDependencyOf(const Target* search_for,
415 const Target* search_from, 462 const Target* search_from,
416 bool require_public, 463 bool require_permitted,
417 std::vector<const Target*>* chain) const { 464 Chain* chain) const {
418 // This method conducts a breadth-first search through the dependency graph 465 // This method conducts a breadth-first search through the dependency graph
419 // to find a shortest chain from search_from to search_for. 466 // to find a shortest chain from search_from to search_for.
420 // 467 //
421 // work_queue maintains a queue of targets which need to be considered as 468 // work_queue maintains a queue of targets which need to be considered as
422 // part of this chain, in the order they were first traversed. 469 // part of this chain, in the order they were first traversed.
423 // 470 //
424 // Each time a new transitive dependency of search_from is discovered for 471 // Each time a new transitive dependency of search_from is discovered for
425 // the first time, it is added to work_queue and a "breadcrumb" is added, 472 // the first time, it is added to work_queue and a "breadcrumb" is added,
426 // indicating which target it was reached from when first discovered. 473 // indicating which target it was reached from when first discovered.
427 // 474 //
428 // Once this search finds search_for, the breadcrumbs are used to reconstruct 475 // Once this search finds search_for, the breadcrumbs are used to reconstruct
429 // a shortest dependency chain (in reverse order) from search_from to 476 // a shortest dependency chain (in reverse order) from search_from to
430 // search_for. 477 // search_for.
431 478
432 std::map<const Target*, const Target*> breadcrumbs; 479 std::map<const Target*, ChainLink> breadcrumbs;
433 std::queue<const Target*> work_queue; 480 std::queue<ChainLink> work_queue;
434 work_queue.push(search_from); 481 work_queue.push(ChainLink(search_from, true));
435 482
483 bool first_time = true;
436 while (!work_queue.empty()) { 484 while (!work_queue.empty()) {
437 const Target* target = work_queue.front(); 485 ChainLink cur_link = work_queue.front();
486 const Target* target = cur_link.target;
438 work_queue.pop(); 487 work_queue.pop();
439 488
440 if (target == search_for) { 489 if (target == search_for) {
441 // Found it! Reconstruct the chain. 490 // Found it! Reconstruct the chain.
442 chain->clear(); 491 chain->clear();
443 while (target != search_from) { 492 while (target != search_from) {
444 chain->push_back(target); 493 chain->push_back(cur_link);
445 target = breadcrumbs[target]; 494 cur_link = breadcrumbs[target];
495 target = cur_link.target;
446 } 496 }
447 chain->push_back(search_from); 497 chain->push_back(ChainLink(search_from, true));
448 return true; 498 return true;
449 } 499 }
450 500
451 // Always consider public dependencies as possibilities. 501 // Always consider public dependencies as possibilities.
452 const LabelTargetVector& public_deps = target->public_deps(); 502 const LabelTargetVector& public_deps = target->public_deps();
453 for (size_t i = 0; i < public_deps.size(); i++) { 503 for (size_t i = 0; i < public_deps.size(); i++) {
454 if (breadcrumbs.insert(std::make_pair(public_deps[i].ptr, target)).second) 504 if (breadcrumbs.insert(
455 work_queue.push(public_deps[i].ptr); 505 std::make_pair(public_deps[i].ptr, cur_link)).second)
506 work_queue.push(ChainLink(public_deps[i].ptr, true));
456 } 507 }
457 508
458 if (!require_public) { 509 if (first_time || !require_permitted) {
459 // Consider all dependencies since all target paths are allowed, so add 510 // Consider all dependencies since all target paths are allowed, so add
460 // in private ones. 511 // in private ones. Also do this the first time through the loop, since
512 // a target can include headers from its direct deps regardless of
513 // public/private-ness.
514 first_time = false;
461 const LabelTargetVector& private_deps = target->private_deps(); 515 const LabelTargetVector& private_deps = target->private_deps();
462 for (size_t i = 0; i < private_deps.size(); i++) { 516 for (size_t i = 0; i < private_deps.size(); i++) {
463 if (breadcrumbs.insert(std::make_pair(private_deps[i].ptr, target)) 517 if (breadcrumbs.insert(
464 .second) 518 std::make_pair(private_deps[i].ptr, cur_link)).second)
465 work_queue.push(private_deps[i].ptr); 519 work_queue.push(ChainLink(private_deps[i].ptr, false));
466 } 520 }
467 } 521 }
468 } 522 }
469 523
470 return false; 524 return false;
471 } 525 }
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