clang  9.0.0
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/ADT/StringSwitch.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 using namespace clang;
30 
31 namespace {
32 struct DepCollectorPPCallbacks : public PPCallbacks {
33  DependencyCollector &DepCollector;
35  DiagnosticsEngine &Diags;
36  DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM,
37  DiagnosticsEngine &Diags)
38  : DepCollector(L), SM(SM), Diags(Diags) {}
39 
40  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
42  FileID PrevFID) override {
43  if (Reason != PPCallbacks::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  const FileEntry *FE =
51  if (!FE)
52  return;
53 
54  StringRef Filename =
55  llvm::sys::path::remove_leading_dotslash(FE->getName());
56 
57  DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
58  isSystem(FileType),
59  /*IsModuleFile*/false, /*IsMissing*/false);
60  }
61 
62  void FileSkipped(const FileEntry &SkippedFile, const Token &FilenameTok,
63  SrcMgr::CharacteristicKind FileType) override {
64  StringRef Filename =
65  llvm::sys::path::remove_leading_dotslash(SkippedFile.getName());
66  DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
67  /*IsSystem=*/isSystem(FileType),
68  /*IsModuleFile=*/false,
69  /*IsMissing=*/false);
70  }
71 
72  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
73  StringRef FileName, bool IsAngled,
74  CharSourceRange FilenameRange, const FileEntry *File,
75  StringRef SearchPath, StringRef RelativePath,
76  const Module *Imported,
77  SrcMgr::CharacteristicKind FileType) override {
78  if (!File)
79  DepCollector.maybeAddDependency(FileName, /*FromModule*/false,
80  /*IsSystem*/false, /*IsModuleFile*/false,
81  /*IsMissing*/true);
82  // Files that actually exist are handled by FileChanged.
83  }
84 
85  void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
86  const FileEntry *File,
87  SrcMgr::CharacteristicKind FileType) override {
88  if (!File)
89  return;
90  StringRef Filename =
91  llvm::sys::path::remove_leading_dotslash(File->getName());
92  DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
93  /*IsSystem=*/isSystem(FileType),
94  /*IsModuleFile=*/false,
95  /*IsMissing=*/false);
96  }
97 
98  void EndOfMainFile() override { DepCollector.finishedMainFile(Diags); }
99 };
100 
101 struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
102  DependencyCollector &DepCollector;
103  DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
104 
105  void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry,
106  bool IsSystem) override {
107  StringRef Filename = Entry.getName();
108  DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
109  /*IsSystem*/IsSystem,
110  /*IsModuleFile*/false,
111  /*IsMissing*/false);
112  }
113 };
114 
115 struct DepCollectorASTListener : public ASTReaderListener {
116  DependencyCollector &DepCollector;
117  DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { }
118  bool needsInputFileVisitation() override { return true; }
119  bool needsSystemInputFileVisitation() override {
120  return DepCollector.needSystemDependencies();
121  }
122  void visitModuleFile(StringRef Filename,
123  serialization::ModuleKind Kind) override {
124  DepCollector.maybeAddDependency(Filename, /*FromModule*/true,
125  /*IsSystem*/false, /*IsModuleFile*/true,
126  /*IsMissing*/false);
127  }
128  bool visitInputFile(StringRef Filename, bool IsSystem,
129  bool IsOverridden, bool IsExplicitModule) override {
130  if (IsOverridden || IsExplicitModule)
131  return true;
132 
133  DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem,
134  /*IsModuleFile*/false, /*IsMissing*/false);
135  return true;
136  }
137 };
138 } // end anonymous namespace
139 
140 void DependencyCollector::maybeAddDependency(StringRef Filename, bool FromModule,
141  bool IsSystem, bool IsModuleFile,
142  bool IsMissing) {
143  if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
144  addDependency(Filename);
145 }
146 
147 bool DependencyCollector::addDependency(StringRef Filename) {
148  if (Seen.insert(Filename).second) {
149  Dependencies.push_back(Filename);
150  return true;
151  }
152  return false;
153 }
154 
155 static bool isSpecialFilename(StringRef Filename) {
156  return llvm::StringSwitch<bool>(Filename)
157  .Case("<built-in>", true)
158  .Case("<stdin>", true)
159  .Default(false);
160 }
161 
162 bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
163  bool IsSystem, bool IsModuleFile,
164  bool IsMissing) {
165  return !isSpecialFilename(Filename) &&
166  (needSystemDependencies() || !IsSystem);
167 }
168 
171  PP.addPPCallbacks(llvm::make_unique<DepCollectorPPCallbacks>(
172  *this, PP.getSourceManager(), PP.getDiagnostics()));
174  llvm::make_unique<DepCollectorMMCallbacks>(*this));
175 }
177  R.addListener(llvm::make_unique<DepCollectorASTListener>(*this));
178 }
179 
181  const DependencyOutputOptions &Opts)
182  : OutputFile(Opts.OutputFile), Targets(Opts.Targets),
183  IncludeSystemHeaders(Opts.IncludeSystemHeaders),
184  PhonyTarget(Opts.UsePhonyTargets),
185  AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
186  IncludeModuleFiles(Opts.IncludeModuleFiles),
187  OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
188  for (const auto &ExtraDep : Opts.ExtraDeps) {
189  if (addDependency(ExtraDep))
190  ++InputFileIndex;
191  }
192 }
193 
195  if (Targets.empty()) {
196  PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT);
197  return;
198  }
199 
200  // Disable the "file not found" diagnostic if the -MG option was given.
201  if (AddMissingHeaderDeps)
203 
205 }
206 
207 bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
208  bool IsSystem, bool IsModuleFile,
209  bool IsMissing) {
210  if (IsMissing) {
211  // Handle the case of missing file from an inclusion directive.
212  if (AddMissingHeaderDeps)
213  return true;
214  SeenMissingHeader = true;
215  return false;
216  }
217  if (IsModuleFile && !IncludeModuleFiles)
218  return false;
219 
220  if (isSpecialFilename(Filename))
221  return false;
222 
223  if (IncludeSystemHeaders)
224  return true;
225 
226  return !IsSystem;
227 }
228 
230  outputDependencyFile(Diags);
231 }
232 
233 /// Print the filename, with escaping or quoting that accommodates the three
234 /// most likely tools that use dependency files: GNU Make, BSD Make, and
235 /// NMake/Jom.
236 ///
237 /// BSD Make is the simplest case: It does no escaping at all. This means
238 /// characters that are normally delimiters, i.e. space and # (the comment
239 /// character) simply aren't supported in filenames.
240 ///
241 /// GNU Make does allow space and # in filenames, but to avoid being treated
242 /// as a delimiter or comment, these must be escaped with a backslash. Because
243 /// backslash is itself the escape character, if a backslash appears in a
244 /// filename, it should be escaped as well. (As a special case, $ is escaped
245 /// as $$, which is the normal Make way to handle the $ character.)
246 /// For compatibility with BSD Make and historical practice, if GNU Make
247 /// un-escapes characters in a filename but doesn't find a match, it will
248 /// retry with the unmodified original string.
249 ///
250 /// GCC tries to accommodate both Make formats by escaping any space or #
251 /// characters in the original filename, but not escaping backslashes. The
252 /// apparent intent is so that filenames with backslashes will be handled
253 /// correctly by BSD Make, and by GNU Make in its fallback mode of using the
254 /// unmodified original string; filenames with # or space characters aren't
255 /// supported by BSD Make at all, but will be handled correctly by GNU Make
256 /// due to the escaping.
257 ///
258 /// A corner case that GCC gets only partly right is when the original filename
259 /// has a backslash immediately followed by space or #. GNU Make would expect
260 /// this backslash to be escaped; however GCC escapes the original backslash
261 /// only when followed by space, not #. It will therefore take a dependency
262 /// from a directive such as
263 /// #include "a\ b\#c.h"
264 /// and emit it as
265 /// a\\\ b\\#c.h
266 /// which GNU Make will interpret as
267 /// a\ b\
268 /// followed by a comment. Failing to find this file, it will fall back to the
269 /// original string, which probably doesn't exist either; in any case it won't
270 /// find
271 /// a\ b\#c.h
272 /// which is the actual filename specified by the include directive.
273 ///
274 /// Clang does what GCC does, rather than what GNU Make expects.
275 ///
276 /// NMake/Jom has a different set of scary characters, but wraps filespecs in
277 /// double-quotes to avoid misinterpreting them; see
278 /// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
279 /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
280 /// for Windows file-naming info.
281 static void PrintFilename(raw_ostream &OS, StringRef Filename,
282  DependencyOutputFormat OutputFormat) {
283  // Convert filename to platform native path
284  llvm::SmallString<256> NativePath;
285  llvm::sys::path::native(Filename.str(), NativePath);
286 
287  if (OutputFormat == DependencyOutputFormat::NMake) {
288  // Add quotes if needed. These are the characters listed as "special" to
289  // NMake, that are legal in a Windows filespec, and that could cause
290  // misinterpretation of the dependency string.
291  if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
292  OS << '\"' << NativePath << '\"';
293  else
294  OS << NativePath;
295  return;
296  }
297  assert(OutputFormat == DependencyOutputFormat::Make);
298  for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
299  if (NativePath[i] == '#') // Handle '#' the broken gcc way.
300  OS << '\\';
301  else if (NativePath[i] == ' ') { // Handle space correctly.
302  OS << '\\';
303  unsigned j = i;
304  while (j > 0 && NativePath[--j] == '\\')
305  OS << '\\';
306  } else if (NativePath[i] == '$') // $ is escaped by $$.
307  OS << '$';
308  OS << NativePath[i];
309  }
310 }
311 
313  if (SeenMissingHeader) {
314  llvm::sys::fs::remove(OutputFile);
315  return;
316  }
317 
318  std::error_code EC;
319  llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text);
320  if (EC) {
321  Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
322  return;
323  }
324 
326 }
327 
329  // Write out the dependency targets, trying to avoid overly long
330  // lines when possible. We try our best to emit exactly the same
331  // dependency file as GCC (4.2), assuming the included files are the
332  // same.
333  const unsigned MaxColumns = 75;
334  unsigned Columns = 0;
335 
336  for (StringRef Target : Targets) {
337  unsigned N = Target.size();
338  if (Columns == 0) {
339  Columns += N;
340  } else if (Columns + N + 2 > MaxColumns) {
341  Columns = N + 2;
342  OS << " \\\n ";
343  } else {
344  Columns += N + 1;
345  OS << ' ';
346  }
347  // Targets already quoted as needed.
348  OS << Target;
349  }
350 
351  OS << ':';
352  Columns += 1;
353 
354  // Now add each dependency in the order it was seen, but avoiding
355  // duplicates.
357  for (StringRef File : Files) {
358  // Start a new line if this would exceed the column limit. Make
359  // sure to leave space for a trailing " \" in case we need to
360  // break the line on the next iteration.
361  unsigned N = File.size();
362  if (Columns + (N + 1) + 2 > MaxColumns) {
363  OS << " \\\n ";
364  Columns = 2;
365  }
366  OS << ' ';
367  PrintFilename(OS, File, OutputFormat);
368  Columns += N + 1;
369  }
370  OS << '\n';
371 
372  // Create phony targets if requested.
373  if (PhonyTarget && !Files.empty()) {
374  unsigned Index = 0;
375  for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
376  if (Index++ == InputFileIndex)
377  continue;
378  OS << '\n';
379  PrintFilename(OS, *I, OutputFormat);
380  OS << ":\n";
381  }
382  }
383 }
DependencyFileGenerator(const DependencyOutputOptions &Opts)
Defines the clang::FileManager interface and associated types.
An interface for collecting the dependencies of a compilation.
Definition: Utils.h:81
Defines the SourceManager interface.
void finishedMainFile(DiagnosticsEngine &Diags) override
Called when the end of the main file is reached.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1297
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
Definition: SourceManager.h:77
ModuleMap & getModuleMap()
Retrieve the module map.
Definition: HeaderSearch.h:660
This interface provides a way to observe the actions of the preprocessor as it does its thing...
Definition: PPCallbacks.h:35
long i
Definition: xmmintrin.h:1456
bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing) final override
Called when a new file is seen.
virtual bool needSystemDependencies()
Return true if system files should be passed to sawDependency().
Definition: Utils.h:100
Token - This structure provides full information about a lexed token.
Definition: Token.h:34
void outputDependencyFile(llvm::raw_ostream &OS)
Describes a module or submodule.
Definition: Module.h:64
void SetSuppressIncludeNotFoundError(bool Suppress)
Definition: Preprocessor.h:947
virtual void attachToASTReader(ASTReader &R)
HeaderSearch & getHeaderSearchInfo() const
Definition: Preprocessor.h:908
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID...
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:149
void addModuleMapCallbacks(std::unique_ptr< ModuleMapCallbacks > Callback)
Add a module map callback.
Definition: ModuleMap.h:413
ModuleKind
Specifies the kind of module that has been loaded.
Definition: Module.h:42
StringRef Filename
Definition: Format.cpp:1711
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...
ArrayRef< std::string > getDependencies() const
Definition: Utils.h:87
static bool isSpecialFilename(StringRef Filename)
Represents a character-granular source range.
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
bool addDependency(StringRef Filename)
Return true if the filename was added to the list of dependencies, false otherwise.
Defines the clang::Preprocessor interface.
ASTEdit remove(RangeSelector S)
Removes the source selected by S.
Definition: Transformer.h:232
A mechanism to observe the actions of the module map parser as it reads module map files...
Definition: ModuleMap.h:46
Record the location of an inclusion directive, such as an #include or #import statement.
const SourceManager & SM
Definition: Format.cpp:1572
SourceManager & getSourceManager() const
Definition: Preprocessor.h:907
#define false
Definition: stdbool.h:17
Kind
void addListener(std::unique_ptr< ASTReaderListener > L)
Add an AST callback listener.
Definition: ASTReader.h:1579
Encodes a location in the source.
StringRef getName() const
Definition: FileManager.h:83
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:59
DependencyOutputOptions - Options for controlling the compiler dependency file generation.
std::vector< std::string > ExtraDeps
A list of filenames to be used as extra dependencies for every target.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Dataflow Directional Tag Classes.
virtual void finishedMainFile(DiagnosticsEngine &Diags)
Called when the end of the main file is reached.
Definition: Utils.h:97
Reads an AST files chain containing the contents of a translation unit.
Definition: ASTReader.h:354
void attachToPreprocessor(Preprocessor &PP) override
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
Abstract interface for callback invocations by the ASTReader.
Definition: ASTReader.h:126
virtual void attachToPreprocessor(Preprocessor &PP)
Defines the PPCallbacks interface.
DiagnosticsEngine & getDiagnostics() const
Definition: Preprocessor.h:900
virtual bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing)
Called when a new file is seen.
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...
DependencyOutputFormat
DependencyOutputFormat - Format for the compiler dependency file.
bool isSystem(CharacteristicKind CK)
Determine whether a file / directory characteristic is for system code.
Definition: SourceManager.h:82
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
Definition: Preprocessor.h:995
This class handles loading and caching of source files into memory.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:124