From bc4e5581ee62d0826625bf1344c3bad7f8711dbc Mon Sep 17 00:00:00 2001 From: Andy Cedilnik Date: Fri, 3 Mar 2006 14:24:31 -0500 Subject: [PATCH] ENH: Add support for exporting graphviz of the project dependencies --- Source/cmTarget.cxx | 5 ++ Source/cmTarget.h | 2 + Source/cmake.cxx | 177 ++++++++++++++++++++++++++++++++++++++++++++ Source/cmake.h | 3 + 4 files changed, 187 insertions(+) diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 3fd75a572..a562f25aa 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -606,6 +606,11 @@ cmTarget::AnalyzeLibDependencies( const cmMakefile& mf ) // The dependency map. DependencyMap dep_map; + if ( m_OriginalLinkLibraries.size() == 0 ) + { + m_OriginalLinkLibraries = m_LinkLibraries; + } + // 1. Build the dependency graph // for(LinkLibraries::reverse_iterator lib = m_LinkLibraries.rbegin(); diff --git a/Source/cmTarget.h b/Source/cmTarget.h index a605a35b6..9990cddaa 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -95,6 +95,7 @@ public: enum LinkLibraryType {GENERAL, DEBUG, OPTIMIZED}; typedef std::vector > LinkLibraries; const LinkLibraries &GetLinkLibraries() {return m_LinkLibraries;} + const LinkLibraries &GetOriginalLinkLibraries() {return m_OriginalLinkLibraries;} /** * Clear the dependency information recorded for this target, if any. @@ -321,6 +322,7 @@ private: std::vector m_SourceFiles; LinkLibraries m_LinkLibraries; LinkLibraries m_PrevLinkedLibraries; + LinkLibraries m_OriginalLinkLibraries; bool m_LinkLibrariesAnalyzed; bool m_LinkDirectoriesComputed; std::vector m_Frameworks; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 42a2d2d6d..8865e1029 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -22,6 +22,7 @@ #include "cmCommands.h" #include "cmCommand.h" #include "cmFileTimeComparison.h" +#include "cmGeneratedFileStream.h" #if defined(CMAKE_BUILD_WITH_CMAKE) # include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback. @@ -398,6 +399,17 @@ void cmake::SetArgs(const std::vector& args) // skip for now i++; } + else if(arg.find("--graphviz=",0) == 0) + { + std::string path = arg.substr(strlen("--graphviz=")); + path = cmSystemTools::CollapseFullPath(path.c_str()); + cmSystemTools::ConvertToUnixSlashes(path); + m_GraphVizFile = path; + if ( m_GraphVizFile.empty() ) + { + cmSystemTools::Error("No file specified for --graphviz"); + } + } else if(arg.find("--debug-trycompile",0) == 0) { std::cout << "debug trycompile on\n"; @@ -1383,6 +1395,11 @@ int cmake::Configure() { this->m_CacheManager->SaveCache(this->GetHomeOutputDirectory()); } + if ( !m_GraphVizFile.empty() ) + { + std::cout << "Generate graphviz: " << m_GraphVizFile << std::endl; + this->GenerateGraphViz(m_GraphVizFile.c_str()); + } if(cmSystemTools::GetErrorOccuredFlag()) { return -1; @@ -1980,3 +1997,163 @@ const char* cmake::GetCPackCommand() return m_CPackCommand.c_str(); } +void cmake::GenerateGraphViz(const char* fileName) +{ + cmGeneratedFileStream str(fileName); + if ( !str ) + { + return; + } + str << "digraph G {" << std::endl; + str << "node [" << std::endl; + str << " fontsize = \"12\"" << std::endl; + str << "];" << std::endl; + + cmGlobalGenerator* gg = this->GetGlobalGenerator(); + std::vector localGenerators; + gg->GetLocalGenerators(localGenerators); + std::vector::iterator lit; + // for target deps + // 1 - cmake target + // 2 - external target + // 0 - no deps + std::map targetDeps; + std::map targetPtrs; + std::map targetNamesNodes; + char tgtName[100]; + int cnt = 0; + // First pass get the list of all cmake targets + for ( lit = localGenerators.begin(); lit != localGenerators.end(); ++ lit ) + { + cmTargets* targets = &((*lit)->GetMakefile()->GetTargets()); + cmTargets::iterator tit; + for ( tit = targets->begin(); tit != targets->end(); ++ tit ) + { + //std::cout << "Found target: " << tit->first.c_str() << std::endl; + sprintf(tgtName, "node%d", cnt++); + targetNamesNodes[tit->first.c_str()] = tgtName; + targetPtrs[tit->first.c_str()] = &tit->second; + //str << " \"" << tgtName << "\" [ label=\"" << tit->first.c_str() << "\" shape=\"box\"];" << std::endl; + } + } + // Ok, now find all the stuff we link to that is not in cmake + for ( lit = localGenerators.begin(); lit != localGenerators.end(); ++ lit ) + { + cmTargets* targets = &((*lit)->GetMakefile()->GetTargets()); + cmTargets::iterator tit; + for ( tit = targets->begin(); tit != targets->end(); ++ tit ) + { + const cmTarget::LinkLibraries* ll = &(tit->second.GetOriginalLinkLibraries()); + cmTarget::LinkLibraries::const_iterator llit; + if ( ll->size() > 0 ) + { + targetDeps[tit->first.c_str()] = 1; + } + for ( llit = ll->begin(); llit != ll->end(); ++ llit ) + { + const char* libName = llit->first.c_str(); + std::map::iterator tarIt = targetNamesNodes.find(libName); + if ( tarIt == targetNamesNodes.end() ) + { + sprintf(tgtName, "node%d", cnt++); + targetDeps[libName] = 2; + targetNamesNodes[libName] = tgtName; + //str << " \"" << tgtName << "\" [ label=\"" << libName << "\" shape=\"ellipse\"];" << std::endl; + } + else + { + std::map::iterator depIt = targetDeps.find(libName); + if ( depIt == targetDeps.end() ) + { + targetDeps[libName] = 1; + } + } + } + } + } + + // Write out nodes + std::map::iterator depIt; + for ( depIt = targetDeps.begin(); depIt != targetDeps.end(); ++ depIt ) + { + const char* tgtName = depIt->first.c_str(); + std::map::iterator tarIt = targetNamesNodes.find(tgtName); + if ( tarIt == targetNamesNodes.end() ) + { + // We should not be here. + std::cout << "Cannot find library: " << tgtName << " even though it was added in the previous pass" << std::endl; + abort(); + } + + str << " \"" << tarIt->second.c_str() << "\" [ label=\"" << tgtName << "\" shape=\""; + if ( depIt->second == 1 ) + { + std::map::iterator tarTypeIt= targetPtrs.find(tgtName); + if ( tarTypeIt == targetNamesNodes.end() ) + { + // We should not be here. + std::cout << "Cannot find library: " << tgtName << " even though it was added in the previous pass" << std::endl; + abort(); + } + cmTarget* tg = tarTypeIt->second; + switch ( tg->GetType() ) + { + case cmTarget::EXECUTABLE: + str << "house"; + break; + case cmTarget::STATIC_LIBRARY: + str << "diamond"; + break; + case cmTarget::SHARED_LIBRARY: + str << "polygon"; + break; + case cmTarget::MODULE_LIBRARY: + str << "octagon"; + break; + default: + str << "box"; + } + } + else + { + str << "ellipse"; + } + str << "\"];" << std::endl; + } + + // Now generate the connectivity + for ( lit = localGenerators.begin(); lit != localGenerators.end(); ++ lit ) + { + cmTargets* targets = &((*lit)->GetMakefile()->GetTargets()); + cmTargets::iterator tit; + for ( tit = targets->begin(); tit != targets->end(); ++ tit ) + { + std::map::iterator depIt = targetDeps.find(tit->first.c_str()); + if ( depIt == targetDeps.end() ) + { + continue; + } + std::map::iterator cmakeTarIt = targetNamesNodes.find(tit->first.c_str()); + const cmTarget::LinkLibraries* ll = &(tit->second.GetOriginalLinkLibraries()); + cmTarget::LinkLibraries::const_iterator llit; + for ( llit = ll->begin(); llit != ll->end(); ++ llit ) + { + const char* libName = llit->first.c_str(); + std::map::iterator tarIt = targetNamesNodes.find(libName); + if ( tarIt == targetNamesNodes.end() ) + { + // We should not be here. + std::cout << "Cannot find library: " << libName << " even though it was added in the previous pass" << std::endl; + abort(); + } + str << " \"" << cmakeTarIt->second.c_str() << "\" -> \"" << tarIt->second.c_str() << "\"" << std::endl; + } + } + } + + // TODO: Use dotted or something for external libraries + //str << " \"node0\":f4 -> \"node12\"[color=\"#0000ff\" style=dotted]" << std::endl; + // + str << "}" << std::endl; +} + diff --git a/Source/cmake.h b/Source/cmake.h index e74b88571..8c4eaaf43 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -311,6 +311,8 @@ protected: //! Make sure all commands are what they say they are and there is no macros. void CleanupCommandsAndMacros(); + + void GenerateGraphViz(const char* fileName); cmVariableWatch* m_VariableWatch; @@ -329,6 +331,7 @@ private: bool m_ClearBuildSystem; bool m_DebugTryCompile; cmFileTimeComparison* m_FileComparison; + std::string m_GraphVizFile; void UpdateConversionPathTable(); };