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"
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"
27 struct PublicGeneratedPair
{
28 PublicGeneratedPair() : is_public(false), is_generated(false) {}
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::StartsWith(file
.value(), gen
.value(),
41 base::CompareCase::SENSITIVE
))
42 return SourceFile("//" + file
.value().substr(gen
.value().size()));
46 // This class makes InputFiles on the stack as it reads files to check. When
47 // we throw an error, the Err indicates a locatin which has a pointer to
48 // an InputFile that must persist as long as the Err does.
50 // To make this work, this function creates a clone of the InputFile managed
51 // by the InputFileManager so the error can refer to something that
52 // persists. This means that the current file contents will live as long as
53 // the program, but this is OK since we're erroring out anyway.
54 LocationRange
CreatePersistentRange(const InputFile
& input_file
,
55 const LocationRange
& range
) {
56 InputFile
* clone_input_file
;
57 std::vector
<Token
>* tokens
; // Don't care about this.
58 scoped_ptr
<ParseNode
>* parse_root
; // Don't care about this.
60 g_scheduler
->input_file_manager()->AddDynamicInput(
61 input_file
.name(), &clone_input_file
, &tokens
, &parse_root
);
62 clone_input_file
->SetContents(input_file
.contents());
64 return LocationRange(Location(clone_input_file
,
65 range
.begin().line_number(),
66 range
.begin().char_offset(),
67 -1 /* TODO(scottmg) */),
68 Location(clone_input_file
,
69 range
.end().line_number(),
70 range
.end().char_offset(),
71 -1 /* TODO(scottmg) */));
74 // Given a reverse dependency chain where the target chain[0]'s includes are
75 // being used by chain[end] and not all deps are public, returns the string
76 // describing the error.
77 std::string
GetDependencyChainPublicError(
78 const HeaderChecker::Chain
& chain
) {
79 std::string ret
= "The target:\n " +
80 chain
[chain
.size() - 1].target
->label().GetUserVisibleName(false) +
81 "\nis including a file from the target:\n " +
82 chain
[0].target
->label().GetUserVisibleName(false) +
85 // Invalid chains should always be 0 (no chain) or more than two
86 // (intermediate private dependencies). 1 and 2 are impossible because a
87 // target can always include headers from itself and its direct dependents.
88 DCHECK(chain
.size() != 1 && chain
.size() != 2);
90 ret
+= "There is no dependency chain between these targets.";
92 // Indirect dependency chain, print the chain.
93 ret
+= "\nIt's usually best to depend directly on the destination target.\n"
94 "In some cases, the destination target is considered a subcomponent\n"
95 "of an intermediate target. In this case, the intermediate target\n"
96 "should depend publicly on the destination to forward the ability\n"
97 "to include headers.\n"
99 "Dependency chain (there may also be others):\n";
101 for (int i
= static_cast<int>(chain
.size()) - 1; i
>= 0; i
--) {
102 ret
.append(" " + chain
[i
].target
->label().GetUserVisibleName(false));
104 // Identify private dependencies so the user can see where in the
105 // dependency chain things went bad. Don't list this for the first link
106 // in the chain since direct dependencies are OK, and listing that as
107 // "private" may make people feel like they need to fix it.
108 if (i
== static_cast<int>(chain
.size()) - 1 || chain
[i
- 1].is_public
)
111 ret
.append(" --[private]-->");
119 // Returns true if the two targets have the same label not counting the
121 bool TargetLabelsMatchExceptToolchain(const Target
* a
, const Target
* b
) {
122 return a
->label().dir() == b
->label().dir() &&
123 a
->label().name() == b
->label().name();
128 HeaderChecker::HeaderChecker(const BuildSettings
* build_settings
,
129 const std::vector
<const Target
*>& targets
)
130 : main_loop_(base::MessageLoop::current()),
131 build_settings_(build_settings
) {
132 for (const auto& target
: targets
)
133 AddTargetToFileMap(target
, &file_map_
);
136 HeaderChecker::~HeaderChecker() {
139 bool HeaderChecker::Run(const std::vector
<const Target
*>& to_check
,
141 std::vector
<Err
>* errors
) {
142 FileMap files_to_check
;
143 for (const auto& check
: to_check
)
144 AddTargetToFileMap(check
, &files_to_check
);
145 RunCheckOverFiles(files_to_check
, force_check
);
153 void HeaderChecker::RunCheckOverFiles(const FileMap
& files
, bool force_check
) {
157 scoped_refptr
<base::SequencedWorkerPool
> pool(
158 new base::SequencedWorkerPool(16, "HeaderChecker"));
159 for (const auto& file
: files
) {
160 // Only check C-like source files (RC files also have includes).
161 SourceFileType type
= GetSourceFileType(file
.first
);
162 if (type
!= SOURCE_CPP
&& type
!= SOURCE_H
&& type
!= SOURCE_C
&&
163 type
!= SOURCE_M
&& type
!= SOURCE_MM
&& type
!= SOURCE_RC
)
166 // If any target marks it as generated, don't check it.
167 bool is_generated
= false;
168 for (const auto& vect_i
: file
.second
)
169 is_generated
|= vect_i
.is_generated
;
173 for (const auto& vect_i
: file
.second
) {
174 if (vect_i
.target
->check_includes()) {
175 pool
->PostWorkerTaskWithShutdownBehavior(
177 base::Bind(&HeaderChecker::DoWork
, this, vect_i
.target
, file
.first
),
178 base::SequencedWorkerPool::BLOCK_SHUTDOWN
);
183 // After this call we're single-threaded again.
187 void HeaderChecker::DoWork(const Target
* target
, const SourceFile
& file
) {
189 if (!CheckFile(target
, file
, &err
)) {
190 base::AutoLock
lock(lock_
);
191 errors_
.push_back(err
);
196 void HeaderChecker::AddTargetToFileMap(const Target
* target
, FileMap
* dest
) {
197 // Files in the sources have this public bit by default.
198 bool default_public
= target
->all_headers_public();
200 std::map
<SourceFile
, PublicGeneratedPair
> files_to_public
;
202 // First collect the normal files, they get the default visibility. Always
203 // trim the root gen dir if it exists. This will only exist on outputs of an
204 // action, but those are often then wired into the sources of a compiled
205 // target to actually compile generated code. If you depend on the compiled
206 // target, it should be enough to be able to include the header.
207 for (const auto& source
: target
->sources()) {
208 SourceFile file
= RemoveRootGenDirFromFile(target
, source
);
209 files_to_public
[file
].is_public
= default_public
;
212 // Add in the public files, forcing them to public. This may overwrite some
213 // entries, and it may add new ones.
214 if (default_public
) // List only used when default is not public.
215 DCHECK(target
->public_headers().empty());
216 for (const auto& source
: target
->public_headers()) {
217 SourceFile file
= RemoveRootGenDirFromFile(target
, source
);
218 files_to_public
[file
].is_public
= true;
221 // Add in outputs from actions. These are treated as public (since if other
222 // targets can't use them, then there wouldn't be any point in outputting).
223 std::vector
<SourceFile
> outputs
;
224 target
->action_values().GetOutputsAsSourceFiles(target
, &outputs
);
225 for (const auto& output
: outputs
) {
226 // For generated files in the "gen" directory, add the filename to the
227 // map assuming "gen" is the source root. This means that when files include
228 // the generated header relative to there (the recommended practice), we'll
230 SourceFile output_file
= RemoveRootGenDirFromFile(target
, output
);
231 PublicGeneratedPair
* pair
= &files_to_public
[output_file
];
232 pair
->is_public
= true;
233 pair
->is_generated
= true;
236 // Add the merged list to the master list of all files.
237 for (const auto& cur
: files_to_public
) {
238 (*dest
)[cur
.first
].push_back(TargetInfo(
239 target
, cur
.second
.is_public
, cur
.second
.is_generated
));
243 bool HeaderChecker::IsFileInOuputDir(const SourceFile
& file
) const {
244 const std::string
& build_dir
= build_settings_
->build_dir().value();
245 return file
.value().compare(0, build_dir
.size(), build_dir
) == 0;
248 // This current assumes all include paths are relative to the source root
249 // which is generally the case for Chromium.
251 // A future enhancement would be to search the include path for the target
252 // containing the source file containing this include and find the file to
253 // handle the cases where people do weird things with the paths.
254 SourceFile
HeaderChecker::SourceFileForInclude(
255 const base::StringPiece
& input
) const {
256 std::string
str("//");
257 input
.AppendToString(&str
);
258 return SourceFile(str
);
261 bool HeaderChecker::CheckFile(const Target
* from_target
,
262 const SourceFile
& file
,
264 ScopedTrace
trace(TraceItem::TRACE_CHECK_HEADER
, file
.value());
266 // Sometimes you have generated source files included as sources in another
267 // target. These won't exist at checking time. Since we require all generated
268 // files to be somewhere in the output tree, we can just check the name to
269 // see if they should be skipped.
270 if (IsFileInOuputDir(file
))
273 base::FilePath path
= build_settings_
->GetFullPath(file
);
274 std::string contents
;
275 if (!base::ReadFileToString(path
, &contents
)) {
276 *err
= Err(from_target
->defined_from(), "Source file not found.",
277 "The target:\n " + from_target
->label().GetUserVisibleName(false) +
278 "\nhas a source file:\n " + file
.value() +
279 "\nwhich was not found.");
283 InputFile
input_file(file
);
284 input_file
.SetContents(contents
);
286 CIncludeIterator
iter(&input_file
);
287 base::StringPiece current_include
;
289 while (iter
.GetNextIncludeString(¤t_include
, &range
)) {
290 SourceFile include
= SourceFileForInclude(current_include
);
291 if (!CheckInclude(from_target
, input_file
, include
, range
, err
))
298 // If the file exists:
299 // - It must be in one or more dependencies of the given target.
300 // - Those dependencies must have visibility from the source file.
301 // - The header must be in the public section of those dependeices.
302 // - Those dependencies must either have no direct dependent configs with
303 // flags that affect the compiler, or those direct dependent configs apply
304 // to the "from_target" (it's one "hop" away). This ensures that if the
305 // include file needs needs compiler settings to compile it, that those
306 // settings are applied to the file including it.
307 bool HeaderChecker::CheckInclude(const Target
* from_target
,
308 const InputFile
& source_file
,
309 const SourceFile
& include_file
,
310 const LocationRange
& range
,
312 // Assume if the file isn't declared in our sources that we don't need to
313 // check it. It would be nice if we could give an error if this happens, but
314 // our include finder is too primitive and returns all includes, even if
315 // they're in a #if not executed in the current build. In that case, it's
316 // not unusual for the buildfiles to not specify that header at all.
317 FileMap::const_iterator found
= file_map_
.find(include_file
);
318 if (found
== file_map_
.end())
321 const TargetVector
& targets
= found
->second
;
322 Chain chain
; // Prevent reallocating in the loop.
324 // If the file is unknown in the current toolchain (rather than being private
325 // or in a target not visible to the current target), ignore it. This is a
326 // bit of a hack to account for the fact that the include finder doesn't
327 // understand the preprocessor.
329 // When not cross-compiling, if a platform specific header is conditionally
330 // included in the build, and preprocessor conditions around #includes of
331 // that match the build conditions, everything will be OK because the file
332 // won't be known to GN even though the #include finder identified the file.
334 // Cross-compiling breaks this. When compiling Android on Linux, for example,
335 // we might see both Linux and Android definitions of a target and know
336 // about the union of all headers in the build. Since the #include finder
337 // ignores preprocessor, we will find the Linux headers in the Android
338 // build and note that a dependency from the Android target to the Linux
339 // one is missing (these might even be the same target in different
341 bool present_in_current_toolchain
= false;
342 for (const auto& target
: targets
) {
343 if (from_target
->label().ToolchainsEqual(target
.target
->label())) {
344 present_in_current_toolchain
= true;
348 if (!present_in_current_toolchain
)
351 // For all targets containing this file, we require that at least one be
352 // a direct or public dependency of the current target, and that the header
353 // is public within the target.
355 // If there is more than one target containing this header, we may encounter
356 // some error cases before finding a good one. This error stores the previous
357 // one encountered, which we may or may not throw away.
360 bool found_dependency
= false;
361 for (const auto& target
: targets
) {
362 // We always allow source files in a target to include headers also in that
364 const Target
* to_target
= target
.target
;
365 if (to_target
== from_target
)
368 bool is_permitted_chain
= false;
369 if (IsDependencyOf(to_target
, from_target
, &chain
, &is_permitted_chain
)) {
370 DCHECK(chain
.size() >= 2);
371 DCHECK(chain
[0].target
== to_target
);
372 DCHECK(chain
[chain
.size() - 1].target
== from_target
);
374 found_dependency
= true;
376 if (target
.is_public
&& is_permitted_chain
) {
377 // This one is OK, we're done.
382 // Diagnose the error.
383 if (!target
.is_public
) {
384 // Danger: must call CreatePersistentRange to put in Err.
385 last_error
= Err(CreatePersistentRange(source_file
, range
),
386 "Including a private header.",
387 "This file is private to the target " +
388 target
.target
->label().GetUserVisibleName(false));
389 } else if (!is_permitted_chain
) {
391 CreatePersistentRange(source_file
, range
),
392 "Can't include this header from here.",
393 GetDependencyChainPublicError(chain
));
398 to_target
->allow_circular_includes_from().find(from_target
->label()) !=
399 to_target
->allow_circular_includes_from().end()) {
400 // Not a dependency, but this include is whitelisted from the destination.
401 found_dependency
= true;
407 if (!found_dependency
) {
408 DCHECK(!last_error
.has_error());
409 *err
= MakeUnreachableError(source_file
, range
, from_target
, targets
);
412 if (last_error
.has_error()) {
413 // Found at least one dependency chain above, but it had an error.
418 // One thing we didn't check for is targets that expose their dependents
419 // headers in their own public headers.
421 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
422 // from C should get the configs also or things could be out-of-sync. Above,
423 // we check for A including C's headers directly, but A could also include a
424 // header from B that in turn includes a header from C.
426 // There are two ways to solve this:
427 // - If a public header in B includes C, force B to publicly depend on C.
428 // This is possible to check, but might be super annoying because most
429 // targets (especially large leaf-node targets) don't declare
430 // public/private headers and you'll get lots of false positives.
432 // - Save the includes found in each file and actually compute the graph of
433 // includes to detect when A implicitly includes C's header. This will not
434 // have the annoying false positive problem, but is complex to write.
439 bool HeaderChecker::IsDependencyOf(const Target
* search_for
,
440 const Target
* search_from
,
442 bool* is_permitted
) const {
443 if (search_for
== search_from
) {
444 // A target is always visible from itself.
445 *is_permitted
= true;
449 // Find the shortest public dependency chain.
450 if (IsDependencyOf(search_for
, search_from
, true, chain
)) {
451 *is_permitted
= true;
455 // If not, try to find any dependency chain at all.
456 if (IsDependencyOf(search_for
, search_from
, false, chain
)) {
457 *is_permitted
= false;
461 *is_permitted
= false;
465 bool HeaderChecker::IsDependencyOf(const Target
* search_for
,
466 const Target
* search_from
,
467 bool require_permitted
,
468 Chain
* chain
) const {
469 // This method conducts a breadth-first search through the dependency graph
470 // to find a shortest chain from search_from to search_for.
472 // work_queue maintains a queue of targets which need to be considered as
473 // part of this chain, in the order they were first traversed.
475 // Each time a new transitive dependency of search_from is discovered for
476 // the first time, it is added to work_queue and a "breadcrumb" is added,
477 // indicating which target it was reached from when first discovered.
479 // Once this search finds search_for, the breadcrumbs are used to reconstruct
480 // a shortest dependency chain (in reverse order) from search_from to
483 std::map
<const Target
*, ChainLink
> breadcrumbs
;
484 std::queue
<ChainLink
> work_queue
;
485 work_queue
.push(ChainLink(search_from
, true));
487 bool first_time
= true;
488 while (!work_queue
.empty()) {
489 ChainLink cur_link
= work_queue
.front();
490 const Target
* target
= cur_link
.target
;
493 if (target
== search_for
) {
494 // Found it! Reconstruct the chain.
496 while (target
!= search_from
) {
497 chain
->push_back(cur_link
);
498 cur_link
= breadcrumbs
[target
];
499 target
= cur_link
.target
;
501 chain
->push_back(ChainLink(search_from
, true));
505 // Always consider public dependencies as possibilities.
506 for (const auto& dep
: target
->public_deps()) {
507 if (breadcrumbs
.insert(std::make_pair(dep
.ptr
, cur_link
)).second
)
508 work_queue
.push(ChainLink(dep
.ptr
, true));
511 if (first_time
|| !require_permitted
) {
512 // Consider all dependencies since all target paths are allowed, so add
513 // in private ones. Also do this the first time through the loop, since
514 // a target can include headers from its direct deps regardless of
515 // public/private-ness.
517 for (const auto& dep
: target
->private_deps()) {
518 if (breadcrumbs
.insert(std::make_pair(dep
.ptr
, cur_link
)).second
)
519 work_queue
.push(ChainLink(dep
.ptr
, false));
527 Err
HeaderChecker::MakeUnreachableError(
528 const InputFile
& source_file
,
529 const LocationRange
& range
,
530 const Target
* from_target
,
531 const TargetVector
& targets
) {
532 // Normally the toolchains will all match, but when cross-compiling, we can
533 // get targets with more than one toolchain in the list of possibilities.
534 std::vector
<const Target
*> targets_with_matching_toolchains
;
535 std::vector
<const Target
*> targets_with_other_toolchains
;
536 for (const TargetInfo
& candidate
: targets
) {
537 if (candidate
.target
->toolchain() == from_target
->toolchain())
538 targets_with_matching_toolchains
.push_back(candidate
.target
);
540 targets_with_other_toolchains
.push_back(candidate
.target
);
543 // It's common when cross-compiling to have a target with the same file in
544 // more than one toolchain. We could output all of them, but this is
545 // generally confusing to people (most end-users won't understand toolchains
548 // So delete any candidates in other toolchains that also appear in the same
549 // toolchain as the from_target.
550 for (int other_index
= 0;
551 other_index
< static_cast<int>(targets_with_other_toolchains
.size());
553 for (const Target
* cur_matching
: targets_with_matching_toolchains
) {
554 if (TargetLabelsMatchExceptToolchain(
555 cur_matching
, targets_with_other_toolchains
[other_index
])) {
556 // Found a duplicate, erase it.
557 targets_with_other_toolchains
.erase(
558 targets_with_other_toolchains
.begin() + other_index
);
565 // Only display toolchains on labels if they don't all match.
566 bool include_toolchain
= !targets_with_other_toolchains
.empty();
568 std::string msg
= "It is not in any dependency of\n " +
569 from_target
->label().GetUserVisibleName(include_toolchain
);
570 msg
+= "\nThe include file is in the target(s):\n";
571 for (const auto& target
: targets_with_matching_toolchains
)
572 msg
+= " " + target
->label().GetUserVisibleName(include_toolchain
) + "\n";
573 for (const auto& target
: targets_with_other_toolchains
)
574 msg
+= " " + target
->label().GetUserVisibleName(include_toolchain
) + "\n";
575 if (targets_with_other_toolchains
.size() +
576 targets_with_matching_toolchains
.size() > 1)
577 msg
+= "at least one of ";
578 msg
+= "which should somehow be reachable.";
580 // Danger: must call CreatePersistentRange to put in Err.
581 return Err(CreatePersistentRange(source_file
, range
),
582 "Include not allowed.", msg
);