summaryrefslogtreecommitdiffstats
path: root/Source/cmComputeLinkDepends.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmComputeLinkDepends.cxx')
-rw-r--r--Source/cmComputeLinkDepends.cxx848
1 files changed, 848 insertions, 0 deletions
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
new file mode 100644
index 0000000..186deb6
--- /dev/null
+++ b/Source/cmComputeLinkDepends.cxx
@@ -0,0 +1,848 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmComputeLinkDepends.h"
+
+#include "cmAlgorithms.h"
+#include "cmComputeComponentGraph.h"
+#include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
+#include "cmListFileCache.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmRange.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmake.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <iterator>
+#include <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <utility>
+
+/*
+
+This file computes an ordered list of link items to use when linking a
+single target in one configuration. Each link item is identified by
+the string naming it. A graph of dependencies is created in which
+each node corresponds to one item and directed edges lead from nodes to
+those which must *follow* them on the link line. For example, the
+graph
+
+ A -> B -> C
+
+will lead to the link line order
+
+ A B C
+
+The set of items placed in the graph is formed with a breadth-first
+search of the link dependencies starting from the main target.
+
+There are two types of items: those with known direct dependencies and
+those without known dependencies. We will call the two types "known
+items" and "unknown items", respectively. Known items are those whose
+names correspond to targets (built or imported) and those for which an
+old-style <item>_LIB_DEPENDS variable is defined. All other items are
+unknown and we must infer dependencies for them. For items that look
+like flags (beginning with '-') we trivially infer no dependencies,
+and do not include them in the dependencies of other items.
+
+Known items have dependency lists ordered based on how the user
+specified them. We can use this order to infer potential dependencies
+of unknown items. For example, if link items A and B are unknown and
+items X and Y are known, then we might have the following dependency
+lists:
+
+ X: Y A B
+ Y: A B
+
+The explicitly known dependencies form graph edges
+
+ X -> Y , X -> A , X -> B , Y -> A , Y -> B
+
+We can also infer the edge
+
+ A -> B
+
+because *every* time A appears B is seen on its right. We do not know
+whether A really needs symbols from B to link, but it *might* so we
+must preserve their order. This is the case also for the following
+explicit lists:
+
+ X: A B Y
+ Y: A B
+
+Here, A is followed by the set {B,Y} in one list, and {B} in the other
+list. The intersection of these sets is {B}, so we can infer that A
+depends on at most B. Meanwhile B is followed by the set {Y} in one
+list and {} in the other. The intersection is {} so we can infer that
+B has no dependencies.
+
+Let's make a more complex example by adding unknown item C and
+considering these dependency lists:
+
+ X: A B Y C
+ Y: A C B
+
+The explicit edges are
+
+ X -> Y , X -> A , X -> B , X -> C , Y -> A , Y -> B , Y -> C
+
+For the unknown items, we infer dependencies by looking at the
+"follow" sets:
+
+ A: intersect( {B,Y,C} , {C,B} ) = {B,C} ; infer edges A -> B , A -> C
+ B: intersect( {Y,C} , {} ) = {} ; infer no edges
+ C: intersect( {} , {B} ) = {} ; infer no edges
+
+Note that targets are never inferred as dependees because outside
+libraries should not depend on them.
+
+------------------------------------------------------------------------------
+
+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.
+
+After the initial exploration of the link interface tree, any
+transitive (dependent) shared libraries that were encountered and not
+included in the interface are processed in their own BFS. This BFS
+follows only the dependent library lists and not the link interfaces.
+They are added to the link items with a mark indicating that the are
+transitive dependencies. Then cmComputeLinkInformation deals with
+them on a per-platform basis.
+
+The complete graph formed from all known and inferred dependencies may
+not be acyclic, so an acyclic version must be created.
+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
+
+ X -> A -> B -> C -> A -> Y
+
+contains strongly connected components {X}, {A,B,C}, and {Y}. The
+implied directed acyclic graph (DAG) is
+
+ {X} -> {A,B,C} -> {Y}
+
+We then compute a topological order for the DAG nodes to serve as a
+reference for satisfying dependencies efficiently. We perform the DFS
+in reverse order and assign topological order indices counting down so
+that the result is as close to the original BFS order as possible
+without violating dependencies.
+
+------------------------------------------------------------------------------
+
+The final link entry order is constructed as follows. We first walk
+through and emit the *original* link line as specified by the user.
+As each item is emitted, a set of pending nodes in the component DAG
+is maintained. When a pending component has been completely seen, it
+is removed from the pending set and its dependencies (following edges
+of the DAG) are added. A trivial component (those with one item) is
+complete as soon as its item is seen. A non-trivial component (one
+with more than one item; assumed to be static libraries) is complete
+when *all* its entries have been seen *twice* (all entries seen once,
+then all entries seen again, not just each entry twice). A pending
+component tracks which items have been seen and a count of how many
+times the component needs to be seen (once for trivial components,
+twice for non-trivial). If at any time another component finishes and
+re-adds an already pending component, the pending component is reset
+so that it needs to be seen in its entirety again. This ensures that
+all dependencies of a component are satisfied no matter where it
+appears.
+
+After the original link line has been completed, we append to it the
+remaining pending components and their dependencies. This is done by
+repeatedly emitting the first item from the first pending component
+and following the same update rules as when traversing the original
+link line. Since the pending components are kept in topological order
+they are emitted with minimal repeats (we do not want to emit a
+component just to have it added again when another component is
+completed later). This process continues until no pending components
+remain. We know it will terminate because the component graph is
+guaranteed to be acyclic.
+
+The final list of items produced by this procedure consists of the
+original user link line followed by minimal additional items needed to
+satisfy dependencies. The final list is then filtered to de-duplicate
+items that we know the linker will re-use automatically (shared libs).
+
+*/
+
+cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
+ const std::string& config)
+{
+ // Store context information.
+ this->Target = target;
+ this->Makefile = this->Target->Target->GetMakefile();
+ this->GlobalGenerator =
+ this->Target->GetLocalGenerator()->GetGlobalGenerator();
+ this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
+
+ // The configuration being linked.
+ this->HasConfig = !config.empty();
+ this->Config = (this->HasConfig) ? config : std::string();
+ std::vector<std::string> debugConfigs =
+ this->Makefile->GetCMakeInstance()->GetDebugConfigs();
+ this->LinkType = CMP0003_ComputeLinkType(this->Config, debugConfigs);
+
+ // Enable debug mode if requested.
+ this->DebugMode = this->Makefile->IsOn("CMAKE_LINK_DEPENDS_DEBUG_MODE");
+
+ // Assume no compatibility until set.
+ this->OldLinkDirMode = false;
+
+ // No computation has been done.
+ this->CCG = nullptr;
+}
+
+cmComputeLinkDepends::~cmComputeLinkDepends()
+{
+ cmDeleteAll(this->InferredDependSets);
+ delete this->CCG;
+}
+
+void cmComputeLinkDepends::SetOldLinkDirMode(bool b)
+{
+ this->OldLinkDirMode = b;
+}
+
+std::vector<cmComputeLinkDepends::LinkEntry> const&
+cmComputeLinkDepends::Compute()
+{
+ // Follow the link dependencies of the target to be linked.
+ this->AddDirectLinkEntries();
+
+ // Complete the breadth-first search of dependencies.
+ while (!this->BFSQueue.empty()) {
+ // Get the next entry.
+ BFSEntry qe = this->BFSQueue.front();
+ this->BFSQueue.pop();
+
+ // Follow the entry's dependencies.
+ this->FollowLinkEntry(qe);
+ }
+
+ // Complete the search of shared library dependencies.
+ while (!this->SharedDepQueue.empty()) {
+ // Handle the next entry.
+ this->HandleSharedDependency(this->SharedDepQueue.front());
+ this->SharedDepQueue.pop();
+ }
+
+ // 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) {
+ fprintf(stderr,
+ "---------------------------------------"
+ "---------------------------------------\n");
+ fprintf(stderr, "Link dependency analysis for target %s, config %s\n",
+ this->Target->GetName().c_str(),
+ this->HasConfig ? this->Config.c_str() : "noconfig");
+ this->DisplayConstraintGraph();
+ }
+
+ // Compute the final ordering.
+ this->OrderLinkEntires();
+
+ // Compute the final set of link entries.
+ // Iterate in reverse order so we can keep only the last occurrence
+ // of a shared library.
+ std::set<int> emmitted;
+ for (int i : cmReverseRange(this->FinalLinkOrder)) {
+ LinkEntry const& e = this->EntryList[i];
+ cmGeneratorTarget const* t = e.Target;
+ // Entries that we know the linker will re-use do not need to be repeated.
+ bool uniquify = t && t->GetType() == cmStateEnums::SHARED_LIBRARY;
+ if (!uniquify || emmitted.insert(i).second) {
+ this->FinalLinkEntries.push_back(e);
+ }
+ }
+ // Reverse the resulting order since we iterated in reverse.
+ std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end());
+
+ // Display the final set.
+ if (this->DebugMode) {
+ this->DisplayFinalEntries();
+ }
+
+ return this->FinalLinkEntries;
+}
+
+std::map<cmLinkItem, int>::iterator cmComputeLinkDepends::AllocateLinkEntry(
+ cmLinkItem const& item)
+{
+ std::map<cmLinkItem, int>::value_type index_entry(
+ item, static_cast<int>(this->EntryList.size()));
+ std::map<cmLinkItem, int>::iterator lei =
+ this->LinkEntryIndex.insert(index_entry).first;
+ this->EntryList.emplace_back();
+ this->InferredDependSets.push_back(nullptr);
+ this->EntryConstraintGraph.emplace_back();
+ return lei;
+}
+
+int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
+{
+ // Check if the item entry has already been added.
+ std::map<cmLinkItem, int>::iterator lei = this->LinkEntryIndex.find(item);
+ if (lei != this->LinkEntryIndex.end()) {
+ // Yes. We do not need to follow the item's dependencies again.
+ return lei->second;
+ }
+
+ // Allocate a spot for the item entry.
+ lei = this->AllocateLinkEntry(item);
+
+ // Initialize the item entry.
+ int index = lei->second;
+ LinkEntry& entry = this->EntryList[index];
+ entry.Item = item.AsStr();
+ entry.Target = item.Target;
+ entry.IsFlag =
+ (!entry.Target && entry.Item[0] == '-' && entry.Item[1] != 'l' &&
+ entry.Item.substr(0, 10) != "-framework");
+
+ // If the item has dependencies queue it to follow them.
+ if (entry.Target) {
+ // Target dependencies are always known. Follow them.
+ BFSEntry qe = { index, nullptr };
+ this->BFSQueue.push(qe);
+ } else {
+ // Look for an old-style <item>_LIB_DEPENDS variable.
+ std::string var = entry.Item;
+ var += "_LIB_DEPENDS";
+ if (const char* val = this->Makefile->GetDefinition(var)) {
+ // The item dependencies are known. Follow them.
+ BFSEntry qe = { index, val };
+ this->BFSQueue.push(qe);
+ } else if (!entry.IsFlag) {
+ // The item dependencies are not known. We need to infer them.
+ this->InferredDependSets[index] = new DependSetList;
+ }
+ }
+
+ return index;
+}
+
+void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
+{
+ // Get this entry representation.
+ int depender_index = qe.Index;
+ LinkEntry const& entry = this->EntryList[depender_index];
+
+ // Follow the item's dependencies.
+ if (entry.Target) {
+ // Follow the target dependencies.
+ if (cmLinkInterface const* iface =
+ entry.Target->GetLinkInterface(this->Config, this->Target)) {
+ const bool isIface =
+ entry.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY;
+ // This target provides its own link interface information.
+ this->AddLinkEntries(depender_index, iface->Libraries);
+
+ if (isIface) {
+ return;
+ }
+
+ // Handle dependent shared libraries.
+ this->FollowSharedDeps(depender_index, iface);
+
+ // Support for CMP0003.
+ for (cmLinkItem const& oi : iface->WrongConfigLibraries) {
+ this->CheckWrongConfigItem(oi);
+ }
+ }
+ } else {
+ // Follow the old-style dependency list.
+ this->AddVarLinkEntries(depender_index, qe.LibDepends);
+ }
+}
+
+void cmComputeLinkDepends::FollowSharedDeps(int depender_index,
+ cmLinkInterface const* iface,
+ bool follow_interface)
+{
+ // Follow dependencies if we have not followed them already.
+ if (this->SharedDepFollowed.insert(depender_index).second) {
+ if (follow_interface) {
+ this->QueueSharedDependencies(depender_index, iface->Libraries);
+ }
+ this->QueueSharedDependencies(depender_index, iface->SharedDeps);
+ }
+}
+
+void cmComputeLinkDepends::QueueSharedDependencies(
+ int depender_index, std::vector<cmLinkItem> const& deps)
+{
+ for (cmLinkItem const& li : deps) {
+ SharedDepEntry qe;
+ qe.Item = li;
+ qe.DependerIndex = depender_index;
+ this->SharedDepQueue.push(qe);
+ }
+}
+
+void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
+{
+ // Check if the target already has an entry.
+ std::map<cmLinkItem, int>::iterator lei =
+ this->LinkEntryIndex.find(dep.Item);
+ if (lei == this->LinkEntryIndex.end()) {
+ // Allocate a spot for the item entry.
+ lei = this->AllocateLinkEntry(dep.Item);
+
+ // Initialize the item entry.
+ LinkEntry& entry = this->EntryList[lei->second];
+ entry.Item = dep.Item.AsStr();
+ entry.Target = dep.Item.Target;
+
+ // This item was added specifically because it is a dependent
+ // shared library. It may get special treatment
+ // in cmComputeLinkInformation.
+ entry.IsSharedDep = true;
+ }
+
+ // Get the link entry for this target.
+ int index = lei->second;
+ LinkEntry& entry = this->EntryList[index];
+
+ // This shared library dependency must follow the item that listed
+ // it.
+ this->EntryConstraintGraph[dep.DependerIndex].emplace_back(
+ index, true, cmListFileBacktrace());
+
+ // Target items may have their own dependencies.
+ if (entry.Target) {
+ if (cmLinkInterface const* iface =
+ entry.Target->GetLinkInterface(this->Config, this->Target)) {
+ // Follow public and private dependencies transitively.
+ this->FollowSharedDeps(index, iface, true);
+ }
+ }
+}
+
+void cmComputeLinkDepends::AddVarLinkEntries(int depender_index,
+ const char* value)
+{
+ // This is called to add the dependencies named by
+ // <item>_LIB_DEPENDS. The variable contains a semicolon-separated
+ // list. The list contains link-type;item pairs and just items.
+ std::vector<std::string> deplist;
+ cmSystemTools::ExpandListArgument(value, deplist);
+
+ // Look for entries meant for this configuration.
+ std::vector<cmLinkItem> actual_libs;
+ cmTargetLinkLibraryType llt = GENERAL_LibraryType;
+ bool haveLLT = false;
+ for (std::string const& d : deplist) {
+ if (d == "debug") {
+ llt = DEBUG_LibraryType;
+ haveLLT = true;
+ } else if (d == "optimized") {
+ llt = OPTIMIZED_LibraryType;
+ haveLLT = true;
+ } else if (d == "general") {
+ llt = GENERAL_LibraryType;
+ haveLLT = true;
+ } else if (!d.empty()) {
+ // If no explicit link type was given prior to this entry then
+ // check if the entry has its own link type variable. This is
+ // needed for compatibility with dependency files generated by
+ // the export_library_dependencies command from CMake 2.4 and
+ // lower.
+ if (!haveLLT) {
+ std::string var = d;
+ var += "_LINK_TYPE";
+ if (const char* val = this->Makefile->GetDefinition(var)) {
+ if (strcmp(val, "debug") == 0) {
+ llt = DEBUG_LibraryType;
+ } else if (strcmp(val, "optimized") == 0) {
+ llt = OPTIMIZED_LibraryType;
+ }
+ }
+ }
+
+ // If the library is meant for this link type then use it.
+ if (llt == GENERAL_LibraryType || llt == this->LinkType) {
+ actual_libs.emplace_back(this->ResolveLinkItem(depender_index, d));
+ } else if (this->OldLinkDirMode) {
+ cmLinkItem item = this->ResolveLinkItem(depender_index, d);
+ this->CheckWrongConfigItem(item);
+ }
+
+ // Reset the link type until another explicit type is given.
+ llt = GENERAL_LibraryType;
+ haveLLT = false;
+ }
+ }
+
+ // Add the entries from this list.
+ this->AddLinkEntries(depender_index, actual_libs);
+}
+
+void cmComputeLinkDepends::AddDirectLinkEntries()
+{
+ // Add direct link dependencies in this configuration.
+ cmLinkImplementation const* impl =
+ this->Target->GetLinkImplementation(this->Config);
+ this->AddLinkEntries(-1, impl->Libraries);
+ for (cmLinkItem const& wi : impl->WrongConfigLibraries) {
+ this->CheckWrongConfigItem(wi);
+ }
+}
+
+template <typename T>
+void cmComputeLinkDepends::AddLinkEntries(int depender_index,
+ std::vector<T> const& libs)
+{
+ // Track inferred dependency sets implied by this list.
+ std::map<int, DependSet> dependSets;
+
+ // Loop over the libraries linked directly by the depender.
+ for (T const& l : libs) {
+ // Skip entries that will resolve to the target getting linked or
+ // are empty.
+ cmLinkItem const& item = l;
+ if (item.AsStr() == this->Target->GetName() || item.AsStr().empty()) {
+ continue;
+ }
+
+ // Add a link entry for this item.
+ int dependee_index = this->AddLinkEntry(l);
+
+ // The dependee must come after the depender.
+ if (depender_index >= 0) {
+ this->EntryConstraintGraph[depender_index].emplace_back(
+ dependee_index, false, cmListFileBacktrace());
+ } else {
+ // This is a direct dependency of the target being linked.
+ this->OriginalEntries.push_back(dependee_index);
+ }
+
+ // Update the inferred dependencies for earlier items.
+ for (auto& dependSet : dependSets) {
+ // Add this item to the inferred dependencies of other items.
+ // Target items are never inferred dependees because unknown
+ // items are outside libraries that should not be depending on
+ // targets.
+ if (!this->EntryList[dependee_index].Target &&
+ !this->EntryList[dependee_index].IsFlag &&
+ dependee_index != dependSet.first) {
+ dependSet.second.insert(dependee_index);
+ }
+ }
+
+ // If this item needs to have dependencies inferred, do so.
+ if (this->InferredDependSets[dependee_index]) {
+ // Make sure an entry exists to hold the set for the item.
+ dependSets[dependee_index];
+ }
+ }
+
+ // Store the inferred dependency sets discovered for this list.
+ for (auto const& dependSet : dependSets) {
+ this->InferredDependSets[dependSet.first]->push_back(dependSet.second);
+ }
+}
+
+cmLinkItem cmComputeLinkDepends::ResolveLinkItem(int depender_index,
+ const std::string& name)
+{
+ // Look for a target in the scope of the depender.
+ cmGeneratorTarget const* from = this->Target;
+ if (depender_index >= 0) {
+ if (cmGeneratorTarget const* depender =
+ this->EntryList[depender_index].Target) {
+ from = depender;
+ }
+ }
+ return from->ResolveLinkItem(name, cmListFileBacktrace());
+}
+
+void cmComputeLinkDepends::InferDependencies()
+{
+ // The inferred dependency sets for each item list the possible
+ // dependencies. The intersection of the sets for one item form its
+ // inferred dependencies.
+ for (unsigned int depender_index = 0;
+ depender_index < this->InferredDependSets.size(); ++depender_index) {
+ // Skip items for which dependencies do not need to be inferred or
+ // for which the inferred dependency sets are empty.
+ DependSetList* sets = this->InferredDependSets[depender_index];
+ if (!sets || sets->empty()) {
+ continue;
+ }
+
+ // Intersect the sets for this item.
+ DependSet common = sets->front();
+ for (DependSet const& i : cmMakeRange(*sets).advance(1)) {
+ DependSet intersection;
+ std::set_intersection(common.begin(), common.end(), i.begin(), i.end(),
+ std::inserter(intersection, intersection.begin()));
+ common = intersection;
+ }
+
+ // Add the inferred dependencies to the graph.
+ cmGraphEdgeList& edges = this->EntryConstraintGraph[depender_index];
+ edges.reserve(edges.size() + common.size());
+ for (auto const& c : common) {
+ edges.emplace_back(c, true, cmListFileBacktrace());
+ }
+ }
+}
+
+void cmComputeLinkDepends::CleanConstraintGraph()
+{
+ for (cmGraphEdgeList& edgeList : this->EntryConstraintGraph) {
+ // Sort the outgoing edges for each graph node so that the
+ // original order will be preserved as much as possible.
+ std::sort(edgeList.begin(), edgeList.end());
+
+ // Make the edge list unique.
+ edgeList.erase(std::unique(edgeList.begin(), edgeList.end()),
+ edgeList.end());
+ }
+}
+
+void cmComputeLinkDepends::DisplayConstraintGraph()
+{
+ // Display the graph nodes and their edges.
+ std::ostringstream e;
+ for (unsigned int i = 0; i < this->EntryConstraintGraph.size(); ++i) {
+ EdgeList const& nl = this->EntryConstraintGraph[i];
+ e << "item " << i << " is [" << this->EntryList[i].Item << "]\n";
+ e << cmWrap(" item ", nl, " must follow it", "\n") << "\n";
+ }
+ fprintf(stderr, "%s\n", e.str().c_str());
+}
+
+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.
+ this->CCG = new cmComputeComponentGraph(this->EntryConstraintGraph);
+
+ // The component graph is guaranteed to be acyclic. Start a DFS
+ // from every entry to compute a topological order for the
+ // components.
+ Graph const& cgraph = this->CCG->GetComponentGraph();
+ int n = static_cast<int>(cgraph.size());
+ this->ComponentVisited.resize(cgraph.size(), 0);
+ this->ComponentOrder.resize(cgraph.size(), n);
+ this->ComponentOrderId = n;
+ // Run in reverse order so the topological order will preserve the
+ // original order where there are no constraints.
+ for (int c = n - 1; c >= 0; --c) {
+ this->VisitComponent(c);
+ }
+
+ // Display the component graph.
+ if (this->DebugMode) {
+ this->DisplayComponents();
+ }
+
+ // Start with the original link line.
+ for (int originalEntry : this->OriginalEntries) {
+ this->VisitEntry(originalEntry);
+ }
+
+ // Now explore anything left pending. Since the component graph is
+ // guaranteed to be acyclic we know this will terminate.
+ while (!this->PendingComponents.empty()) {
+ // Visit one entry from the first pending component. The visit
+ // logic will update the pending components accordingly. Since
+ // the pending components are kept in topological order this will
+ // not repeat one.
+ int e = *this->PendingComponents.begin()->second.Entries.begin();
+ this->VisitEntry(e);
+ }
+}
+
+void cmComputeLinkDepends::DisplayComponents()
+{
+ fprintf(stderr, "The strongly connected components are:\n");
+ std::vector<NodeList> const& components = this->CCG->GetComponents();
+ for (unsigned int c = 0; c < components.size(); ++c) {
+ fprintf(stderr, "Component (%u):\n", c);
+ NodeList const& nl = components[c];
+ for (int i : nl) {
+ fprintf(stderr, " item %d [%s]\n", i, this->EntryList[i].Item.c_str());
+ }
+ EdgeList const& ol = this->CCG->GetComponentGraphEdges(c);
+ for (cmGraphEdge const& oi : ol) {
+ int i = oi;
+ fprintf(stderr, " followed by Component (%d)\n", i);
+ }
+ fprintf(stderr, " topo order index %d\n", this->ComponentOrder[c]);
+ }
+ fprintf(stderr, "\n");
+}
+
+void cmComputeLinkDepends::VisitComponent(unsigned int c)
+{
+ // Check if the node has already been visited.
+ if (this->ComponentVisited[c]) {
+ return;
+ }
+
+ // We are now visiting this component so mark it.
+ this->ComponentVisited[c] = 1;
+
+ // Visit the neighbors of the component first.
+ // Run in reverse order so the topological order will preserve the
+ // original order where there are no constraints.
+ EdgeList const& nl = this->CCG->GetComponentGraphEdges(c);
+ for (cmGraphEdge const& edge : cmReverseRange(nl)) {
+ this->VisitComponent(edge);
+ }
+
+ // Assign an ordering id to this component.
+ this->ComponentOrder[c] = --this->ComponentOrderId;
+}
+
+void cmComputeLinkDepends::VisitEntry(int index)
+{
+ // Include this entry on the link line.
+ this->FinalLinkOrder.push_back(index);
+
+ // This entry has now been seen. Update its component.
+ bool completed = false;
+ int component = this->CCG->GetComponentMap()[index];
+ std::map<int, PendingComponent>::iterator mi =
+ this->PendingComponents.find(this->ComponentOrder[component]);
+ if (mi != this->PendingComponents.end()) {
+ // The entry is in an already pending component.
+ PendingComponent& pc = mi->second;
+
+ // Remove the entry from those pending in its component.
+ pc.Entries.erase(index);
+ if (pc.Entries.empty()) {
+ // The complete component has been seen since it was last needed.
+ --pc.Count;
+
+ if (pc.Count == 0) {
+ // The component has been completed.
+ this->PendingComponents.erase(mi);
+ completed = true;
+ } else {
+ // The whole component needs to be seen again.
+ NodeList const& nl = this->CCG->GetComponent(component);
+ assert(nl.size() > 1);
+ pc.Entries.insert(nl.begin(), nl.end());
+ }
+ }
+ } else {
+ // The entry is not in an already pending component.
+ NodeList const& nl = this->CCG->GetComponent(component);
+ if (nl.size() > 1) {
+ // This is a non-trivial component. It is now pending.
+ PendingComponent& pc = this->MakePendingComponent(component);
+
+ // The starting entry has already been seen.
+ pc.Entries.erase(index);
+ } else {
+ // This is a trivial component, so it is already complete.
+ completed = true;
+ }
+ }
+
+ // If the entry completed a component, the component's dependencies
+ // are now pending.
+ if (completed) {
+ EdgeList const& ol = this->CCG->GetComponentGraphEdges(component);
+ for (cmGraphEdge const& oi : ol) {
+ // This entire component is now pending no matter whether it has
+ // been partially seen already.
+ this->MakePendingComponent(oi);
+ }
+ }
+}
+
+cmComputeLinkDepends::PendingComponent&
+cmComputeLinkDepends::MakePendingComponent(unsigned int component)
+{
+ // Create an entry (in topological order) for the component.
+ PendingComponent& pc =
+ this->PendingComponents[this->ComponentOrder[component]];
+ pc.Id = component;
+ NodeList const& nl = this->CCG->GetComponent(component);
+
+ if (nl.size() == 1) {
+ // Trivial components need be seen only once.
+ pc.Count = 1;
+ } else {
+ // 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.
+ pc.Count = this->ComputeComponentCount(nl);
+ }
+
+ // Store the entries to be seen.
+ pc.Entries.insert(nl.begin(), nl.end());
+
+ return pc;
+}
+
+int cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl)
+{
+ unsigned int count = 2;
+ for (int ni : nl) {
+ if (cmGeneratorTarget const* target = this->EntryList[ni].Target) {
+ if (cmLinkInterface const* iface =
+ target->GetLinkInterface(this->Config, this->Target)) {
+ if (iface->Multiplicity > count) {
+ count = iface->Multiplicity;
+ }
+ }
+ }
+ }
+ return count;
+}
+
+void cmComputeLinkDepends::DisplayFinalEntries()
+{
+ fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str());
+ for (LinkEntry const& lei : this->FinalLinkEntries) {
+ if (lei.Target) {
+ fprintf(stderr, " target [%s]\n", lei.Target->GetName().c_str());
+ } else {
+ fprintf(stderr, " item [%s]\n", lei.Item.c_str());
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+void cmComputeLinkDepends::CheckWrongConfigItem(cmLinkItem const& item)
+{
+ if (!this->OldLinkDirMode) {
+ return;
+ }
+
+ // For CMake 2.4 bug-compatibility we need to consider the output
+ // directories of targets linked in another configuration as link
+ // directories.
+ if (item.Target && !item.Target->IsImported()) {
+ this->OldWrongConfigItems.insert(item.Target);
+ }
+}