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
This commit is contained in:
Brad King 2008-02-07 16:14:05 -05:00
parent 0bfad2946d
commit 4987e17f46
9 changed files with 515 additions and 257 deletions

View File

@ -87,6 +87,8 @@ SET(SRCS
cmCommandArgumentLexer.cxx cmCommandArgumentLexer.cxx
cmCommandArgumentParser.cxx cmCommandArgumentParser.cxx
cmCommandArgumentParserHelper.cxx cmCommandArgumentParserHelper.cxx
cmComputeComponentGraph.cxx
cmComputeComponentGraph.h
cmComputeLinkDepends.cxx cmComputeLinkDepends.cxx
cmComputeLinkDepends.h cmComputeLinkDepends.h
cmComputeLinkInformation.cxx cmComputeLinkInformation.cxx
@ -138,6 +140,7 @@ SET(SRCS
cmGlobalGenerator.h cmGlobalGenerator.h
cmGlobalUnixMakefileGenerator3.cxx cmGlobalUnixMakefileGenerator3.cxx
cmGlobalUnixMakefileGenerator3.h cmGlobalUnixMakefileGenerator3.h
cmGraphAdjacencyList.h
cmInstallGenerator.h cmInstallGenerator.h
cmInstallGenerator.cxx cmInstallGenerator.cxx
cmInstallExportGenerator.cxx cmInstallExportGenerator.cxx

View File

@ -0,0 +1,161 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
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);
}
}
}
}

View File

@ -0,0 +1,87 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
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

View File

