DependencyGraph.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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. #include "clang/Frontend/Utils.h"
  14. #include "clang/Basic/FileManager.h"
  15. #include "clang/Basic/SourceManager.h"
  16. #include "clang/Frontend/FrontendDiagnostic.h"
  17. #include "clang/Lex/PPCallbacks.h"
  18. #include "clang/Lex/Preprocessor.h"
  19. #include "llvm/ADT/SetVector.h"
  20. #include "llvm/Support/GraphWriter.h"
  21. #include "llvm/Support/raw_ostream.h"
  22. using namespace clang;
  23. namespace DOT = llvm::DOT;
  24. namespace {
  25. class DependencyGraphCallback : public PPCallbacks {
  26. const Preprocessor *PP;
  27. std::string OutputFile;
  28. std::string SysRoot;
  29. llvm::SetVector<FileEntryRef> AllFiles;
  30. using DependencyMap =
  31. llvm::DenseMap<FileEntryRef, SmallVector<FileEntryRef, 2>>;
  32. DependencyMap Dependencies;
  33. private:
  34. raw_ostream &writeNodeReference(raw_ostream &OS,
  35. const FileEntry *Node);
  36. void OutputGraphFile();
  37. public:
  38. DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile,
  39. StringRef SysRoot)
  40. : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { }
  41. void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
  42. StringRef FileName, bool IsAngled,
  43. CharSourceRange FilenameRange,
  44. OptionalFileEntryRef File, StringRef SearchPath,
  45. StringRef RelativePath, const Module *Imported,
  46. SrcMgr::CharacteristicKind FileType) override;
  47. void EndOfMainFile() override {
  48. OutputGraphFile();
  49. }
  50. };
  51. }
  52. void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile,
  53. StringRef SysRoot) {
  54. PP.addPPCallbacks(std::make_unique<DependencyGraphCallback>(&PP, OutputFile,
  55. SysRoot));
  56. }
  57. void DependencyGraphCallback::InclusionDirective(
  58. SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
  59. bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
  60. StringRef SearchPath, StringRef RelativePath, const Module *Imported,
  61. SrcMgr::CharacteristicKind FileType) {
  62. if (!File)
  63. return;
  64. SourceManager &SM = PP->getSourceManager();
  65. OptionalFileEntryRef FromFile =
  66. SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(HashLoc)));
  67. if (!FromFile)
  68. return;
  69. Dependencies[*FromFile].push_back(*File);
  70. AllFiles.insert(*File);
  71. AllFiles.insert(*FromFile);
  72. }
  73. raw_ostream &
  74. DependencyGraphCallback::writeNodeReference(raw_ostream &OS,
  75. const FileEntry *Node) {
  76. OS << "header_" << Node->getUID();
  77. return OS;
  78. }
  79. void DependencyGraphCallback::OutputGraphFile() {
  80. std::error_code EC;
  81. llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
  82. if (EC) {
  83. PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile
  84. << EC.message();
  85. return;
  86. }
  87. OS << "digraph \"dependencies\" {\n";
  88. // Write the nodes
  89. for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) {
  90. // Write the node itself.
  91. OS.indent(2);
  92. writeNodeReference(OS, AllFiles[I]);
  93. OS << " [ shape=\"box\", label=\"";
  94. StringRef FileName = AllFiles[I].getName();
  95. if (FileName.startswith(SysRoot))
  96. FileName = FileName.substr(SysRoot.size());
  97. OS << DOT::EscapeString(std::string(FileName)) << "\"];\n";
  98. }
  99. // Write the edges
  100. for (DependencyMap::iterator F = Dependencies.begin(),
  101. FEnd = Dependencies.end();
  102. F != FEnd; ++F) {
  103. for (unsigned I = 0, N = F->second.size(); I != N; ++I) {
  104. OS.indent(2);
  105. writeNodeReference(OS, F->first);
  106. OS << " -> ";
  107. writeNodeReference(OS, F->second[I]);
  108. OS << ";\n";
  109. }
  110. }
  111. OS << "}\n";
  112. }