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<FileEntryRef> AllFiles; 33 using DependencyMap = 34 llvm::DenseMap<FileEntryRef, SmallVector<FileEntryRef, 2>>; 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, 51 OptionalFileEntryRef File, StringRef SearchPath, 52 StringRef RelativePath, const Module *SuggestedModule, 53 bool ModuleImported, 54 SrcMgr::CharacteristicKind FileType) override; 55 56 void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled, 57 OptionalFileEntryRef File, 58 const LexEmbedParametersResult &Params) override; 59 60 void EndOfMainFile() override { 61 OutputGraphFile(); 62 } 63 64 }; 65 } 66 67 void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, 68 StringRef SysRoot) { 69 PP.addPPCallbacks(std::make_unique<DependencyGraphCallback>(&PP, OutputFile, 70 SysRoot)); 71 } 72 73 void DependencyGraphCallback::InclusionDirective( 74 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, 75 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, 76 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule, 77 bool ModuleImported, SrcMgr::CharacteristicKind FileType) { 78 if (!File) 79 return; 80 81 SourceManager &SM = PP->getSourceManager(); 82 OptionalFileEntryRef FromFile = 83 SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); 84 if (!FromFile) 85 return; 86 87 Dependencies[*FromFile].push_back(*File); 88 89 AllFiles.insert(*File); 90 AllFiles.insert(*FromFile); 91 } 92 93 void DependencyGraphCallback::EmbedDirective(SourceLocation HashLoc, StringRef, 94 bool, OptionalFileEntryRef File, 95 const LexEmbedParametersResult &) { 96 if (!File) 97 return; 98 99 SourceManager &SM = PP->getSourceManager(); 100 OptionalFileEntryRef FromFile = 101 SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); 102 if (!FromFile) 103 return; 104 105 Dependencies[*FromFile].push_back(*File); 106 107 AllFiles.insert(*File); 108 AllFiles.insert(*FromFile); 109 } 110 111 raw_ostream & 112 DependencyGraphCallback::writeNodeReference(raw_ostream &OS, 113 const FileEntry *Node) { 114 OS << "header_" << Node->getUID(); 115 return OS; 116 } 117 118 void DependencyGraphCallback::OutputGraphFile() { 119 std::error_code EC; 120 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF); 121 if (EC) { 122 PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile 123 << EC.message(); 124 return; 125 } 126 127 OS << "digraph \"dependencies\" {\n"; 128 129 // Write the nodes 130 for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) { 131 // Write the node itself. 132 OS.indent(2); 133 writeNodeReference(OS, AllFiles[I]); 134 OS << " [ shape=\"box\", label=\""; 135 StringRef FileName = AllFiles[I].getName(); 136 FileName.consume_front(SysRoot); 137 138 OS << DOT::EscapeString(std::string(FileName)) << "\"];\n"; 139 } 140 141 // Write the edges 142 for (DependencyMap::iterator F = Dependencies.begin(), 143 FEnd = Dependencies.end(); 144 F != FEnd; ++F) { 145 for (unsigned I = 0, N = F->second.size(); I != N; ++I) { 146 OS.indent(2); 147 writeNodeReference(OS, F->first); 148 OS << " -> "; 149 writeNodeReference(OS, F->second[I]); 150 OS << ";\n"; 151 } 152 } 153 OS << "}\n"; 154 } 155 156