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 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 } | 114 } |
115 return ret; | 115 return ret; |
116 } | 116 } |
117 | 117 |
118 } // namespace | 118 } // namespace |
119 | 119 |
120 HeaderChecker::HeaderChecker(const BuildSettings* build_settings, | 120 HeaderChecker::HeaderChecker(const BuildSettings* build_settings, |
121 const std::vector<const Target*>& targets) | 121 const std::vector<const Target*>& targets) |
122 : main_loop_(base::MessageLoop::current()), | 122 : main_loop_(base::MessageLoop::current()), |
123 build_settings_(build_settings) { | 123 build_settings_(build_settings) { |
124 for (size_t i = 0; i < targets.size(); i++) | 124 for (const auto& target : targets) |
125 AddTargetToFileMap(targets[i], &file_map_); | 125 AddTargetToFileMap(target, &file_map_); |
126 } | 126 } |
127 | 127 |
128 HeaderChecker::~HeaderChecker() { | 128 HeaderChecker::~HeaderChecker() { |
129 } | 129 } |
130 | 130 |
131 bool HeaderChecker::Run(const std::vector<const Target*>& to_check, | 131 bool HeaderChecker::Run(const std::vector<const Target*>& to_check, |
132 bool force_check, | 132 bool force_check, |
133 std::vector<Err>* errors) { | 133 std::vector<Err>* errors) { |
134 if (to_check.empty()) { | 134 if (to_check.empty()) { |
135 // Check all files. | 135 // Check all files. |
136 RunCheckOverFiles(file_map_, force_check); | 136 RunCheckOverFiles(file_map_, force_check); |
137 } else { | 137 } else { |
138 // Run only over the files in the given targets. | 138 // Run only over the files in the given targets. |
139 FileMap files_to_check; | 139 FileMap files_to_check; |
140 for (size_t i = 0; i < to_check.size(); i++) | 140 for (const auto& check : to_check) |
141 AddTargetToFileMap(to_check[i], &files_to_check); | 141 AddTargetToFileMap(check, &files_to_check); |
142 RunCheckOverFiles(files_to_check, force_check); | 142 RunCheckOverFiles(files_to_check, force_check); |
143 } | 143 } |
144 | 144 |
145 if (errors_.empty()) | 145 if (errors_.empty()) |
146 return true; | 146 return true; |
147 *errors = errors_; | 147 *errors = errors_; |
148 return false; | 148 return false; |
149 } | 149 } |
150 | 150 |
151 void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) { | 151 void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) { |
152 if (files.empty()) | 152 if (files.empty()) |
153 return; | 153 return; |
154 | 154 |
155 scoped_refptr<base::SequencedWorkerPool> pool( | 155 scoped_refptr<base::SequencedWorkerPool> pool( |
156 new base::SequencedWorkerPool(16, "HeaderChecker")); | 156 new base::SequencedWorkerPool(16, "HeaderChecker")); |
157 for (FileMap::const_iterator file_i = files.begin(); | 157 for (const auto& file : files) { |
158 file_i != files.end(); ++file_i) { | |
159 const TargetVector& vect = file_i->second; | |
160 | |
161 // Only check C-like source files (RC files also have includes). | 158 // Only check C-like source files (RC files also have includes). |
162 SourceFileType type = GetSourceFileType(file_i->first); | 159 SourceFileType type = GetSourceFileType(file.first); |
163 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C && | 160 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C && |
164 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC) | 161 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC) |
165 continue; | 162 continue; |
166 | 163 |
167 // 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 |
168 // this source file must exclude it from checking, or any target | 165 // this source file must exclude it from checking, or any target |
169 // must mark it as generated (for cases where one target generates a file, | 166 // must mark it as generated (for cases where one target generates a file, |
170 // and another lists it as a source to compile it). | 167 // and another lists it as a source to compile it). |
171 if (!force_check) { | 168 if (!force_check) { |
172 bool check_includes = false; | 169 bool check_includes = false; |
173 bool is_generated = false; | 170 bool is_generated = false; |
174 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) { | 171 for (const auto& vect_i : file.second) { |
175 check_includes |= vect[vect_i].target->check_includes(); | 172 check_includes |= vect_i.target->check_includes(); |
176 is_generated |= vect[vect_i].is_generated; | 173 is_generated |= vect_i.is_generated; |
177 } | 174 } |
178 if (!check_includes || is_generated) | 175 if (!check_includes || is_generated) |
179 continue; | 176 continue; |
180 } | 177 } |
181 | 178 |
182 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) { | 179 for (const auto& vect_i : file.second) { |
183 pool->PostWorkerTaskWithShutdownBehavior( | 180 pool->PostWorkerTaskWithShutdownBehavior( |
184 FROM_HERE, | 181 FROM_HERE, |
185 base::Bind(&HeaderChecker::DoWork, this, | 182 base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first), |
186 vect[vect_i].target, file_i->first), | |
187 base::SequencedWorkerPool::BLOCK_SHUTDOWN); | 183 base::SequencedWorkerPool::BLOCK_SHUTDOWN); |
188 } | 184 } |
189 } | 185 } |
190 | 186 |
191 // After this call we're single-threaded again. | 187 // After this call we're single-threaded again. |
192 pool->Shutdown(); | 188 pool->Shutdown(); |
193 } | 189 } |
194 | 190 |
195 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) { | 191 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) { |
196 Err err; | 192 Err err; |
197 if (!CheckFile(target, file, &err)) { | 193 if (!CheckFile(target, file, &err)) { |
198 base::AutoLock lock(lock_); | 194 base::AutoLock lock(lock_); |
199 errors_.push_back(err); | 195 errors_.push_back(err); |
200 } | 196 } |
201 } | 197 } |
202 | 198 |
203 // static | 199 // static |
204 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) { | 200 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) { |
205 // Files in the sources have this public bit by default. | 201 // Files in the sources have this public bit by default. |
206 bool default_public = target->all_headers_public(); | 202 bool default_public = target->all_headers_public(); |
207 | 203 |
208 std::map<SourceFile, PublicGeneratedPair> files_to_public; | 204 std::map<SourceFile, PublicGeneratedPair> files_to_public; |
209 | 205 |
210 // First collect the normal files, they get the default visibility. Always | 206 // First collect the normal files, they get the default visibility. Always |
211 // trim the root gen dir if it exists. This will only exist on outputs of an | 207 // trim the root gen dir if it exists. This will only exist on outputs of an |
212 // action, but those are often then wired into the sources of a compiled | 208 // action, but those are often then wired into the sources of a compiled |
213 // target to actually compile generated code. If you depend on the compiled | 209 // target to actually compile generated code. If you depend on the compiled |
214 // target, it should be enough to be able to include the header. | 210 // target, it should be enough to be able to include the header. |
215 const Target::FileList& sources = target->sources(); | 211 for (const auto& source : target->sources()) { |
216 for (size_t i = 0; i < sources.size(); i++) { | 212 SourceFile file = RemoveRootGenDirFromFile(target, source); |
217 SourceFile file = RemoveRootGenDirFromFile(target, sources[i]); | |
218 files_to_public[file].is_public = default_public; | 213 files_to_public[file].is_public = default_public; |
219 } | 214 } |
220 | 215 |
221 // Add in the public files, forcing them to public. This may overwrite some | 216 // Add in the public files, forcing them to public. This may overwrite some |
222 // entries, and it may add new ones. | 217 // entries, and it may add new ones. |
223 const Target::FileList& public_list = target->public_headers(); | 218 if (default_public) // List only used when default is not public. |
224 if (default_public) | 219 DCHECK(target->public_headers().empty()); |
225 DCHECK(public_list.empty()); // List only used when default is not public. | 220 for (const auto& source : target->public_headers()) { |
226 for (size_t i = 0; i < public_list.size(); i++) { | 221 SourceFile file = RemoveRootGenDirFromFile(target, source); |
227 SourceFile file = RemoveRootGenDirFromFile(target, public_list[i]); | |
228 files_to_public[file].is_public = true; | 222 files_to_public[file].is_public = true; |
229 } | 223 } |
230 | 224 |
231 // Add in outputs from actions. These are treated as public (since if other | 225 // Add in outputs from actions. These are treated as public (since if other |
232 // targets can't use them, then there wouldn't be any point in outputting). | 226 // targets can't use them, then there wouldn't be any point in outputting). |
233 std::vector<SourceFile> outputs; | 227 std::vector<SourceFile> outputs; |
234 target->action_values().GetOutputsAsSourceFiles(target, &outputs); | 228 target->action_values().GetOutputsAsSourceFiles(target, &outputs); |
235 for (size_t i = 0; i < outputs.size(); i++) { | 229 for (const auto& output : outputs) { |
236 // For generated files in the "gen" directory, add the filename to the | 230 // For generated files in the "gen" directory, add the filename to the |
237 // map assuming "gen" is the source root. This means that when files include | 231 // map assuming "gen" is the source root. This means that when files include |
238 // the generated header relative to there (the recommended practice), we'll | 232 // the generated header relative to there (the recommended practice), we'll |
239 // find the file. | 233 // find the file. |
240 SourceFile output_file = RemoveRootGenDirFromFile(target, outputs[i]); | 234 SourceFile output_file = RemoveRootGenDirFromFile(target, output); |
241 PublicGeneratedPair* pair = &files_to_public[output_file]; | 235 PublicGeneratedPair* pair = &files_to_public[output_file]; |
242 pair->is_public = true; | 236 pair->is_public = true; |
243 pair->is_generated = true; | 237 pair->is_generated = true; |
244 } | 238 } |
245 | 239 |
246 // Add the merged list to the master list of all files. | 240 // Add the merged list to the master list of all files. |
247 for (std::map<SourceFile, PublicGeneratedPair>::const_iterator i = | 241 for (const auto& cur : files_to_public) { |
248 files_to_public.begin(); | 242 (*dest)[cur.first].push_back(TargetInfo( |
249 i != files_to_public.end(); ++i) { | 243 target, cur.second.is_public, cur.second.is_generated)); |
250 (*dest)[i->first].push_back(TargetInfo( | |
251 target, i->second.is_public, i->second.is_generated)); | |
252 } | 244 } |
253 } | 245 } |
254 | 246 |
255 bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const { | 247 bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const { |
256 const std::string& build_dir = build_settings_->build_dir().value(); | 248 const std::string& build_dir = build_settings_->build_dir().value(); |
257 return file.value().compare(0, build_dir.size(), build_dir) == 0; | 249 return file.value().compare(0, build_dir.size(), build_dir) == 0; |
258 } | 250 } |
259 | 251 |
260 // This current assumes all include paths are relative to the source root | 252 // This current assumes all include paths are relative to the source root |
261 // which is generally the case for Chromium. | 253 // which is generally the case for Chromium. |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
389 break; | 381 break; |
390 } | 382 } |
391 } | 383 } |
392 | 384 |
393 if (!found_dependency) { | 385 if (!found_dependency) { |
394 DCHECK(!last_error.has_error()); | 386 DCHECK(!last_error.has_error()); |
395 | 387 |
396 std::string msg = "It is not in any dependency of " + | 388 std::string msg = "It is not in any dependency of " + |
397 from_target->label().GetUserVisibleName(false); | 389 from_target->label().GetUserVisibleName(false); |
398 msg += "\nThe include file is in the target(s):\n"; | 390 msg += "\nThe include file is in the target(s):\n"; |
399 for (size_t i = 0; i < targets.size(); i++) | 391 for (const auto& target : targets) |
400 msg += " " + targets[i].target->label().GetUserVisibleName(false) + "\n"; | 392 msg += " " + target.target->label().GetUserVisibleName(false) + "\n"; |
401 if (targets.size() > 1) | 393 if (targets.size() > 1) |
402 msg += "at least one of "; | 394 msg += "at least one of "; |
403 msg += "which should somehow be reachable from " + | 395 msg += "which should somehow be reachable from " + |
404 from_target->label().GetUserVisibleName(false); | 396 from_target->label().GetUserVisibleName(false); |
405 | 397 |
406 // Danger: must call CreatePersistentRange to put in Err. | 398 // Danger: must call CreatePersistentRange to put in Err. |
407 *err = Err(CreatePersistentRange(source_file, range), | 399 *err = Err(CreatePersistentRange(source_file, range), |
408 "Include not allowed.", msg); | 400 "Include not allowed.", msg); |
409 return false; | 401 return false; |
410 } | 402 } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 while (target != search_from) { | 487 while (target != search_from) { |
496 chain->push_back(cur_link); | 488 chain->push_back(cur_link); |
497 cur_link = breadcrumbs[target]; | 489 cur_link = breadcrumbs[target]; |
498 target = cur_link.target; | 490 target = cur_link.target; |
499 } | 491 } |
500 chain->push_back(ChainLink(search_from, true)); | 492 chain->push_back(ChainLink(search_from, true)); |
501 return true; | 493 return true; |
502 } | 494 } |
503 | 495 |
504 // Always consider public dependencies as possibilities. | 496 // Always consider public dependencies as possibilities. |
505 const LabelTargetVector& public_deps = target->public_deps(); | 497 for (const auto& dep : target->public_deps()) { |
506 for (size_t i = 0; i < public_deps.size(); i++) { | 498 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second) |
507 if (breadcrumbs.insert( | 499 work_queue.push(ChainLink(dep.ptr, true)); |
508 std::make_pair(public_deps[i].ptr, cur_link)).second) | |
509 work_queue.push(ChainLink(public_deps[i].ptr, true)); | |
510 } | 500 } |
511 | 501 |
512 if (first_time || !require_permitted) { | 502 if (first_time || !require_permitted) { |
513 // Consider all dependencies since all target paths are allowed, so add | 503 // Consider all dependencies since all target paths are allowed, so add |
514 // in private ones. Also do this the first time through the loop, since | 504 // in private ones. Also do this the first time through the loop, since |
515 // a target can include headers from its direct deps regardless of | 505 // a target can include headers from its direct deps regardless of |
516 // public/private-ness. | 506 // public/private-ness. |
517 first_time = false; | 507 first_time = false; |
518 const LabelTargetVector& private_deps = target->private_deps(); | 508 for (const auto& dep : target->private_deps()) { |
519 for (size_t i = 0; i < private_deps.size(); i++) { | 509 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second) |
520 if (breadcrumbs.insert( | 510 work_queue.push(ChainLink(dep.ptr, false)); |
521 std::make_pair(private_deps[i].ptr, cur_link)).second) | |
522 work_queue.push(ChainLink(private_deps[i].ptr, false)); | |
523 } | 511 } |
524 } | 512 } |
525 } | 513 } |
526 | 514 |
527 return false; | 515 return false; |
528 } | 516 } |
OLD | NEW |