[cmake-commits] king committed CMakeLists.txt 1.392 1.393 cmComputeComponentGraph.cxx NONE 1.1 cmComputeComponentGraph.h NONE 1.1 cmComputeLinkDepends.cxx 1.7 1.8 cmComputeLinkDepends.h 1.3 1.4 cmComputeTargetDepends.cxx 1.1 1.2 cmComputeTargetDepends.h 1.1 1.2 cmGraphAdjacencyList.h NONE 1.1

cmake-commits at cmake.org cmake-commits at cmake.org
Thu Feb 7 16:14:07 EST 2008


Update of /cvsroot/CMake/CMake/Source
In directory public:/mounts/ram/cvs-serv27251/Source

Modified Files:
	CMakeLists.txt cmComputeLinkDepends.cxx cmComputeLinkDepends.h 
	cmComputeTargetDepends.cxx cmComputeTargetDepends.h 
Added Files:
	cmComputeComponentGraph.cxx cmComputeComponentGraph.h 
	cmGraphAdjacencyList.h 
Log Message:
ENH: Improve link line generation for static library cycles.

  - Move Tarjan algorithm from cmComputeTargetDepends
    into its own class cmComputeComponentGraph
  - Use cmComputeComponentGraph to identify the component DAG
    of link dependencies in cmComputeLinkDepends
  - Emit non-trivial component members more than once but always
    in a contiguous group on the link line


Index: cmComputeLinkDepends.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmComputeLinkDepends.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- cmComputeLinkDepends.h	31 Jan 2008 20:45:30 -0000	1.3
+++ cmComputeLinkDepends.h	7 Feb 2008 21:14:05 -0000	1.4
@@ -20,8 +20,11 @@
 #include "cmStandardIncludes.h"
 #include "cmTarget.h"
 
+#include "cmGraphAdjacencyList.h"
+
 #include <queue>
 
+class cmComputeComponentGraph;
 class cmGlobalGenerator;
 class cmLocalGenerator;
 class cmMakefile;
@@ -109,16 +112,18 @@
   void InferDependencies();
 
   // Ordering constraint graph adjacency list.
-  struct EntryConstraintSet: public std::set<int> {};
-  std::vector<EntryConstraintSet> EntryConstraintGraph;
+  typedef cmGraphNodeList NodeList;
+  typedef cmGraphAdjacencyList Graph;
+  Graph EntryConstraintGraph;
+  void CleanConstraintGraph();
   void DisplayConstraintGraph();
 
   // Ordering algorithm.
-  std::vector<int> EntryVisited;
-  std::set<int> EntryEmitted;
-  int WalkId;
   void OrderLinkEntires();
-  void VisitLinkEntry(unsigned int i);
+  std::vector<char> ComponentVisited;
+  void DisplayComponents(cmComputeComponentGraph const& ccg);
+  void VisitComponent(cmComputeComponentGraph const& ccg, unsigned int i);
+  void EmitComponent(NodeList const& nl);
   void DisplayFinalEntries();
 };
 

Index: cmComputeTargetDepends.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmComputeTargetDepends.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- cmComputeTargetDepends.h	6 Feb 2008 04:10:41 -0000	1.1
+++ cmComputeTargetDepends.h	7 Feb 2008 21:14:05 -0000	1.2
@@ -19,8 +19,11 @@
 
 #include "cmStandardIncludes.h"
 
+#include "cmGraphAdjacencyList.h"
+
 #include <stack>
 
+class cmComputeComponentGraph;
 class cmGlobalGenerator;
 class cmTarget;
 
@@ -46,7 +49,7 @@
   void CollectDepends();
   void CollectTargetDepends(int depender_index);
   void AddTargetDepend(int depender_index, const char* dependee_name);
-  void ComputeFinalDepends();
+  void ComputeFinalDepends(cmComputeComponentGraph const& ccg);
 
   cmGlobalGenerator* GlobalGenerator;
   bool DebugMode;
@@ -58,33 +61,16 @@
   // Represent the target dependency graph.  The entry at each
   // top-level index corresponds to a depender whose dependencies are
   // listed.
-  struct TargetDependList: public std::vector<int> {};
-  std::vector<TargetDependList> TargetDependGraph;
-  std::vector<TargetDependList> FinalDependGraph;
-  void DisplayGraph(std::vector<TargetDependList> const& graph,
-                    const char* name);
-
-  // Tarjan's algorithm.
-  struct TarjanEntry
-  {
-    int Root;
-    int Component;
-    int VisitIndex;
-  };
-  int TarjanWalkId;
-  std::vector<int> TarjanVisited;
-  std::vector<TarjanEntry> TarjanEntries;
-  std::stack<int> TarjanStack;
-  int TarjanIndex;
-  void Tarjan();
-  void TarjanVisit(int i);
+  typedef cmGraphNodeList NodeList;
+  typedef cmGraphAdjacencyList Graph;
+  Graph InitialGraph;
+  Graph FinalGraph;
+  void DisplayGraph(Graph const& graph, const char* name);
 
