clang 20.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/VirtualFileSystem.h"
27#include "llvm/Support/raw_ostream.h"
28#include <optional>
29#include <system_error>
30
31using namespace clang;
32
33namespace {
34struct DepCollectorPPCallbacks : public PPCallbacks {
35 DependencyCollector &DepCollector;
36 Preprocessor &PP;
37 DepCollectorPPCallbacks(DependencyCollector &L, Preprocessor &PP)
38 : DepCollector(L), PP(PP) {}
39
40 void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
42 SourceLocation Loc) override {
43 if (Reason != PPCallbacks::LexedFileChangeReason::EnterFile)
44 return;
45
46 // Dependency generation really does want to go all the way to the
47 // file entry for a source location to find out what is depended on.
48 // We do not want #line markers to affect dependency generation!
49 if (std::optional<StringRef> Filename =
51 DepCollector.maybeAddDependency(
52 llvm::sys::path::remove_leading_dotslash(*Filename),
53 /*FromModule*/ false, isSystem(FileType), /*IsModuleFile*/ false,
54 /*IsMissing*/ false);
55 }
56
57 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
59 StringRef Filename =
60 llvm::sys::path::remove_leading_dotslash(SkippedFile.getName());
61 DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
62 /*IsSystem=*/isSystem(FileType),
63 /*IsModuleFile=*/false,
64 /*IsMissing=*/false);
65 }
66
67 void EmbedDirective(SourceLocation, StringRef, bool,
69 const LexEmbedParametersResult &) override {
70 assert(File && "expected to only be called when the file is found");
71 StringRef FileName =
72 llvm::sys::path::remove_leading_dotslash(File->getName());
73 DepCollector.maybeAddDependency(FileName,
74 /*FromModule*/ false,
75 /*IsSystem*/ false,
76 /*IsModuleFile*/ false,
77 /*IsMissing*/ false);
78 }
79
80 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
81 StringRef FileName, bool IsAngled,
82 CharSourceRange FilenameRange,
83 OptionalFileEntryRef File, StringRef SearchPath,
84 StringRef RelativePath, const Module *SuggestedModule,
85 bool ModuleImported,
87 if (!File)
88 DepCollector.maybeAddDependency(FileName, /*FromModule*/ false,
89 /*IsSystem*/ false,
90 /*IsModuleFile*/ false,
91 /*IsMissing*/ true);
92 // Files that actually exist are handled by FileChanged.
93 }
94
95 void HasEmbed(SourceLocation, StringRef, bool,
96 OptionalFileEntryRef File) override {
97 if (!File)
98 return;
99 StringRef Filename =
100 llvm::sys::path::remove_leading_dotslash(File->getName());
101 DepCollector.maybeAddDependency(Filename,
102 /*FromModule=*/false, false,
103 /*IsModuleFile=*/false,
104 /*IsMissing=*/false);
105 }
106
107 void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
110 if (!File)
111 return;
112 StringRef Filename =
113 llvm::sys::path::remove_leading_dotslash(File->getName());
114 DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
115 /*IsSystem=*/isSystem(FileType),
116 /*IsModuleFile=*/false,
117 /*IsMissing=*/false);
118 }
119
120 void EndOfMainFile() override {
121 DepCollector.finishedMainFile(PP.getDiagnostics());
122 }
123};
124
125struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
126 DependencyCollector &DepCollector;
127 DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
128
130 bool IsSystem) override {
131 StringRef Filename = Entry.getName();
132 DepCollector.maybeAddDependency(Filename, /*FromModule*/ false,
133 /*IsSystem*/ IsSystem,
134 /*IsModuleFile*/ false,
135 /*IsMissing*/ false);
136 }
137};
138
139struct DepCollectorASTListener : public ASTReaderListener {
140 DependencyCollector &DepCollector;
141 FileManager &FileMgr;
142 DepCollectorASTListener(DependencyCollector &L, FileManager &FileMgr)
143 : DepCollector(L), FileMgr(FileMgr) {}
144 bool needsInputFileVisitation() override { return true; }
145 bool needsSystemInputFileVisitation() override {
146 return DepCollector.needSystemDependencies();
147 }
148 void visitModuleFile(StringRef Filename,
149 serialization::ModuleKind Kind) override {
150 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true,
151 /*IsSystem*/ false, /*IsModuleFile*/ true,
152 /*IsMissing*/ false);
153 }
154 bool visitInputFile(StringRef Filename, bool IsSystem,
155 bool IsOverridden, bool IsExplicitModule) override {
156 if (IsOverridden || IsExplicitModule)
157 return true;
158
159 // Run this through the FileManager in order to respect 'use-external-name'
160 // in case we have a VFS overlay.
161 if (auto FE = FileMgr.getOptionalFileRef(Filename))
162 Filename = FE->getName();
163
164 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true, IsSystem,
165 /*IsModuleFile*/ false,
166 /*IsMissing*/ false);
167 return true;
168 }
169};
170} // end anonymous namespace
171
173 bool FromModule, bool IsSystem,
174 bool IsModuleFile,
175 bool IsMissing) {
176 if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
178}
179
181 StringRef SearchPath;
182#ifdef _WIN32
183 // Make the search insensitive to case and separators.
185 llvm::sys::path::native(TmpPath);
186 std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower);
187 SearchPath = TmpPath.str();
188#else
189 SearchPath = Filename;
190#endif
191
192 if (Seen.insert(SearchPath).second) {
193 Dependencies.push_back(std::string(Filename));
194 return true;
195 }
196 return false;
197}
198
199static bool isSpecialFilename(StringRef Filename) {
200 return Filename == "<built-in>";
201}
202
203bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
204 bool IsSystem, bool IsModuleFile,
205 bool IsMissing) {
206 return !isSpecialFilename(Filename) &&
207 (needSystemDependencies() || !IsSystem);
208}
209
212 PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(*this, PP));
214 std::make_unique<DepCollectorMMCallbacks>(*this));
215}
217 R.addListener(
218 std::make_unique<DepCollectorASTListener>(*this, R.getFileManager()));
219}
220
222 const DependencyOutputOptions &Opts)
223 : OutputFile(Opts.OutputFile), Targets(Opts.Targets),
224 IncludeSystemHeaders(Opts.IncludeSystemHeaders),
225 PhonyTarget(Opts.UsePhonyTargets),
226 AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
227 IncludeModuleFiles(Opts.IncludeModuleFiles),
228 OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
229 for (const auto &ExtraDep : Opts.ExtraDeps) {
230 if (addDependency(ExtraDep.first))
231 ++InputFileIndex;
232 }
233}
234
236 // Disable the "file not found" diagnostic if the -MG option was given.
237 if (AddMissingHeaderDeps)
239
242}
243
244bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
245 bool IsSystem, bool IsModuleFile,
246 bool IsMissing) {
247 if (IsMissing) {
248 // Handle the case of missing file from an inclusion directive.
249 if (AddMissingHeaderDeps)
250 return true;
251 SeenMissingHeader = true;
252 return false;
253 }
254 if (IsModuleFile && !IncludeModuleFiles)
255 return false;
256
258 return false;
259
260 if (IncludeSystemHeaders)
261 return true;
262
263 return !IsSystem;
264}
265
268}
269
270/// Print the filename, with escaping or quoting that accommodates the three
271/// most likely tools that use dependency files: GNU Make, BSD Make, and
272/// NMake/Jom.
273///
274/// BSD Make is the simplest case: It does no escaping at all. This means
275/// characters that are normally delimiters, i.e. space and # (the comment
276/// character) simply aren't supported in filenames.
277///
278/// GNU Make does allow space and # in filenames, but to avoid being treated
279/// as a delimiter or comment, these must be escaped with a backslash. Because
280/// backslash is itself the escape character, if a backslash appears in a
281/// filename, it should be escaped as well. (As a special case, $ is escaped
282/// as $$, which is the normal Make way to handle the $ character.)
283/// For compatibility with BSD Make and historical practice, if GNU Make
284/// un-escapes characters in a filename but doesn't find a match, it will
285/// retry with the unmodified original string.
286///
287/// GCC tries to accommodate both Make formats by escaping any space or #
288/// characters in the original filename, but not escaping backslashes. The
289/// apparent intent is so that filenames with backslashes will be handled
290/// correctly by BSD Make, and by GNU Make in its fallback mode of using the
291/// unmodified original string; filenames with # or space characters aren't
292/// supported by BSD Make at all, but will be handled correctly by GNU Make
293/// due to the escaping.
294///
295/// A corner case that GCC gets only partly right is when the original filename
296/// has a backslash immediately followed by space or #. GNU Make would expect
297/// this backslash to be escaped; however GCC escapes the original backslash
298/// only when followed by space, not #. It will therefore take a dependency
299/// from a directive such as
300/// #include "a\ b\#c.h"
301/// and emit it as
302/// a\\\ b\\#c.h
303/// which GNU Make will interpret as
304/// a\ b\
305/// followed by a comment. Failing to find this file, it will fall back to the
306/// original string, which probably doesn't exist either; in any case it won't
307/// find
308/// a\ b\#c.h
309/// which is the actual filename specified by the include directive.
310///
311/// Clang does what GCC does, rather than what GNU Make expects.
312///
313/// NMake/Jom has a different set of scary characters, but wraps filespecs in
314/// double-quotes to avoid misinterpreting them; see
315/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
316/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
317/// for Windows file-naming info.
318static void printFilename(raw_ostream &OS, llvm::vfs::FileSystem *FS,
319 StringRef Filename,
320 DependencyOutputFormat OutputFormat) {
321 // Convert filename to platform native path
322 llvm::SmallString<256> NativePath;
323 llvm::sys::path::native(Filename.str(), NativePath);
324 // Resolve absolute path. Make and Ninja canonicalize paths
325 // without checking for symbolic links in the path, for performance concerns.
326 // If there is something like `/bin/../lib64` -> `/usr/lib64`
327 // (where `/bin` links to `/usr/bin`), Make will see them as `/lib64`.
328 if (FS != nullptr && llvm::sys::path::is_absolute(NativePath)) {
329 llvm::SmallString<256> NativePathTmp = NativePath;
330 std::error_code EC = FS->getRealPath(NativePathTmp, NativePath);
331 if (EC)
332 NativePath = NativePathTmp;
333 }
334
335 if (OutputFormat == DependencyOutputFormat::NMake) {
336 // Add quotes if needed. These are the characters listed as "special" to
337 // NMake, that are legal in a Windows filespec, and that could cause
338 // misinterpretation of the dependency string.
339 if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
340 OS << '\"' << NativePath << '\"';
341 else
342 OS << NativePath;
343 return;
344 }
345 assert(OutputFormat == DependencyOutputFormat::Make);
346 for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
347 if (NativePath[i] == '#') // Handle '#' the broken gcc way.
348 OS << '\\';
349 else if (NativePath[i] == ' ') { // Handle space correctly.
350 OS << '\\';
351 unsigned j = i;
352 while (j > 0 && NativePath[--j] == '\\')
353 OS << '\\';
354 } else if (NativePath[i] == '$') // $ is escaped by $$.
355 OS << '$';
356 OS << NativePath[i];
357 }
358}
359
361 if (SeenMissingHeader) {
362 llvm::sys::fs::remove(OutputFile);
363 return;
364 }
365
366 std::error_code EC;
367 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
368 if (EC) {
369 Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
370 return;
371 }
372
374}
375
377 // Write out the dependency targets, trying to avoid overly long
378 // lines when possible. We try our best to emit exactly the same
379 // dependency file as GCC>=10, assuming the included files are the
380 // same.
381 const unsigned MaxColumns = 75;
382 unsigned Columns = 0;
383
384 for (StringRef Target : Targets) {
385 unsigned N = Target.size();
386 if (Columns == 0) {
387 Columns += N;
388 } else if (Columns + N + 2 > MaxColumns) {
389 Columns = N + 2;
390 OS << " \\\n ";
391 } else {
392 Columns += N + 1;
393 OS << ' ';
394 }
395 // Targets already quoted as needed.
396 OS << Target;
397 }
398
399 OS << ':';
400 Columns += 1;
401
402 // Now add each dependency in the order it was seen, but avoiding
403 // duplicates.
405 for (StringRef File : Files) {
406 if (File == "<stdin>")
407 continue;
408 // Start a new line if this would exceed the column limit. Make
409 // sure to leave space for a trailing " \" in case we need to
410 // break the line on the next iteration.
411 unsigned N = File.size();
412 if (Columns + (N + 1) + 2 > MaxColumns) {
413 OS << " \\\n ";
414 Columns = 2;
415 }
416 OS << ' ';
417 printFilename(OS, FS.get(), File, OutputFormat);
418 Columns += N + 1;
419 }
420 OS << '\n';
421
422 // Create phony targets if requested.
423 if (PhonyTarget && !Files.empty()) {
424 unsigned Index = 0;
425 for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
426 if (Index++ == InputFileIndex)
427 continue;
428 printFilename(OS, FS.get(), *I, OutputFormat);
429 OS << ":\n";
430 }
431 }
432}
Expr * E
static void printFilename(raw_ostream &OS, llvm::vfs::FileSystem *FS, 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:3051
llvm::MachO::FileType FileType
Definition: MachO.h:46
llvm::MachO::Target Target
Definition: MachO.h:51
Defines the PPCallbacks interface.
Defines the clang::Preprocessor interface.
SourceLocation Loc
Definition: SemaObjC.cpp:759
Defines the SourceManager interface.
Abstract interface for callback invocations by the ASTReader.
Definition: ASTReader.h:116
virtual bool needsInputFileVisitation()
Returns true if this ASTReaderListener wants to receive the input files of the AST file via visitInpu...
Definition: ASTReader.h:223
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:235
virtual void visitModuleFile(StringRef Filename, serialization::ModuleKind Kind)
This is called for each AST file loaded.
Definition: ASTReader.h:218
virtual bool needsSystemInputFileVisitation()
Returns true if this ASTReaderListener wants to receive the system input files of the AST file via vi...
Definition: ASTReader.h:227
Reads an AST files chain containing the contents of a translation unit.
Definition: ASTReader.h:383
void addListener(std::unique_ptr< ASTReaderListener > L)
Add an AST callback listener.
Definition: ASTReader.h:1811
FileManager & getFileManager() const
Definition: ASTReader.h:1718
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:231
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1493
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:245
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
Definition: FileManager.h:258
ModuleMap & getModuleMap()
Retrieve the module map.
Definition: HeaderSearch.h:821
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:427
Describes a module or submodule.
Definition: Module.h:115
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition: PPCallbacks.h:36
virtual void HasEmbed(SourceLocation Loc, StringRef FileName, bool IsAngled, OptionalFileEntryRef File)
Hook called when a '__has_embed' directive is read.
Definition: PPCallbacks.h:366
virtual void EndOfMainFile()
Callback invoked when the end of the main file is reached.
Definition: PPCallbacks.h:217
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:83
virtual void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled, OptionalFileEntryRef File, const LexEmbedParametersResult &Params)
Callback invoked whenever an embed directive has been processed, regardless of whether the embed will...
Definition: PPCallbacks.h:111
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:16
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:71
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:138
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
SourceManager & getSourceManager() const
FileManager & getFileManager() 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:43
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:26