1 //===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/Frontend/DependencyOutputOptions.h"
10 #include "clang/Frontend/Utils.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/Frontend/FrontendDiagnostic.h"
13 #include "clang/Lex/Preprocessor.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/Support/JSON.h"
16 #include "llvm/Support/raw_ostream.h"
17 using namespace clang
;
20 class HeaderIncludesCallback
: public PPCallbacks
{
22 raw_ostream
*OutputFile
;
23 const DependencyOutputOptions
&DepOpts
;
24 unsigned CurrentIncludeDepth
;
25 bool HasProcessedPredefines
;
32 HeaderIncludesCallback(const Preprocessor
*PP
, bool ShowAllHeaders_
,
33 raw_ostream
*OutputFile_
,
34 const DependencyOutputOptions
&DepOpts
,
35 bool OwnsOutputFile_
, bool ShowDepth_
, bool MSStyle_
)
36 : SM(PP
->getSourceManager()), OutputFile(OutputFile_
), DepOpts(DepOpts
),
37 CurrentIncludeDepth(0), HasProcessedPredefines(false),
38 OwnsOutputFile(OwnsOutputFile_
), ShowAllHeaders(ShowAllHeaders_
),
39 ShowDepth(ShowDepth_
), MSStyle(MSStyle_
) {}
41 ~HeaderIncludesCallback() override
{
46 HeaderIncludesCallback(const HeaderIncludesCallback
&) = delete;
47 HeaderIncludesCallback
&operator=(const HeaderIncludesCallback
&) = delete;
49 void FileChanged(SourceLocation Loc
, FileChangeReason Reason
,
50 SrcMgr::CharacteristicKind FileType
,
51 FileID PrevFID
) override
;
53 void FileSkipped(const FileEntryRef
&SkippedFile
, const Token
&FilenameTok
,
54 SrcMgr::CharacteristicKind FileType
) override
;
57 bool ShouldShowHeader(SrcMgr::CharacteristicKind HeaderType
) {
58 if (!DepOpts
.IncludeSystemHeaders
&& isSystem(HeaderType
))
61 // Show the current header if we are (a) past the predefines, or (b) showing
62 // all headers and in the predefines at a depth past the initial file and
63 // command line buffers.
64 return (HasProcessedPredefines
||
65 (ShowAllHeaders
&& CurrentIncludeDepth
> 2));
69 /// A callback for emitting header usage information to a file in JSON. Each
70 /// line in the file is a JSON object that includes the source file name and
71 /// the list of headers directly or indirectly included from it. For example:
73 /// {"source":"/tmp/foo.c",
74 /// "includes":["/usr/include/stdio.h", "/usr/include/stdlib.h"]}
76 /// To reduce the amount of data written to the file, we only record system
77 /// headers that are directly included from a file that isn't in the system
79 class HeaderIncludesJSONCallback
: public PPCallbacks
{
81 raw_ostream
*OutputFile
;
83 SmallVector
<std::string
, 16> IncludedHeaders
;
86 HeaderIncludesJSONCallback(const Preprocessor
*PP
, raw_ostream
*OutputFile_
,
88 : SM(PP
->getSourceManager()), OutputFile(OutputFile_
),
89 OwnsOutputFile(OwnsOutputFile_
) {}
91 ~HeaderIncludesJSONCallback() override
{
96 HeaderIncludesJSONCallback(const HeaderIncludesJSONCallback
&) = delete;
97 HeaderIncludesJSONCallback
&
98 operator=(const HeaderIncludesJSONCallback
&) = delete;
100 void EndOfMainFile() override
;
102 void FileChanged(SourceLocation Loc
, FileChangeReason Reason
,
103 SrcMgr::CharacteristicKind FileType
,
104 FileID PrevFID
) override
;
106 void FileSkipped(const FileEntryRef
&SkippedFile
, const Token
&FilenameTok
,
107 SrcMgr::CharacteristicKind FileType
) override
;
111 static void PrintHeaderInfo(raw_ostream
*OutputFile
, StringRef Filename
,
112 bool ShowDepth
, unsigned CurrentIncludeDepth
,
114 // Write to a temporary string to avoid unnecessary flushing on errs().
115 SmallString
<512> Pathname(Filename
);
117 Lexer::Stringify(Pathname
);
119 SmallString
<256> Msg
;
121 Msg
+= "Note: including file:";
124 // The main source file is at depth 1, so skip one dot.
125 for (unsigned i
= 1; i
!= CurrentIncludeDepth
; ++i
)
126 Msg
+= MSStyle
? ' ' : '.';
138 void clang::AttachHeaderIncludeGen(Preprocessor
&PP
,
139 const DependencyOutputOptions
&DepOpts
,
140 bool ShowAllHeaders
, StringRef OutputPath
,
141 bool ShowDepth
, bool MSStyle
) {
142 raw_ostream
*OutputFile
= &llvm::errs();
143 bool OwnsOutputFile
= false;
145 // Choose output stream, when printing in cl.exe /showIncludes style.
147 switch (DepOpts
.ShowIncludesDest
) {
149 llvm_unreachable("Invalid destination for /showIncludes output!");
150 case ShowIncludesDestination::Stderr
:
151 OutputFile
= &llvm::errs();
153 case ShowIncludesDestination::Stdout
:
154 OutputFile
= &llvm::outs();
159 // Open the output file, if used.
160 if (!OutputPath
.empty()) {
162 llvm::raw_fd_ostream
*OS
= new llvm::raw_fd_ostream(
163 OutputPath
.str(), EC
,
164 llvm::sys::fs::OF_Append
| llvm::sys::fs::OF_TextWithCRLF
);
166 PP
.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure
)
172 OwnsOutputFile
= true;
176 switch (DepOpts
.HeaderIncludeFormat
) {
178 llvm_unreachable("unexpected header format kind");
179 case HIFMT_Textual
: {
180 assert(DepOpts
.HeaderIncludeFiltering
== HIFIL_None
&&
181 "header filtering is currently always disabled when output format is"
183 // Print header info for extra headers, pretending they were discovered by
184 // the regular preprocessor. The primary use case is to support proper
185 // generation of Make / Ninja file dependencies for implicit includes, such
186 // as sanitizer ignorelists. It's only important for cl.exe compatibility,
187 // the GNU way to generate rules is -M / -MM / -MD / -MMD.
188 for (const auto &Header
: DepOpts
.ExtraDeps
)
189 PrintHeaderInfo(OutputFile
, Header
.first
, ShowDepth
, 2, MSStyle
);
190 PP
.addPPCallbacks(std::make_unique
<HeaderIncludesCallback
>(
191 &PP
, ShowAllHeaders
, OutputFile
, DepOpts
, OwnsOutputFile
, ShowDepth
,
196 assert(DepOpts
.HeaderIncludeFiltering
== HIFIL_Only_Direct_System
&&
197 "only-direct-system is the only option for filtering");
198 PP
.addPPCallbacks(std::make_unique
<HeaderIncludesJSONCallback
>(
199 &PP
, OutputFile
, OwnsOutputFile
));
205 void HeaderIncludesCallback::FileChanged(SourceLocation Loc
,
206 FileChangeReason Reason
,
207 SrcMgr::CharacteristicKind NewFileType
,
209 // Unless we are exiting a #include, make sure to skip ahead to the line the
210 // #include directive was at.
211 PresumedLoc UserLoc
= SM
.getPresumedLoc(Loc
);
212 if (UserLoc
.isInvalid())
215 // Adjust the current include depth.
216 if (Reason
== PPCallbacks::EnterFile
) {
217 ++CurrentIncludeDepth
;
218 } else if (Reason
== PPCallbacks::ExitFile
) {
219 if (CurrentIncludeDepth
)
220 --CurrentIncludeDepth
;
222 // We track when we are done with the predefines by watching for the first
223 // place where we drop back to a nesting depth of 1.
224 if (CurrentIncludeDepth
== 1 && !HasProcessedPredefines
)
225 HasProcessedPredefines
= true;
232 if (!ShouldShowHeader(NewFileType
))
235 unsigned IncludeDepth
= CurrentIncludeDepth
;
236 if (!HasProcessedPredefines
)
237 --IncludeDepth
; // Ignore indent from <built-in>.
239 // FIXME: Identify headers in a more robust way than comparing their name to
240 // "<command line>" and "<built-in>" in a bunch of places.
241 if (Reason
== PPCallbacks::EnterFile
&&
242 UserLoc
.getFilename() != StringRef("<command line>")) {
243 PrintHeaderInfo(OutputFile
, UserLoc
.getFilename(), ShowDepth
, IncludeDepth
,
248 void HeaderIncludesCallback::FileSkipped(const FileEntryRef
&SkippedFile
, const
250 SrcMgr::CharacteristicKind FileType
) {
251 if (!DepOpts
.ShowSkippedHeaderIncludes
)
254 if (!ShouldShowHeader(FileType
))
257 PrintHeaderInfo(OutputFile
, SkippedFile
.getName(), ShowDepth
,
258 CurrentIncludeDepth
+ 1, MSStyle
);
261 void HeaderIncludesJSONCallback::EndOfMainFile() {
262 OptionalFileEntryRef FE
= SM
.getFileEntryRefForID(SM
.getMainFileID());
263 SmallString
<256> MainFile(FE
->getName());
264 SM
.getFileManager().makeAbsolutePath(MainFile
);
267 llvm::raw_string_ostream
OS(Str
);
268 llvm::json::OStream
JOS(OS
);
270 JOS
.attribute("source", MainFile
.c_str());
271 JOS
.attributeArray("includes", [&] {
272 llvm::StringSet
<> SeenHeaders
;
273 for (const std::string
&H
: IncludedHeaders
)
274 if (SeenHeaders
.insert(H
).second
)
280 if (OutputFile
->get_kind() == raw_ostream::OStreamKind::OK_FDStream
) {
281 llvm::raw_fd_ostream
*FDS
= static_cast<llvm::raw_fd_ostream
*>(OutputFile
);
282 if (auto L
= FDS
->lock())
288 /// Determine whether the header file should be recorded. The header file should
289 /// be recorded only if the header file is a system header and the current file
290 /// isn't a system header.
291 static bool shouldRecordNewFile(SrcMgr::CharacteristicKind NewFileType
,
292 SourceLocation PrevLoc
, SourceManager
&SM
) {
293 return SrcMgr::isSystem(NewFileType
) && !SM
.isInSystemHeader(PrevLoc
);
296 void HeaderIncludesJSONCallback::FileChanged(
297 SourceLocation Loc
, FileChangeReason Reason
,
298 SrcMgr::CharacteristicKind NewFileType
, FileID PrevFID
) {
299 if (PrevFID
.isInvalid() ||
300 !shouldRecordNewFile(NewFileType
, SM
.getLocForStartOfFile(PrevFID
), SM
))
303 // Unless we are exiting a #include, make sure to skip ahead to the line the
304 // #include directive was at.
305 PresumedLoc UserLoc
= SM
.getPresumedLoc(Loc
);
306 if (UserLoc
.isInvalid())
309 if (Reason
== PPCallbacks::EnterFile
&&
310 UserLoc
.getFilename() != StringRef("<command line>"))
311 IncludedHeaders
.push_back(UserLoc
.getFilename());
314 void HeaderIncludesJSONCallback::FileSkipped(
315 const FileEntryRef
&SkippedFile
, const Token
&FilenameTok
,
316 SrcMgr::CharacteristicKind FileType
) {
317 if (!shouldRecordNewFile(FileType
, FilenameTok
.getLocation(), SM
))
320 IncludedHeaders
.push_back(SkippedFile
.getName().str());