-  // Connected components.
-  struct ComponentList: public std::vector<int> {};
-  std::vector<ComponentList> Components;
-  void DisplayComponents();
-  bool CheckComponents();
-  void ComplainAboutBadComponent(int c);
+  // Deal with connected components.
+  void DisplayComponents(cmComputeComponentGraph const& ccg);
+  bool CheckComponents(cmComputeComponentGraph const& ccg);
+  void ComplainAboutBadComponent(cmComputeComponentGraph const& ccg, int c);
 };
 
 #endif

Index: cmComputeLinkDepends.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmComputeLinkDepends.cxx,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- cmComputeLinkDepends.cxx	1 Feb 2008 13:56:00 -0000	1.7
+++ cmComputeLinkDepends.cxx	7 Feb 2008 21:14:05 -0000	1.8
@@ -16,6 +16,7 @@
 =========================================================================*/
 #include "cmComputeLinkDepends.h"
 
+#include "cmComputeComponentGraph.h"
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
@@ -96,31 +97,47 @@
   B: intersect( {Y,C}   , {}    ) = {}    ; infer no edges
   C: intersect( {}      , {B}   ) = {}    ; infer no edges
 
+------------------------------------------------------------------------------
+
 Once the complete graph is formed from all known and inferred
-dependencies, we walk the graph with a series of depth-first-searches
-in order to emit link items.  When visiting a node all edges are
-followed first because the neighbors must precede the item.  Once
-neighbors across all edges have been emitted it is safe to emit the
-current node.
+dependencies we must use it to produce a valid link line.  If the
+dependency graph were known to be acyclic a simple depth-first-search
+would produce a correct link line.  Unfortunately we cannot make this
+assumption so the following technique is used.
 
-If a single DFS returns to a node it previously reached then a cycle
-is present.  Cyclic link dependencies are resolved simply by repeating
-one of the cycle entries at the beginning and end of the cycle
-members.  For example, the graph
+The original graph is converted to a directed acyclic graph in which
+each node corresponds to a strongly connected component of the
+original graph.  For example, the dependency graph
 
-  A <- B  ,  B <- C  ,  C <- A
+  X <- A <- B <- C <- A <- Y
 
-can be satisfied with the link item list
+contains strongly connected components {X}, {A,B,C}, and {Y}.  The
+implied directed acyclic graph (DAG) is
 
-  A B C A
+  {X} <- {A,B,C} <- {Y}
 
-When a node is reached a second time during the same DFS we make sure
-its item has been emitted and then skip following its outgoing edges
-again.
+The final list of link items is constructed by a series of
+depth-first-searches through this DAG of components.  When visiting a
+component all outgoing edges are followed first because the neighbors
+must precede it.  Once neighbors across all edges have been emitted it
+is safe to emit the current component.
+
+Trivial components (those with one item) are handled simply by
+emitting the item.  Non-trivial components (those with more than one
+item) are assumed to consist only of static libraries that may be
+safely repeated on the link line.  We emit members of the component
+multiple times (see code below for details).  The final link line for
+the example graph might be
+
+  X A B C A B C Y
+
+------------------------------------------------------------------------------
 
 The initial exploration of dependencies using a BFS associates an
 integer index with each link item.  When the graph is built outgoing
-edges are sorted by this index.  This preserves the original link
+edges are sorted by this index.
+
+This preserves the original link
 order as much as possible subject to the dependencies.
 
 After the initial exploration of the link interface tree, any
@@ -190,6 +207,9 @@
   // Infer dependencies of targets for which they were not known.
   this->InferDependencies();
 
+  // Cleanup the constraint graph.
+  this->CleanConstraintGraph();
+
   // Display the constraint graph.
   if(this->DebugMode)
     {
@@ -223,7 +243,7 @@
     lei = this->LinkEntryIndex.insert(index_entry).first;
   this->EntryList.push_back(LinkEntry());
   this->InferredDependSets.push_back(0);
-  this->EntryConstraintGraph.push_back(EntryConstraintSet());
+  this->EntryConstraintGraph.push_back(NodeList());
   return lei;
 }
 
@@ -354,7 +374,7 @@
 
   // This shared library dependency must be preceded by the item that
   // listed it.
-  this->EntryConstraintGraph[index].insert(dep.DependerIndex);
+  this->EntryConstraintGraph[index].push_back(dep.DependerIndex);
 
   // Target items may have their own dependencies.
   if(entry.Target)
@@ -469,7 +489,7 @@
     // The depender must come before the dependee.
     if(depender_index >= 0)
       {
-      this->EntryConstraintGraph[dependee_index].insert(depender_index);
+      this->EntryConstraintGraph[dependee_index].push_back(depender_index);
       }
 
     // Update the inferred dependencies for earlier items.
