Home | History | Annotate | Download | only in Frontend
      1 //===--- DependencyGraph.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 a header dependency graph in DOT format, for use
     11 // with, e.g., GraphViz.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "clang/Frontend/Utils.h"
     16 #include "clang/Basic/FileManager.h"
     17 #include "clang/Basic/SourceManager.h"
     18 #include "clang/Frontend/FrontendDiagnostic.h"
     19 #include "clang/Lex/PPCallbacks.h"
     20 #include "clang/Lex/Preprocessor.h"
     21 #include "llvm/ADT/SetVector.h"
     22 #include "llvm/Support/GraphWriter.h"
     23 #include "llvm/Support/raw_ostream.h"
     24 
     25 using namespace clang;
     26 namespace DOT = llvm::DOT;
     27 
     28 namespace {
     29 class DependencyGraphCallback : public PPCallbacks {
     30   const Preprocessor *PP;
     31   std::string OutputFile;
     32   std::string SysRoot;
     33   llvm::SetVector<const FileEntry *> AllFiles;
     34   typedef llvm::DenseMap<const FileEntry *,
     35                          SmallVector<const FileEntry *, 2> > DependencyMap;
     36 
     37   DependencyMap Dependencies;
     38 
     39 private:
     40   raw_ostream &writeNodeReference(raw_ostream &OS,
     41                                   const FileEntry *Node);
     42   void OutputGraphFile();
     43 
     44 public:
     45   DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile,
     46                           StringRef SysRoot)
     47     : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { }
     48 
     49   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
     50                           StringRef FileName, bool IsAngled,
     51                           CharSourceRange FilenameRange, const FileEntry *File,
     52                           StringRef SearchPath, StringRef RelativePath,
     53                           const Module *Imported) override;
     54 
     55   void EndOfMainFile() override {
     56     OutputGraphFile();
     57   }
     58 
     59 };
     60 }
     61 
     62 void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile,
     63                                      StringRef SysRoot) {
     64   PP.addPPCallbacks(llvm::make_unique<DependencyGraphCallback>(&PP, OutputFile,
     65                                                                SysRoot));
     66 }
     67 
     68 void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc,
     69                                                  const Token &IncludeTok,
     70                                                  StringRef FileName,
     71                                                  bool IsAngled,
     72                                                  CharSourceRange FilenameRange,
     73                                                  const FileEntry *File,
     74                                                  StringRef SearchPath,
     75                                                  StringRef RelativePath,
     76                                                  const Module *Imported) {
     77   if (!File)
     78     return;
     79 
     80   SourceManager &SM = PP->getSourceManager();
     81   const FileEntry *FromFile
     82     = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc)));
     83   if (!FromFile)
     84     return;
     85 
     86   Dependencies[FromFile].push_back(File);
     87 
     88   AllFiles.insert(File);
     89   AllFiles.insert(FromFile);
     90 }
     91 
     92 raw_ostream &
     93 DependencyGraphCallback::writeNodeReference(raw_ostream &OS,
     94                                             const FileEntry *Node) {
     95   OS << "header_" << Node->getUID();
     96   return OS;
     97 }
     98 
     99 void DependencyGraphCallback::OutputGraphFile() {
    100   std::error_code EC;
    101   llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text);
    102   if (EC) {
    103     PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile
    104                                                             << EC.message();
    105     return;
    106   }
    107 
    108   OS << "digraph \"dependencies\" {\n";
    109 
    110   // Write the nodes
    111   for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) {
    112     // Write the node itself.
    113     OS.indent(2);
    114     writeNodeReference(OS, AllFiles[I]);
    115     OS << " [ shape=\"box\", label=\"";
    116     StringRef FileName = AllFiles[I]->getName();
    117     if (FileName.startswith(SysRoot))
    118       FileName = FileName.substr(SysRoot.size());
    119 
    120     OS << DOT::EscapeString(FileName)
    121     << "\"];\n";
    122   }
    123 
    124   // Write the edges
    125   for (DependencyMap::iterator F = Dependencies.begin(),
    126                             FEnd = Dependencies.end();
    127        F != FEnd; ++F) {
    128     for (unsigned I = 0, N = F->second.size(); I != N; ++I) {
    129       OS.indent(2);
    130       writeNodeReference(OS, F->first);
    131       OS << " -> ";
    132       writeNodeReference(OS, F->second[I]);
    133       OS << ";\n";
    134     }
    135   }
    136   OS << "}\n";
    137 }
    138 
    139