1 //===--- DependencyGraph.cpp - Generate dependency file -------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This code generates a header dependency graph in DOT format, for use 10 // with, e.g., GraphViz. 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/GraphWriter.h" 22 #include "llvm/Support/raw_ostream.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 SmallVector<const FileEntry *, 2> > DependencyMap; 35 36 DependencyMap Dependencies; 37 38 private: 39 raw_ostream &writeNodeReference(raw_ostream &OS, 40 const FileEntry *Node); 41 void OutputGraphFile(); 42 43 public: 44 DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile, 45 StringRef SysRoot) 46 : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { } 47 48 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, 49 StringRef FileName, bool IsAngled, 50 CharSourceRange FilenameRange, const FileEntry *File, 51 StringRef SearchPath, StringRef RelativePath, 52 const Module *Imported, 53 SrcMgr::CharacteristicKind FileType) 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(std::make_unique<DependencyGraphCallback>(&PP, OutputFile, 65 SysRoot)); 66 } 67 68 void DependencyGraphCallback::InclusionDirective( 69 SourceLocation HashLoc, 70 const Token &IncludeTok, 71 StringRef FileName, 72 bool IsAngled, 73 CharSourceRange FilenameRange, 74 const FileEntry *File, 75 StringRef SearchPath, 76 StringRef RelativePath, 77 const Module *Imported, 78 SrcMgr::CharacteristicKind FileType) { 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) 86 return; 87 88 Dependencies[FromFile].push_back(File); 89 90 AllFiles.insert(File); 91 AllFiles.insert(FromFile); 92 } 93 94 raw_ostream & 95 DependencyGraphCallback::writeNodeReference(raw_ostream &OS, 96 const FileEntry *Node) { 97 OS << "header_" << Node->getUID(); 98 return OS; 99 } 100 101 void DependencyGraphCallback::OutputGraphFile() { 102 std::error_code EC; 103 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_Text); 104 if (EC) { 105 PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile 106 << EC.message(); 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(std::string(FileName)) << "\"];\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