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