Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / tools / gn / header_checker.cc
blob9092638e9b530d11dbc84c34f5fa896c894e93f7
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 if (to_check.empty()) {
135 // Check all files.
136 RunCheckOverFiles(file_map_, force_check);
137 } else {
138 // Run only over the files in the given targets.
139 FileMap files_to_check;
140 for (const auto& check : to_check)
141 AddTargetToFileMap(check, &files_to_check);
142 RunCheckOverFiles(files_to_check, force_check);
145 if (errors_.empty())
146 return true;
147 *errors = errors_;
148 return false;
151 void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
152 if (files.empty())
153 return;
155 scoped_refptr<base::SequencedWorkerPool> pool(
156 new base::SequencedWorkerPool(16, "HeaderChecker"));
157 for (const auto& file : files) {
158 // Only check C-like source files (RC files also have includes).
159 SourceFileType type = GetSourceFileType(file.first);
160 if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C &&
161 type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
162 continue;
164 // Do a first pass to find if this should be skipped. All targets including
165 // this source file must exclude it from checking, or any target
166 // must mark it as generated (for cases where one target generates a file,
167 // and another lists it as a source to compile it).
168 if (!force_check) {
169 bool check_includes = false;
170 bool is_generated = false;
171 for (const auto& vect_i : file.second) {
172 check_includes |= vect_i.target->check_includes();
173 is_generated |= vect_i.is_generated;
175 if (!check_includes || is_generated)
176 continue;
179 for (const auto& vect_i : file.second) {
180 pool->PostWorkerTaskWithShutdownBehavior(
181 FROM_HERE,
182 base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first),
183 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
187 // After this call we're single-threaded again.
188 pool->Shutdown();
191 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
192 Err err;
193 if (!CheckFile(target, file, &err)) {
194 base::AutoLock lock(lock_);
195 errors_.push_back(err);
199 // static
200 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
201 // Files in the sources have this public bit by default.
202 bool default_public = target->all_headers_public();
204 std::map<SourceFile, PublicGeneratedPair> files_to_public;
206 // First collect the normal files, they get the default visibility. Always
207 // trim the root gen dir if it exists. This will only exist on outputs of an
208 // action, but those are often then wired into the sources of a compiled
209 // target to actually compile generated code. If you depend on the compiled
210 // target, it should be enough to be able to include the header.
211 for (const auto& source : target->sources()) {
212 SourceFile file = RemoveRootGenDirFromFile(target, source);
213 files_to_public[file].is_public = default_public;
216 // Add in the public files, forcing them to public. This may overwrite some
217 // entries, and it may add new ones.
218 if (default_public) // List only used when default is not public.
219 DCHECK(target->public_headers().empty());
220 for (const auto& source : target->public_headers()) {
221 SourceFile file = RemoveRootGenDirFromFile(target, source);
222 files_to_public[file].is_public = true;
225 // Add in outputs from actions. These are treated as public (since if other
226 // targets can't use them, then there wouldn't be any point in outputting).
227 std::vector<SourceFile> outputs;
228 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
229 for (const auto& output : outputs) {
230 // For generated files in the "gen" directory, add the filename to the
231 // map assuming "gen" is the source root. This means that when files include
232 // the generated header relative to there (the recommended practice), we'll
233 // find the file.
234 SourceFile output_file = RemoveRootGenDirFromFile(target, output);
235 PublicGeneratedPair* pair = &files_to_public[output_file];
236 pair->is_public = true;
237 pair->is_generated = true;
240 // Add the merged list to the master list of all files.
241 for (const auto& cur : files_to_public) {
242 (*dest)[cur.first].push_back(TargetInfo(
243 target, cur.second.is_public, cur.second.is_generated));
247 bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
248 const std::string& build_dir = build_settings_->build_dir().value();
249 return file.value().compare(0, build_dir.size(), build_dir) == 0;
252 // This current assumes all include paths are relative to the source root
253 // which is generally the case for Chromium.
255 // A future enhancement would be to search the include path for the target
256 // containing the source file containing this include and find the file to
257 // handle the cases where people do weird things with the paths.
258 SourceFile HeaderChecker::SourceFileForInclude(
259 const base::StringPiece& input) const {
260 std::string str("//");
261 input.AppendToString(&str);
262 return SourceFile(str);
265 bool HeaderChecker::CheckFile(const Target* from_target,
266 const SourceFile& file,
267 Err* err) const {
268 ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
270 // Sometimes you have generated source files included as sources in another
271 // target. These won't exist at checking time. Since we require all generated
272 // files to be somewhere in the output tree, we can just check the name to
273 // see if they should be skipped.
274 if (IsFileInOuputDir(file))
275 return true;
277 base::FilePath path = build_settings_->GetFullPath(file);
278 std::string contents;
279 if (!base::ReadFileToString(path, &contents)) {
280 *err = Err(from_target->defined_from(), "Source file not found.",
281 "The target:\n " + from_target->label().GetUserVisibleName(false) +
282 "\nhas a source file:\n " + file.value() +
283 "\nwhich was not found.");
284 return false;
287 InputFile input_file(file);
288 input_file.SetContents(contents);
290 CIncludeIterator iter(&input_file);
291 base::StringPiece current_include;
292 LocationRange range;
293 while (iter.GetNextIncludeString(&current_include, &range)) {
294 SourceFile include = SourceFileForInclude(current_include);
295 if (!CheckInclude(from_target, input_file, include, range, err))
296 return false;
299 return true;
302 // If the file exists:
303 // - It must be in one or more dependencies of the given target.
304 // - Those dependencies must have visibility from the source file.
305 // - The header must be in the public section of those dependeices.
306 // - Those dependencies must either have no direct dependent configs with
307 // flags that affect the compiler, or those direct dependent configs apply
308 // to the "from_target" (it's one "hop" away). This ensures that if the
309 // include file needs needs compiler settings to compile it, that those
310 // settings are applied to the file including it.
311 bool HeaderChecker::CheckInclude(const Target* from_target,
312 const InputFile& source_file,
313 const SourceFile& include_file,
314 const LocationRange& range,
315 Err* err) const {
316 // Assume if the file isn't declared in our sources that we don't need to
317 // check it. It would be nice if we could give an error if this happens, but
318 // our include finder is too primitive and returns all includes, even if
319 // they're in a #if not executed in the current build. In that case, it's
320 // not unusual for the buildfiles to not specify that header at all.
321 FileMap::const_iterator found = file_map_.find(include_file);
322 if (found == file_map_.end())
323 return true;
325 const TargetVector& targets = found->second;
326 Chain chain; // Prevent reallocating in the loop.
328 // For all targets containing this file, we require that at least one be
329 // a direct or public dependency of the current target, and that the header
330 // is public within the target.
332 // If there is more than one target containing this header, we may encounter
333 // some error cases before finding a good one. This error stores the previous
334 // one encountered, which we may or may not throw away.
335 Err last_error;
337 bool found_dependency = false;
338 for (size_t i = 0; i < targets.size(); i++) {
339 // We always allow source files in a target to include headers also in that
340 // target.
341 const Target* to_target = targets[i].target;
342 if (to_target == from_target)
343 return true;
345 bool is_permitted_chain = false;
346 if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
347 DCHECK(chain.size() >= 2);
348 DCHECK(chain[0].target == to_target);
349 DCHECK(chain[chain.size() - 1].target == from_target);
351 found_dependency = true;
353 if (targets[i].is_public && is_permitted_chain) {
354 // This one is OK, we're done.
355 last_error = Err();
356 break;
359 // Diagnose the error.
360 if (!targets[i].is_public) {
361 // Danger: must call CreatePersistentRange to put in Err.
362 last_error = Err(
363 CreatePersistentRange(source_file, range),
364 "Including a private header.",
365 "This file is private to the target " +
366 targets[i].target->label().GetUserVisibleName(false));
367 } else if (!is_permitted_chain) {
368 last_error = Err(
369 CreatePersistentRange(source_file, range),
370 "Can't include this header from here.",
371 GetDependencyChainPublicError(chain));
372 } else {
373 NOTREACHED();
375 } else if (
376 to_target->allow_circular_includes_from().find(from_target->label()) !=
377 to_target->allow_circular_includes_from().end()) {
378 // Not a dependency, but this include is whitelisted from the destination.
379 found_dependency = true;
380 last_error = Err();
381 break;
385 if (!found_dependency) {
386 DCHECK(!last_error.has_error());
388 std::string msg = "It is not in any dependency of " +
389 from_target->label().GetUserVisibleName(false);
390 msg += "\nThe include file is in the target(s):\n";
391 for (const auto& target : targets)
392 msg += " " + target.target->label().GetUserVisibleName(false) + "\n";
393 if (targets.size() > 1)
394 msg += "at least one of ";
395 msg += "which should somehow be reachable from " +
396 from_target->label().GetUserVisibleName(false);
398 // Danger: must call CreatePersistentRange to put in Err.
399 *err = Err(CreatePersistentRange(source_file, range),
400 "Include not allowed.", msg);
401 return false;
403 if (last_error.has_error()) {
404 // Found at least one dependency chain above, but it had an error.
405 *err = last_error;
406 return false;
409 // One thing we didn't check for is targets that expose their dependents
410 // headers in their own public headers.
412 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
413 // from C should get the configs also or things could be out-of-sync. Above,
414 // we check for A including C's headers directly, but A could also include a
415 // header from B that in turn includes a header from C.
417 // There are two ways to solve this:
418 // - If a public header in B includes C, force B to publicly depend on C.
419 // This is possible to check, but might be super annoying because most
420 // targets (especially large leaf-node targets) don't declare
421 // public/private headers and you'll get lots of false positives.
423 // - Save the includes found in each file and actually compute the graph of
424 // includes to detect when A implicitly includes C's header. This will not
425 // have the annoying false positive problem, but is complex to write.
427 return true;
430 bool HeaderChecker::IsDependencyOf(const Target* search_for,
431 const Target* search_from,
432 Chain* chain,
433 bool* is_permitted) const {
434 if (search_for == search_from) {
435 // A target is always visible from itself.
436 *is_permitted = true;
437 return false;
440 // Find the shortest public dependency chain.
441 if (IsDependencyOf(search_for, search_from, true, chain)) {
442 *is_permitted = true;
443 return true;
446 // If not, try to find any dependency chain at all.
447 if (IsDependencyOf(search_for, search_from, false, chain)) {
448 *is_permitted = false;
449 return true;
452 *is_permitted = false;
453 return false;
456 bool HeaderChecker::IsDependencyOf(const Target* search_for,
457 const Target* search_from,
458 bool require_permitted,
459 Chain* chain) const {
460 // This method conducts a breadth-first search through the dependency graph
461 // to find a shortest chain from search_from to search_for.
463 // work_queue maintains a queue of targets which need to be considered as
464 // part of this chain, in the order they were first traversed.
466 // Each time a new transitive dependency of search_from is discovered for
467 // the first time, it is added to work_queue and a "breadcrumb" is added,
468 // indicating which target it was reached from when first discovered.
470 // Once this search finds search_for, the breadcrumbs are used to reconstruct
471 // a shortest dependency chain (in reverse order) from search_from to
472 // search_for.
474 std::map<const Target*, ChainLink> breadcrumbs;
475 std::queue<ChainLink> work_queue;
476 work_queue.push(ChainLink(search_from, true));
478 bool first_time = true;
479 while (!work_queue.empty()) {
480 ChainLink cur_link = work_queue.front();
481 const Target* target = cur_link.target;
482 work_queue.pop();
484 if (target == search_for) {
485 // Found it! Reconstruct the chain.
486 chain->clear();
487 while (target != search_from) {
488 chain->push_back(cur_link);
489 cur_link = breadcrumbs[target];
490 target = cur_link.target;
492 chain->push_back(ChainLink(search_from, true));
493 return true;
496 // Always consider public dependencies as possibilities.
497 for (const auto& dep : target->public_deps()) {
498 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
499 work_queue.push(ChainLink(dep.ptr, true));
502 if (first_time || !require_permitted) {
503 // Consider all dependencies since all target paths are allowed, so add
504 // in private ones. Also do this the first time through the loop, since
505 // a target can include headers from its direct deps regardless of
506 // public/private-ness.
507 first_time = false;
508 for (const auto& dep : target->private_deps()) {
509 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
510 work_queue.push(ChainLink(dep.ptr, false));
515 return false;