| 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 |