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 19 matching lines...) Expand all Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |