156 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This code generates a header dependency graph in DOT format, for use10// with, e.g., GraphViz.11//12//===----------------------------------------------------------------------===//13 14#include "clang/Basic/DiagnosticFrontend.h"15#include "clang/Basic/FileManager.h"16#include "clang/Basic/SourceManager.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 24using namespace clang;25namespace DOT = llvm::DOT;26 27namespace {28class 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 38private:39 raw_ostream &writeNodeReference(raw_ostream &OS,40 const FileEntry *Node);41 void OutputGraphFile();42 43public: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 67void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile,68 StringRef SysRoot) {69 PP.addPPCallbacks(std::make_unique<DependencyGraphCallback>(&PP, OutputFile,70 SysRoot));71}72 73void 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 93void 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 111raw_ostream &112DependencyGraphCallback::writeNodeReference(raw_ostream &OS,113 const FileEntry *Node) {114 OS << "header_" << Node->getUID();115 return OS;116}117 118void 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) << OutputFile123 << EC.message();124 return;125 }126 127 OS << "digraph \"dependencies\" {\n";128 129 // Write the nodes130 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 edges142 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