From: Douglas Gregor Date: Thu, 2 Feb 2012 00:54:52 +0000 (+0000) Subject: Introduce a -cc1 option "-dependency-graphviz" that determines header X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c69a181049ab52da29f8f69316a34c90c3ea3b8e;p=clang 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 --- diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index b58c105af5..662095e207 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -233,6 +233,8 @@ def pg : Flag<"-pg">, HelpText<"Enable mcount instrumentation">; def dependency_file : Separate<"-dependency-file">, HelpText<"Filename (or -) to write dependency output to">; +def dependency_graphviz : Separate<"-dependency-graphviz">, + HelpText<"Filename to write GraphViz-formatted header dependencies to">; def sys_header_deps : Flag<"-sys-header-deps">, HelpText<"Include system headers in dependency output">; def header_include_file : Separate<"-header-include-file">, diff --git a/include/clang/Frontend/DependencyOutputOptions.h b/include/clang/Frontend/DependencyOutputOptions.h index 1e22c227fc..63e2abf887 100644 --- a/include/clang/Frontend/DependencyOutputOptions.h +++ b/include/clang/Frontend/DependencyOutputOptions.h @@ -25,7 +25,7 @@ public: /// dependency, which can avoid some 'make' /// problems. unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list - + /// The file to write dependency output to. std::string OutputFile; @@ -39,6 +39,9 @@ public: /// must contain at least one entry. std::vector Targets; + /// \brief The file to write GraphViz-formatted header dependencies to. + std::string GraphvizOutputFile; + public: DependencyOutputOptions() { IncludeSystemHeaders = 0; diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h index 3cdda2aefa..fd0ddda85e 100644 --- a/include/clang/Frontend/Utils.h +++ b/include/clang/Frontend/Utils.h @@ -73,6 +73,11 @@ void DoPrintPreprocessedInput(Preprocessor &PP, raw_ostream* OS, void AttachDependencyFileGen(Preprocessor &PP, const DependencyOutputOptions &Opts); +/// AttachDependencyGraphGen - Create a dependency graph generator, and attach +/// it to the given preprocessor. + void AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, + StringRef SysRoot); + /// AttachHeaderIncludeGen - Create a header include list generator, and attach /// it to the given preprocessor. /// 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 AllFiles; + typedef llvm::DenseMap > + 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"; +} +