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