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