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 3 // found in the LICENSE file. 4 5 #ifndef TOOLS_GN_HEADER_CHECKER_H_ 6 #define TOOLS_GN_HEADER_CHECKER_H_ 7 8 #include <condition_variable> 9 #include <functional> 10 #include <map> 11 #include <mutex> 12 #include <set> 13 #include <string_view> 14 #include <vector> 15 16 #include "base/atomic_ref_count.h" 17 #include "base/gtest_prod_util.h" 18 #include "base/memory/ref_counted.h" 19 #include "gn/c_include_iterator.h" 20 #include "gn/err.h" 21 #include "gn/source_dir.h" 22 23 class BuildSettings; 24 class InputFile; 25 class SourceFile; 26 class Target; 27 28 namespace base { 29 class FilePath; 30 } 31 32 class HeaderChecker : public base::RefCountedThreadSafe<HeaderChecker> { 33 public: 34 // Represents a dependency chain. 35 struct ChainLink { ChainLinkChainLink36 ChainLink() : target(nullptr), is_public(false) {} ChainLinkChainLink37 ChainLink(const Target* t, bool p) : target(t), is_public(p) {} 38 39 const Target* target; 40 41 // True when the dependency on this target is public. 42 bool is_public; 43 44 // Used for testing. 45 bool operator==(const ChainLink& other) const { 46 return target == other.target && is_public == other.is_public; 47 } 48 }; 49 using Chain = std::vector<ChainLink>; 50 51 // check_generated, if true, will also check generated 52 // files. Something that can only be done after running a build that 53 // has generated them. 54 HeaderChecker(const BuildSettings* build_settings, 55 const std::vector<const Target*>& targets, 56 bool check_generated, 57 bool check_system); 58 59 // Runs the check. The targets in to_check will be checked. 60 // 61 // This assumes that the current thread already has a message loop. On 62 // error, fills the given vector with the errors and returns false. Returns 63 // true on success. 64 // 65 // force_check, if true, will override targets opting out of header checking 66 // with "check_includes = false" and will check them anyway. 67 bool Run(const std::vector<const Target*>& to_check, 68 bool force_check, 69 std::vector<Err>* errors); 70 71 private: 72 friend class base::RefCountedThreadSafe<HeaderChecker>; 73 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, IsDependencyOf); 74 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckInclude); 75 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, PublicFirst); 76 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckIncludeAllowCircular); 77 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckIncludeSwiftModule); 78 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, SourceFileForInclude); 79 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, 80 SourceFileForInclude_FileNotFound); 81 FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, Friend); 82 83 ~HeaderChecker(); 84 85 struct TargetInfo { TargetInfoTargetInfo86 TargetInfo() : target(nullptr), is_public(false), is_generated(false) {} TargetInfoTargetInfo87 TargetInfo(const Target* t, bool is_pub, bool is_gen) 88 : target(t), is_public(is_pub), is_generated(is_gen) {} 89 90 const Target* target; 91 92 // True if the file is public in the given target. 93 bool is_public; 94 95 // True if this file is generated and won't actually exist on disk. 96 bool is_generated; 97 }; 98 99 using TargetVector = std::vector<TargetInfo>; 100 using FileMap = std::map<SourceFile, TargetVector>; 101 using PathExistsCallback = std::function<bool(const base::FilePath& path)>; 102 103 // Backend for Run() that takes the list of files to check. The errors_ list 104 // will be populate on failure. 105 void RunCheckOverFiles(const FileMap& flies, bool force_check); 106 107 void DoWork(const Target* target, const SourceFile& file); 108 109 // Adds the sources and public files from the given target to the given map. 110 static void AddTargetToFileMap(const Target* target, FileMap* dest); 111 112 // Returns true if the given file is in the output directory. 113 bool IsFileInOuputDir(const SourceFile& file) const; 114 115 // Resolves the contents of an include to a SourceFile. 116 SourceFile SourceFileForInclude(const IncludeStringWithLocation& include, 117 const std::vector<SourceDir>& include_dirs, 118 const InputFile& source_file, 119 Err* err) const; 120 121 // from_target is the target the file was defined from. It will be used in 122 // error messages. 123 bool CheckFile(const Target* from_target, 124 const SourceFile& file, 125 std::vector<Err>* err) const; 126 127 // Checks that the given file in the given target can include the 128 // given include file. If disallowed, adds the error or errors to 129 // the errors array. The range indicates the location of the 130 // include in the file for error reporting. 131 // |no_depeency_cache| is used to cache or check whether there is no 132 // dependency from |from_target| to target having |include_file|. 133 void CheckInclude( 134 const Target* from_target, 135 const InputFile& source_file, 136 const SourceFile& include_file, 137 const LocationRange& range, 138 std::set<std::pair<const Target*, const Target*>>* no_dependency_cache, 139 std::vector<Err>* errors) const; 140 141 // Returns true if the given search_for target is a dependency of 142 // search_from. 143 // 144 // If found, the vector given in "chain" will be filled with the reverse 145 // dependency chain from the dest target (chain[0] = search_for) to the src 146 // target (chain[chain.size() - 1] = search_from). 147 // 148 // Chains with permitted dependencies will be considered first. If a 149 // permitted match is found, *is_permitted will be set to true. A chain with 150 // indirect, non-public dependencies will only be considered if there are no 151 // public or direct chains. In this case, *is_permitted will be false. 152 // 153 // A permitted dependency is a sequence of public dependencies. The first 154 // one may be private, since a direct dependency always allows headers to be 155 // included. 156 bool IsDependencyOf(const Target* search_for, 157 const Target* search_from, 158 Chain* chain, 159 bool* is_permitted) const; 160 161 // For internal use by the previous override of IsDependencyOf. If 162 // require_public is true, only public dependency chains are searched. 163 bool IsDependencyOf(const Target* search_for, 164 const Target* search_from, 165 bool require_permitted, 166 Chain* chain) const; 167 168 // Makes a very descriptive error message for when an include is disallowed 169 // from a given from_target, with a missing dependency to one of the given 170 // targets. 171 static Err MakeUnreachableError(const InputFile& source_file, 172 const LocationRange& range, 173 const Target* from_target, 174 const TargetVector& targets); 175 176 // Non-locked variables ------------------------------------------------------ 177 // 178 // These are initialized during construction (which happens on one thread) 179 // and are not modified after, so any thread can read these without locking. 180 181 const BuildSettings* build_settings_; 182 183 bool check_generated_; 184 185 bool check_system_; 186 187 // Maps source files to targets it appears in (usually just one target). 188 FileMap file_map_; 189 190 // Number of tasks posted by RunCheckOverFiles() that haven't completed their 191 // execution. 192 base::AtomicRefCount task_count_; 193 194 // Locked variables ---------------------------------------------------------- 195 // 196 // These are mutable during runtime and require locking. 197 198 std::mutex lock_; 199 200 std::vector<Err> errors_; 201 202 // Signaled when |task_count_| becomes zero. 203 std::condition_variable task_count_cv_; 204 205 HeaderChecker(const HeaderChecker&) = delete; 206 HeaderChecker& operator=(const HeaderChecker&) = delete; 207 }; 208 209 #endif // TOOLS_GN_HEADER_CHECKER_H_ 210