@@ -531,22 +551,37 @@
     for(DependSet::const_iterator j = common.begin(); j != common.end(); ++j)
       {
       int dependee_index = *j;
-      this->EntryConstraintGraph[dependee_index].insert(depender_index);
+      this->EntryConstraintGraph[dependee_index].push_back(depender_index);
       }
     }
 }
 
 //----------------------------------------------------------------------------
+void cmComputeLinkDepends::CleanConstraintGraph()
+{
+  for(Graph::iterator i = this->EntryConstraintGraph.begin();
+      i != this->EntryConstraintGraph.end(); ++i)
+    {
+    // Sort the outgoing edges for each graph node so that the
+    // original order will be preserved as much as possible.
+    cmsys_stl::sort(i->begin(), i->end());
+
+    // Make the edge list unique.
+    NodeList::iterator last = cmsys_stl::unique(i->begin(), i->end());
+    i->erase(last, i->end());
+    }
+}
+
+//----------------------------------------------------------------------------
 void cmComputeLinkDepends::DisplayConstraintGraph()
 {
-  // Display the conflict graph.
+  // Display the graph nodes and their edges.
   cmOStringStream e;
   for(unsigned int i=0; i < this->EntryConstraintGraph.size(); ++i)
     {
-    EntryConstraintSet const& cset = this->EntryConstraintGraph[i];
+    NodeList const& nl = this->EntryConstraintGraph[i];
     e << "item " << i << " is [" << this->EntryList[i].Item << "]\n";
-    for(EntryConstraintSet::const_iterator j = cset.begin();
-        j != cset.end(); ++j)
+    for(NodeList::const_iterator j = nl.begin(); j != nl.end(); ++j)
       {
       e << "  item " << *j << " must precede it\n";
       }
@@ -557,56 +592,107 @@
 //----------------------------------------------------------------------------
 void cmComputeLinkDepends::OrderLinkEntires()
 {
+  // Compute the DAG of strongly connected components.  The algorithm
+  // used by cmComputeComponentGraph should identify the components in
+  // the same order in which the items were originally discovered in
+  // the BFS.  This should preserve the original order when no
+  // constraints disallow it.
+  cmComputeComponentGraph ccg(this->EntryConstraintGraph);
+  Graph const& cgraph = ccg.GetComponentGraph();
+  if(this->DebugMode)
+    {
+    this->DisplayComponents(ccg);
+    }
+
   // Setup visit tracking.
-  this->EntryVisited.resize(this->EntryList.size(), 0);
-  this->WalkId = 0;
+  this->ComponentVisited.resize(cgraph.size(), 0);
 
-  // Start a DFS from every entry.
-  for(unsigned int i=0; i < this->EntryList.size(); ++i)
+  // The component graph is guaranteed to be acyclic.  Start a DFS
+  // from every entry.
+  for(unsigned int c=0; c < cgraph.size(); ++c)
     {
-    ++this->WalkId;
-    this->VisitLinkEntry(i);
+    this->VisitComponent(ccg, c);
     }
 }
 
 //----------------------------------------------------------------------------
-void cmComputeLinkDepends::VisitLinkEntry(unsigned int i)
+void
+cmComputeLinkDepends::DisplayComponents(cmComputeComponentGraph const& ccg)
 {
-  // Check if the node has already been visited.
-  if(this->EntryVisited[i])
+  fprintf(stderr, "The strongly connected components are:\n");
+  std::vector<NodeList> const& components = ccg.GetComponents();
+  for(unsigned int c=0; c < components.size(); ++c)
     {
-    if(this->EntryVisited[i] == this->WalkId)
+    fprintf(stderr, "Component (%u):\n", c);
+    NodeList const& nl = components[c];
+    for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
       {
-      // We have reached a node previously visited on this DFS.  There
-      // is a cycle.  In order to allow linking with cyclic
-      // dependencies we make sure the item is emitted but do not
-      // follow its outgoing edges again.
-      if(this->EntryEmitted.insert(i).second)
-        {
-        // The item has not been previously emitted so we emit it now.
-        // It will be emitted again when the stack unwinds back up to
-        // the beginning of the cycle.
-        this->FinalLinkEntries.push_back(this->EntryList[i]);
-        }
+      int i = *ni;
+      fprintf(stderr, "  item %d [%s]\n", i,
+              this->EntryList[i].Item.c_str());
       }
+    }
+  fprintf(stderr, "\n");
+}
+
+//----------------------------------------------------------------------------
+void
+cmComputeLinkDepends::VisitComponent(cmComputeComponentGraph const& ccg,
+                                     unsigned int c)
+{
+  // Check if the node has already been visited.
+  if(this->ComponentVisited[c])
+    {
     return;
     }
 
-  // We are now visiting this node so mark it.
-  this->EntryVisited[i] = this->WalkId;
+  // We are now visiting this component so mark it.
+  this->ComponentVisited[c] = 1;
 
-  // Visit the neighbors of the node first.
-  EntryConstraintSet const& cset = this->EntryConstraintGraph[i];
-  for(EntryConstraintSet::const_iterator j = cset.begin();
-      j != cset.end(); ++j)
+  // Visit the neighbors of the component first.
+  NodeList const& nl = ccg.GetComponentGraphEdges(c);
+  for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
     {
-    this->VisitLinkEntry(*j);
+    this->VisitComponent(ccg, *ni);
     }
 
   // Now that all items required to come before this one have been
-  // emmitted, emit this item.
-  this->EntryEmitted.insert(i);
-  this->FinalLinkEntries.push_back(this->EntryList[i]);
+  // emmitted, emit this component's items.
+  this->EmitComponent(ccg.GetComponent(c));
+}
+
+//----------------------------------------------------------------------------
+void cmComputeLinkDepends::EmitComponent(NodeList const& nl)
+{
+  assert(!nl.empty());
+
+  // Handle trivial components.
+  if(nl.size() == 1)
+    {
+    this->FinalLinkEntries.push_back(this->EntryList[nl[0]]);
+    return;
+    }
+
+  // This is a non-trivial strongly connected component of the
+  // original graph.  It consists of two or more libraries (archives)
+  // that mutually require objects from one another.  In the worst
+  // case we may have to repeat the list of libraries as many times as
+  // there are object files in the biggest archive.  For now we just
+  // list them twice.
+  //
+  // The list of items in the component has been sorted by the order
+  // of discovery in the original BFS of dependencies.  This has the
+  // advantage that the item directly linked by a target requiring
+  // this component will come first which minimizes the number of
+  // repeats needed.
+  for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
+    {
+    this->FinalLinkEntries.push_back(this->EntryList[*ni]);
+    }
+  for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
+    {
+    this->FinalLinkEntries.push_back(this->EntryList[*ni]);
+    }
 }
 
 //----------------------------------------------------------------------------

Index: cmComputeTargetDepends.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmComputeTargetDepends.cxx,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- cmComputeTargetDepends.cxx	6 Feb 2008 04:10:41 -0000	1.1
+++ cmComputeTargetDepends.cxx	7 Feb 2008 21:14:05 -0000	1.2
@@ -16,6 +16,7 @@
 =========================================================================*/
 #include "cmComputeTargetDepends.h"
 
+#include "cmComputeComponentGraph.h"
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
@@ -117,25 +118,25 @@
   this->CollectDepends();
   if(this->DebugMode)
     {
-    this->DisplayGraph(this->TargetDependGraph, "initial");
+    this->DisplayGraph(this->InitialGraph, "initial");
     }
 
   // Identify components.
-  this->Tarjan();
+  cmComputeComponentGraph ccg(this->InitialGraph);
   if(this->DebugMode)
     {
-    this->DisplayComponents();
+    this->DisplayComponents(ccg);
     }
-  if(!this->CheckComponents())
+  if(!this->CheckComponents(ccg))
     {
     return false;
     }
 
   // Compute the final dependency graph.
-  this->ComputeFinalDepends();
+  this->ComputeFinalDepends(ccg);
   if(this->DebugMode)
     {
-    this->DisplayGraph(this->FinalDependGraph, "final");
+    this->DisplayGraph(this->FinalGraph, "final");
     }
 
   return true;
@@ -153,11 +154,10 @@
   int i = tii->second;
 
   // Get its final dependencies.
-  TargetDependList const& tdl = this->FinalDependGraph[i];
-  for(TargetDependList::const_iterator di = tdl.begin();
-      di != tdl.end(); ++di)
+  NodeList const& nl = this->FinalGraph[i];
+  for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
     {
-    deps.insert(this->Targets[*di]);
+    deps.insert(this->Targets[*ni]);
     }
 }
 
@@ -184,7 +184,7 @@
 void cmComputeTargetDepends::CollectDepends()
 {
   // Allocate the dependency graph adjacency lists.
-  this->TargetDependGraph.resize(this->Targets.size());
+  this->InitialGraph.resize(this->Targets.size());
 
   // Compute each dependency list.
   for(unsigned int i=0; i < this->Targets.size(); ++i)
@@ -265,27 +265,24 @@
   int dependee_index = tii->second;
 
   // Add this entry to the dependency graph.
-  this->TargetDependGraph[depender_index].push_back(dependee_index);
+  this->InitialGraph[depender_index].push_back(dependee_index);
 }
 
 //----------------------------------------------------------------------------
 void
-cmComputeTargetDepends
-::DisplayGraph(std::vector<TargetDependList> const& graph,
-               const char* name)
+cmComputeTargetDepends::DisplayGraph(Graph const& graph, const char* name)
 {
   fprintf(stderr, "The %s target dependency graph is:\n", name);
   int n = static_cast<int>(graph.size());
   for(int depender_index = 0; depender_index < n; ++depender_index)
     {
-    TargetDependList const& tdl = graph[depender_index];
+    NodeList const& nl = graph[depender_index];
     cmTarget* depender = this->Targets[depender_index];
     fprintf(stderr, "target %d is [%s]\n",
             depender_index, depender->GetName());
-    for(TargetDependList::const_iterator di = tdl.begin();
-        di != tdl.end(); ++di)
+    for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
       {
-      int dependee_index = *di;
+      int dependee_index = *ni;
       cmTarget* dependee = this->Targets[dependee_index];
       fprintf(stderr, "  depends on target %d [%s]\n", dependee_index,
               dependee->GetName());
@@ -295,115 +292,20 @@
 }
 
 //----------------------------------------------------------------------------
-void cmComputeTargetDepends::Tarjan()
-{
-  int n = static_cast<int>(this->TargetDependGraph.size());
-  TarjanEntry entry = {0,-1,0};
-  this->TarjanEntries.resize(n, entry);
-  this->TarjanWalkId = 0;
-  this->TarjanVisited.resize(n, 0);
-  for(int i = 0; i < n; ++i)
-    {
-    // Start a new DFS from this node if it has never been visited.
-    if(!this->TarjanVisited[i])
-      {
-      assert(this->TarjanStack.empty());
-      ++this->TarjanWalkId;
-      this->TarjanIndex = 0;
-      this->TarjanVisit(i);
-      }
-    }
-}
-
-//----------------------------------------------------------------------------
-void cmComputeTargetDepends::TarjanVisit(int i)
-{
-  // We are now visiting this node.
-  this->TarjanVisited[i] = this->TarjanWalkId;
-
-  // Initialize the entry.
-  this->TarjanEntries[i].Root = i;
-  this->TarjanEntries[i].Component = -1;
-  this->TarjanEntries[i].VisitIndex = ++this->TarjanIndex;
-  this->TarjanStack.push(i);
-
-  // Follow outgoing edges.
-  TargetDependList const& tdl = this->TargetDependGraph[i];
-  for(TargetDependList::const_iterator di = tdl.begin();
-      di != tdl.end(); ++di)
-    {
-    int j = *di;
-
-    // Ignore edges to nodes that have been reached by a previous DFS
-    // walk.  Since we did not reach the current node from that walk
-    // it must not belong to the same component and it has already
-    // been assigned to a component.
-    if(this->TarjanVisited[j] > 0 &&
-       this->TarjanVisited[j] < this->TarjanWalkId)
-      {
-      continue;
-      }
-
-    // Visit the destination if it has not yet been visited.
-    if(!this->TarjanVisited[j])
-      {
-      this->TarjanVisit(j);
-      }
-
-    // If the destination has not yet been assigned to a component,
-    // check if it is a better potential root for the current object.
-    if(this->TarjanEntries[j].Component < 0)
-      {
-      if(this->TarjanEntries[this->TarjanEntries[j].Root].VisitIndex <
-         this->TarjanEntries[this->TarjanEntries[i].Root].VisitIndex)
-        {
-        this->TarjanEntries[i].Root = this->TarjanEntries[j].Root;
-        }
-      }
-    }
-
-  // Check if we have found a component.
-  if(this->TarjanEntries[i].Root == i)
-    {
-    // Yes.  Create it.
-    int c = static_cast<int>(this->Components.size());
-    this->Components.push_back(ComponentList());
-    ComponentList& component = this->Components[c];
-
-    // Populate the component list.
-    int j;
-    do
-      {
-      // Get the next member of the component.
-      j = this->TarjanStack.top();
-      this->TarjanStack.pop();
-
-      // Assign the member to the component.
-      this->TarjanEntries[j].Component = c;
-      this->TarjanEntries[j].Root = i;
-
-      // Store the node in its component.
-      component.push_back(j);
-      } while(j != i);
-
-    // Sort the component members for clarity.
-    std::sort(component.begin(), component.end());
-    }
-}
-
-//----------------------------------------------------------------------------
-void cmComputeTargetDepends::DisplayComponents()
+void
+cmComputeTargetDepends
+::DisplayComponents(cmComputeComponentGraph const& ccg)
 {
   fprintf(stderr, "The strongly connected components are:\n");
-  int n = static_cast<int>(this->Components.size());
+  std::vector<NodeList> const& components = ccg.GetComponents();
+  int n = static_cast<int>(components.size());
   for(int c = 0; c < n; ++c)
     {
-    ComponentList const& cl = this->Components[c];
+    NodeList const& nl = components[c];
     fprintf(stderr, "Component (%d):\n", c);
-    for(ComponentList::const_iterator ci = cl.begin();
-        ci != cl.end(); ++ci)
+    for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
       {
-      int i = *ci;
+      int i = *ni;
       fprintf(stderr, "  contains target %d [%s]\n",
               i, this->Targets[i]->GetName());
       }
@@ -412,28 +314,31 @@
 }
 
 //----------------------------------------------------------------------------
-bool cmComputeTargetDepends::CheckComponents()
+bool
+cmComputeTargetDepends
+::CheckComponents(cmComputeComponentGraph const& ccg)
 {
   // All non-trivial components should consist only of static
   // libraries.
-  int nc = static_cast<int>(this->Components.size());
+  std::vector<NodeList> const& components = ccg.GetComponents();
+  int nc = static_cast<int>(components.size());
   for(int c=0; c < nc; ++c)
     {
     // Get the current component.
-    ComponentList const& cl = this->Components[c];
+    NodeList const& nl = components[c];
 
     // Skip trivial components.
-    if(cl.size() < 2)
+    if(nl.size() < 2)
       {
       continue;
       }
 
     // Make sure the component is all STATIC_LIBRARY targets.
-    for(ComponentList::const_iterator ci = cl.begin(); ci != cl.end(); ++ci)
+    for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
       {
-      if(this->Targets[*ci]->GetType() != cmTarget::STATIC_LIBRARY)
+      if(this->Targets[*ni]->GetType() != cmTarget::STATIC_LIBRARY)
         {
-        this->ComplainAboutBadComponent(c);
+        this->ComplainAboutBadComponent(ccg, c);
         return false;
         }
       }
@@ -443,16 +348,17 @@
 
 //----------------------------------------------------------------------------
 void
-cmComputeTargetDepends::ComplainAboutBadComponent(int c)
+cmComputeTargetDepends
+::ComplainAboutBadComponent(cmComputeComponentGraph const& ccg, int c)
 {
-  // Get the bad component.
-  ComponentList const& cl = this->Components[c];
-
   // Construct the error message.
   cmOStringStream e;
   e << "The inter-target dependency graph contains the following "
     << "strongly connected component (cycle):\n";
-  for(ComponentList::const_iterator ci = cl.begin(); ci != cl.end(); ++ci)
+  std::vector<NodeList> const& components = ccg.GetComponents();
+  std::vector<int> const& cmap = ccg.GetComponentMap();
+  NodeList const& cl = components[c];
+  for(NodeList::const_iterator ci = cl.begin(); ci != cl.end(); ++ci)
     {
     // Get the depender.
     int i = *ci;
@@ -463,12 +369,11 @@
       << cmTarget::TargetTypeNames[depender->GetType()] << "\n";
 
     // List its dependencies that are inside the component.
-    TargetDependList const& tdl = this->TargetDependGraph[i];
-    for(TargetDependList::const_iterator di = tdl.begin();
-        di != tdl.end(); ++di)
+    NodeList const& nl = this->InitialGraph[i];
+    for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
       {
-      int j = *di;
-      if(this->TarjanEntries[j].Component == c)
+      int j = *ni;
+      if(cmap[j] == c)
         {
         cmTarget* dependee = this->Targets[j];
         e << "    depends on " << dependee->GetName() << "\n";
@@ -481,46 +386,45 @@
 }
 
 //----------------------------------------------------------------------------
-void cmComputeTargetDepends::ComputeFinalDepends()
+void
+cmComputeTargetDepends
+::ComputeFinalDepends(cmComputeComponentGraph const& ccg)
 {
-  int n = static_cast<int>(this->TargetDependGraph.size());
-  this->FinalDependGraph.resize(n);
+  // Get the component graph information.
+  std::vector<NodeList> const& components = ccg.GetComponents();
+  Graph const& cgraph = ccg.GetComponentGraph();
+
+  // Allocate the final graph.
+  this->FinalGraph.resize(0);
+  this->FinalGraph.resize(this->InitialGraph.size());
 
   // Convert inter-component edges to connect component tails to heads.
-  for(int i=0; i < n; ++i)
+  int n = static_cast<int>(cgraph.size());
+  for(int depender_component=0; depender_component < n; ++depender_component)
     {
-    int depender_component = this->TarjanEntries[i].Component;
-    int depender_component_tail =
-      this->Components[depender_component].back();
-
-    TargetDependList const& tdl = this->TargetDependGraph[i];
-    for(TargetDependList::const_iterator di = tdl.begin();
-        di != tdl.end(); ++di)
+    int depender_component_tail = components[depender_component].back();
+    NodeList const& nl = cgraph[depender_component];
+    for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
       {
-      int j = *di;
-      int dependee_component = this->TarjanEntries[j].Component;
-      int dependee_component_head =
-        this->Components[dependee_component].front();
-      if(depender_component != dependee_component)
-        {
-        this->FinalDependGraph[depender_component_tail]
-          .push_back(dependee_component_head);
-        }
+      int dependee_component = *ni;
+      int dependee_component_head = components[dependee_component].front();
+      this->FinalGraph[depender_component_tail]
+        .push_back(dependee_component_head);
       }
     }
 
   // Compute intra-component edges.
-  int nc = static_cast<int>(this->Components.size());
+  int nc = static_cast<int>(components.size());
   for(int c=0; c < nc; ++c)
     {
     // Within the component each target depends on that following it.
-    ComponentList const& cl = this->Components[c];
-    ComponentList::const_iterator ci = cl.begin();
-    int last_i = *ci;
-    for(++ci; ci != cl.end(); ++ci)
+    NodeList const& nl = components[c];
+    NodeList::const_iterator ni = nl.begin();
+    int last_i = *ni;
+    for(++ni; ni != nl.end(); ++ni)
       {
-      int i = *ci;
-      this->FinalDependGraph[last_i].push_back(i);
+      int i = *ni;
+      this->FinalGraph[last_i].push_back(i);
       last_i = i;
       }
     }

Index: CMakeLists.txt
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CMakeLists.txt,v
retrieving revision 1.392
retrieving revision 1.393
diff -u -d -r1.392 -r1.393
--- CMakeLists.txt	6 Feb 2008 14:35:02 -0000	1.392
+++ CMakeLists.txt	7 Feb 2008 21:14:05 -0000	1.393
@@ -87,6 +87,8 @@
   cmCommandArgumentLexer.cxx
   cmCommandArgumentParser.cxx
   cmCommandArgumentParserHelper.cxx
+  cmComputeComponentGraph.cxx
+  cmComputeComponentGraph.h
   cmComputeLinkDepends.cxx
   cmComputeLinkDepends.h
   cmComputeLinkInformation.cxx
@@ -138,6 +140,7 @@
   cmGlobalGenerator.h
   cmGlobalUnixMakefileGenerator3.cxx
   cmGlobalUnixMakefileGenerator3.h
+  cmGraphAdjacencyList.h
   cmInstallGenerator.h
   cmInstallGenerator.cxx
   cmInstallExportGenerator.cxx

--- NEW FILE: cmGraphAdjacencyList.h ---
/*=========================================================================

  Program:   CMake - Cross-Platform Makefile Generator
  Module:    $RCSfile: cmGraphAdjacencyList.h,v $
  Language:  C++
  Date:      $Date: 2008/02/07 21:14:05 $
  Version:   $Revision: 1.1 $

  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#ifndef cmGraphAdjacencyList_h
#define cmGraphAdjacencyList_h

#include "cmStandardIncludes.h"

struct cmGraphNodeList: public std::vector<int> {};
struct cmGraphAdjacencyList: public std::vector<cmGraphNodeList> {};

#endif

--- NEW FILE: cmComputeComponentGraph.cxx ---
/*=========================================================================

  Program:   CMake - Cross-Platform Makefile Generator
  Module:    $RCSfile: cmComputeComponentGraph.cxx,v $
  Language:  C++
  Date:      $Date: 2008/02/07 21:14:05 $
  Version:   $Revision: 1.1 $

  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "cmComputeComponentGraph.h"

#include <algorithm>

#include <assert.h>

//----------------------------------------------------------------------------
cmComputeComponentGraph::cmComputeComponentGraph(Graph const& input):
  InputGraph(input)
{
  // Identify components.
  this->Tarjan();

  // Compute the component graph.
  this->ComponentGraph.resize(0);
  this->ComponentGraph.resize(this->Components.size());
  this->TransferEdges();
}

//----------------------------------------------------------------------------
cmComputeComponentGraph::~cmComputeComponentGraph()
{
}

//----------------------------------------------------------------------------
void cmComputeComponentGraph::Tarjan()
{
  int n = static_cast<int>(this->InputGraph.size());
  TarjanEntry entry = {0,0};
  this->TarjanEntries.resize(0);
  this->TarjanEntries.resize(n, entry);
  this->TarjanComponents.resize(0);
  this->TarjanComponents.resize(n, -1);
  this->TarjanWalkId = 0;
  this->TarjanVisited.resize(0);
  this->TarjanVisited.resize(n, 0);
  for(int i = 0; i < n; ++i)
    {
    // Start a new DFS from this node if it has never been visited.
    if(!this->TarjanVisited[i])
      {
      assert(this->TarjanStack.empty());
      ++this->TarjanWalkId;
      this->TarjanIndex = 0;
      this->TarjanVisit(i);
      }
    }
}

//----------------------------------------------------------------------------
void cmComputeComponentGraph::TarjanVisit(int i)
{
  // We are now visiting this node.
  this->TarjanVisited[i] = this->TarjanWalkId;

  // Initialize the entry.
  this->TarjanEntries[i].Root = i;
  this->TarjanComponents[i] = -1;
  this->TarjanEntries[i].VisitIndex = ++this->TarjanIndex;
  this->TarjanStack.push(i);

  // Follow outgoing edges.
  NodeList const& nl = this->InputGraph[i];
  for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
    {
    int j = *ni;

    // Ignore edges to nodes that have been reached by a previous DFS
    // walk.  Since we did not reach the current node from that walk
    // it must not belong to the same component and it has already
    // been assigned to a component.
    if(this->TarjanVisited[j] > 0 &&
       this->TarjanVisited[j] < this->TarjanWalkId)
      {
      continue;
      }

    // Visit the destination if it has not yet been visited.
    if(!this->TarjanVisited[j])
      {
      this->TarjanVisit(j);
      }

    // If the destination has not yet been assigned to a component,
    // check if it has a better root for the current object.
    if(this->TarjanComponents[j] < 0)
      {
      if(this->TarjanEntries[this->TarjanEntries[j].Root].VisitIndex <
         this->TarjanEntries[this->TarjanEntries[i].Root].VisitIndex)
        {
        this->TarjanEntries[i].Root = this->TarjanEntries[j].Root;
        }
      }
    }

  // Check if we have found a component.
  if(this->TarjanEntries[i].Root == i)
    {
    // Yes.  Create it.
    int c = static_cast<int>(this->Components.size());
    this->Components.push_back(NodeList());
    NodeList& component = this->Components[c];

    // Populate the component list.
    int j;
    do
      {
      // Get the next member of the component.
      j = this->TarjanStack.top();
      this->TarjanStack.pop();

      // Assign the member to the component.
      this->TarjanComponents[j] = c;
      this->TarjanEntries[j].Root = i;

      // Store the node in its component.
      component.push_back(j);
      } while(j != i);

    // Sort the component members for clarity.
    std::sort(component.begin(), component.end());
    }
}

//----------------------------------------------------------------------------
void cmComputeComponentGraph::TransferEdges()
{
  // Map inter-component edges in the original graph to edges in the
  // component graph.
  int n = static_cast<int>(this->InputGraph.size());
  for(int i=0; i < n; ++i)
    {
    int i_component = this->TarjanComponents[i];
    NodeList const& nl = this->InputGraph[i];
    for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
      {
      int j = *ni;
      int j_component = this->TarjanComponents[j];
      if(i_component != j_component)
        {
        this->ComponentGraph[i_component].push_back(j_component);
        }
      }
    }
}

--- NEW FILE: cmComputeComponentGraph.h ---
/*=========================================================================

  Program:   CMake - Cross-Platform Makefile Generator
  Module:    $RCSfile: cmComputeComponentGraph.h,v $
  Language:  C++
  Date:      $Date: 2008/02/07 21:14:05 $
  Version:   $Revision: 1.1 $

  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#ifndef cmComputeComponentGraph_h
#define cmComputeComponentGraph_h

#include "cmStandardIncludes.h"

#include "cmGraphAdjacencyList.h"

#include <stack>

/** \class cmComputeComponentGraph
 * \brief Analyze a graph to determine strongly connected components.
 *
 * Convert a directed graph into a directed acyclic graph whose nodes
 * correspond to strongly connected components of the original graph.
 *
 * We use Tarjan's algorithm to enumerate the components efficiently.
 * An advantage of this approach is that the components are identified
 * in a topologically sorted order.
 */
class cmComputeComponentGraph
{
public:
  // Represent the graph with an adjacency list.
  typedef cmGraphNodeList NodeList;
  typedef cmGraphAdjacencyList Graph;

  cmComputeComponentGraph(Graph const& input);
  ~cmComputeComponentGraph();

  /** Get the adjacency list of the component graph.  */
  Graph const& GetComponentGraph() const
    { return this->ComponentGraph; }
  NodeList const& GetComponentGraphEdges(int c) const
    { return this->ComponentGraph[c]; }

  /** Get map from component index to original node indices.  */
  std::vector<NodeList> const& GetComponents() const
    { return this->Components; }
  NodeList const& GetComponent(int c) const
    { return this->Components[c]; }

  /** Get map from original node index to component index.  */
  std::vector<int> const& GetComponentMap() const
    { return this->TarjanComponents; }

private:
  void TransferEdges();

  Graph const& InputGraph;
  Graph ComponentGraph;

  // Tarjan's algorithm.
  struct TarjanEntry
  {
    int Root;
    int VisitIndex;
  };
  int TarjanWalkId;
  std::vector<int> TarjanVisited;
  std::vector<int> TarjanComponents;
  std::vector<TarjanEntry> TarjanEntries;
  std::stack<int> TarjanStack;
  int TarjanIndex;
  void Tarjan();
  void TarjanVisit(int i);

  // Connected components.
  std::vector<NodeList> Components;
};

#endif



More information about the Cmake-commits mailing list