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   virtual void InclusionDirective(SourceLocation HashLoc,
     50                                   const Token &IncludeTok,
     51                                   StringRef FileName,
     52                                   bool IsAngled,
     53                                   CharSourceRange FilenameRange,
     54                                   const FileEntry *File,
     55                                   StringRef SearchPath,
     56                                   StringRef RelativePath,
     57                                   const Module *Imported);
     58 
     59   virtual void EndOfMainFile() {
     60     OutputGraphFile();
     61   }
     62 
     63 };
     64 }
     65 
     66 void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile,
     67                                      StringRef SysRoot) {
     68   PP.addPPCallbacks(new DependencyGraphCallback(&PP, OutputFile, SysRoot));
     69 }
     70 
     71 void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc,
     72                                                  const Token &IncludeTok,
     73                                                  StringRef FileName,
     74                                                  bool IsAngled,
     75                                                  CharSourceRange FilenameRange,
     76                                                  const FileEntry *File,
     77                                                  StringRef SearchPath,
     78                                                  StringRef RelativePath,
     79                                                  const Module *Imported) {
     80   if (!File)
     81     return;
     82 
     83   SourceManager &SM = PP->getSourceManager();
     84   const FileEntry *FromFile
     85     = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc)));
     86   if (FromFile == 0)
     87     return;
     88 
     89   Dependencies[FromFile].push_back(File);
     90 
     91   AllFiles.insert(File);
     92   AllFiles.insert(FromFile);
     93 }
     94 
     95 raw_ostream &
     96 DependencyGraphCallback::writeNodeReference(raw_ostream &OS,
     97                                             const FileEntry *Node) {
     98   OS << "header_" << Node->getUID();
     99   return OS;
    100 }
    101 
    102 void DependencyGraphCallback::OutputGraphFile() {
    103   std::string Err;
    104   llvm::raw_fd_ostream OS(OutputFile.c_str(), Err);
    105   if (!Err.empty()) {
    106     PP->getDiagnostics().Report(diag::err_fe_error_opening)
    107       << OutputFile << Err;
    108     return;
    109   }
    110 
    111   OS << "digraph \"dependencies\" {\n";
    112 
    113   // Write the nodes
    114   for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) {
    115     // Write the node itself.
    116     OS.indent(2);
    117     writeNodeReference(OS, AllFiles[I]);
    118     OS << " [ shape=\"box\", label=\"";
    119     StringRef FileName = AllFiles[I]->getName();
    120     if (FileName.startswith(SysRoot))
    121       FileName = FileName.substr(SysRoot.size());
    122 
    123     OS << DOT::EscapeString(FileName)
    124     << "\"];\n";
    125   }
    126 
    127   // Write the edges
    128   for (DependencyMap::iterator F = Dependencies.begin(),
    129                             FEnd = Dependencies.end();
    130        F != FEnd; ++F) {
    131     for (unsigned I = 0, N = F->second.size(); I != N; ++I) {
    132       OS.indent(2);
    133       writeNodeReference(OS, F->first);
    134       OS << " -> ";
    135       writeNodeReference(OS, F->second[I]);
    136       OS << ";\n";
    137     }
    138   }
    139   OS << "}\n";
    140 }
    141 
    142