athena: Improve the overview mode support for split-view mode.
[chromium-blink-merge.git] / tools / gn / header_checker.cc
blobdf74125bfd99b3645adaa6fdaa92b44683b5bbc3
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/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 (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(
64 Location(clone_input_file, range.begin().line_number(),
65 range.begin().char_offset()),
66 Location(clone_input_file, range.end().line_number(),
67 range.end().char_offset()));
70 // Returns true if the given config could affect how the compiler runs (rather
71 // than being empty or just affecting linker flags).
72 bool ConfigHasCompilerSettings(const Config* config) {
73 const ConfigValues& values = config->config_values();
74 return
75 !values.cflags().empty() ||
76 !values.cflags_c().empty() ||
77 !values.cflags_cc().empty() ||
78 !values.cflags_objc().empty() ||
79 !values.cflags_objcc().empty() ||
80 !values.defines().empty() ||
81 !values.include_dirs().empty();
84 // Returns true if the given target has any direct dependent configs with
85 // compiler settings in it.
86 bool HasDirectDependentCompilerSettings(const Target* target) {
87 const UniqueVector<LabelConfigPair>& direct =
88 target->direct_dependent_configs();
89 for (size_t i = 0; i < direct.size(); i++) {
90 if (ConfigHasCompilerSettings(direct[i].ptr))
91 return true;
93 return false;
96 // Given a reverse dependency chain where the target chain[0]'s dependent
97 // configs don't apply to chain[end], returns the string describing the error.
98 // The problematic index is the target where the dependent configs were lost.
99 std::string GetDependentConfigChainError(
100 const std::vector<const Target*>& chain,
101 size_t problematic_index) {
102 // Erroneous dependent config chains are always at least three long, since
103 // dependent configs would apply if it was length two.
104 DCHECK(chain.size() >= 3);
106 std::string from_label =
107 chain[chain.size() - 1]->label().GetUserVisibleName(false);
108 std::string to_label =
109 chain[0]->label().GetUserVisibleName(false);
110 std::string problematic_label =
111 chain[problematic_index]->label().GetUserVisibleName(false);
112 std::string problematic_upstream_label =
113 chain[problematic_index - 1]->label().GetUserVisibleName(false);
115 return
116 "You have the dependency tree: SOURCE -> MID -> DEST\n"
117 "Where a file from:\n"
118 " SOURCE = " + from_label + "\n"
119 "is including a header from:\n"
120 " DEST = " + to_label + "\n"
121 "\n"
122 "DEST has direct_dependent_configs, and they don't apply to SOURCE "
123 "because\nSOURCE is more than one hop away. This means that DEST's "
124 "headers might not\nreceive the expected compiler flags.\n"
125 "\n"
126 "To fix this, make SOURCE depend directly on DEST.\n"
127 "\n"
128 "Alternatively, if the target:\n"
129 " MID = " + problematic_label + "\n"
130 "exposes DEST as part of its public API, you can declare this by "
131 "adding:\n"
132 " forward_dependent_configs_from = [\n"
133 " \"" + problematic_upstream_label + "\"\n"
134 " ]\n"
135 "to MID. This will apply DEST's direct dependent configs to SOURCE.\n";
138 } // namespace
140 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
141 const std::vector<const Target*>& targets)
142 : main_loop_(base::MessageLoop::current()),
143 build_settings_(build_settings) {
144 for (size_t i = 0; i < targets.size(); i++)
145 AddTargetToFileMap(targets[i], &file_map_);
148 HeaderChecker::~HeaderChecker() {
151 bool HeaderChecker::Run(const std::vector<const Target*>& to_check,
152 bool force_check,
153 std::vector<Err>* errors) {
154 if (to_check.empty()) {
155 // Check all files.
156 RunCheckOverFiles(file_map_, force_check);
157 } else {
158 // Run only over the files in the given targets.
159 FileMap files_to_check;
160 for (size_t i = 0; i < to_check.size(); i++)
161 AddTargetToFileMap(to_check[i], &files_to_check);
162 RunCheckOverFiles(files_to_check, force_check);
165 if (errors_.empty())
166 return true;
167 *errors = errors_;
168 return false;
171 void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
172 if (files.empty())
173 return;
175 scoped_refptr<base::SequencedWorkerPool> pool(
176 new base::SequencedWorkerPool(16, "HeaderChecker"));
177 for (FileMap::const_iterator file_i = files.begin();
178 file_i != files.end(); ++file_i) {
179 const TargetVector& vect = file_i->second;
181 // Only check C-like source files (RC files also have includes).
182 SourceFileType type = GetSourceFileType(file_i->first);
183 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C &&
184 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
185 continue;
187 // Do a first pass to find if this should be skipped. All targets including
188 // this source file must exclude it from checking, or it must be generated.
189 if (!force_check) {
190 bool check_includes = false;
191 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) {
192 check_includes |=
193 vect[vect_i].target->check_includes() &&
194 !vect[vect_i].is_generated;
196 if (!check_includes)
197 continue;
200 for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) {
201 pool->PostWorkerTaskWithShutdownBehavior(
202 FROM_HERE,
203 base::Bind(&HeaderChecker::DoWork, this,
204 vect[vect_i].target, file_i->first),
205 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
209 // After this call we're single-threaded again.
210 pool->Shutdown();
213 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
214 Err err;
215 if (!CheckFile(target, file, &err)) {
216 base::AutoLock lock(lock_);
217 errors_.push_back(err);
221 // static
222 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
223 // Files in the sources have this public bit by default.
224 bool default_public = target->all_headers_public();
226 // First collect the normal files, they get the default visibility.
227 std::map<SourceFile, PublicGeneratedPair> files_to_public;
228 const Target::FileList& sources = target->sources();
229 for (size_t i = 0; i < sources.size(); i++)
230 files_to_public[sources[i]].is_public = default_public;
232 // Add in the public files, forcing them to public. This may overwrite some
233 // entries, and it may add new ones.
234 const Target::FileList& public_list = target->public_headers();
235 if (default_public)
236 DCHECK(public_list.empty()); // List only used when default is not public.
237 for (size_t i = 0; i < public_list.size(); i++)
238 files_to_public[public_list[i]].is_public = true;
240 // Add in outputs from actions. These are treated as public (since if other
241 // targets can't use them, then there wouldn't be any point in outputting).
242 std::vector<SourceFile> outputs;
243 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
244 for (size_t i = 0; i < outputs.size(); i++) {
245 // For generated files in the "gen" directory, add the filename to the
246 // map assuming "gen" is the source root. This means that when files include
247 // the generated header relative to there (the recommended practice), we'll
248 // find the file.
249 SourceFile output_file = RemoveRootGenDirFromFile(target, outputs[i]);
250 PublicGeneratedPair* pair = &files_to_public[output_file];
251 pair->is_public = true;
252 pair->is_generated = true;
255 // Add the merged list to the master list of all files.
256 for (std::map<SourceFile, PublicGeneratedPair>::const_iterator i =
257 files_to_public.begin();
258 i != files_to_public.end(); ++i) {
259 (*dest)[i->first].push_back(TargetInfo(
260 target, i->second.is_public, i->second.is_generated));
264 bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
265 const std::string& build_dir = build_settings_->build_dir().value();
266 return file.value().compare(0, build_dir.size(), build_dir) == 0;
269 // This current assumes all include paths are relative to the source root
270 // which is generally the case for Chromium.
272 // A future enhancement would be to search the include path for the target
273 // containing the source file containing this include and find the file to
274 // handle the cases where people do weird things with the paths.
275 SourceFile HeaderChecker::SourceFileForInclude(
276 const base::StringPiece& input) const {
277 std::string str("//");
278 input.AppendToString(&str);
279 return SourceFile(str);
282 bool HeaderChecker::CheckFile(const Target* from_target,
283 const SourceFile& file,
284 Err* err) const {
285 ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
287 // Sometimes you have generated source files included as sources in another
288 // target. These won't exist at checking time. Since we require all generated
289 // files to be somewhere in the output tree, we can just check the name to
290 // see if they should be skipped.
291 if (IsFileInOuputDir(file))
292 return true;
294 base::FilePath path = build_settings_->GetFullPath(file);
295 std::string contents;
296 if (!base::ReadFileToString(path, &contents)) {
297 *err = Err(from_target->defined_from(), "Source file not found.",
298 "This target includes as a source:\n " + file.value() +
299 "\nwhich was not found.");
300 return false;
303 InputFile input_file(file);
304 input_file.SetContents(contents);
306 CIncludeIterator iter(&input_file);
307 base::StringPiece current_include;
308 LocationRange range;
309 while (iter.GetNextIncludeString(&current_include, &range)) {
310 SourceFile include = SourceFileForInclude(current_include);
311 if (!CheckInclude(from_target, input_file, include, range, err))
312 return false;
315 return true;
318 // If the file exists:
319 // - It must be in one or more dependencies of the given target.
320 // - Those dependencies must have visibility from the source file.
321 // - The header must be in the public section of those dependeices.
322 // - Those dependencies must either have no direct dependent configs with
323 // flags that affect the compiler, or those direct dependent configs apply
324 // to the "from_target" (it's one "hop" away). This ensures that if the
325 // include file needs needs compiler settings to compile it, that those
326 // settings are applied to the file including it.
327 bool HeaderChecker::CheckInclude(const Target* from_target,
328 const InputFile& source_file,
329 const SourceFile& include_file,
330 const LocationRange& range,
331 Err* err) const {
332 // Assume if the file isn't declared in our sources that we don't need to
333 // check it. It would be nice if we could give an error if this happens, but
334 // our include finder is too primitive and returns all includes, even if
335 // they're in a #if not executed in the current build. In that case, it's
336 // not unusual for the buildfiles to not specify that header at all.
337 FileMap::const_iterator found = file_map_.find(include_file);
338 if (found == file_map_.end())
339 return true;
341 const TargetVector& targets = found->second;
342 std::vector<const Target*> chain; // Prevent reallocating in the loop.
343 bool direct_dependent_configs_apply = false;
345 // For all targets containing this file, we require that at least one be
346 // a dependency of the current target, and all targets that are dependencies
347 // must have the file listed in the public section.
348 bool found_dependency = false;
349 for (size_t i = 0; i < targets.size(); i++) {
350 // We always allow source files in a target to include headers also in that
351 // target.
352 const Target* to_target = targets[i].target;
353 if (to_target == from_target)
354 return true;
356 bool has_direct_dependent_compiler_settings =
357 HasDirectDependentCompilerSettings(to_target);
358 if (IsDependencyOf(to_target,
359 from_target,
360 has_direct_dependent_compiler_settings,
361 &chain,
362 &direct_dependent_configs_apply)) {
363 DCHECK(chain.size() >= 2);
364 DCHECK(chain[0] == to_target);
365 DCHECK(chain[chain.size() - 1] == from_target);
367 // The include is in a target that's a proper dependency. Verify that
368 // the including target has visibility.
369 if (!to_target->visibility().CanSeeMe(from_target->label())) {
370 std::string msg = "The included file is in " +
371 to_target->label().GetUserVisibleName(false) +
372 "\nwhich is not visible from " +
373 from_target->label().GetUserVisibleName(false) +
374 "\n(see \"gn help visibility\").";
376 // Danger: must call CreatePersistentRange to put in Err.
377 *err = Err(CreatePersistentRange(source_file, range),
378 "Including a header from non-visible target.", msg);
379 return false;
382 // The file must be public in the target.
383 if (!targets[i].is_public) {
384 // Danger: must call CreatePersistentRange to put in Err.
385 *err = Err(CreatePersistentRange(source_file, range),
386 "Including a private header.",
387 "This file is private to the target " +
388 targets[i].target->label().GetUserVisibleName(false));
389 return false;
392 // If the to_target has direct_dependent_configs, they must apply to the
393 // from_target.
394 if (has_direct_dependent_compiler_settings &&
395 !direct_dependent_configs_apply) {
396 size_t problematic_index = GetDependentConfigChainProblemIndex(chain);
397 *err = Err(CreatePersistentRange(source_file, range),
398 "Can't include this header from here.",
399 GetDependentConfigChainError(chain, problematic_index));
400 return false;
403 found_dependency = true;
404 } else if (
405 to_target->allow_circular_includes_from().find(from_target->label()) !=
406 to_target->allow_circular_includes_from().end()) {
407 // Not a dependency, but this include is whitelisted from the destination.
408 found_dependency = true;
412 if (!found_dependency) {
413 std::string msg = "It is not in any dependency of " +
414 from_target->label().GetUserVisibleName(false);
415 msg += "\nThe include file is in the target(s):\n";
416 for (size_t i = 0; i < targets.size(); i++)
417 msg += " " + targets[i].target->label().GetUserVisibleName(false) + "\n";
418 if (targets.size() > 1)
419 msg += "at least one of ";
420 msg += "which should somehow be reachable from " +
421 from_target->label().GetUserVisibleName(false);
423 // Danger: must call CreatePersistentRange to put in Err.
424 *err = Err(CreatePersistentRange(source_file, range),
425 "Include not allowed.", msg);
426 return false;
429 // One thing we didn't check for is targets that expose their dependents
430 // headers in their own public headers.
432 // Say we have A -> B -> C. If C has direct_dependent_configs, everybody
433 // getting headers from C should get the configs also or things could be
434 // out-of-sync. Above, we check for A including C's headers directly, but A
435 // could also include a header from B that in turn includes a header from C.
437 // There are two ways to solve this:
438 // - If a public header in B includes C, force B to forward C's direct
439 // dependent configs. This is possible to check, but might be super
440 // annoying because most targets (especially large leaf-node targets)
441 // don't declare public/private headers and you'll get lots of false
442 // positives.
444 // - Save the includes found in each file and actually compute the graph of
445 // includes to detect when A implicitly includes C's header. This will not
446 // have the annoying false positive problem, but is complex to write.
448 return true;
451 bool HeaderChecker::IsDependencyOf(const Target* search_for,
452 const Target* search_from,
453 bool prefer_direct_dependent_configs,
454 std::vector<const Target*>* chain,
455 bool* direct_dependent_configs_apply) const {
456 if (search_for == search_from)
457 return false;
459 // Find the shortest chain that forwards dependent configs, if one exists.
460 if (prefer_direct_dependent_configs &&
461 IsDependencyOf(search_for, search_from, true, chain)) {
462 if (direct_dependent_configs_apply)
463 *direct_dependent_configs_apply = true;
464 return true;
467 // If not, try to find any dependency chain at all.
468 if (IsDependencyOf(search_for, search_from, false, chain)) {
469 if (direct_dependent_configs_apply)
470 *direct_dependent_configs_apply = false;
471 return true;
474 return false;
477 bool HeaderChecker::IsDependencyOf(const Target* search_for,
478 const Target* search_from,
479 bool requires_dependent_configs,
480 std::vector<const Target*>* chain) const {
481 // This method conducts a breadth-first search through the dependency graph
482 // to find a shortest chain from search_from to search_for.
484 // work_queue maintains a queue of targets which need to be considered as
485 // part of this chain, in the order they were first traversed.
487 // Each time a new transitive dependency of search_from is discovered for
488 // the first time, it is added to work_queue and a "breadcrumb" is added,
489 // indicating which target it was reached from when first discovered.
491 // Once this search finds search_for, the breadcrumbs are used to reconstruct
492 // a shortest dependency chain (in reverse order) from search_from to
493 // search_for.
495 std::map<const Target*, const Target*> breadcrumbs;
496 std::queue<const Target*> work_queue;
497 work_queue.push(search_from);
499 while (!work_queue.empty()) {
500 const Target* target = work_queue.front();
501 work_queue.pop();
503 if (target == search_for) {
504 // Found it! Reconstruct the chain.
505 chain->clear();
506 while (target != search_from) {
507 chain->push_back(target);
508 target = breadcrumbs[target];
510 chain->push_back(search_from);
511 return true;
514 // If the callee requires direct-dependent configs be forwarded, then
515 // only targets for which they will be forwarded should be explored.
516 // Groups implicitly forward direct-dependent configs of all of their deps.
517 bool uses_all_deps = !requires_dependent_configs || target == search_from ||
518 target->output_type() == Target::GROUP;
520 const LabelTargetVector& deps =
521 uses_all_deps ? target->deps()
522 : target->forward_dependent_configs().vector();
523 for (size_t i = 0; i < deps.size(); i++) {
524 bool seeing_for_first_time =
525 breadcrumbs.insert(std::make_pair(deps[i].ptr, target)).second;
526 if (seeing_for_first_time)
527 work_queue.push(deps[i].ptr);
531 return false;
534 // static
535 size_t HeaderChecker::GetDependentConfigChainProblemIndex(
536 const std::vector<const Target*>& chain) {
537 // Direct dependent configs go up the chain one level with the following
538 // exceptions:
539 // - Skip over groups
540 // - Skip anything that explicitly forwards it
542 // Chains of length less than three have no problems.
543 // These should have been filtered out earlier.
544 DCHECK(chain.size() >= 3);
546 for (size_t i = 1; i < chain.size() - 1; i++) {
547 if (chain[i]->output_type() == Target::GROUP)
548 continue; // This one is OK, skip to next one.
550 // The forward list on this target should have contained in it the target
551 // at the next lower level.
552 const UniqueVector<LabelTargetPair>& forwarded =
553 chain[i]->forward_dependent_configs();
554 if (std::find_if(forwarded.begin(), forwarded.end(),
555 LabelPtrPtrEquals<Target>(chain[i - 1])) ==
556 forwarded.end())
557 return i;
560 CHECK(false) << "Unable to diagnose dependent config chain problem.";
561 return 0;