Browse Source

ENH: Add support for exporting graphviz of the project dependencies

Andy Cedilnik 19 years ago
parent
commit
bc4e5581ee
4 changed files with 187 additions and 0 deletions
  1. 5 0
      Source/cmTarget.cxx
  2. 2 0
      Source/cmTarget.h
  3. 177 0
      Source/cmake.cxx
  4. 3 0
      Source/cmake.h

+ 5 - 0
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();

+ 2 - 0
Source/cmTarget.h

@@ -95,6 +95,7 @@ public:
   enum LinkLibraryType {GENERAL, DEBUG, OPTIMIZED};
   typedef std::vector<std::pair<std::string,LinkLibraryType> > 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<cmSourceFile*> m_SourceFiles;
   LinkLibraries m_LinkLibraries;
   LinkLibraries m_PrevLinkedLibraries;
+  LinkLibraries m_OriginalLinkLibraries;
   bool m_LinkLibrariesAnalyzed;
   bool m_LinkDirectoriesComputed;
   std::vector<std::string> m_Frameworks;

+ 177 - 0
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<std::string>& 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<cmLocalGenerator*> localGenerators;
+  gg->GetLocalGenerators(localGenerators);
+  std::vector<cmLocalGenerator*>::iterator lit;
+  // for target deps
+  // 1 - cmake target
+  // 2 - external target
+  // 0 - no deps
+  std::map<cmStdString, int> targetDeps;
+  std::map<cmStdString, cmTarget*> targetPtrs;
+  std::map<cmStdString, cmStdString> 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<cmStdString, cmStdString>::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<cmStdString, int>::iterator depIt = targetDeps.find(libName);
+          if ( depIt == targetDeps.end() )
+            {
+            targetDeps[libName] = 1;
+            }
+          }
+        }
+      }
+    }
+
+  // Write out nodes
+  std::map<cmStdString, int>::iterator depIt;
+  for ( depIt = targetDeps.begin(); depIt != targetDeps.end(); ++ depIt )
+    {
+    const char* tgtName = depIt->first.c_str();
+    std::map<cmStdString, cmStdString>::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<cmStdString, cmTarget*>::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<cmStdString, int>::iterator depIt = targetDeps.find(tit->first.c_str());
+      if ( depIt == targetDeps.end() )
+        {
+        continue;
+        }
+      std::map<cmStdString, cmStdString>::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<cmStdString, cmStdString>::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;
+}
+

+ 3 - 0
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();
 };