@ -16,6 +16,7 @@
=========================================================================*/ =========================================================================*/
#include "cmComputeLinkDepends.h" #include "cmComputeLinkDepends.h"
#include "cmComputeComponentGraph.h"
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h" #include "cmLocalGenerator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
@ -96,31 +97,47 @@ For the unknown items, we infer dependencies by looking at the
B: intersect( {Y,C} , {} ) = {} ; infer no edges B: intersect( {Y,C} , {} ) = {} ; infer no edges
C: intersect( {} , {B} ) = {} ; infer no edges C: intersect( {} , {B} ) = {} ; infer no edges
------------------------------------------------------------------------------
Once the complete graph is formed from all known and inferred Once the complete graph is formed from all known and inferred
dependencies, we walk the graph with a series of depth-first-searches dependencies we must use it to produce a valid link line. If the
in order to emit link items. When visiting a node all edges are dependency graph were known to be acyclic a simple depth-first-search
followed first because the neighbors must precede the item. Once would produce a correct link line. Unfortunately we cannot make this
neighbors across all edges have been emitted it is safe to emit the assumption so the following technique is used.
current node.
If a single DFS returns to a node it previously reached then a cycle The original graph is converted to a directed acyclic graph in which
is present. Cyclic link dependencies are resolved simply by repeating each node corresponds to a strongly connected component of the
one of the cycle entries at the beginning and end of the cycle original graph. For example, the dependency graph
members. For example, the 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 The final list of link items is constructed by a series of
its item has been emitted and then skip following its outgoing edges depth-first-searches through this DAG of components. When visiting a
again. 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 The initial exploration of dependencies using a BFS associates an
integer index with each link item. When the graph is built outgoing 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. order as much as possible subject to the dependencies.
After the initial exploration of the link interface tree, any After the initial exploration of the link interface tree, any
@ -190,6 +207,9 @@ cmComputeLinkDepends::Compute()
// Infer dependencies of targets for which they were not known. // Infer dependencies of targets for which they were not known.
this->InferDependencies(); this->InferDependencies();
// Cleanup the constraint graph.
this->CleanConstraintGraph();
// Display the constraint graph. // Display the constraint graph.
if(this->DebugMode) if(this->DebugMode)
{ {
@ -223,7 +243,7 @@ cmComputeLinkDepends::AllocateLinkEntry(std::string const& item)
lei = this->LinkEntryIndex.insert(index_entry).first; lei = this->LinkEntryIndex.insert(index_entry).first;
this->EntryList.push_back(LinkEntry()); this->EntryList.push_back(LinkEntry());
this->InferredDependSets.push_back(0); this->InferredDependSets.push_back(0);
this->EntryConstraintGraph.push_back(EntryConstraintSet()); this->EntryConstraintGraph.push_back(NodeList());
return lei; return lei;
} }
@ -354,7 +374,7 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
// This shared library dependency must be preceded by the item that // This shared library dependency must be preceded by the item that
// listed it. // listed it.
this->EntryConstraintGraph[index].insert(dep.DependerIndex); this->EntryConstraintGraph[index].push_back(dep.DependerIndex);
// Target items may have their own dependencies. // Target items may have their own dependencies.
if(entry.Target) if(entry.Target)
@ -469,7 +489,7 @@ cmComputeLinkDepends::AddLinkEntries(int depender_index,
// The depender must come before the dependee. // The depender must come before the dependee.
if(depender_index >= 0) 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. // Update the inferred dependencies for earlier items.
@ -531,22 +551,37 @@ void cmComputeLinkDepends::InferDependencies()
for(DependSet::const_iterator j = common.begin(); j != common.end(); ++j) for(DependSet::const_iterator j = common.begin(); j != common.end(); ++j)
{ {
int dependee_index = *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() void cmComputeLinkDepends::DisplayConstraintGraph()
{ {
// Display the conflict graph. // Display the graph nodes and their edges.
cmOStringStream e; cmOStringStream e;
for(unsigned int i=0; i < this->EntryConstraintGraph.size(); ++i) 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"; e << "item " << i << " is [" << this->EntryList[i].Item << "]\n";
for(EntryConstraintSet::const_iterator j = cset.begin(); for(NodeList::const_iterator j = nl.begin(); j != nl.end(); ++j)
j != cset.end(); ++j)
{ {
e << " item " << *j << " must precede it\n"; e << " item " << *j << " must precede it\n";
} }
@ -557,56 +592,107 @@ void cmComputeLinkDepends::DisplayConstraintGraph()
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeLinkDepends::OrderLinkEntires() void cmComputeLinkDepends::OrderLinkEntires()
{ {
// Setup visit tracking. // Compute the DAG of strongly connected components. The algorithm
this->EntryVisited.resize(this->EntryList.size(), 0); // used by cmComputeComponentGraph should identify the components in
this->WalkId = 0; // the same order in which the items were originally discovered in
// the BFS. This should preserve the original order when no
// Start a DFS from every entry. // constraints disallow it.
for(unsigned int i=0; i < this->EntryList.size(); ++i) cmComputeComponentGraph ccg(this->EntryConstraintGraph);
Graph const& cgraph = ccg.GetComponentGraph();
if(this->DebugMode)
{ {
++this->WalkId; this->DisplayComponents(ccg);
this->VisitLinkEntry(i); }
// Setup visit tracking.
this->ComponentVisited.resize(cgraph.size(), 0);
// The component graph is guaranteed to be acyclic. Start a DFS
// from every entry.
for(unsigned int c=0; c < cgraph.size(); ++c)
{
this->VisitComponent(ccg, c);
} }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeLinkDepends::VisitLinkEntry(unsigned int i) void
cmComputeLinkDepends::DisplayComponents(cmComputeComponentGraph const& ccg)
{
fprintf(stderr, "The strongly connected components are:\n");
std::vector<NodeList> const& components = ccg.GetComponents();
for(unsigned int c=0; c < components.size(); ++c)
{
fprintf(stderr, "Component (%u):\n", c);
NodeList const& nl = components[c];
for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
{
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. // Check if the node has already been visited.
if(this->EntryVisited[i]) if(this->ComponentVisited[c])
{ {
if(this->EntryVisited[i] == this->WalkId)
{
// 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]);
}
}
return; return;
} }
// We are now visiting this node so mark it. // We are now visiting this component so mark it.
this->EntryVisited[i] = this->WalkId; this->ComponentVisited[c] = 1;
// Visit the neighbors of the node first. // Visit the neighbors of the component first.
EntryConstraintSet const& cset = this->EntryConstraintGraph[i]; NodeList const& nl = ccg.GetComponentGraphEdges(c);
for(EntryConstraintSet::const_iterator j = cset.begin(); for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
j != cset.end(); ++j)
{ {
this->VisitLinkEntry(*j); this->VisitComponent(ccg, *ni);
} }
// Now that all items required to come before this one have been // Now that all items required to come before this one have been
// emmitted, emit this item. // emmitted, emit this component's items.
this->EntryEmitted.insert(i); this->EmitComponent(ccg.GetComponent(c));
this->FinalLinkEntries.push_back(this->EntryList[i]); }
//----------------------------------------------------------------------------
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]);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -20,8 +20,11 @@
#include "cmStandardIncludes.h" #include "cmStandardIncludes.h"
#include "cmTarget.h" #include "cmTarget.h"
#include "cmGraphAdjacencyList.h"
#include <queue> #include <queue>
class cmComputeComponentGraph;
class cmGlobalGenerator; class cmGlobalGenerator;
class cmLocalGenerator; class cmLocalGenerator;
class cmMakefile; class cmMakefile;
@ -109,16 +112,18 @@ private:
void InferDependencies(); void InferDependencies();
// Ordering constraint graph adjacency list. // Ordering constraint graph adjacency list.
struct EntryConstraintSet: public std::set<int> {}; typedef cmGraphNodeList NodeList;
std::vector<EntryConstraintSet> EntryConstraintGraph; typedef cmGraphAdjacencyList Graph;
Graph EntryConstraintGraph;
void CleanConstraintGraph();
void DisplayConstraintGraph(); void DisplayConstraintGraph();
// Ordering algorithm. // Ordering algorithm.
std::vector<int> EntryVisited;
std::set<int> EntryEmitted;
int WalkId;
void OrderLinkEntires(); 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(); void DisplayFinalEntries();
}; };

View File

@ -16,6 +16,7 @@
=========================================================================*/ =========================================================================*/
#include "cmComputeTargetDepends.h" #include "cmComputeTargetDepends.h"
#include "cmComputeComponentGraph.h"
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h" #include "cmLocalGenerator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
@ -117,25 +118,25 @@ bool cmComputeTargetDepends::Compute()
this->CollectDepends(); this->CollectDepends();
if(this->DebugMode) if(this->DebugMode)
{ {
this->DisplayGraph(this->TargetDependGraph, "initial"); this->DisplayGraph(this->InitialGraph, "initial");
} }
// Identify components. // Identify components.
this->Tarjan(); cmComputeComponentGraph ccg(this->InitialGraph);
if(this->DebugMode) if(this->DebugMode)
{ {
this->DisplayComponents(); this->DisplayComponents(ccg);
} }
if(!this->CheckComponents()) if(!this->CheckComponents(ccg))
{ {
return false; return false;
} }
// Compute the final dependency graph. // Compute the final dependency graph.
this->ComputeFinalDepends(); this->ComputeFinalDepends(ccg);
if(this->DebugMode) if(this->DebugMode)
{ {
this->DisplayGraph(this->FinalDependGraph, "final"); this->DisplayGraph(this->FinalGraph, "final");
} }
return true; return true;
@ -153,11 +154,10 @@ cmComputeTargetDepends::GetTargetDirectDepends(cmTarget* t,
int i = tii->second; int i = tii->second;
// Get its final dependencies. // Get its final dependencies.
TargetDependList const& tdl = this->FinalDependGraph[i]; NodeList const& nl = this->FinalGraph[i];
for(TargetDependList::const_iterator di = tdl.begin(); for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
di != tdl.end(); ++di)
{ {
deps.insert(this->Targets[*di]); deps.insert(this->Targets[*ni]);
} }
} }
@ -184,7 +184,7 @@ void cmComputeTargetDepends::CollectTargets()
void cmComputeTargetDepends::CollectDepends() void cmComputeTargetDepends::CollectDepends()
{ {
// Allocate the dependency graph adjacency lists. // Allocate the dependency graph adjacency lists.
this->TargetDependGraph.resize(this->Targets.size()); this->InitialGraph.resize(this->Targets.size());
// Compute each dependency list. // Compute each dependency list.
for(unsigned int i=0; i < this->Targets.size(); ++i) for(unsigned int i=0; i < this->Targets.size(); ++i)
@ -265,27 +265,24 @@ void cmComputeTargetDepends::AddTargetDepend(int depender_index,
int dependee_index = tii->second; int dependee_index = tii->second;
// Add this entry to the dependency graph. // Add this entry to the dependency graph.
this->TargetDependGraph[depender_index].push_back(dependee_index); this->InitialGraph[depender_index].push_back(dependee_index);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmComputeTargetDepends cmComputeTargetDepends::DisplayGraph(Graph const& graph, const char* name)
::DisplayGraph(std::vector<TargetDependList> const& graph,
const char* name)
{ {
fprintf(stderr, "The %s target dependency graph is:\n", name); fprintf(stderr, "The %s target dependency graph is:\n", name);
int n = static_cast<int>(graph.size()); int n = static_cast<int>(graph.size());
for(int depender_index = 0; depender_index < n; ++depender_index) 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]; cmTarget* depender = this->Targets[depender_index];
fprintf(stderr, "target %d is [%s]\n", fprintf(stderr, "target %d is [%s]\n",
depender_index, depender->GetName()); depender_index, depender->GetName());
for(TargetDependList::const_iterator di = tdl.begin(); for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
di != tdl.end(); ++di)
{ {
int dependee_index = *di; int dependee_index = *ni;
cmTarget* dependee = this->Targets[dependee_index]; cmTarget* dependee = this->Targets[dependee_index];
fprintf(stderr, " depends on target %d [%s]\n", dependee_index, fprintf(stderr, " depends on target %d [%s]\n", dependee_index,
dependee->GetName()); dependee->GetName());
@ -295,115 +292,20 @@ cmComputeTargetDepends
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeTargetDepends::Tarjan() void
{ cmComputeTargetDepends
int n = static_cast<int>(this->TargetDependGraph.size()); ::DisplayComponents(cmComputeComponentGraph const& ccg)
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()
{ {
fprintf(stderr, "The strongly connected components are:\n"); 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) for(int c = 0; c < n; ++c)
{ {
ComponentList const& cl = this->Components[c]; NodeList const& nl = components[c];
fprintf(stderr, "Component (%d):\n", c); fprintf(stderr, "Component (%d):\n", c);
for(ComponentList::const_iterator ci = cl.begin(); for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
ci != cl.end(); ++ci)
{ {
int i = *ci; int i = *ni;
fprintf(stderr, " contains target %d [%s]\n", fprintf(stderr, " contains target %d [%s]\n",
i, this->Targets[i]->GetName()); i, this->Targets[i]->GetName());
} }
@ -412,28 +314,31 @@ void cmComputeTargetDepends::DisplayComponents()
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmComputeTargetDepends::CheckComponents() bool
cmComputeTargetDepends
::CheckComponents(cmComputeComponentGraph const& ccg)
{ {
// All non-trivial components should consist only of static // All non-trivial components should consist only of static
// libraries. // 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) for(int c=0; c < nc; ++c)
{ {
// Get the current component. // Get the current component.
ComponentList const& cl = this->Components[c]; NodeList const& nl = components[c];
// Skip trivial components. // Skip trivial components.
if(cl.size() < 2) if(nl.size() < 2)
{ {
continue; continue;
} }
// Make sure the component is all STATIC_LIBRARY targets. // 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; return false;
} }
} }
@ -443,16 +348,17 @@ bool cmComputeTargetDepends::CheckComponents()
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void 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. // Construct the error message.
cmOStringStream e; cmOStringStream e;
e << "The inter-target dependency graph contains the following " e << "The inter-target dependency graph contains the following "
<< "strongly connected component (cycle):\n"; << "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. // Get the depender.
int i = *ci; int i = *ci;
@ -463,12 +369,11 @@ cmComputeTargetDepends::ComplainAboutBadComponent(int c)
<< cmTarget::TargetTypeNames[depender->GetType()] << "\n"; << cmTarget::TargetTypeNames[depender->GetType()] << "\n";
// List its dependencies that are inside the component. // List its dependencies that are inside the component.
TargetDependList const& tdl = this->TargetDependGraph[i]; NodeList const& nl = this->InitialGraph[i];
for(TargetDependList::const_iterator di = tdl.begin(); for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
di != tdl.end(); ++di)
{ {
int j = *di; int j = *ni;
if(this->TarjanEntries[j].Component == c) if(cmap[j] == c)
{ {
cmTarget* dependee = this->Targets[j]; cmTarget* dependee = this->Targets[j];
e << " depends on " << dependee->GetName() << "\n"; e << " depends on " << dependee->GetName() << "\n";
@ -481,46 +386,45 @@ cmComputeTargetDepends::ComplainAboutBadComponent(int c)
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeTargetDepends::ComputeFinalDepends() void
cmComputeTargetDepends
::ComputeFinalDepends(cmComputeComponentGraph const& ccg)
{ {
int n = static_cast<int>(this->TargetDependGraph.size()); // Get the component graph information.
this->FinalDependGraph.resize(n); 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. // 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 = components[depender_component].back();
int depender_component_tail = NodeList const& nl = cgraph[depender_component];
this->Components[depender_component].back(); for(NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
TargetDependList const& tdl = this->TargetDependGraph[i];
for(TargetDependList::const_iterator di = tdl.begin();
di != tdl.end(); ++di)
{ {
int j = *di; int dependee_component = *ni;
int dependee_component = this->TarjanEntries[j].Component; int dependee_component_head = components[dependee_component].front();
int dependee_component_head = this->FinalGraph[depender_component_tail]
this->Components[dependee_component].front(); .push_back(dependee_component_head);
if(depender_component != dependee_component)
{
this->FinalDependGraph[depender_component_tail]
.push_back(dependee_component_head);
}
} }
} }
// Compute intra-component edges. // 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) for(int c=0; c < nc; ++c)
{ {
// Within the component each target depends on that following it. // Within the component each target depends on that following it.
ComponentList const& cl = this->Components[c]; NodeList const& nl = components[c];
ComponentList::const_iterator ci = cl.begin(); NodeList::const_iterator ni = nl.begin();
int last_i = *ci; int last_i = *ni;
for(++ci; ci != cl.end(); ++ci) for(++ni; ni != nl.end(); ++ni)
{ {
int i = *ci; int i = *ni;
this->FinalDependGraph[last_i].push_back(i); this->FinalGraph[last_i].push_back(i);
last_i = i; last_i = i;
} }
} }

View File

@ -19,8 +19,11 @@
#include "cmStandardIncludes.h" #include "cmStandardIncludes.h"
#include "cmGraphAdjacencyList.h"
#include <stack> #include <stack>
class cmComputeComponentGraph;
class cmGlobalGenerator; class cmGlobalGenerator;
class cmTarget; class cmTarget;
@ -46,7 +49,7 @@ private:
void CollectDepends(); void CollectDepends();
void CollectTargetDepends(int depender_index); void CollectTargetDepends(int depender_index);
void AddTargetDepend(int depender_index, const char* dependee_name); void AddTargetDepend(int depender_index, const char* dependee_name);
void ComputeFinalDepends(); void ComputeFinalDepends(cmComputeComponentGraph const& ccg);
cmGlobalGenerator* GlobalGenerator; cmGlobalGenerator* GlobalGenerator;
bool DebugMode; bool DebugMode;
@ -58,33 +61,16 @@ private:
// Represent the target dependency graph. The entry at each // Represent the target dependency graph. The entry at each
// top-level index corresponds to a depender whose dependencies are // top-level index corresponds to a depender whose dependencies are
// listed. // listed.
struct TargetDependList: public std::vector<int> {}; typedef cmGraphNodeList NodeList;
std::vector<TargetDependList> TargetDependGraph; typedef cmGraphAdjacencyList Graph;
std::vector<TargetDependList> FinalDependGraph; Graph InitialGraph;
void DisplayGraph(std::vector<TargetDependList> const& graph, Graph FinalGraph;
const char* name); void DisplayGraph(Graph const& graph, const char* name);
// Tarjan's algorithm. // Deal with connected components.
struct TarjanEntry void DisplayComponents(cmComputeComponentGraph const& ccg);
{ bool CheckComponents(cmComputeComponentGraph const& ccg);
int Root; void ComplainAboutBadComponent(cmComputeComponentGraph const& ccg, int c);
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);
// Connected components.
struct ComponentList: public std::vector<int> {};
std::vector<ComponentList> Components;
void DisplayComponents();
bool CheckComponents();
void ComplainAboutBadComponent(int c);
}; };
#endif #endif

View File

@ -0,0 +1,25 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
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

View File

@ -171,6 +171,7 @@ CMAKE_CXX_SOURCES="\
cmComputeLinkInformation \ cmComputeLinkInformation \
cmOrderRuntimeDirectories \ cmOrderRuntimeDirectories \
cmComputeTargetDepends \ cmComputeTargetDepends \
cmComputeComponentGraph \
" "
if ${cmake_system_mingw}; then if ${cmake_system_mingw}; then