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