clang 22.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 =
48 PP.getSourceManager().getNonBuiltinFilenameForID(FID))
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 EmbedDirective(SourceLocation, StringRef, bool,
67 const LexEmbedParametersResult &) override {
68 assert(File && "expected to only be called when the file is found");
69 StringRef FileName =
70 llvm::sys::path::remove_leading_dotslash(File->getName());
71 DepCollector.maybeAddDependency(FileName,
72 /*FromModule*/ false,
73 /*IsSystem*/ false,
74 /*IsModuleFile*/ false,
75 /*IsMissing*/ false);
76 }
77
78 bool EmbedFileNotFound(StringRef FileName) override {
79 DepCollector.maybeAddDependency(
80 llvm::sys::path::remove_leading_dotslash(FileName),
81 /*FromModule=*/false,
82 /*IsSystem=*/false,
83 /*IsModuleFile=*/false,
84 /*IsMissing=*/true);
85 // Return true to silence the file not found diagnostic.
86 return true;
87 }
88
89 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
90 StringRef FileName, bool IsAngled,
91 CharSourceRange FilenameRange,
92 OptionalFileEntryRef File, StringRef SearchPath,
93 StringRef RelativePath, const Module *SuggestedModule,
94 bool ModuleImported,
96 if (!File)
97 DepCollector.maybeAddDependency(FileName, /*FromModule*/ false,
98 /*IsSystem*/ false,
99 /*IsModuleFile*/ false,
100 /*IsMissing*/ true);
101 // Files that actually exist are handled by FileChanged.
102 }
103
104 void HasEmbed(SourceLocation, StringRef, bool,
105 OptionalFileEntryRef File) override {
106 if (!File)
107 return;
108 StringRef Filename =
109 llvm::sys::path::remove_leading_dotslash(File->getName());
110 DepCollector.maybeAddDependency(Filename,
111 /*FromModule=*/false, false,
112 /*IsModuleFile=*/false,
113 /*IsMissing=*/false);
114 }
115
116 void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
119 if (!File)
120 return;
121 StringRef Filename =
122 llvm::sys::path::remove_leading_dotslash(File->getName());
123 DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
124 /*IsSystem=*/isSystem(FileType),
125 /*IsModuleFile=*/false,
126 /*IsMissing=*/false);
127 }
128
129 void EndOfMainFile() override {
130 DepCollector.finishedMainFile(PP.getDiagnostics());
131 }
132};
133
134struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
135 DependencyCollector &DepCollector;
136 DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
137
138 void moduleMapFileRead(SourceLocation Loc, FileEntryRef Entry,
139 bool IsSystem) override {
140 StringRef Filename = Entry.getName();
141 DepCollector.maybeAddDependency(Filename, /*FromModule*/ false,
142 /*IsSystem*/ IsSystem,
143 /*IsModuleFile*/ false,
144 /*IsMissing*/ false);
145 }
146};
147
148struct DepCollectorASTListener : public ASTReaderListener {
149 DependencyCollector &DepCollector;
150 FileManager &FileMgr;
151 DepCollectorASTListener(DependencyCollector &L, FileManager &FileMgr)
152 : DepCollector(L), FileMgr(FileMgr) {}
153 bool needsInputFileVisitation() override { return true; }
154 bool needsSystemInputFileVisitation() override {
155 return DepCollector.needSystemDependencies();
156 }
157 void visitModuleFile(StringRef Filename,
158 serialization::ModuleKind Kind) override {
159 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true,
160 /*IsSystem*/ false, /*IsModuleFile*/ true,
161 /*IsMissing*/ false);
162 }
163 bool visitInputFile(StringRef Filename, bool IsSystem,
164 bool IsOverridden, bool IsExplicitModule) override {
165 if (IsOverridden || IsExplicitModule)
166 return true;
167
168 // Run this through the FileManager in order to respect 'use-external-name'
169 // in case we have a VFS overlay.
170 if (auto FE = FileMgr.getOptionalFileRef(Filename))
171 Filename = FE->getName();
172
173 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true, IsSystem,
174 /*IsModuleFile*/ false,
175 /*IsMissing*/ false);
176 return true;
177 }
178};
179} // end anonymous namespace
180
182 bool FromModule, bool IsSystem,
183 bool IsModuleFile,
184 bool IsMissing) {
185 if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
186 addDependency(Filename);
187}
188
189bool DependencyCollector::addDependency(StringRef Filename) {
190 StringRef SearchPath;
191#ifdef _WIN32
192 // Make the search insensitive to case and separators.
193 llvm::SmallString<256> TmpPath = Filename;
194 llvm::sys::path::native(TmpPath);
195 std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower);
196 SearchPath = TmpPath.str();
197#else
198 SearchPath = Filename;
199#endif
200
201 if (Seen.insert(SearchPath).second) {
202 Dependencies.push_back(std::string(Filename));
203 return true;
204 }
205 return false;
206}
207
208static bool isSpecialFilename(StringRef Filename) {
209 return Filename == "<built-in>";
210}
211
212bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
213 bool IsSystem, bool IsModuleFile,
214 bool IsMissing) {
215 return !isSpecialFilename(Filename) &&
216 (needSystemDependencies() || !IsSystem);
217}
218
221 PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(*this, PP));
223 std::make_unique<DepCollectorMMCallbacks>(*this));
224}
226 R.addListener(
227 std::make_unique<DepCollectorASTListener>(*this, R.getFileManager()));
228}
229
231 const DependencyOutputOptions &Opts)
232 : OutputFile(Opts.OutputFile), Targets(Opts.Targets),
233 IncludeSystemHeaders(Opts.IncludeSystemHeaders),
234 PhonyTarget(Opts.UsePhonyTargets),
235 AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
236 IncludeModuleFiles(Opts.IncludeModuleFiles),
237 OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
238 for (const auto &ExtraDep : Opts.ExtraDeps) {
239 if (addDependency(ExtraDep.first))
240 ++InputFileIndex;
241 }
242}
243
245 // Disable the "file not found" diagnostic if the -MG option was given.
246 if (AddMissingHeaderDeps)
248
250}
251
252bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
253 bool IsSystem, bool IsModuleFile,
254 bool IsMissing) {
255 if (IsMissing) {
256 // Handle the case of missing file from an inclusion directive.
257 if (AddMissingHeaderDeps)
258 return true;
259 SeenMissingHeader = true;
260 return false;
261 }
262 if (IsModuleFile && !IncludeModuleFiles)
263 return false;
264
265 if (isSpecialFilename(Filename))
266 return false;
267
268 if (IncludeSystemHeaders)
269 return true;
270
271 return !IsSystem;
272}
273
277
278/// Print the filename, with escaping or quoting that accommodates the three
279/// most likely tools that use dependency files: GNU Make, BSD Make, and
280/// NMake/Jom.
281///
282/// BSD Make is the simplest case: It does no escaping at all. This means
283/// characters that are normally delimiters, i.e. space and # (the comment
284/// character) simply aren't supported in filenames.
285///
286/// GNU Make does allow space and # in filenames, but to avoid being treated
287/// as a delimiter or comment, these must be escaped with a backslash. Because
288/// backslash is itself the escape character, if a backslash appears in a
289/// filename, it should be escaped as well. (As a special case, $ is escaped
290/// as $$, which is the normal Make way to handle the $ character.)
291/// For compatibility with BSD Make and historical practice, if GNU Make
292/// un-escapes characters in a filename but doesn't find a match, it will
293/// retry with the unmodified original string.
294///
295/// GCC tries to accommodate both Make formats by escaping any space or #
296/// characters in the original filename, but not escaping backslashes. The
297/// apparent intent is so that filenames with backslashes will be handled
298/// correctly by BSD Make, and by GNU Make in its fallback mode of using the
299/// unmodified original string; filenames with # or space characters aren't
300/// supported by BSD Make at all, but will be handled correctly by GNU Make
301/// due to the escaping.
302///
303/// A corner case that GCC gets only partly right is when the original filename
304/// has a backslash immediately followed by space or #. GNU Make would expect
305/// this backslash to be escaped; however GCC escapes the original backslash
306/// only when followed by space, not #. It will therefore take a dependency
307/// from a directive such as
308/// #include "a\ b\#c.h"
309/// and emit it as
310/// a\\\ b\\#c.h
311/// which GNU Make will interpret as
312/// a\ b\
313/// followed by a comment. Failing to find this file, it will fall back to the
314/// original string, which probably doesn't exist either; in any case it won't
315/// find
316/// a\ b\#c.h
317/// which is the actual filename specified by the include directive.
318///
319/// Clang does what GCC does, rather than what GNU Make expects.
320///
321/// NMake/Jom has a different set of scary characters, but wraps filespecs in
322/// double-quotes to avoid misinterpreting them; see
323/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
324/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
325/// for Windows file-naming info.
326static void PrintFilename(raw_ostream &OS, StringRef Filename,
327 DependencyOutputFormat OutputFormat) {
328 // Convert filename to platform native path
329 llvm::SmallString<256> NativePath;
330 llvm::sys::path::native(Filename.str(), NativePath);
331
332 if (OutputFormat == DependencyOutputFormat::NMake) {
333 // Add quotes if needed. These are the characters listed as "special" to
334 // NMake, that are legal in a Windows filespec, and that could cause
335 // misinterpretation of the dependency string.
336 if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
337 OS << '\"' << NativePath << '\"';
338 else
339 OS << NativePath;
340 return;
341 }
342 assert(OutputFormat == DependencyOutputFormat::Make);
343 for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
344 if (NativePath[i] == '#') // Handle '#' the broken gcc way.
345 OS << '\\';
346 else if (NativePath[i] == ' ') { // Handle space correctly.
347 OS << '\\';
348 unsigned j = i;
349 while (j > 0 && NativePath[--j] == '\\')
350 OS << '\\';
351 } else if (NativePath[i] == '$') // $ is escaped by $$.
352 OS << '$';
353 OS << NativePath[i];
354 }
355}
356
358 if (SeenMissingHeader) {
359 llvm::sys::fs::remove(OutputFile);
360 return;
361 }
362
363 std::error_code EC;
364 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
365 if (EC) {
366 Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
367 return;
368 }
369
371}
372
374 // Write out the dependency targets, trying to avoid overly long
375 // lines when possible. We try our best to emit exactly the same
376 // dependency file as GCC>=10, assuming the included files are the
377 // same.
378 const unsigned MaxColumns = 75;
379 unsigned Columns = 0;
380
381 for (StringRef Target : Targets) {
382 unsigned N = Target.size();
383 if (Columns == 0) {
384 Columns += N;
385 } else if (Columns + N + 2 > MaxColumns) {
386 Columns = N + 2;
387 OS << " \\\n ";
388 } else {
389 Columns += N + 1;
390 OS << ' ';
391 }
392 // Targets already quoted as needed.
393 OS << Target;
394 }
395
396 OS << ':';
397 Columns += 1;
398
399 // Now add each dependency in the order it was seen, but avoiding
400 // duplicates.
402 for (StringRef File : Files) {
403 if (File == "<stdin>")
404 continue;
405 // Start a new line if this would exceed the column limit. Make
406 // sure to leave space for a trailing " \" in case we need to
407 // break the line on the next iteration.
408 unsigned N = File.size();
409 if (Columns + (N + 1) + 2 > MaxColumns) {
410 OS << " \\\n ";
411 Columns = 2;
412 }
413 OS << ' ';
414 PrintFilename(OS, File, OutputFormat);
415 Columns += N + 1;
416 }
417 OS << '\n';
418
419 // Create phony targets if requested.
420 if (PhonyTarget && !Files.empty()) {
421 unsigned Index = 0;
422 for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
423 if (Index++ == InputFileIndex)
424 continue;
425 PrintFilename(OS, *I, OutputFormat);
426 OS << ":\n";
427 }
428 }
429}
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.
llvm::MachO::FileType FileType
Definition MachO.h:46
Defines the PPCallbacks interface.
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
Abstract interface for callback invocations by the ASTReader.
Definition ASTReader.h:117
Reads an AST files chain containing the contents of a translation unit.
Definition ASTReader.h:430
void addListener(std::unique_ptr< ASTReaderListener > L)
Add an AST callback listener.
Definition ASTReader.h:1910
FileManager & getFileManager() const
Definition ASTReader.h:1816
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 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:232
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
StringRef getName() const
The name of this FileEntry.
Definition FileEntry.h:61
ModuleMap & getModuleMap()
Retrieve the module map.
A mechanism to observe the actions of the module map loader as it reads module map files.
Definition ModuleMap.h:49
void addModuleMapCallbacks(std::unique_ptr< ModuleMapCallbacks > Callback)
Add a module map callback.
Definition ModuleMap.h:413
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition PPCallbacks.h:37
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
HeaderSearch & getHeaderSearchInfo() const
void SetSuppressIncludeNotFoundError(bool Suppress)
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
bool isSystem(CharacteristicKind CK)
Determine whether a file / directory characteristic is for system code.
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
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.
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:208
@ Module
Module linkage, which indicates that the entity can be referred to from other translation units withi...
Definition Linkage.h:54
DependencyOutputFormat
DependencyOutputFormat - Format for the compiler dependency file.
#define false
Definition stdbool.h:26