clang API Documentation

DependencyGraph.cpp
Go to the documentation of this file.
00001 //===--- DependencyGraph.cpp - Generate dependency file -------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This code generates a header dependency graph in DOT format, for use
00011 // with, e.g., GraphViz.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "clang/Frontend/Utils.h"
00016 #include "clang/Basic/FileManager.h"
00017 #include "clang/Basic/SourceManager.h"
00018 #include "clang/Frontend/FrontendDiagnostic.h"
00019 #include "clang/Lex/PPCallbacks.h"
00020 #include "clang/Lex/Preprocessor.h"
00021 #include "llvm/ADT/SetVector.h"
00022 #include "llvm/Support/GraphWriter.h"
00023 #include "llvm/Support/raw_ostream.h"
00024 
00025 using namespace clang;
00026 namespace DOT = llvm::DOT;
00027 
00028 namespace {
00029 class DependencyGraphCallback : public PPCallbacks {
00030   const Preprocessor *PP;
00031   std::string OutputFile;
00032   std::string SysRoot;
00033   llvm::SetVector<const FileEntry *> AllFiles;
00034   typedef llvm::DenseMap<const FileEntry *,
00035                          SmallVector<const FileEntry *, 2> > DependencyMap;
00036   
00037   DependencyMap Dependencies;
00038   
00039 private:
00040   raw_ostream &writeNodeReference(raw_ostream &OS,
00041                                   const FileEntry *Node);
00042   void OutputGraphFile();
00043 
00044 public:
00045   DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile,
00046                           StringRef SysRoot)
00047     : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { }
00048 
00049   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
00050                           StringRef FileName, bool IsAngled,
00051                           CharSourceRange FilenameRange, const FileEntry *File,
00052                           StringRef SearchPath, StringRef RelativePath,
00053                           const Module *Imported) override;
00054 
00055   void EndOfMainFile() override {
00056     OutputGraphFile();
00057   }
00058   
00059 };
00060 }
00061 
00062 void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile,
00063                                      StringRef SysRoot) {
00064   PP.addPPCallbacks(llvm::make_unique<DependencyGraphCallback>(&PP, OutputFile,
00065                                                                SysRoot));
00066 }
00067 
00068 void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc,
00069                                                  const Token &IncludeTok,
00070                                                  StringRef FileName,
00071                                                  bool IsAngled,
00072                                                  CharSourceRange FilenameRange,
00073                                                  const FileEntry *File,
00074                                                  StringRef SearchPath,
00075                                                  StringRef RelativePath,
00076                                                  const Module *Imported) {
00077   if (!File)
00078     return;
00079   
00080   SourceManager &SM = PP->getSourceManager();
00081   const FileEntry *FromFile
00082     = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc)));
00083   if (!FromFile)
00084     return;
00085 
00086   Dependencies[FromFile].push_back(File);
00087   
00088   AllFiles.insert(File);
00089   AllFiles.insert(FromFile);
00090 }
00091 
00092 raw_ostream &
00093 DependencyGraphCallback::writeNodeReference(raw_ostream &OS,
00094                                             const FileEntry *Node) {
00095   OS << "header_" << Node->getUID();
00096   return OS;
00097 }
00098 
00099 void DependencyGraphCallback::OutputGraphFile() {
00100   std::error_code EC;
00101   llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text);
00102   if (EC) {
00103     PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile
00104                                                             << EC.message();
00105     return;
00106   }
00107 
00108   OS << "digraph \"dependencies\" {\n";
00109   
00110   // Write the nodes
00111   for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) {
00112     // Write the node itself.
00113     OS.indent(2);
00114     writeNodeReference(OS, AllFiles[I]);
00115     OS << " [ shape=\"box\", label=\"";
00116     StringRef FileName = AllFiles[I]->getName();
00117     if (FileName.startswith(SysRoot))
00118       FileName = FileName.substr(SysRoot.size());
00119     
00120     OS << DOT::EscapeString(FileName)
00121     << "\"];\n";
00122   }
00123 
00124   // Write the edges
00125   for (DependencyMap::iterator F = Dependencies.begin(), 
00126                             FEnd = Dependencies.end();
00127        F != FEnd; ++F) {    
00128     for (unsigned I = 0, N = F->second.size(); I != N; ++I) {
00129       OS.indent(2);
00130       writeNodeReference(OS, F->first);
00131       OS << " -> ";
00132       writeNodeReference(OS, F->second[I]);
00133       OS << ";\n";
00134     }
00135   }
00136   OS << "}\n";
00137 }
00138