Change next_proto member type.
[chromium-blink-merge.git] / tools / gn / header_checker.cc
blobea6384440bddf9d0afd41fff850d87b70aa0e3e1
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.
5 #include "tools/gn/header_checker.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/files/file_util.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "tools/gn/build_settings.h"
15 #include "tools/gn/builder.h"
16 #include "tools/gn/c_include_iterator.h"
17 #include "tools/gn/config.h"
18 #include "tools/gn/err.h"
19 #include "tools/gn/filesystem_utils.h"
20 #include "tools/gn/scheduler.h"
21 #include "tools/gn/source_file_type.h"
22 #include "tools/gn/target.h"
23 #include "tools/gn/trace.h"
25 namespace {
27 struct PublicGeneratedPair {
28 PublicGeneratedPair() : is_public(false), is_generated(false) {}
29 bool is_public;
30 bool is_generated;
33 // If the given file is in the "gen" folder, trims this so it treats the gen
34 // directory as the source root:
35 // //out/Debug/gen/foo/bar.h -> //foo/bar.h
36 // If the file isn't in the generated root, returns the input unchanged.
37 SourceFile RemoveRootGenDirFromFile(const Target* target,
38 const SourceFile& file) {
39 const SourceDir& gen = target->settings()->toolchain_gen_dir();
40 if (!gen.is_null() && StartsWithASCII(file.value(), gen.value(), true))
41 return SourceFile("//" + file.value().substr(gen.value().size()));
42 return file;
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
47 // an InputFile that must persist as long as the Err does.
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
51 // persists. This means that the current file contents will live as long as
52 // the program, but this is OK since we're erroring out anyway.
53 LocationRange CreatePersistentRange(const InputFile& input_file,
54 const LocationRange& range) {
55 InputFile* clone_input_file;
56 std::vector<Token>* tokens; // Don't care about this.
57 scoped_ptr<ParseNode>* parse_root; // Don't care about this.
59 g_scheduler->input_file_manager()->AddDynamicInput(
60 input_file.name(), &clone_input_file, &tokens, &parse_root);
61 clone_input_file->SetContents(input_file.contents());
63 return LocationRange(Location(clone_input_file,
64 range.begin().line_number(),
65 range.begin().char_offset(),
66 -1 /* TODO(scottmg) */),
67 Location(clone_input_file,
68 range.end().line_number(),
69 range.end().char_offset(),
70 -1 /* TODO(scottmg) */));
73 // Given a reverse dependency chain where the target chain[0]'s includes are
74 // being used by chain[end] and not all deps are public, returns the string
75 // describing the error.
76 std::string GetDependencyChainPublicError(
77 const HeaderChecker::Chain& chain) {
78 std::string ret = "The target:\n " +
79 chain[chain.size() - 1].target->label().GetUserVisibleName(false) +
80 "\nis including a file from the target:\n " +
81 chain[0].target->label().GetUserVisibleName(false) +
82 "\n";
84 // Invalid chains should always be 0 (no chain) or more than two
85 // (intermediate private dependencies). 1 and 2 are impossible because a
86 // target can always include headers from itself and its direct dependents.
87 DCHECK(chain.size() != 1 && chain.size() != 2);
88 if (chain.empty()) {
89 ret += "There is no dependency chain between these targets.";
90 } else {
91 // Indirect dependency chain, print the chain.
92 ret += "\nIt's usually best to depend directly on the destination target.\n"
93 "In some cases, the destination target is considered a subcomponent\n"
94 "of an intermediate target. In this case, the intermediate target\n"
95 "should depend publicly on the destination to forward the ability\n"
96 "to include headers.\n"
97 "\n"
98 "Dependency chain (there may also be others):\n";
100 for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
101 ret.append(" " + chain[i].target->label().GetUserVisibleName(false));
102 if (i != 0) {
103 // Identify private dependencies so the user can see where in the
104 // dependency chain things went bad. Don't list this for the first link
105 // in the chain since direct dependencies are OK, and listing that as
106 // "private" may make people feel like they need to fix it.
107 if (i == static_cast<int>(chain.size()) - 1 || chain[i - 1].is_public)
108 ret.append(" -->");
109 else
110 ret.append(" --[private]-->");
112 ret.append("\n");
115 return ret;
118 } // namespace
120 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
121 const std::vector<const Target*>& targets)
122 : main_loop_(base::MessageLoop::current()),
123 build_settings_(build_settings) {
124 for (const auto& target : targets)
125 AddTargetToFileMap(target, &file_map_);
128 HeaderChecker::~HeaderChecker() {
131 bool HeaderChecker::Run(const std::vector<const Target*>& to_check,
132 bool force_check,
133 std::vector<Err>* errors) {
134 FileMap files_to_check;
135 for (const auto& check : to_check)
136 AddTargetToFileMap(check, &files_to_check);
137 RunCheckOverFiles(files_to_check, force_check);
139 if (errors_.empty())
140 return true;
141 *errors = errors_;
142 return false;
145 void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
146 if (files.empty())
147 return;
149 scoped_refptr<base::SequencedWorkerPool> pool(
150 new base::SequencedWorkerPool(16, "HeaderChecker"));
151 for (const auto& file : files) {
152 // Only check C-like source files (RC files also have includes).
153 SourceFileType type = GetSourceFileType(file.first);
154 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C &&
155 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
156 continue;
158 // Do a first pass to find if this should be skipped. All targets including
159 // this source file must exclude it from checking, or any target
160 // must mark it as generated (for cases where one target generates a file,
161 // and another lists it as a source to compile it).
162 if (!force_check) {
163 bool check_includes = false;
164 bool is_generated = false;
165 for (const auto& vect_i : file.second) {
166 check_includes |= vect_i.target->check_includes();
167 is_generated |= vect_i.is_generated;
169 if (!check_includes || is_generated)
170 continue;
173 for (const auto& vect_i : file.second) {
174 pool->PostWorkerTaskWithShutdownBehavior(
175 FROM_HERE,
176 base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first),
177 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
181 // After this call we're single-threaded again.
182 pool->Shutdown();
185 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
186 Err err;
187 if (!CheckFile(target, file, &err)) {
188 base::AutoLock lock(lock_);
189 errors_.push_back(err);
193 // static
194 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
195 // Files in the sources have this public bit by default.
196 bool default_public = target->all_headers_public();
198 std::map<SourceFile, PublicGeneratedPair> files_to_public;
200 // First collect the normal files, they get the default visibility. Always
201 // trim the root gen dir if it exists. This will only exist on outputs of an
202 // action, but those are often then wired into the sources of a compiled
203 // target to actually compile generated code. If you depend on the compiled
204 // target, it should be enough to be able to include the header.
205 for (const auto& source : target->sources()) {
206 SourceFile file = RemoveRootGenDirFromFile(target, source);
207 files_to_public[file].is_public = default_public;
210 // Add in the public files, forcing them to public. This may overwrite some
211 // entries, and it may add new ones.
212 if (default_public) // List only used when default is not public.
213 DCHECK(target->public_headers().empty());
214 for (const auto& source : target->public_headers()) {
215 SourceFile file = RemoveRootGenDirFromFile(target, source);
216 files_to_public[file].is_public = true;
219 // Add in outputs from actions. These are treated as public (since if other
220 // targets can't use them, then there wouldn't be any point in outputting).
221 std::vector<SourceFile> outputs;
222 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
223 for (const auto& output : outputs) {
224 // For generated files in the "gen" directory, add the filename to the
225 // map assuming "gen" is the source root. This means that when files include
226 // the generated header relative to there (the recommended practice), we'll
227 // find the file.
228 SourceFile output_file = RemoveRootGenDirFromFile(target, output);
229 PublicGeneratedPair* pair = &files_to_public[output_file];
230 pair->is_public = true;
231 pair->is_generated = true;
234 // Add the merged list to the master list of all files.
235 for (const auto& cur : files_to_public) {
236 (*dest)[cur.first].push_back(TargetInfo(
237 target, cur.second.is_public, cur.second.is_generated));
241 bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
242 const std::string& build_dir = build_settings_->build_dir().value();
243 return file.value().compare(0, build_dir.size(), build_dir) == 0;
246 // This current assumes all include paths are relative to the source root
247 // which is generally the case for Chromium.
249 // A future enhancement would be to search the include path for the target
250 // containing the source file containing this include and find the file to
251 // handle the cases where people do weird things with the paths.
252 SourceFile HeaderChecker::SourceFileForInclude(
253 const base::StringPiece& input) const {
254 std::string str("//");
255 input.AppendToString(&str);
256 return SourceFile(str);
259 bool HeaderChecker::CheckFile(const Target* from_target,
260 const SourceFile& file,
261 Err* err) const {
262 ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
264 // Sometimes you have generated source files included as sources in another
265 // target. These won't exist at checking time. Since we require all generated
266 // files to be somewhere in the output tree, we can just check the name to
267 // see if they should be skipped.
268 if (IsFileInOuputDir(file))
269 return true;
271 base::FilePath path = build_settings_->GetFullPath(file);
272 std::string contents;
273 if (!base::ReadFileToString(path, &contents)) {
274 *err = Err(from_target->defined_from(), "Source file not found.",
275 "The target:\n " + from_target->label().GetUserVisibleName(false) +
276 "\nhas a source file:\n " + file.value() +
277 "\nwhich was not found.");
278 return false;
281 InputFile input_file(file);
282 input_file.SetContents(contents);
284 CIncludeIterator iter(&input_file);
285 base::StringPiece current_include;
286 LocationRange range;
287 while (iter.GetNextIncludeString(&current_include, &range)) {
288 SourceFile include = SourceFileForInclude(current_include);
289 if (!CheckInclude(from_target, input_file, include, range, err))
290 return false;
293 return true;
296 // If the file exists:
297 // - It must be in one or more dependencies of the given target.
298 // - Those dependencies must have visibility from the source file.
299 // - The header must be in the public section of those dependeices.
300 // - Those dependencies must either have no direct dependent configs with
301 // flags that affect the compiler, or those direct dependent configs apply
302 // to the "from_target" (it's one "hop" away). This ensures that if the
303 // include file needs needs compiler settings to compile it, that those
304 // settings are applied to the file including it.
305 bool HeaderChecker::CheckInclude(const Target* from_target,
306 const InputFile& source_file,
307 const SourceFile& include_file,
308 const LocationRange& range,
309 Err* err) const {
310 // Assume if the file isn't declared in our sources that we don't need to
311 // check it. It would be nice if we could give an error if this happens, but
312 // our include finder is too primitive and returns all includes, even if
313 // they're in a #if not executed in the current build. In that case, it's
314 // not unusual for the buildfiles to not specify that header at all.
315 FileMap::const_iterator found = file_map_.find(include_file);
316 if (found == file_map_.end())
317 return true;
319 const TargetVector& targets = found->second;
320 Chain chain; // Prevent reallocating in the loop.
322 // For all targets containing this file, we require that at least one be
323 // a direct or public dependency of the current target, and that the header
324 // is public within the target.
326 // If there is more than one target containing this header, we may encounter
327 // some error cases before finding a good one. This error stores the previous
328 // one encountered, which we may or may not throw away.
329 Err last_error;
331 bool found_dependency = false;
332 for (size_t i = 0; i < targets.size(); i++) {
333 // We always allow source files in a target to include headers also in that
334 // target.
335 const Target* to_target = targets[i].target;
336 if (to_target == from_target)
337 return true;
339 bool is_permitted_chain = false;
340 if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
341 DCHECK(chain.size() >= 2);
342 DCHECK(chain[0].target == to_target);
343 DCHECK(chain[chain.size() - 1].target == from_target);
345 found_dependency = true;
347 if (targets[i].is_public && is_permitted_chain) {
348 // This one is OK, we're done.
349 last_error = Err();
350 break;
353 // Diagnose the error.
354 if (!targets[i].is_public) {
355 // Danger: must call CreatePersistentRange to put in Err.
356 last_error = Err(
357 CreatePersistentRange(source_file, range),
358 "Including a private header.",
359 "This file is private to the target " +
360 targets[i].target->label().GetUserVisibleName(false));
361 } else if (!is_permitted_chain) {
362 last_error = Err(
363 CreatePersistentRange(source_file, range),
364 "Can't include this header from here.",
365 GetDependencyChainPublicError(chain));
366 } else {
367 NOTREACHED();
369 } else if (
370 to_target->allow_circular_includes_from().find(from_target->label()) !=
371 to_target->allow_circular_includes_from().end()) {
372 // Not a dependency, but this include is whitelisted from the destination.
373 found_dependency = true;
374 last_error = Err();
375 break;
379 if (!found_dependency) {
380 DCHECK(!last_error.has_error());
382 std::string msg = "It is not in any dependency of " +
383 from_target->label().GetUserVisibleName(false);
384 msg += "\nThe include file is in the target(s):\n";
385 for (const auto& target : targets)
386 msg += " " + target.target->label().GetUserVisibleName(false) + "\n";
387 if (targets.size() > 1)
388 msg += "at least one of ";
389 msg += "which should somehow be reachable from " +
390 from_target->label().GetUserVisibleName(false);
392 // Danger: must call CreatePersistentRange to put in Err.
393 *err = Err(CreatePersistentRange(source_file, range),
394 "Include not allowed.", msg);
395 return false;
397 if (last_error.has_error()) {
398 // Found at least one dependency chain above, but it had an error.
399 *err = last_error;
400 return false;
403 // One thing we didn't check for is targets that expose their dependents
404 // headers in their own public headers.
406 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
407 // from C should get the configs also or things could be out-of-sync. Above,
408 // we check for A including C's headers directly, but A could also include a
409 // header from B that in turn includes a header from C.
411 // There are two ways to solve this:
412 // - If a public header in B includes C, force B to publicly depend on C.
413 // This is possible to check, but might be super annoying because most
414 // targets (especially large leaf-node targets) don't declare
415 // public/private headers and you'll get lots of false positives.
417 // - Save the includes found in each file and actually compute the graph of
418 // includes to detect when A implicitly includes C's header. This will not
419 // have the annoying false positive problem, but is complex to write.
421 return true;
424 bool HeaderChecker::IsDependencyOf(const Target* search_for,
425 const Target* search_from,
426 Chain* chain,
427 bool* is_permitted) const {
428 if (search_for == search_from) {
429 // A target is always visible from itself.
430 *is_permitted = true;
431 return false;
434 // Find the shortest public dependency chain.
435 if (IsDependencyOf(search_for, search_from, true, chain)) {
436 *is_permitted = true;
437 return true;
440 // If not, try to find any dependency chain at all.
441 if (IsDependencyOf(search_for, search_from, false, chain)) {
442 *is_permitted = false;
443 return true;
446 *is_permitted = false;
447 return false;
450 bool HeaderChecker::IsDependencyOf(const Target* search_for,
451 const Target* search_from,
452 bool require_permitted,
453 Chain* chain) const {
454 // This method conducts a breadth-first search through the dependency graph
455 // to find a shortest chain from search_from to search_for.
457 // work_queue maintains a queue of targets which need to be considered as
458 // part of this chain, in the order they were first traversed.
460 // Each time a new transitive dependency of search_from is discovered for
461 // the first time, it is added to work_queue and a "breadcrumb" is added,
462 // indicating which target it was reached from when first discovered.
464 // Once this search finds search_for, the breadcrumbs are used to reconstruct
465 // a shortest dependency chain (in reverse order) from search_from to
466 // search_for.
468 std::map<const Target*, ChainLink> breadcrumbs;
469 std::queue<ChainLink> work_queue;
470 work_queue.push(ChainLink(search_from, true));
472 bool first_time = true;
473 while (!work_queue.empty()) {
474 ChainLink cur_link = work_queue.front();
475 const Target* target = cur_link.target;
476 work_queue.pop();
478 if (target == search_for) {
479 // Found it! Reconstruct the chain.
480 chain->clear();
481 while (target != search_from) {
482 chain->push_back(cur_link);
483 cur_link = breadcrumbs[target];
484 target = cur_link.target;
486 chain->push_back(ChainLink(search_from, true));
487 return true;
490 // Always consider public dependencies as possibilities.
491 for (const auto& dep : target->public_deps()) {
492 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
493 work_queue.push(ChainLink(dep.ptr, true));
496 if (first_time || !require_permitted) {
497 // Consider all dependencies since all target paths are allowed, so add
498 // in private ones. Also do this the first time through the loop, since
499 // a target can include headers from its direct deps regardless of
500 // public/private-ness.
501 first_time = false;
502 for (const auto& dep : target->private_deps()) {
503 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
504 work_queue.push(ChainLink(dep.ptr, false));
509 return false;