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() && StartsWithASCII(file
.value(), gen
.value(), true))
41 return SourceFile("//" + file
.value().substr(gen
.value().size()));
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());
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 // Given a reverse dependency chain where the target chain[0]'s includes are
71 // being used by chain[end] and not all deps are public, returns the string
72 // describing the error.
73 std::string
GetDependencyChainPublicError(
74 const HeaderChecker::Chain
& chain
) {
75 std::string ret
= "The target:\n " +
76 chain
[chain
.size() - 1].target
->label().GetUserVisibleName(false) +
77 "\nis including a file from the target:\n " +
78 chain
[0].target
->label().GetUserVisibleName(false) +
81 // Invalid chains should always be 0 (no chain) or more than two
82 // (intermediate private dependencies). 1 and 2 are impossible because a
83 // target can always include headers from itself and its direct dependents.
84 DCHECK(chain
.size() != 1 && chain
.size() != 2);
86 ret
+= "There is no dependency chain between these targets.";
88 // Indirect dependency chain, print the chain.
89 ret
+= "\nIt's usually best to depend directly on the destination target.\n"
90 "In some cases, the destination target is considered a subcomponent\n"
91 "of an intermediate target. In this case, the intermediate target\n"
92 "should depend publicly on the destination to forward the ability\n"
93 "to include headers.\n"
95 "Dependency chain (there may also be others):\n";
97 for (int i
= static_cast<int>(chain
.size()) - 1; i
>= 0; i
--) {
98 ret
.append(" " + chain
[i
].target
->label().GetUserVisibleName(false));
100 // Identify private dependencies so the user can see where in the
101 // dependency chain things went bad. Don't list this for the first link
102 // in the chain since direct dependencies are OK, and listing that as
103 // "private" may make people feel like they need to fix it.
104 if (i
== static_cast<int>(chain
.size()) - 1 || chain
[i
- 1].is_public
)
107 ret
.append(" --[private]-->");
117 HeaderChecker::HeaderChecker(const BuildSettings
* build_settings
,
118 const std::vector
<const Target
*>& targets
)
119 : main_loop_(base::MessageLoop::current()),
120 build_settings_(build_settings
) {
121 for (size_t i
= 0; i
< targets
.size(); i
++)
122 AddTargetToFileMap(targets
[i
], &file_map_
);
125 HeaderChecker::~HeaderChecker() {
128 bool HeaderChecker::Run(const std::vector
<const Target
*>& to_check
,
130 std::vector
<Err
>* errors
) {
131 if (to_check
.empty()) {
133 RunCheckOverFiles(file_map_
, force_check
);
135 // Run only over the files in the given targets.
136 FileMap files_to_check
;
137 for (size_t i
= 0; i
< to_check
.size(); i
++)
138 AddTargetToFileMap(to_check
[i
], &files_to_check
);
139 RunCheckOverFiles(files_to_check
, force_check
);
148 void HeaderChecker::RunCheckOverFiles(const FileMap
& files
, bool force_check
) {
152 scoped_refptr
<base::SequencedWorkerPool
> pool(
153 new base::SequencedWorkerPool(16, "HeaderChecker"));
154 for (FileMap::const_iterator file_i
= files
.begin();
155 file_i
!= files
.end(); ++file_i
) {
156 const TargetVector
& vect
= file_i
->second
;
158 // Only check C-like source files (RC files also have includes).
159 SourceFileType type
= GetSourceFileType(file_i
->first
);
160 if (type
!= SOURCE_CC
&& type
!= SOURCE_H
&& type
!= SOURCE_C
&&
161 type
!= SOURCE_M
&& type
!= SOURCE_MM
&& type
!= SOURCE_RC
)
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).
169 bool check_includes
= false;
170 bool is_generated
= false;
171 for (size_t vect_i
= 0; vect_i
< vect
.size(); ++vect_i
) {
172 check_includes
|= vect
[vect_i
].target
->check_includes();
173 is_generated
|= vect
[vect_i
].is_generated
;
175 if (!check_includes
|| is_generated
)
179 for (size_t vect_i
= 0; vect_i
< vect
.size(); ++vect_i
) {
180 pool
->PostWorkerTaskWithShutdownBehavior(
182 base::Bind(&HeaderChecker::DoWork
, this,
183 vect
[vect_i
].target
, file_i
->first
),
184 base::SequencedWorkerPool::BLOCK_SHUTDOWN
);
188 // After this call we're single-threaded again.
192 void HeaderChecker::DoWork(const Target
* target
, const SourceFile
& file
) {
194 if (!CheckFile(target
, file
, &err
)) {
195 base::AutoLock
lock(lock_
);
196 errors_
.push_back(err
);
201 void HeaderChecker::AddTargetToFileMap(const Target
* target
, FileMap
* dest
) {
202 // Files in the sources have this public bit by default.
203 bool default_public
= target
->all_headers_public();
205 std::map
<SourceFile
, PublicGeneratedPair
> files_to_public
;
207 // First collect the normal files, they get the default visibility. Always
208 // trim the root gen dir if it exists. This will only exist on outputs of an
209 // action, but those are often then wired into the sources of a compiled
210 // target to actually compile generated code. If you depend on the compiled
211 // target, it should be enough to be able to include the header.
212 const Target::FileList
& sources
= target
->sources();
213 for (size_t i
= 0; i
< sources
.size(); i
++) {
214 SourceFile file
= RemoveRootGenDirFromFile(target
, sources
[i
]);
215 files_to_public
[file
].is_public
= default_public
;
218 // Add in the public files, forcing them to public. This may overwrite some
219 // entries, and it may add new ones.
220 const Target::FileList
& public_list
= target
->public_headers();
222 DCHECK(public_list
.empty()); // List only used when default is not public.
223 for (size_t i
= 0; i
< public_list
.size(); i
++) {
224 SourceFile file
= RemoveRootGenDirFromFile(target
, public_list
[i
]);
225 files_to_public
[file
].is_public
= true;
228 // Add in outputs from actions. These are treated as public (since if other
229 // targets can't use them, then there wouldn't be any point in outputting).
230 std::vector
<SourceFile
> outputs
;
231 target
->action_values().GetOutputsAsSourceFiles(target
, &outputs
);
232 for (size_t i
= 0; i
< outputs
.size(); i
++) {
233 // For generated files in the "gen" directory, add the filename to the
234 // map assuming "gen" is the source root. This means that when files include
235 // the generated header relative to there (the recommended practice), we'll
237 SourceFile output_file
= RemoveRootGenDirFromFile(target
, outputs
[i
]);
238 PublicGeneratedPair
* pair
= &files_to_public
[output_file
];
239 pair
->is_public
= true;
240 pair
->is_generated
= true;
243 // Add the merged list to the master list of all files.
244 for (std::map
<SourceFile
, PublicGeneratedPair
>::const_iterator i
=
245 files_to_public
.begin();
246 i
!= files_to_public
.end(); ++i
) {
247 (*dest
)[i
->first
].push_back(TargetInfo(
248 target
, i
->second
.is_public
, i
->second
.is_generated
));
252 bool HeaderChecker::IsFileInOuputDir(const SourceFile
& file
) const {
253 const std::string
& build_dir
= build_settings_
->build_dir().value();
254 return file
.value().compare(0, build_dir
.size(), build_dir
) == 0;
257 // This current assumes all include paths are relative to the source root
258 // which is generally the case for Chromium.
260 // A future enhancement would be to search the include path for the target
261 // containing the source file containing this include and find the file to
262 // handle the cases where people do weird things with the paths.
263 SourceFile
HeaderChecker::SourceFileForInclude(
264 const base::StringPiece
& input
) const {
265 std::string
str("//");
266 input
.AppendToString(&str
);
267 return SourceFile(str
);
270 bool HeaderChecker::CheckFile(const Target
* from_target
,
271 const SourceFile
& file
,
273 ScopedTrace
trace(TraceItem::TRACE_CHECK_HEADER
, file
.value());
275 // Sometimes you have generated source files included as sources in another
276 // target. These won't exist at checking time. Since we require all generated
277 // files to be somewhere in the output tree, we can just check the name to
278 // see if they should be skipped.
279 if (IsFileInOuputDir(file
))
282 base::FilePath path
= build_settings_
->GetFullPath(file
);
283 std::string contents
;
284 if (!base::ReadFileToString(path
, &contents
)) {
285 *err
= Err(from_target
->defined_from(), "Source file not found.",
286 "The target:\n " + from_target
->label().GetUserVisibleName(false) +
287 "\nhas a source file:\n " + file
.value() +
288 "\nwhich was not found.");
292 InputFile
input_file(file
);
293 input_file
.SetContents(contents
);
295 CIncludeIterator
iter(&input_file
);
296 base::StringPiece current_include
;
298 while (iter
.GetNextIncludeString(¤t_include
, &range
)) {
299 SourceFile include
= SourceFileForInclude(current_include
);
300 if (!CheckInclude(from_target
, input_file
, include
, range
, err
))
307 // If the file exists:
308 // - It must be in one or more dependencies of the given target.
309 // - Those dependencies must have visibility from the source file.
310 // - The header must be in the public section of those dependeices.
311 // - Those dependencies must either have no direct dependent configs with
312 // flags that affect the compiler, or those direct dependent configs apply
313 // to the "from_target" (it's one "hop" away). This ensures that if the
314 // include file needs needs compiler settings to compile it, that those
315 // settings are applied to the file including it.
316 bool HeaderChecker::CheckInclude(const Target
* from_target
,
317 const InputFile
& source_file
,
318 const SourceFile
& include_file
,
319 const LocationRange
& range
,
321 // Assume if the file isn't declared in our sources that we don't need to
322 // check it. It would be nice if we could give an error if this happens, but
323 // our include finder is too primitive and returns all includes, even if
324 // they're in a #if not executed in the current build. In that case, it's
325 // not unusual for the buildfiles to not specify that header at all.
326 FileMap::const_iterator found
= file_map_
.find(include_file
);
327 if (found
== file_map_
.end())
330 const TargetVector
& targets
= found
->second
;
331 Chain chain
; // Prevent reallocating in the loop.
333 // For all targets containing this file, we require that at least one be
334 // a direct or public dependency of the current target, and that the header
335 // is public within the target.
337 // If there is more than one target containing this header, we may encounter
338 // some error cases before finding a good one. This error stores the previous
339 // one encountered, which we may or may not throw away.
342 bool found_dependency
= false;
343 for (size_t i
= 0; i
< targets
.size(); i
++) {
344 // We always allow source files in a target to include headers also in that
346 const Target
* to_target
= targets
[i
].target
;
347 if (to_target
== from_target
)
350 bool is_permitted_chain
= false;
351 if (IsDependencyOf(to_target
, from_target
, &chain
, &is_permitted_chain
)) {
352 DCHECK(chain
.size() >= 2);
353 DCHECK(chain
[0].target
== to_target
);
354 DCHECK(chain
[chain
.size() - 1].target
== from_target
);
356 found_dependency
= true;
358 if (targets
[i
].is_public
&& is_permitted_chain
) {
359 // This one is OK, we're done.
364 // Diagnose the error.
365 if (!targets
[i
].is_public
) {
366 // Danger: must call CreatePersistentRange to put in Err.
368 CreatePersistentRange(source_file
, range
),
369 "Including a private header.",
370 "This file is private to the target " +
371 targets
[i
].target
->label().GetUserVisibleName(false));
372 } else if (!is_permitted_chain
) {
374 CreatePersistentRange(source_file
, range
),
375 "Can't include this header from here.",
376 GetDependencyChainPublicError(chain
));
381 to_target
->allow_circular_includes_from().find(from_target
->label()) !=
382 to_target
->allow_circular_includes_from().end()) {
383 // Not a dependency, but this include is whitelisted from the destination.
384 found_dependency
= true;
390 if (!found_dependency
) {
391 DCHECK(!last_error
.has_error());
393 std::string msg
= "It is not in any dependency of " +
394 from_target
->label().GetUserVisibleName(false);
395 msg
+= "\nThe include file is in the target(s):\n";
396 for (size_t i
= 0; i
< targets
.size(); i
++)
397 msg
+= " " + targets
[i
].target
->label().GetUserVisibleName(false) + "\n";
398 if (targets
.size() > 1)
399 msg
+= "at least one of ";
400 msg
+= "which should somehow be reachable from " +
401 from_target
->label().GetUserVisibleName(false);
403 // Danger: must call CreatePersistentRange to put in Err.
404 *err
= Err(CreatePersistentRange(source_file
, range
),
405 "Include not allowed.", msg
);
408 if (last_error
.has_error()) {
409 // Found at least one dependency chain above, but it had an error.
414 // One thing we didn't check for is targets that expose their dependents
415 // headers in their own public headers.
417 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
418 // from C should get the configs also or things could be out-of-sync. Above,
419 // we check for A including C's headers directly, but A could also include a
420 // header from B that in turn includes a header from C.
422 // There are two ways to solve this:
423 // - If a public header in B includes C, force B to publicly depend on C.
424 // This is possible to check, but might be super annoying because most
425 // targets (especially large leaf-node targets) don't declare
426 // public/private headers and you'll get lots of false positives.
428 // - Save the includes found in each file and actually compute the graph of
429 // includes to detect when A implicitly includes C's header. This will not
430 // have the annoying false positive problem, but is complex to write.
435 bool HeaderChecker::IsDependencyOf(const Target
* search_for
,
436 const Target
* search_from
,
438 bool* is_permitted
) const {
439 if (search_for
== search_from
) {
440 // A target is always visible from itself.
441 *is_permitted
= true;
445 // Find the shortest public dependency chain.
446 if (IsDependencyOf(search_for
, search_from
, true, chain
)) {
447 *is_permitted
= true;
451 // If not, try to find any dependency chain at all.
452 if (IsDependencyOf(search_for
, search_from
, false, chain
)) {
453 *is_permitted
= false;
457 *is_permitted
= false;
461 bool HeaderChecker::IsDependencyOf(const Target
* search_for
,
462 const Target
* search_from
,
463 bool require_permitted
,
464 Chain
* chain
) const {
465 // This method conducts a breadth-first search through the dependency graph
466 // to find a shortest chain from search_from to search_for.
468 // work_queue maintains a queue of targets which need to be considered as
469 // part of this chain, in the order they were first traversed.
471 // Each time a new transitive dependency of search_from is discovered for
472 // the first time, it is added to work_queue and a "breadcrumb" is added,
473 // indicating which target it was reached from when first discovered.
475 // Once this search finds search_for, the breadcrumbs are used to reconstruct
476 // a shortest dependency chain (in reverse order) from search_from to
479 std::map
<const Target
*, ChainLink
> breadcrumbs
;
480 std::queue
<ChainLink
> work_queue
;
481 work_queue
.push(ChainLink(search_from
, true));
483 bool first_time
= true;
484 while (!work_queue
.empty()) {
485 ChainLink cur_link
= work_queue
.front();
486 const Target
* target
= cur_link
.target
;
489 if (target
== search_for
) {
490 // Found it! Reconstruct the chain.
492 while (target
!= search_from
) {
493 chain
->push_back(cur_link
);
494 cur_link
= breadcrumbs
[target
];
495 target
= cur_link
.target
;
497 chain
->push_back(ChainLink(search_from
, true));
501 // Always consider public dependencies as possibilities.
502 const LabelTargetVector
& public_deps
= target
->public_deps();
503 for (size_t i
= 0; i
< public_deps
.size(); i
++) {
504 if (breadcrumbs
.insert(
505 std::make_pair(public_deps
[i
].ptr
, cur_link
)).second
)
506 work_queue
.push(ChainLink(public_deps
[i
].ptr
, true));
509 if (first_time
|| !require_permitted
) {
510 // Consider all dependencies since all target paths are allowed, so add
511 // in private ones. Also do this the first time through the loop, since
512 // a target can include headers from its direct deps regardless of
513 // public/private-ness.
515 const LabelTargetVector
& private_deps
= target
->private_deps();
516 for (size_t i
= 0; i
< private_deps
.size(); i
++) {
517 if (breadcrumbs
.insert(
518 std::make_pair(private_deps
[i
].ptr
, cur_link
)).second
)
519 work_queue
.push(ChainLink(private_deps
[i
].ptr
, false));