diff options
author | Douglas Gregor <dgregor@apple.com> | 2012-02-02 00:54:52 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2012-02-02 00:54:52 +0000 |
commit | c69a181049ab52da29f8f69316a34c90c3ea3b8e (patch) | |
tree | b1280e8c18379ff252743117256d46a97e22eb43 /lib/Frontend | |
parent | c645ddf240efb112ff8f17371811deb3ebc1a5b5 (diff) |
Introduce a -cc1 option "-dependency-graphviz" that determines header
dependencies and outputs them in GraphViz format.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149575 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Frontend')
-rw-r--r-- | lib/Frontend/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Frontend/CompilerInstance.cpp | 4 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 1 | ||||
-rw-r--r-- | lib/Frontend/DependencyGraph.cpp | 139 |
4 files changed, 145 insertions, 0 deletions
diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 1148bc4721..467dba064e 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -19,6 +19,7 @@ add_clang_library(clangFrontend CompilerInvocation.cpp CreateInvocationFromCommandLine.cpp DependencyFile.cpp + DependencyGraph.cpp DiagnosticRenderer.cpp FrontendAction.cpp FrontendActions.cpp diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 1501da4ff4..97dc708cc4 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -275,7 +275,11 @@ void CompilerInstance::createPreprocessor() { const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); if (!DepOpts.OutputFile.empty()) AttachDependencyFileGen(*PP, DepOpts); + if (!DepOpts.GraphvizOutputFile.empty()) + AttachDependencyGraphGen(*PP, DepOpts.GraphvizOutputFile, + getHeaderSearchOpts().Sysroot); + // Handle generating header include information, if requested. if (DepOpts.ShowHeaderIncludes) AttachHeaderIncludeGen(*PP); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index e9bb30325c..1be75c8887 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1191,6 +1191,7 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); Opts.HeaderIncludeOutputFile = Args.getLastArgValue(OPT_header_include_file); Opts.AddMissingHeaderDeps = Args.hasArg(OPT_MG); + Opts.GraphvizOutputFile = Args.getLastArgValue(OPT_dependency_graphviz); } static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, diff --git a/lib/Frontend/DependencyGraph.cpp b/lib/Frontend/DependencyGraph.cpp new file mode 100644 index 0000000000..6d4825f6b7 --- /dev/null +++ b/lib/Frontend/DependencyGraph.cpp @@ -0,0 +1,139 @@ +//===--- DependencyGraph.cpp - Generate dependency file -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code generates a header dependency graph in GraphViz format. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/GraphWriter.h" + +using namespace clang; +namespace DOT = llvm::DOT; + +namespace { +class DependencyGraphCallback : public PPCallbacks { + const Preprocessor *PP; + std::string OutputFile; + std::string SysRoot; + llvm::SetVector<const FileEntry *> AllFiles; + typedef llvm::DenseMap<const FileEntry *, + llvm::SmallVector<const FileEntry *, 2> > + DependencyMap; + + DependencyMap Dependencies; + +private: + llvm::raw_ostream &writeNodeReference(llvm::raw_ostream &OS, + const FileEntry *Node); + void OutputGraphFile(); + +public: + DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile, + StringRef SysRoot) + : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { } + + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + StringRef SearchPath, + StringRef RelativePath); + + virtual void EndOfMainFile() { + OutputGraphFile(); + } + +}; +} + +void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, + StringRef SysRoot) { + PP.addPPCallbacks(new DependencyGraphCallback(&PP, OutputFile, SysRoot)); +} + +void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + StringRef SearchPath, + StringRef RelativePath) { + if (!File) + return; + + SourceManager &SM = PP->getSourceManager(); + const FileEntry *FromFile + = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); + if (FromFile == 0) + return; + + Dependencies[FromFile].push_back(File); + + AllFiles.insert(File); + AllFiles.insert(FromFile); +} + +llvm::raw_ostream & +DependencyGraphCallback::writeNodeReference(llvm::raw_ostream &OS, + const FileEntry *Node) { + OS << "header_" << Node->getUID(); + return OS; +} + +void DependencyGraphCallback::OutputGraphFile() { + std::string Err; + llvm::raw_fd_ostream OS(OutputFile.c_str(), Err); + if (!Err.empty()) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) + << OutputFile << Err; + return; + } + + OS << "digraph \"dependencies\" {\n"; + + // Write the nodes + for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) { + // Write the node itself. + OS.indent(2); + writeNodeReference(OS, AllFiles[I]); + OS << " [ shape=\"box\", label=\""; + StringRef FileName = AllFiles[I]->getName(); + if (FileName.startswith(SysRoot)) + FileName = FileName.substr(SysRoot.size()); + + OS << DOT::EscapeString(FileName) + << "\"];\n"; + } + + // Write the edges + for (DependencyMap::iterator F = Dependencies.begin(), + FEnd = Dependencies.end(); + F != FEnd; ++F) { + for (unsigned I = 0, N = F->second.size(); I != N; ++I) { + OS.indent(2); + writeNodeReference(OS, F->first); + OS << " -> "; + writeNodeReference(OS, F->second[I]); + OS << ";\n"; + } + } + OS << "}\n"; +} + |