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 
13 #include "clang/Frontend/Utils.h"
19 #include "clang/Lex/ModuleMap.h"
20 #include "clang/Lex/PPCallbacks.h"
21 #include "clang/Lex/Preprocessor.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 
29 using namespace clang;
30 
31 namespace {
32 struct 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 {
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 
98 struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
99  DependencyCollector &DepCollector;
100  DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
101 
102  void moduleMapFileRead(SourceLocation Loc, FileEntryRef Entry,
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 
112 struct 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 
172 static bool isSpecialFilename(StringRef Filename) {
173  return Filename == "<built-in>";
174 }
175 
176 bool 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),
197  DependencyFilter(Opts.DependencyFilter), Targets(Opts.Targets),
198  IncludeSystemHeaders(Opts.IncludeSystemHeaders),
199  PhonyTarget(Opts.UsePhonyTargets),
200  AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
201  IncludeModuleFiles(Opts.IncludeModuleFiles),
202  OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
203  for (const auto &ExtraDep : Opts.ExtraDeps) {
204  if (addDependency(ExtraDep.first))
205  ++InputFileIndex;
206  }
207 }
208 
210  // Disable the "file not found" diagnostic if the -MG option was given.
211  if (AddMissingHeaderDeps)
213 
215 }
216 
217 bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
218  bool IsSystem, bool IsModuleFile,
219  bool IsMissing) {
220  if (IsMissing) {
221  // Handle the case of missing file from an inclusion directive.
222  if (AddMissingHeaderDeps)
223  return true;
224  SeenMissingHeader = true;
225  return false;
226  }
227  if (IsModuleFile && !IncludeModuleFiles)
228  return false;
229 
231  return false;
232 
233  if (DependencyFilter.size() &&
234  DependencyFilter.compare(0, DependencyFilter.size(), Filename.data(),
235  DependencyFilter.size()) == 0)
236  // Remove dependencies that are prefixed by the Filter string.
237  return false;
238 
239  if (IncludeSystemHeaders)
240  return true;
241 
242  return !IsSystem;
243 }
244 
246  outputDependencyFile(Diags);
247 }
248 
249 /// Print the filename, with escaping or quoting that accommodates the three
250 /// most likely tools that use dependency files: GNU Make, BSD Make, and
251 /// NMake/Jom.
252 ///
253 /// BSD Make is the simplest case: It does no escaping at all. This means
254 /// characters that are normally delimiters, i.e. space and # (the comment
255 /// character) simply aren't supported in filenames.
256 ///
257 /// GNU Make does allow space and # in filenames, but to avoid being treated
258 /// as a delimiter or comment, these must be escaped with a backslash. Because
259 /// backslash is itself the escape character, if a backslash appears in a
260 /// filename, it should be escaped as well. (As a special case, $ is escaped
261 /// as $$, which is the normal Make way to handle the $ character.)
262 /// For compatibility with BSD Make and historical practice, if GNU Make
263 /// un-escapes characters in a filename but doesn't find a match, it will
264 /// retry with the unmodified original string.
265 ///
266 /// GCC tries to accommodate both Make formats by escaping any space or #
267 /// characters in the original filename, but not escaping backslashes. The
268 /// apparent intent is so that filenames with backslashes will be handled
269 /// correctly by BSD Make, and by GNU Make in its fallback mode of using the
270 /// unmodified original string; filenames with # or space characters aren't
271 /// supported by BSD Make at all, but will be handled correctly by GNU Make
272 /// due to the escaping.
273 ///
274 /// A corner case that GCC gets only partly right is when the original filename
275 /// has a backslash immediately followed by space or #. GNU Make would expect
276 /// this backslash to be escaped; however GCC escapes the original backslash
277 /// only when followed by space, not #. It will therefore take a dependency
278 /// from a directive such as
279 /// #include "a\ b\#c.h"
280 /// and emit it as
281 /// a\\\ b\\#c.h
282 /// which GNU Make will interpret as
283 /// a\ b\
284 /// followed by a comment. Failing to find this file, it will fall back to the
285 /// original string, which probably doesn't exist either; in any case it won't
286 /// find
287 /// a\ b\#c.h
288 /// which is the actual filename specified by the include directive.
289 ///
290 /// Clang does what GCC does, rather than what GNU Make expects.
291 ///
292 /// NMake/Jom has a different set of scary characters, but wraps filespecs in
293 /// double-quotes to avoid misinterpreting them; see
294 /// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
295 /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
296 /// for Windows file-naming info.
297 static void PrintFilename(raw_ostream &OS, StringRef Filename,
298  DependencyOutputFormat OutputFormat) {
299  // Convert filename to platform native path
300  llvm::SmallString<256> NativePath;
301  llvm::sys::path::native(Filename.str(), NativePath);
302 
303  if (OutputFormat == DependencyOutputFormat::NMake) {
304  // Add quotes if needed. These are the characters listed as "special" to
305  // NMake, that are legal in a Windows filespec, and that could cause
306  // misinterpretation of the dependency string.
307  if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
308  OS << '\"' << NativePath << '\"';
309  else
310  OS << NativePath;
311  return;
312  }
313  assert(OutputFormat == DependencyOutputFormat::Make);
314  for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
315  if (NativePath[i] == '#') // Handle '#' the broken gcc way.
316  OS << '\\';
317  else if (NativePath[i] == ' ') { // Handle space correctly.
318  OS << '\\';
319  unsigned j = i;
320  while (j > 0 && NativePath[--j] == '\\')
321  OS << '\\';
322  } else if (NativePath[i] == '$') // $ is escaped by $$.
323  OS << '$';
324  OS << NativePath[i];
325  }
326 }
327 
329  if (SeenMissingHeader) {
330  llvm::sys::fs::remove(OutputFile);
331  return;
332  }
333 
334  std::error_code EC;
335  llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
336  if (EC) {
337  Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
338  return;
339  }
340 
342 }
343 
345  // Write out the dependency targets, trying to avoid overly long
346  // lines when possible. We try our best to emit exactly the same
347  // dependency file as GCC>=10, assuming the included files are the
348  // same.
349  const unsigned MaxColumns = 75;
350  unsigned Columns = 0;
351 
352  for (StringRef Target : Targets) {
353  unsigned N = Target.size();
354  if (Columns == 0) {
355  Columns += N;
356  } else if (Columns + N + 2 > MaxColumns) {
357  Columns = N + 2;
358  OS << " \\\n ";
359  } else {
360  Columns += N + 1;
361  OS << ' ';
362  }
363  // Targets already quoted as needed.
364  OS << Target;
365  }
366 
367  OS << ':';
368  Columns += 1;
369 
370  // Now add each dependency in the order it was seen, but avoiding
371  // duplicates.
373  for (StringRef File : Files) {
374  if (File == "<stdin>")
375  continue;
376  // Start a new line if this would exceed the column limit. Make
377  // sure to leave space for a trailing " \" in case we need to
378  // break the line on the next iteration.
379  unsigned N = File.size();
380  if (Columns + (N + 1) + 2 > MaxColumns) {
381  OS << " \\\n ";
382  Columns = 2;
383  }
384  OS << ' ';
385  PrintFilename(OS, File, OutputFormat);
386  Columns += N + 1;
387  }
388  OS << '\n';
389 
390  // Create phony targets if requested.
391  if (PhonyTarget && !Files.empty()) {
392  unsigned Index = 0;
393  for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
394  if (Index++ == InputFileIndex)
395  continue;
396  PrintFilename(OS, *I, OutputFormat);
397  OS << ":\n";
398  }
399  }
400 }
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:2976
llvm::MachO::FileType FileType
Definition: MachO.h:45
llvm::MachO::Target Target
Definition: MachO.h:50
Defines the PPCallbacks interface.
Defines the clang::Preprocessor interface.
SourceLocation Loc
Definition: SemaObjC.cpp:755
Defines the SourceManager interface.
Abstract interface for callback invocations by the ASTReader.
Definition: ASTReader.h:114
Reads an AST files chain containing the contents of a translation unit.
Definition: ASTReader.h:366
FileManager & getFileManager() const
Definition: ASTReader.h:1601
void addListener(std::unique_ptr< ASTReaderListener > L)
Add an AST callback listener.
Definition: ASTReader.h:1694
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)
virtual void finishedMainFile(DiagnosticsEngine &Diags)
Called when the end of the main file is reached.
Definition: Utils.h:79
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:193
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1553
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:240
ModuleMap & getModuleMap()
Retrieve the module map.
Definition: HeaderSearch.h:837
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
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
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:128
SourceManager & getSourceManager() const
HeaderSearch & getHeaderSearchInfo() const
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
DiagnosticsEngine & getDiagnostics() const
void SetSuppressIncludeNotFoundError(bool Suppress)
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
ASTEdit remove(RangeSelector S)
Removes the source selected by S.
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