clang 24.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
20#include "clang/Lex/ModuleMap.h"
24#include "llvm/ADT/StringSet.h"
25#include "llvm/Support/FileSystem.h"
26#include "llvm/Support/Path.h"
27#include "llvm/Support/raw_ostream.h"
28#include <optional>
29
30using namespace clang;
31
32namespace {
33struct DepCollectorPPCallbacks : public PPCallbacks {
34 DependencyCollector &DepCollector;
35 Preprocessor &PP;
36 DepCollectorPPCallbacks(DependencyCollector &L, Preprocessor &PP)
37 : DepCollector(L), PP(PP) {}
38
39 void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
41 SourceLocation Loc) override {
42 if (Reason != PPCallbacks::LexedFileChangeReason::EnterFile)
43 return;
44
45 // Dependency generation really does want to go all the way to the
46 // file entry for a source location to find out what is depended on.
47 // We do not want #line markers to affect dependency generation!
48 if (std::optional<StringRef> Filename =
49 PP.getSourceManager().getNonBuiltinFilenameForID(FID))
50 DepCollector.maybeAddDependency(
51 llvm::sys::path::remove_leading_dotslash(*Filename),
52 /*FromModule*/ false, isSystem(FileType), /*IsModuleFile*/ false,
53 /*IsDirectModuleImport*/ false, /*IsMissing*/ false);
54 }
55
56 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
58 StringRef Filename =
59 llvm::sys::path::remove_leading_dotslash(SkippedFile.getName());
60 DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
61 /*IsSystem=*/isSystem(FileType),
62 /*IsModuleFile=*/false,
63 /*IsDirectModuleImport=*/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 /*IsDirectModuleImport*/ false,
78 /*IsMissing*/ false);
79 }
80
81 bool EmbedFileNotFound(StringRef FileName) override {
82 DepCollector.maybeAddDependency(
83 llvm::sys::path::remove_leading_dotslash(FileName),
84 /*FromModule=*/false,
85 /*IsSystem=*/false,
86 /*IsModuleFile=*/false,
87 /*IsDirectModuleImport=*/false,
88 /*IsMissing=*/true);
89 // Return true to silence the file not found diagnostic.
90 return true;
91 }
92
93 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
94 StringRef FileName, bool IsAngled,
95 CharSourceRange FilenameRange,
96 OptionalFileEntryRef File, StringRef SearchPath,
97 StringRef RelativePath, const Module *SuggestedModule,
98 bool ModuleImported,
100 if (!File)
101 DepCollector.maybeAddDependency(FileName, /*FromModule*/ false,
102 /*IsSystem*/ false,
103 /*IsModuleFile*/ false,
104 /*IsDirectModuleImport*/ false,
105 /*IsMissing*/ true);
106 // Files that actually exist are handled by FileChanged.
107 }
108
109 void HasEmbed(SourceLocation, StringRef, bool,
110 OptionalFileEntryRef File) override {
111 if (!File)
112 return;
113 StringRef Filename =
114 llvm::sys::path::remove_leading_dotslash(File->getName());
115 DepCollector.maybeAddDependency(Filename,
116 /*FromModule=*/false, false,
117 /*IsModuleFile=*/false,
118 /*IsDirectModuleImport=*/false,
119 /*IsMissing=*/false);
120 }
121
122 void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
125 if (!File)
126 return;
127 StringRef Filename =
128 llvm::sys::path::remove_leading_dotslash(File->getName());
129 DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
130 /*IsSystem=*/isSystem(FileType),
131 /*IsModuleFile=*/false,
132 /*IsDirectModuleImport=*/false,
133 /*IsMissing=*/false);
134 }
135
136 void EndOfMainFile() override {
137 DepCollector.finishedMainFile(PP.getDiagnostics());
138 }
139};
140
141struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
142 DependencyCollector &DepCollector;
143 DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
144
145 void moduleMapFileRead(SourceLocation Loc, FileEntryRef Entry,
146 bool IsSystem) override {
147 StringRef Filename = Entry.getName();
148 DepCollector.maybeAddDependency(Filename, /*FromModule*/ false,
149 /*IsSystem*/ IsSystem,
150 /*IsModuleFile*/ false,
151 /*IsDirectModuleImport*/ false,
152 /*IsMissing*/ false);
153 }
154};
155
156struct DepCollectorASTListener : public ASTReaderListener {
157 DependencyCollector &DepCollector;
158 FileManager &FileMgr;
159 DepCollectorASTListener(DependencyCollector &L, FileManager &FileMgr)
160 : DepCollector(L), FileMgr(FileMgr) {}
161 bool needsInputFileVisitation() override { return true; }
162 bool needsSystemInputFileVisitation() override {
163 return DepCollector.needSystemDependencies();
164 }
165 void visitModuleFile(ModuleFileName Filename, serialization::ModuleKind Kind,
166 bool DirectlyImported) override {
167 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true,
168 /*IsSystem*/ false, /*IsModuleFile*/ true,
169 /*IsDirectModuleImport*/ DirectlyImported,
170 /*IsMissing*/ false);
171 }
172 bool visitInputFile(StringRef Filename, bool IsSystem,
173 bool IsOverridden, bool IsExplicitModule) override {
174 if (IsOverridden || IsExplicitModule)
175 return true;
176
177 // Run this through the FileManager in order to respect 'use-external-name'
178 // in case we have a VFS overlay.
179 if (auto FE = FileMgr.getOptionalFileRef(Filename))
180 Filename = FE->getName();
181
182 DepCollector.maybeAddDependency(Filename, /*FromModule*/ true, IsSystem,
183 /*IsModuleFile*/ false,
184 /*IsDirectModuleImport*/ false,
185 /*IsMissing*/ false);
186 return true;
187 }
188};
189} // end anonymous namespace
190
192 bool FromModule, bool IsSystem,
193 bool IsModuleFile,
194 bool IsDirectModuleImport,
195 bool IsMissing) {
196 if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile,
197 IsDirectModuleImport, IsMissing))
198 addDependency(Filename);
199}
200
201bool DependencyCollector::addDependency(StringRef Filename) {
202 StringRef SearchPath;
203#ifdef _WIN32
204 // Make the search insensitive to case and separators.
205 llvm::SmallString<256> TmpPath = Filename;
206 llvm::sys::path::native(TmpPath);
207 std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower);
208 SearchPath = TmpPath.str();
209#else
210 SearchPath = Filename;
211#endif
212
213 if (Seen.insert(SearchPath).second) {
214 Dependencies.push_back(std::string(Filename));
215 return true;
216 }
217 return false;
218}
219
220static bool isSpecialFilename(StringRef Filename) {
221 return Filename == "<built-in>";
222}
223
224bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
225 bool IsSystem, bool IsModuleFile,
226 bool IsDirectModuleImport,
227 bool IsMissing) {
228 return !isSpecialFilename(Filename) &&
229 (needSystemDependencies() || !IsSystem);
230}
231
234 PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(*this, PP));
236 std::make_unique<DepCollectorMMCallbacks>(*this));
237}
239 R.addListener(
240 std::make_unique<DepCollectorASTListener>(*this, R.getFileManager()));
241}
242
244 const DependencyOutputOptions &Opts)
245 : OutputFile(Opts.OutputFile), Targets(Opts.Targets),
246 IncludeSystemHeaders(Opts.IncludeSystemHeaders),
247 PhonyTarget(Opts.UsePhonyTargets),
248 AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
249 IncludeModuleFiles(
250 static_cast<ModuleFileDepsKind>(Opts.IncludeModuleFiles)),
251 OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
252 for (const auto &ExtraDep : Opts.ExtraDeps) {
253 if (addDependency(ExtraDep.first))
254 ++InputFileIndex;
255 }
256}
257
259 // Disable the "file not found" diagnostic if the -MG option was given.
260 if (AddMissingHeaderDeps)
262
264}
265
266bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
267 bool IsSystem, bool IsModuleFile,
268 bool IsDirectModuleImport,
269 bool IsMissing) {
270 if (IsMissing) {
271 // Handle the case of missing file from an inclusion directive.
272 if (AddMissingHeaderDeps)
273 return true;
274 SeenMissingHeader = true;
275 return false;
276 }
277 if (IsModuleFile) {
278 if (IncludeModuleFiles == MFDK_None)
279 return false;
280 if (IncludeModuleFiles == MFDK_Direct && !IsDirectModuleImport)
281 return false;
282 }
283
284 if (isSpecialFilename(Filename))
285 return false;
286
287 if (IncludeSystemHeaders)
288 return true;
289
290 return !IsSystem;
291}
292
296
298 if (SeenMissingHeader) {
299 llvm::sys::fs::remove(OutputFile);
300 return;
301 }
302
303 std::error_code EC;
304 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
305 if (EC) {
306 Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
307 return;
308 }
309
311}
312
314 clang::printMakeDependencyFile(OS, Targets, getDependencies(), OutputFormat,
315 PhonyTarget, InputFileIndex);
316}
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:427
bool addDependency(StringRef Filename)
Return true if the filename was added to the list of dependencies, false otherwise.
virtual void attachToPreprocessor(Preprocessor &PP)
virtual void maybeAddDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsDirectModuleImport, bool IsMissing)
Add a dependency Filename if it has not been seen before and sawDependency() returns true.
ArrayRef< std::string > getDependencies() const
Definition Utils.h:69
virtual void attachToASTReader(ASTReader &R)
virtual bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsDirectModuleImport, bool IsMissing)
Called when a new file is seen.
virtual bool needSystemDependencies()
Return true if system files should be passed to sawDependency().
Definition Utils.h:83
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 IsDirectModuleImport, 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:234
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:434
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.
ModuleKind
Specifies the kind of module that has been loaded.
Definition ModuleFile.h:44
Top level wrappers for InstallAPI frontend operations.
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:196
@ Module
Module linkage, which indicates that the entity can be referred to from other translation units withi...
Definition Linkage.h:54
ModuleFileDepsKind
ModuleFileDepsKind - Whether to include module file dependencies.
@ MFDK_Direct
Include only directly imported module file dependencies.
@ MFDK_None
Do not include module file dependencies.
void printMakeDependencyFile(llvm::raw_ostream &OS, llvm::ArrayRef< std::string > Targets, llvm::ArrayRef< std::string > Files, DependencyOutputFormat Format=DependencyOutputFormat::Make, bool PhonyTargets=false, unsigned InputFileIndex=0)
Write Make-style dependency output to the output stream, in the form: target1 target2 ....
#define false
Definition stdbool.h:26