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