1//===--- DependencyFile.cpp - Generate dependency file --------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This code generates dependency files.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Frontend/Utils.h"
15#include "clang/Basic/FileManager.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Frontend/DependencyOutputOptions.h"
18#include "clang/Frontend/FrontendDiagnostic.h"
19#include "clang/Lex/DirectoryLookup.h"
20#include "clang/Lex/LexDiagnostic.h"
21#include "clang/Lex/PPCallbacks.h"
22#include "clang/Lex/Preprocessor.h"
23#include "clang/Serialization/ASTReader.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
29using namespace clang;
30
31namespace {
32struct DepCollectorPPCallbacks : public PPCallbacks {
33  DependencyCollector &DepCollector;
34  SourceManager &SM;
35  DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM)
36      : DepCollector(L), SM(SM) { }
37
38  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
39                   SrcMgr::CharacteristicKind FileType,
40                   FileID PrevFID) override {
41    if (Reason != PPCallbacks::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    const FileEntry *FE =
48        SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc)));
49    if (!FE)
50      return;
51
52    StringRef Filename = FE->getName();
53
54    // Remove leading "./" (or ".//" or "././" etc.)
55    while (Filename.size() > 2 && Filename[0] == '.' &&
56           llvm::sys::path::is_separator(Filename[1])) {
57      Filename = Filename.substr(1);
58      while (llvm::sys::path::is_separator(Filename[0]))
59        Filename = Filename.substr(1);
60    }
61
62    DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
63                                   FileType != SrcMgr::C_User,
64                                   /*IsModuleFile*/false, /*IsMissing*/false);
65  }
66
67  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
68                          StringRef FileName, bool IsAngled,
69                          CharSourceRange FilenameRange, const FileEntry *File,
70                          StringRef SearchPath, StringRef RelativePath,
71                          const Module *Imported) override {
72    if (!File)
73      DepCollector.maybeAddDependency(FileName, /*FromModule*/false,
74                                     /*IsSystem*/false, /*IsModuleFile*/false,
75                                     /*IsMissing*/true);
76    // Files that actually exist are handled by FileChanged.
77  }
78
79  void EndOfMainFile() override {
80    DepCollector.finishedMainFile();
81  }
82};
83
84struct DepCollectorASTListener : public ASTReaderListener {
85  DependencyCollector &DepCollector;
86  DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { }
87  bool needsInputFileVisitation() override { return true; }
88  bool needsSystemInputFileVisitation() override {
89    return DepCollector.needSystemDependencies();
90  }
91  void visitModuleFile(StringRef Filename) override {
92    DepCollector.maybeAddDependency(Filename, /*FromModule*/true,
93                                   /*IsSystem*/false, /*IsModuleFile*/true,
94                                   /*IsMissing*/false);
95  }
96  bool visitInputFile(StringRef Filename, bool IsSystem,
97                      bool IsOverridden) override {
98    if (IsOverridden)
99      return true;
100
101    DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem,
102                                   /*IsModuleFile*/false, /*IsMissing*/false);
103    return true;
104  }
105};
106} // end anonymous namespace
107
108void DependencyCollector::maybeAddDependency(StringRef Filename, bool FromModule,
109                                            bool IsSystem, bool IsModuleFile,
110                                            bool IsMissing) {
111  if (Seen.insert(Filename) &&
112      sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
113    Dependencies.push_back(Filename);
114}
115
116bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
117                                       bool IsSystem, bool IsModuleFile,
118                                       bool IsMissing) {
119  return Filename != "<built-in>" && (needSystemDependencies() || !IsSystem);
120}
121
122DependencyCollector::~DependencyCollector() { }
123void DependencyCollector::attachToPreprocessor(Preprocessor &PP) {
124  PP.addPPCallbacks(new DepCollectorPPCallbacks(*this, PP.getSourceManager()));
125}
126void DependencyCollector::attachToASTReader(ASTReader &R) {
127  R.addListener(new DepCollectorASTListener(*this));
128}
129
130namespace {
131/// Private implementation for DependencyFileGenerator
132class DFGImpl : public PPCallbacks {
133  std::vector<std::string> Files;
134  llvm::StringSet<> FilesSet;
135  const Preprocessor *PP;
136  std::string OutputFile;
137  std::vector<std::string> Targets;
138  bool IncludeSystemHeaders;
139  bool PhonyTarget;
140  bool AddMissingHeaderDeps;
141  bool SeenMissingHeader;
142  bool IncludeModuleFiles;
143private:
144  bool FileMatchesDepCriteria(const char *Filename,
145                              SrcMgr::CharacteristicKind FileType);
146  void OutputDependencyFile();
147
148public:
149  DFGImpl(const Preprocessor *_PP, const DependencyOutputOptions &Opts)
150    : PP(_PP), OutputFile(Opts.OutputFile), Targets(Opts.Targets),
151      IncludeSystemHeaders(Opts.IncludeSystemHeaders),
152      PhonyTarget(Opts.UsePhonyTargets),
153      AddMissingHeaderDeps(Opts.AddMissingHeaderDeps),
154      SeenMissingHeader(false),
155      IncludeModuleFiles(Opts.IncludeModuleFiles) {}
156
157  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
158                   SrcMgr::CharacteristicKind FileType,
159                   FileID PrevFID) override;
160  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
161                          StringRef FileName, bool IsAngled,
162                          CharSourceRange FilenameRange, const FileEntry *File,
163                          StringRef SearchPath, StringRef RelativePath,
164                          const Module *Imported) override;
165
166  void EndOfMainFile() override {
167    OutputDependencyFile();
168  }
169
170  void AddFilename(StringRef Filename);
171  bool includeSystemHeaders() const { return IncludeSystemHeaders; }
172  bool includeModuleFiles() const { return IncludeModuleFiles; }
173};
174
175class DFGASTReaderListener : public ASTReaderListener {
176  DFGImpl &Parent;
177public:
178  DFGASTReaderListener(DFGImpl &Parent)
179  : Parent(Parent) { }
180  bool needsInputFileVisitation() override { return true; }
181  bool needsSystemInputFileVisitation() override {
182    return Parent.includeSystemHeaders();
183  }
184  void visitModuleFile(StringRef Filename) override;
185  bool visitInputFile(StringRef Filename, bool isSystem,
186                      bool isOverridden) override;
187};
188}
189
190DependencyFileGenerator::DependencyFileGenerator(void *Impl)
191: Impl(Impl) { }
192
193DependencyFileGenerator *DependencyFileGenerator::CreateAndAttachToPreprocessor(
194    clang::Preprocessor &PP, const clang::DependencyOutputOptions &Opts) {
195
196  if (Opts.Targets.empty()) {
197    PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT);
198    return nullptr;
199  }
200
201  // Disable the "file not found" diagnostic if the -MG option was given.
202  if (Opts.AddMissingHeaderDeps)
203    PP.SetSuppressIncludeNotFoundError(true);
204
205  DFGImpl *Callback = new DFGImpl(&PP, Opts);
206  PP.addPPCallbacks(Callback); // PP owns the Callback
207  return new DependencyFileGenerator(Callback);
208}
209
210void DependencyFileGenerator::AttachToASTReader(ASTReader &R) {
211  DFGImpl *I = reinterpret_cast<DFGImpl *>(Impl);
212  assert(I && "missing implementation");
213  R.addListener(new DFGASTReaderListener(*I));
214}
215
216/// FileMatchesDepCriteria - Determine whether the given Filename should be
217/// considered as a dependency.
218bool DFGImpl::FileMatchesDepCriteria(const char *Filename,
219                                     SrcMgr::CharacteristicKind FileType) {
220  if (strcmp("<built-in>", Filename) == 0)
221    return false;
222
223  if (IncludeSystemHeaders)
224    return true;
225
226  return FileType == SrcMgr::C_User;
227}
228
229void DFGImpl::FileChanged(SourceLocation Loc,
230                          FileChangeReason Reason,
231                          SrcMgr::CharacteristicKind FileType,
232                          FileID PrevFID) {
233  if (Reason != PPCallbacks::EnterFile)
234    return;
235
236  // Dependency generation really does want to go all the way to the
237  // file entry for a source location to find out what is depended on.
238  // We do not want #line markers to affect dependency generation!
239  SourceManager &SM = PP->getSourceManager();
240
241  const FileEntry *FE =
242    SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc)));
243  if (!FE) return;
244
245  StringRef Filename = FE->getName();
246  if (!FileMatchesDepCriteria(Filename.data(), FileType))
247    return;
248
249  // Remove leading "./" (or ".//" or "././" etc.)
250  while (Filename.size() > 2 && Filename[0] == '.' &&
251         llvm::sys::path::is_separator(Filename[1])) {
252    Filename = Filename.substr(1);
253    while (llvm::sys::path::is_separator(Filename[0]))
254      Filename = Filename.substr(1);
255  }
256
257  AddFilename(Filename);
258}
259
260void DFGImpl::InclusionDirective(SourceLocation HashLoc,
261                                 const Token &IncludeTok,
262                                 StringRef FileName,
263                                 bool IsAngled,
264                                 CharSourceRange FilenameRange,
265                                 const FileEntry *File,
266                                 StringRef SearchPath,
267                                 StringRef RelativePath,
268                                 const Module *Imported) {
269  if (!File) {
270    if (AddMissingHeaderDeps)
271      AddFilename(FileName);
272    else
273      SeenMissingHeader = true;
274  }
275}
276
277void DFGImpl::AddFilename(StringRef Filename) {
278  if (FilesSet.insert(Filename))
279    Files.push_back(Filename);
280}
281
282/// PrintFilename - GCC escapes spaces, # and $, but apparently not ' or " or
283/// other scary characters.
284static void PrintFilename(raw_ostream &OS, StringRef Filename) {
285  for (unsigned i = 0, e = Filename.size(); i != e; ++i) {
286    if (Filename[i] == ' ' || Filename[i] == '#')
287      OS << '\\';
288    else if (Filename[i] == '$') // $ is escaped by $$.
289      OS << '$';
290    OS << Filename[i];
291  }
292}
293
294void DFGImpl::OutputDependencyFile() {
295  if (SeenMissingHeader) {
296    llvm::sys::fs::remove(OutputFile);
297    return;
298  }
299
300  std::string Err;
301  llvm::raw_fd_ostream OS(OutputFile.c_str(), Err, llvm::sys::fs::F_Text);
302  if (!Err.empty()) {
303    PP->getDiagnostics().Report(diag::err_fe_error_opening)
304      << OutputFile << Err;
305    return;
306  }
307
308  // Write out the dependency targets, trying to avoid overly long
309  // lines when possible. We try our best to emit exactly the same
310  // dependency file as GCC (4.2), assuming the included files are the
311  // same.
312  const unsigned MaxColumns = 75;
313  unsigned Columns = 0;
314
315  for (std::vector<std::string>::iterator
316         I = Targets.begin(), E = Targets.end(); I != E; ++I) {
317    unsigned N = I->length();
318    if (Columns == 0) {
319      Columns += N;
320    } else if (Columns + N + 2 > MaxColumns) {
321      Columns = N + 2;
322      OS << " \\\n  ";
323    } else {
324      Columns += N + 1;
325      OS << ' ';
326    }
327    // Targets already quoted as needed.
328    OS << *I;
329  }
330
331  OS << ':';
332  Columns += 1;
333
334  // Now add each dependency in the order it was seen, but avoiding
335  // duplicates.
336  for (std::vector<std::string>::iterator I = Files.begin(),
337         E = Files.end(); I != E; ++I) {
338    // Start a new line if this would exceed the column limit. Make
339    // sure to leave space for a trailing " \" in case we need to
340    // break the line on the next iteration.
341    unsigned N = I->length();
342    if (Columns + (N + 1) + 2 > MaxColumns) {
343      OS << " \\\n ";
344      Columns = 2;
345    }
346    OS << ' ';
347    PrintFilename(OS, *I);
348    Columns += N + 1;
349  }
350  OS << '\n';
351
352  // Create phony targets if requested.
353  if (PhonyTarget && !Files.empty()) {
354    // Skip the first entry, this is always the input file itself.
355    for (std::vector<std::string>::iterator I = Files.begin() + 1,
356           E = Files.end(); I != E; ++I) {
357      OS << '\n';
358      PrintFilename(OS, *I);
359      OS << ":\n";
360    }
361  }
362}
363
364bool DFGASTReaderListener::visitInputFile(llvm::StringRef Filename,
365                                          bool IsSystem, bool IsOverridden) {
366  assert(!IsSystem || needsSystemInputFileVisitation());
367  if (IsOverridden)
368    return true;
369
370  Parent.AddFilename(Filename);
371  return true;
372}
373
374void DFGASTReaderListener::visitModuleFile(llvm::StringRef Filename) {
375  if (Parent.includeModuleFiles())
376    Parent.AddFilename(Filename);
377}
378