Home | History | Annotate | Line # | Download | only in Frontend
      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_TextWithCRLF);
    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