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/raw_ostream.h" 23 #include "llvm/Support/GraphWriter.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 llvm::SmallVector<const FileEntry *, 2> > 36 DependencyMap; 37 38 DependencyMap Dependencies; 39 40 private: 41 llvm::raw_ostream &writeNodeReference(llvm::raw_ostream &OS, 42 const FileEntry *Node); 43 void OutputGraphFile(); 44 45 public: 46 DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile, 47 StringRef SysRoot) 48 : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { } 49 50 virtual void InclusionDirective(SourceLocation HashLoc, 51 const Token &IncludeTok, 52 StringRef FileName, 53 bool IsAngled, 54 const FileEntry *File, 55 SourceLocation EndLoc, 56 StringRef SearchPath, 57 StringRef RelativePath); 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 const FileEntry *File, 76 SourceLocation EndLoc, 77 StringRef SearchPath, 78 StringRef RelativePath) { 79 if (!File) 80 return; 81 82 SourceManager &SM = PP->getSourceManager(); 83 const FileEntry *FromFile 84 = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); 85 if (FromFile == 0) 86 return; 87 88 Dependencies[FromFile].push_back(File); 89 90 AllFiles.insert(File); 91 AllFiles.insert(FromFile); 92 } 93 94 llvm::raw_ostream & 95 DependencyGraphCallback::writeNodeReference(llvm::raw_ostream &OS, 96 const FileEntry *Node) { 97 OS << "header_" << Node->getUID(); 98 return OS; 99 } 100 101 void DependencyGraphCallback::OutputGraphFile() { 102 std::string Err; 103 llvm::raw_fd_ostream OS(OutputFile.c_str(), Err); 104 if (!Err.empty()) { 105 PP->getDiagnostics().Report(diag::err_fe_error_opening) 106 << OutputFile << Err; 107 return; 108 } 109 110 OS << "digraph \"dependencies\" {\n"; 111 112 // Write the nodes 113 for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) { 114 // Write the node itself. 115 OS.indent(2); 116 writeNodeReference(OS, AllFiles[I]); 117 OS << " [ shape=\"box\", label=\""; 118 StringRef FileName = AllFiles[I]->getName(); 119 if (FileName.startswith(SysRoot)) 120 FileName = FileName.substr(SysRoot.size()); 121 122 OS << DOT::EscapeString(FileName) 123 << "\"];\n"; 124 } 125 126 // Write the edges 127 for (DependencyMap::iterator F = Dependencies.begin(), 128 FEnd = Dependencies.end(); 129 F != FEnd; ++F) { 130 for (unsigned I = 0, N = F->second.size(); I != N; ++I) { 131 OS.indent(2); 132 writeNodeReference(OS, F->first); 133 OS << " -> "; 134 writeNodeReference(OS, F->second[I]); 135 OS << ";\n"; 136 } 137 } 138 OS << "}\n"; 139 } 140 141