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