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
cmCommandArgumentParser.cxx
cmCommandArgumentParserHelper.cxx
cmComputeComponentGraph.cxx
cmComputeComponentGraph.h
cmComputeLinkDepends.cxx
cmComputeLinkDepends.h
cmComputeLinkInformation.cxx
@ -138,6 +140,7 @@ SET(SRCS
cmGlobalGenerator.h
cmGlobalUnixMakefileGenerator3.cxx
cmGlobalUnixMakefileGenerator3.h
cmGraphAdjacencyList.h
cmInstallGenerator.h
cmInstallGenerator.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 "cmComputeComponentGraph.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.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
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 @@ cmComputeLinkDepends::Compute()
// 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 @@ cmComputeLinkDepends::AllocateLinkEntry(std::string const& item)
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 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
// 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 @@ cmComputeLinkDepends::AddLinkEntries(int depender_index,
// 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 @@ void cmComputeLinkDepends::InferDependencies()
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::DisplayConstraintGraph()
//----------------------------------------------------------------------------
void cmComputeLinkDepends::OrderLinkEntires()
{
// Setup visit tracking.
this->EntryVisited.resize(this->EntryList.size(), 0);
this->WalkId = 0;
// Start a DFS from every entry.
for(unsigned int i=0; i < this->EntryList.size(); ++i)
// 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->WalkId;
this->VisitLinkEntry(i);
this->DisplayComponents(ccg);
}
// 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.
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;
}
// 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]);
}
}
//----------------------------------------------------------------------------

View File

@ -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 @@ private:
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();
};

View File

@ -16,6 +16,7 @@
=========================================================================*/
#include "cmComputeTargetDepends.h"
#include "cmComputeComponentGraph.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
@ -117,25 +118,25 @@ bool cmComputeTargetDepends::Compute()
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 @@ cmComputeTargetDepends::GetTargetDirectDepends(cmTarget* t,
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::CollectTargets()
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 @@ void cmComputeTargetDepends::AddTargetDepend(int depender_index,
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 @@ cmComputeTargetDepends
}
//----------------------------------------------------------------------------
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 @@ void cmComputeTargetDepends::DisplayComponents()
}
//----------------------------------------------------------------------------
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 @@ bool cmComputeTargetDepends::CheckComponents()
//----------------------------------------------------------------------------
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 @@ cmComputeTargetDepends::ComplainAboutBadComponent(int c)
<< 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 @@ cmComputeTargetDepends::ComplainAboutBadComponent(int c)
}
//----------------------------------------------------------------------------
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;
}
}

View File

@ -19,8 +19,11 @@
#include "cmStandardIncludes.h"
#include "cmGraphAdjacencyList.h"
#include <stack>
class cmComputeComponentGraph;
class cmGlobalGenerator;
class cmTarget;
@ -46,7 +49,7 @@ private:
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 @@ private:
// 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);
typedef cmGraphNodeList NodeList;
typedef cmGraphAdjacencyList Graph;
Graph InitialGraph;
Graph FinalGraph;
void DisplayGraph(Graph 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);
// 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

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 \
cmOrderRuntimeDirectories \
cmComputeTargetDepends \
cmComputeComponentGraph \
"
if ${cmake_system_mingw}; then