Update WebCore.FeatureObserver histogram owner.
[chromium-blink-merge.git] / tools / gn / header_checker.cc
blob1141e6efd6ac73eca9e90383bc53eb38229b12d0
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() && base::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 // Returns true if the two targets have the same label not counting the
119 // toolchain.
120 bool TargetLabelsMatchExceptToolchain(const Target* a, const Target* b) {
121 return a->label().dir() == b->label().dir() &&
122 a->label().name() == b->label().name();
125 } // namespace
127 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
128 const std::vector<const Target*>& targets)
129 : main_loop_(base::MessageLoop::current()),
130 build_settings_(build_settings) {
131 for (const auto& target : targets)
132 AddTargetToFileMap(target, &file_map_);
135 HeaderChecker::~HeaderChecker() {
138 bool HeaderChecker::Run(const std::vector<const Target*>& to_check,
139 bool force_check,
140 std::vector<Err>* errors) {
141 FileMap files_to_check;
142 for (const auto& check : to_check)
143 AddTargetToFileMap(check, &files_to_check);
144 RunCheckOverFiles(files_to_check, force_check);
146 if (errors_.empty())
147 return true;
148 *errors = errors_;
149 return false;
152 void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
153 if (files.empty())
154 return;
156 scoped_refptr<base::SequencedWorkerPool> pool(
157 new base::SequencedWorkerPool(16, "HeaderChecker"));
158 for (const auto& file : files) {
159 // Only check C-like source files (RC files also have includes).
160 SourceFileType type = GetSourceFileType(file.first);
161 if (type != SOURCE_CPP && type != SOURCE_H && type != SOURCE_C &&
162 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
163 continue;
165 // If any target marks it as generated, don't check it.
166 bool is_generated = false;
167 for (const auto& vect_i : file.second)
168 is_generated |= vect_i.is_generated;
169 if (is_generated)
170 continue;
172 for (const auto& vect_i : file.second) {
173 if (vect_i.target->check_includes()) {
174 pool->PostWorkerTaskWithShutdownBehavior(
175 FROM_HERE,
176 base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first),
177 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
182 // After this call we're single-threaded again.
183 pool->Shutdown();
186 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
187 Err err;
188 if (!CheckFile(target, file, &err)) {
189 base::AutoLock lock(lock_);
190 errors_.push_back(err);
194 // static
195 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
196 // Files in the sources have this public bit by default.
197 bool default_public = target->all_headers_public();
199 std::map<SourceFile, PublicGeneratedPair> files_to_public;
201 // First collect the normal files, they get the default visibility. Always
202 // trim the root gen dir if it exists. This will only exist on outputs of an
203 // action, but those are often then wired into the sources of a compiled
204 // target to actually compile generated code. If you depend on the compiled
205 // target, it should be enough to be able to include the header.
206 for (const auto& source : target->sources()) {
207 SourceFile file = RemoveRootGenDirFromFile(target, source);
208 files_to_public[file].is_public = default_public;
211 // Add in the public files, forcing them to public. This may overwrite some
212 // entries, and it may add new ones.
213 if (default_public) // List only used when default is not public.
214 DCHECK(target->public_headers().empty());
215 for (const auto& source : target->public_headers()) {
216 SourceFile file = RemoveRootGenDirFromFile(target, source);
217 files_to_public[file].is_public = true;
220 // Add in outputs from actions. These are treated as public (since if other
221 // targets can't use them, then there wouldn't be any point in outputting).
222 std::vector<SourceFile> outputs;
223 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
224 for (const auto& output : outputs) {
225 // For generated files in the "gen" directory, add the filename to the
226 // map assuming "gen" is the source root. This means that when files include
227 // the generated header relative to there (the recommended practice), we'll
228 // find the file.
229 SourceFile output_file = RemoveRootGenDirFromFile(target, output);
230 PublicGeneratedPair* pair = &files_to_public[output_file];
231 pair->is_public = true;
232 pair->is_generated = true;
235 // Add the merged list to the master list of all files.
236 for (const auto& cur : files_to_public) {
237 (*dest)[cur.first].push_back(TargetInfo(
238 target, cur.second.is_public, cur.second.is_generated));
242 bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
243 const std::string& build_dir = build_settings_->build_dir().value();
244 return file.value().compare(0, build_dir.size(), build_dir) == 0;
247 // This current assumes all include paths are relative to the source root
248 // which is generally the case for Chromium.
250 // A future enhancement would be to search the include path for the target
251 // containing the source file containing this include and find the file to
252 // handle the cases where people do weird things with the paths.
253 SourceFile HeaderChecker::SourceFileForInclude(
254 const base::StringPiece& input) const {
255 std::string str("//");
256 input.AppendToString(&str);
257 return SourceFile(str);
260 bool HeaderChecker::CheckFile(const Target* from_target,
261 const SourceFile& file,
262 Err* err) const {
263 ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
265 // Sometimes you have generated source files included as sources in another
266 // target. These won't exist at checking time. Since we require all generated
267 // files to be somewhere in the output tree, we can just check the name to
268 // see if they should be skipped.
269 if (IsFileInOuputDir(file))
270 return true;
272 base::FilePath path = build_settings_->GetFullPath(file);
273 std::string contents;
274 if (!base::ReadFileToString(path, &contents)) {
275 *err = Err(from_target->defined_from(), "Source file not found.",
276 "The target:\n " + from_target->label().GetUserVisibleName(false) +
277 "\nhas a source file:\n " + file.value() +
278 "\nwhich was not found.");
279 return false;
282 InputFile input_file(file);
283 input_file.SetContents(contents);
285 CIncludeIterator iter(&input_file);
286 base::StringPiece current_include;
287 LocationRange range;
288 while (iter.GetNextIncludeString(&current_include, &range)) {
289 SourceFile include = SourceFileForInclude(current_include);
290 if (!CheckInclude(from_target, input_file, include, range, err))
291 return false;
294 return true;
297 // If the file exists:
298 // - It must be in one or more dependencies of the given target.
299 // - Those dependencies must have visibility from the source file.
300 // - The header must be in the public section of those dependeices.
301 // - Those dependencies must either have no direct dependent configs with
302 // flags that affect the compiler, or those direct dependent configs apply
303 // to the "from_target" (it's one "hop" away). This ensures that if the
304 // include file needs needs compiler settings to compile it, that those
305 // settings are applied to the file including it.
306 bool HeaderChecker::CheckInclude(const Target* from_target,
307 const InputFile& source_file,
308 const SourceFile& include_file,
309 const LocationRange& range,
310 Err* err) const {
311 // Assume if the file isn't declared in our sources that we don't need to
312 // check it. It would be nice if we could give an error if this happens, but
313 // our include finder is too primitive and returns all includes, even if
314 // they're in a #if not executed in the current build. In that case, it's
315 // not unusual for the buildfiles to not specify that header at all.
316 FileMap::const_iterator found = file_map_.find(include_file);
317 if (found == file_map_.end())
318 return true;
320 const TargetVector& targets = found->second;
321 Chain chain; // Prevent reallocating in the loop.
323 // If the file is unknown in the current toolchain (rather than being private
324 // or in a target not visible to the current target), ignore it. This is a
325 // bit of a hack to account for the fact that the include finder doesn't
326 // understand the preprocessor.
328 // When not cross-compiling, if a platform specific header is conditionally
329 // included in the build, and preprocessor conditions around #includes of
330 // that match the build conditions, everything will be OK because the file
331 // won't be known to GN even though the #include finder identified the file.
333 // Cross-compiling breaks this. When compiling Android on Linux, for example,
334 // we might see both Linux and Android definitions of a target and know
335 // about the union of all headers in the build. Since the #include finder
336 // ignores preprocessor, we will find the Linux headers in the Android
337 // build and note that a dependency from the Android target to the Linux
338 // one is missing (these might even be the same target in different
339 // toolchains!).
340 bool present_in_current_toolchain = false;
341 for (const auto& target : targets) {
342 if (from_target->label().ToolchainsEqual(target.target->label())) {
343 present_in_current_toolchain = true;
344 break;
347 if (!present_in_current_toolchain)
348 return true;
350 // For all targets containing this file, we require that at least one be
351 // a direct or public dependency of the current target, and that the header
352 // is public within the target.
354 // If there is more than one target containing this header, we may encounter
355 // some error cases before finding a good one. This error stores the previous
356 // one encountered, which we may or may not throw away.
357 Err last_error;
359 bool found_dependency = false;
360 for (const auto& target : targets) {
361 // We always allow source files in a target to include headers also in that
362 // target.
363 const Target* to_target = target.target;
364 if (to_target == from_target)
365 return true;
367 bool is_permitted_chain = false;
368 if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
369 DCHECK(chain.size() >= 2);
370 DCHECK(chain[0].target == to_target);
371 DCHECK(chain[chain.size() - 1].target == from_target);
373 found_dependency = true;
375 if (target.is_public && is_permitted_chain) {
376 // This one is OK, we're done.
377 last_error = Err();
378 break;
381 // Diagnose the error.
382 if (!target.is_public) {
383 // Danger: must call CreatePersistentRange to put in Err.
384 last_error = Err(CreatePersistentRange(source_file, range),
385 "Including a private header.",
386 "This file is private to the target " +
387 target.target->label().GetUserVisibleName(false));
388 } else if (!is_permitted_chain) {
389 last_error = Err(
390 CreatePersistentRange(source_file, range),
391 "Can't include this header from here.",
392 GetDependencyChainPublicError(chain));
393 } else {
394 NOTREACHED();
396 } else if (
397 to_target->allow_circular_includes_from().find(from_target->label()) !=
398 to_target->allow_circular_includes_from().end()) {
399 // Not a dependency, but this include is whitelisted from the destination.
400 found_dependency = true;
401 last_error = Err();
402 break;
406 if (!found_dependency) {
407 DCHECK(!last_error.has_error());
408 *err = MakeUnreachableError(source_file, range, from_target, targets);
409 return false;
411 if (last_error.has_error()) {
412 // Found at least one dependency chain above, but it had an error.
413 *err = last_error;
414 return false;
417 // One thing we didn't check for is targets that expose their dependents
418 // headers in their own public headers.
420 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
421 // from C should get the configs also or things could be out-of-sync. Above,
422 // we check for A including C's headers directly, but A could also include a
423 // header from B that in turn includes a header from C.
425 // There are two ways to solve this:
426 // - If a public header in B includes C, force B to publicly depend on C.
427 // This is possible to check, but might be super annoying because most
428 // targets (especially large leaf-node targets) don't declare
429 // public/private headers and you'll get lots of false positives.
431 // - Save the includes found in each file and actually compute the graph of
432 // includes to detect when A implicitly includes C's header. This will not
433 // have the annoying false positive problem, but is complex to write.
435 return true;
438 bool HeaderChecker::IsDependencyOf(const Target* search_for,
439 const Target* search_from,
440 Chain* chain,
441 bool* is_permitted) const {
442 if (search_for == search_from) {
443 // A target is always visible from itself.
444 *is_permitted = true;
445 return false;
448 // Find the shortest public dependency chain.
449 if (IsDependencyOf(search_for, search_from, true, chain)) {
450 *is_permitted = true;
451 return true;
454 // If not, try to find any dependency chain at all.
455 if (IsDependencyOf(search_for, search_from, false, chain)) {
456 *is_permitted = false;
457 return true;
460 *is_permitted = false;
461 return false;
464 bool HeaderChecker::IsDependencyOf(const Target* search_for,
465 const Target* search_from,
466 bool require_permitted,
467 Chain* chain) const {
468 // This method conducts a breadth-first search through the dependency graph
469 // to find a shortest chain from search_from to search_for.
471 // work_queue maintains a queue of targets which need to be considered as
472 // part of this chain, in the order they were first traversed.
474 // Each time a new transitive dependency of search_from is discovered for
475 // the first time, it is added to work_queue and a "breadcrumb" is added,
476 // indicating which target it was reached from when first discovered.
478 // Once this search finds search_for, the breadcrumbs are used to reconstruct
479 // a shortest dependency chain (in reverse order) from search_from to
480 // search_for.
482 std::map<const Target*, ChainLink> breadcrumbs;
483 std::queue<ChainLink> work_queue;
484 work_queue.push(ChainLink(search_from, true));
486 bool first_time = true;
487 while (!work_queue.empty()) {
488 ChainLink cur_link = work_queue.front();
489 const Target* target = cur_link.target;
490 work_queue.pop();
492 if (target == search_for) {
493 // Found it! Reconstruct the chain.
494 chain->clear();
495 while (target != search_from) {
496 chain->push_back(cur_link);
497 cur_link = breadcrumbs[target];
498 target = cur_link.target;
500 chain->push_back(ChainLink(search_from, true));
501 return true;
504 // Always consider public dependencies as possibilities.
505 for (const auto& dep : target->public_deps()) {
506 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
507 work_queue.push(ChainLink(dep.ptr, true));
510 if (first_time || !require_permitted) {
511 // Consider all dependencies since all target paths are allowed, so add
512 // in private ones. Also do this the first time through the loop, since
513 // a target can include headers from its direct deps regardless of
514 // public/private-ness.
515 first_time = false;
516 for (const auto& dep : target->private_deps()) {
517 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
518 work_queue.push(ChainLink(dep.ptr, false));
523 return false;
526 Err HeaderChecker::MakeUnreachableError(
527 const InputFile& source_file,
528 const LocationRange& range,
529 const Target* from_target,
530 const TargetVector& targets) {
531 // Normally the toolchains will all match, but when cross-compiling, we can
532 // get targets with more than one toolchain in the list of possibilities.
533 std::vector<const Target*> targets_with_matching_toolchains;
534 std::vector<const Target*> targets_with_other_toolchains;
535 for (const TargetInfo& candidate : targets) {
536 if (candidate.target->toolchain() == from_target->toolchain())
537 targets_with_matching_toolchains.push_back(candidate.target);
538 else
539 targets_with_other_toolchains.push_back(candidate.target);
542 // It's common when cross-compiling to have a target with the same file in
543 // more than one toolchain. We could output all of them, but this is
544 // generally confusing to people (most end-users won't understand toolchains
545 // well).
547 // So delete any candidates in other toolchains that also appear in the same
548 // toolchain as the from_target.
549 for (int other_index = 0;
550 other_index < static_cast<int>(targets_with_other_toolchains.size());
551 other_index++) {
552 for (const Target* cur_matching : targets_with_matching_toolchains) {
553 if (TargetLabelsMatchExceptToolchain(
554 cur_matching, targets_with_other_toolchains[other_index])) {
555 // Found a duplicate, erase it.
556 targets_with_other_toolchains.erase(
557 targets_with_other_toolchains.begin() + other_index);
558 other_index--;
559 break;
564 // Only display toolchains on labels if they don't all match.
565 bool include_toolchain = !targets_with_other_toolchains.empty();
567 std::string msg = "It is not in any dependency of\n " +
568 from_target->label().GetUserVisibleName(include_toolchain);
569 msg += "\nThe include file is in the target(s):\n";
570 for (const auto& target : targets_with_matching_toolchains)
571 msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
572 for (const auto& target : targets_with_other_toolchains)
573 msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
574 if (targets_with_other_toolchains.size() +
575 targets_with_matching_toolchains.size() > 1)
576 msg += "at least one of ";
577 msg += "which should somehow be reachable.";
579 // Danger: must call CreatePersistentRange to put in Err.
580 return Err(CreatePersistentRange(source_file, range),
581 "Include not allowed.", msg);