clang 19.0.0git
DependencyFile.cpp
Go to the documentation of this file.
1//===--- DependencyFile.cpp - Generate dependency file --------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This code generates dependency files.
10//
11//===----------------------------------------------------------------------===//
12
19#include "clang/Lex/ModuleMap.h"
23#include "llvm/ADT/StringSet.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/Path.h"
26#include "llvm/Support/raw_ostream.h"
27#include <optional>
28
29using namespace clang;
30
31namespace {
32struct DepCollectorPPCallbacks : public PPCallbacks {
33 DependencyCollector &DepCollector;
34 Preprocessor &PP;
35 DepCollectorPPCallbacks(DependencyCollector &L, Preprocessor &PP)
36 : DepCollector(L), PP(PP) {}
37
38 void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
40 SourceLocation Loc) override {
41 if (Reason != PPCallbacks::LexedFileChangeReason::EnterFile)
42 return;
43
44 // Dependency generation really does want to go all the way to the
45 // file entry for a source location to find out what is depended on.
46 // We do not want #line markers to affect dependency generation!
47 if (std::optional<StringRef> Filename =
49 DepCollector.maybeAddDependency(
50 llvm::sys::path::remove_leading_dotslash(*Filename),
51 /*FromModule*/ false, isSystem(FileType), /*IsModuleFile*/ false,
52 /*IsMissing*/ false);
53 }
54
55 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
57 StringRef Filename =
58 llvm::sys::path::remove_leading_dotslash(SkippedFile.getName());
59 DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
60 /*IsSystem=*/isSystem(FileType),
61 /*IsModuleFile=*/false,
62 /*IsMissing=*/false);
63 }
64
65 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
66 StringRef FileName, bool IsAngled,
67 CharSourceRange FilenameRange,
68 OptionalFileEntryRef File, StringRef SearchPath,
69 StringRef RelativePath, const Module *SuggestedModule,
70 bool ModuleImported,
72 if (!File)
73 DepCollector.maybeAddDependency(FileName, /*FromModule*/ false,
74 /*IsSystem*/ false,
75 /*IsModuleFile*/ false,
76 /*IsMissing*/ true);
77 // Files that actually exist are handled by FileChanged.
78 }
79
80 void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
83 if (!File)
84 return;
85 StringRef Filename =
86 llvm::sys::path::remove_leading_dotslash(File->getName());
87 DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
88 /*IsSystem=*/isSystem(FileType),
89 /*IsModuleFile=*/false,
90 /*IsMissing=*/false);
91 }
92
93 void EndOfMainFile() override {
94 DepCollector.finishedMainFile(PP.getDiagnostics());
95 }
96};
97
98struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
99 DependencyCollector &DepCollector;
100 DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
101
103 bool IsSystem) override {
104 StringRef Filename = Entry.getName();
105 DepCollector.maybeAddDependency(Filename, /*FromModule*/ false,
106 /*IsSystem*/ IsSystem,
107 /*IsModuleFile*/ false,
108 /*IsMissing*/ false);
109 }
110};
111
112struct DepCollectorASTListener : public ASTReaderListener {
113 DependencyCollector &DepCollector;
114 FileManager &FileMgr;
115 DepCollectorASTListener(DependencyCollector &L, FileManager &FileMgr)
116 : DepCollector(L), FileMgr(FileMgr) {}
117 bool needsInputFileVisitation() override { return true; }
118 bool needsSystemInputFileVisitation() override {
119 return DepCollector.needSystemDependencies();
120 }
121 void visitModuleFile(StringRef Filename,
122 serialization::ModuleKind Kind) override {
123 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true,
124 /*IsSystem*/ false, /*IsModuleFile*/ true,
125 /*IsMissing*/ false);
126 }
127 bool visitInputFile(StringRef Filename, bool IsSystem,
128 bool IsOverridden, bool IsExplicitModule) override {
129 if (IsOverridden || IsExplicitModule)
130 return true;
131
132 // Run this through the FileManager in order to respect 'use-external-name'
133 // in case we have a VFS overlay.
134 if (auto FE = FileMgr.getOptionalFileRef(Filename))
135 Filename = FE->getName();
136
137 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true, IsSystem,
138 /*IsModuleFile*/ false,
139 /*IsMissing*/ false);
140 return true;
141 }
142};
143} // end anonymous namespace
144
146 bool FromModule, bool IsSystem,
147 bool IsModuleFile,
148 bool IsMissing) {
149 if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
151}
152
154 StringRef SearchPath;
155#ifdef _WIN32
156 // Make the search insensitive to case and separators.
158 llvm::sys::path::native(TmpPath);
159 std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower);
160 SearchPath = TmpPath.str();
161#else
162 SearchPath = Filename;
163#endif
164
165 if (Seen.insert(SearchPath).second) {
166 Dependencies.push_back(std::string(Filename));
167 return true;
168 }
169 return false;
170}
171
172static bool isSpecialFilename(StringRef Filename) {
173 return Filename == "<built-in>";
174}
175
176bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
177 bool IsSystem, bool IsModuleFile,
178 bool IsMissing) {
179 return !isSpecialFilename(Filename) &&
180 (needSystemDependencies() || !IsSystem);
181}
182
185 PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(*this, PP));
187 std::make_unique<DepCollectorMMCallbacks>(*this));
188}
190 R.addListener(
191 std::make_unique<DepCollectorASTListener>(*this, R.getFileManager()));
192}
193
195 const DependencyOutputOptions &Opts)
196 : OutputFile(Opts.OutputFile), Targets(Opts.Targets),
197 IncludeSystemHeaders(Opts.IncludeSystemHeaders),
198 PhonyTarget(Opts.UsePhonyTargets),
199 AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
200 IncludeModuleFiles(Opts.IncludeModuleFiles),
201 OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
202 for (const auto &ExtraDep : Opts.ExtraDeps) {
203 if (addDependency(ExtraDep.first))
204 ++InputFileIndex;
205 }
206}
207
209 // Disable the "file not found" diagnostic if the -MG option was given.
210 if (AddMissingHeaderDeps)
212
214}
215
216bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
217 bool IsSystem, bool IsModuleFile,
218 bool IsMissing) {
219 if (IsMissing) {
220 // Handle the case of missing file from an inclusion directive.
221 if (AddMissingHeaderDeps)
222 return true;
223 SeenMissingHeader = true;
224 return false;
225 }
226 if (IsModuleFile && !IncludeModuleFiles)
227 return false;
228
230 return false;
231
232 if (IncludeSystemHeaders)
233 return true;
234
235 return !IsSystem;
236}
237
240}
241
242/// Print the filename, with escaping or quoting that accommodates the three
243/// most likely tools that use dependency files: GNU Make, BSD Make, and
244/// NMake/Jom.
245///
246/// BSD Make is the simplest case: It does no escaping at all. This means
247/// characters that are normally delimiters, i.e. space and # (the comment
248/// character) simply aren't supported in filenames.
249///
250/// GNU Make does allow space and # in filenames, but to avoid being treated
251/// as a delimiter or comment, these must be escaped with a backslash. Because
252/// backslash is itself the escape character, if a backslash appears in a
253/// filename, it should be escaped as well. (As a special case, $ is escaped
254/// as $$, which is the normal Make way to handle the $ character.)
255/// For compatibility with BSD Make and historical practice, if GNU Make
256/// un-escapes characters in a filename but doesn't find a match, it will
257/// retry with the unmodified original string.
258///
259/// GCC tries to accommodate both Make formats by escaping any space or #
260/// characters in the original filename, but not escaping backslashes. The
261/// apparent intent is so that filenames with backslashes will be handled
262/// correctly by BSD Make, and by GNU Make in its fallback mode of using the
263/// unmodified original string; filenames with # or space characters aren't
264/// supported by BSD Make at all, but will be handled correctly by GNU Make
265/// due to the escaping.
266///
267/// A corner case that GCC gets only partly right is when the original filename
268/// has a backslash immediately followed by space or #. GNU Make would expect
269/// this backslash to be escaped; however GCC escapes the original backslash
270/// only when followed by space, not #. It will therefore take a dependency
271/// from a directive such as
272/// #include "a\ b\#c.h"
273/// and emit it as
274/// a\\\ b\\#c.h
275/// which GNU Make will interpret as
276/// a\ b\
277/// followed by a comment. Failing to find this file, it will fall back to the
278/// original string, which probably doesn't exist either; in any case it won't
279/// find
280/// a\ b\#c.h
281/// which is the actual filename specified by the include directive.
282///
283/// Clang does what GCC does, rather than what GNU Make expects.
284///
285/// NMake/Jom has a different set of scary characters, but wraps filespecs in
286/// double-quotes to avoid misinterpreting them; see
287/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
288/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
289/// for Windows file-naming info.
290static void PrintFilename(raw_ostream &OS, StringRef Filename,
291 DependencyOutputFormat OutputFormat) {
292 // Convert filename to platform native path
293 llvm::SmallString<256> NativePath;
294 llvm::sys::path::native(Filename.str(), NativePath);
295
296 if (OutputFormat == DependencyOutputFormat::NMake) {
297 // Add quotes if needed. These are the characters listed as "special" to
298 // NMake, that are legal in a Windows filespec, and that could cause
299 // misinterpretation of the dependency string.
300 if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
301 OS << '\"' << NativePath << '\"';
302 else
303 OS << NativePath;
304 return;
305 }
306 assert(OutputFormat == DependencyOutputFormat::Make);
307 for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
308 if (NativePath[i] == '#') // Handle '#' the broken gcc way.
309 OS << '\\';
310 else if (NativePath[i] == ' ') { // Handle space correctly.
311 OS << '\\';
312 unsigned j = i;
313 while (j > 0 && NativePath[--j] == '\\')
314 OS << '\\';
315 } else if (NativePath[i] == '$') // $ is escaped by $$.
316 OS << '$';
317 OS << NativePath[i];
318 }
319}
320
322 if (SeenMissingHeader) {
323 llvm::sys::fs::remove(OutputFile);
324 return;
325 }
326
327 std::error_code EC;
328 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
329 if (EC) {
330 Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
331 return;
332 }
333
335}
336
338 // Write out the dependency targets, trying to avoid overly long
339 // lines when possible. We try our best to emit exactly the same
340 // dependency file as GCC>=10, assuming the included files are the
341 // same.
342 const unsigned MaxColumns = 75;
343 unsigned Columns = 0;
344
345 for (StringRef Target : Targets) {
346 unsigned N = Target.size();
347 if (Columns == 0) {
348 Columns += N;
349 } else if (Columns + N + 2 > MaxColumns) {
350 Columns = N + 2;
351 OS << " \\\n ";
352 } else {
353 Columns += N + 1;
354 OS << ' ';
355 }
356 // Targets already quoted as needed.
357 OS << Target;
358 }
359
360 OS << ':';
361 Columns += 1;
362
363 // Now add each dependency in the order it was seen, but avoiding
364 // duplicates.
366 for (StringRef File : Files) {
367 if (File == "<stdin>")
368 continue;
369 // Start a new line if this would exceed the column limit. Make
370 // sure to leave space for a trailing " \" in case we need to
371 // break the line on the next iteration.
372 unsigned N = File.size();
373 if (Columns + (N + 1) + 2 > MaxColumns) {
374 OS << " \\\n ";
375 Columns = 2;
376 }
377 OS << ' ';
378 PrintFilename(OS, File, OutputFormat);
379 Columns += N + 1;
380 }
381 OS << '\n';
382
383 // Create phony targets if requested.
384 if (PhonyTarget && !Files.empty()) {
385 unsigned Index = 0;
386 for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
387 if (Index++ == InputFileIndex)
388 continue;
389 PrintFilename(OS, *I, OutputFormat);
390 OS << ":\n";
391 }
392 }
393}
static void PrintFilename(raw_ostream &OS, StringRef Filename, DependencyOutputFormat OutputFormat)
Print the filename, with escaping or quoting that accommodates the three most likely tools that use d...
static bool isSpecialFilename(StringRef Filename)
Defines the clang::FileManager interface and associated types.
StringRef Filename
Definition: Format.cpp:2972
llvm::MachO::FileType FileType
Definition: MachO.h:44
llvm::MachO::Target Target
Definition: MachO.h:47
Defines the PPCallbacks interface.
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
Abstract interface for callback invocations by the ASTReader.
Definition: ASTReader.h:114
virtual bool needsInputFileVisitation()
Returns true if this ASTReaderListener wants to receive the input files of the AST file via visitInpu...
Definition: ASTReader.h:218
virtual bool visitInputFile(StringRef Filename, bool isSystem, bool isOverridden, bool isExplicitModule)
if needsInputFileVisitation returns true, this is called for each non-system input file of the AST Fi...
Definition: ASTReader.h:230
virtual void visitModuleFile(StringRef Filename, serialization::ModuleKind Kind)
This is called for each AST file loaded.
Definition: ASTReader.h:213
virtual bool needsSystemInputFileVisitation()
Returns true if this ASTReaderListener wants to receive the system input files of the AST file via vi...
Definition: ASTReader.h:222
Reads an AST files chain containing the contents of a translation unit.
Definition: ASTReader.h:366
void addListener(std::unique_ptr< ASTReaderListener > L)
Add an AST callback listener.
Definition: ASTReader.h:1685
FileManager & getFileManager() const
Definition: ASTReader.h:1592
Represents a character-granular source range.
An interface for collecting the dependencies of a compilation.
Definition: Utils.h:63
virtual void maybeAddDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing)
Add a dependency Filename if it has not been seen before and sawDependency() returns true.
bool addDependency(StringRef Filename)
Return true if the filename was added to the list of dependencies, false otherwise.
virtual void attachToPreprocessor(Preprocessor &PP)
ArrayRef< std::string > getDependencies() const
Definition: Utils.h:69
virtual void finishedMainFile(DiagnosticsEngine &Diags)
Called when the end of the main file is reached.
Definition: Utils.h:79
virtual void attachToASTReader(ASTReader &R)
virtual bool needSystemDependencies()
Return true if system files should be passed to sawDependency().
Definition: Utils.h:82
virtual bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing)
Called when a new file is seen.
void outputDependencyFile(llvm::raw_ostream &OS)
void attachToPreprocessor(Preprocessor &PP) override
void finishedMainFile(DiagnosticsEngine &Diags) override
Called when the end of the main file is reached.
bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing) final
Called when a new file is seen.
DependencyFileGenerator(const DependencyOutputOptions &Opts)
DependencyOutputOptions - Options for controlling the compiler dependency file generation.
std::vector< std::pair< std::string, ExtraDepKind > > ExtraDeps
A list of extra dependencies (filename and kind) to be used for every target.
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:192
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1547
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
Definition: FileEntry.h:57
StringRef getName() const
The name of this FileEntry.
Definition: FileEntry.h:61
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Implements support for file system lookup, file system caching, and directory search management.
Definition: FileManager.h:53
OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Get a FileEntryRef if it exists, without doing anything on error.
Definition: FileManager.h:234
ModuleMap & getModuleMap()
Retrieve the module map.
Definition: HeaderSearch.h:831
Record the location of an inclusion directive, such as an #include or #import statement.
A mechanism to observe the actions of the module map parser as it reads module map files.
Definition: ModuleMap.h:48
virtual void moduleMapFileRead(SourceLocation FileStart, FileEntryRef File, bool IsSystem)
Called when a module map file has been read.
Definition: ModuleMap.h:60
void addModuleMapCallbacks(std::unique_ptr< ModuleMapCallbacks > Callback)
Add a module map callback.
Definition: ModuleMap.h:424
Describes a module or submodule.
Definition: Module.h:105
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition: PPCallbacks.h:35
virtual void EndOfMainFile()
Callback invoked when the end of the main file is reached.
Definition: PPCallbacks.h:188
virtual void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, SrcMgr::CharacteristicKind FileType)
Callback invoked whenever a source file is skipped as the result of header guard optimization.
Definition: PPCallbacks.h:82
virtual void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled, OptionalFileEntryRef File, SrcMgr::CharacteristicKind FileType)
Hook called when a '__has_include' or '__has_include_next' directive is read.
Definition: PPCallbacks.cpp:17
virtual void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID, SourceLocation Loc)
Callback invoked whenever the Lexer moves to a different file for lexing.
Definition: PPCallbacks.h:70
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:128
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
SourceManager & getSourceManager() const
HeaderSearch & getHeaderSearchInfo() const
void SetSuppressIncludeNotFoundError(bool Suppress)
DiagnosticsEngine & getDiagnostics() const
Encodes a location in the source.
std::optional< StringRef > getNonBuiltinFilenameForID(FileID FID) const
Returns the filename for the provided FileID, unless it's a built-in buffer that's not represented by...
Token - This structure provides full information about a lexed token.
Definition: Token.h:36
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
Definition: SourceManager.h:81
bool isSystem(CharacteristicKind CK)
Determine whether a file / directory characteristic is for system code.
Definition: SourceManager.h:90
ModuleKind
Specifies the kind of module that has been loaded.
Definition: ModuleFile.h:42
The JSON file list parser is used to communicate input to InstallAPI.
DependencyOutputFormat
DependencyOutputFormat - Format for the compiler dependency file.
#define false
Definition: stdbool.h:22