diff options
Diffstat (limited to 'Source/cmComputeTargetDepends.cxx')
-rw-r--r-- | Source/cmComputeTargetDepends.cxx | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx new file mode 100644 index 0000000..ff7eb0b --- /dev/null +++ b/Source/cmComputeTargetDepends.cxx @@ -0,0 +1,591 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmComputeTargetDepends.h" + +#include "cmComputeComponentGraph.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmSourceFile.h" +#include "cmState.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmake.h" + +#include <algorithm> + +#include <assert.h> + +/* + +This class is meant to analyze inter-target dependencies globally +during the generation step. The goal is to produce a set of direct +dependencies for each target such that no cycles are left and the +build order is safe. + +For most target types cyclic dependencies are not allowed. However +STATIC libraries may depend on each other in a cyclic fashion. In +general the directed dependency graph forms a directed-acyclic-graph +of strongly connected components. All strongly connected components +should consist of only STATIC_LIBRARY targets. + +In order to safely break dependency cycles we must preserve all other +dependencies passing through the corresponding strongly connected component. +The approach taken by this class is as follows: + + - Collect all targets and form the original dependency graph + - Run Tarjan's algorithm to extract the strongly connected components + (error if any member of a non-trivial component is not STATIC) + - The original dependencies imply a DAG on the components. + Use the implied DAG to construct a final safe set of dependencies. + +The final dependency set is constructed as follows: + + - For each connected component targets are placed in an arbitrary + order. Each target depends on the target following it in the order. + The first target is designated the head and the last target the tail. + (most components will be just 1 target anyway) + + - Original dependencies between targets in different components are + converted to connect the depender's component tail to the + dependee's component head. + +In most cases this will reproduce the original dependencies. However +when there are cycles of static libraries they will be broken in a +safe manner. + +For example, consider targets A0, A1, A2, B0, B1, B2, and C with these +dependencies: + + A0 -> A1 -> A2 -> A0 , B0 -> B1 -> B2 -> B0 -> A0 , C -> B0 + +Components may be identified as + + Component 0: A0, A1, A2 + Component 1: B0, B1, B2 + Component 2: C + +Intra-component dependencies are: + + 0: A0 -> A1 -> A2 , head=A0, tail=A2 + 1: B0 -> B1 -> B2 , head=B0, tail=B2 + 2: head=C, tail=C + +The inter-component dependencies are converted as: + + B0 -> A0 is component 1->0 and becomes B2 -> A0 + C -> B0 is component 2->1 and becomes C -> B0 + +This leads to the final target dependencies: + + C -> B0 -> B1 -> B2 -> A0 -> A1 -> A2 + +These produce a safe build order since C depends directly or +transitively on all the static libraries it links. + +*/ + +cmComputeTargetDepends::cmComputeTargetDepends(cmGlobalGenerator* gg) +{ + this->GlobalGenerator = gg; + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + this->DebugMode = + cm->GetState()->GetGlobalPropertyAsBool("GLOBAL_DEPENDS_DEBUG_MODE"); + this->NoCycles = + cm->GetState()->GetGlobalPropertyAsBool("GLOBAL_DEPENDS_NO_CYCLES"); +} + +cmComputeTargetDepends::~cmComputeTargetDepends() +{ +} + +bool cmComputeTargetDepends::Compute() +{ + // Build the original graph. + this->CollectTargets(); + this->CollectDepends(); + if (this->DebugMode) { + this->DisplayGraph(this->InitialGraph, "initial"); + } + + // Identify components. + cmComputeComponentGraph ccg(this->InitialGraph); + if (this->DebugMode) { + this->DisplayComponents(ccg); + } + if (!this->CheckComponents(ccg)) { + return false; + } + + // Compute the final dependency graph. + if (!this->ComputeFinalDepends(ccg)) { + return false; + } + if (this->DebugMode) { + this->DisplayGraph(this->FinalGraph, "final"); + } + + return true; +} + +void cmComputeTargetDepends::GetTargetDirectDepends(cmGeneratorTarget const* t, + cmTargetDependSet& deps) +{ + // Lookup the index for this target. All targets should be known by + // this point. + std::map<cmGeneratorTarget const*, int>::const_iterator tii = + this->TargetIndex.find(t); + assert(tii != this->TargetIndex.end()); + int i = tii->second; + + // Get its final dependencies. + EdgeList const& nl = this->FinalGraph[i]; + for (EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) { + cmGeneratorTarget const* dep = this->Targets[*ni]; + cmTargetDependSet::iterator di = deps.insert(dep).first; + di->SetType(ni->IsStrong()); + } +} + +void cmComputeTargetDepends::CollectTargets() +{ + // Collect all targets from all generators. + std::vector<cmLocalGenerator*> const& lgens = + this->GlobalGenerator->GetLocalGenerators(); + for (unsigned int i = 0; i < lgens.size(); ++i) { + const std::vector<cmGeneratorTarget*> targets = + lgens[i]->GetGeneratorTargets(); + for (std::vector<cmGeneratorTarget*>::const_iterator ti = targets.begin(); + ti != targets.end(); ++ti) { + cmGeneratorTarget* gt = *ti; + int index = static_cast<int>(this->Targets.size()); + this->TargetIndex[gt] = index; + this->Targets.push_back(gt); + } + } +} + +void cmComputeTargetDepends::CollectDepends() +{ + // Allocate the dependency graph adjacency lists. + this->InitialGraph.resize(this->Targets.size()); + + // Compute each dependency list. + for (unsigned int i = 0; i < this->Targets.size(); ++i) { + this->CollectTargetDepends(i); + } +} + +void cmComputeTargetDepends::CollectTargetDepends(int depender_index) +{ + // Get the depender. + cmGeneratorTarget const* depender = this->Targets[depender_index]; + if (depender->GetType() == cmState::INTERFACE_LIBRARY) { + return; + } + + // Loop over all targets linked directly in all configs. + // We need to make targets depend on the union of all config-specific + // dependencies in all targets, because the generated build-systems can't + // deal with config-specific dependencies. + { + std::set<std::string> emitted; + + std::vector<std::string> configs; + depender->Makefile->GetConfigurations(configs); + if (configs.empty()) { + configs.push_back(""); + } + for (std::vector<std::string>::const_iterator it = configs.begin(); + it != configs.end(); ++it) { + std::vector<cmSourceFile const*> objectFiles; + depender->GetExternalObjects(objectFiles, *it); + for (std::vector<cmSourceFile const*>::const_iterator oi = + objectFiles.begin(); + oi != objectFiles.end(); ++oi) { + std::string objLib = (*oi)->GetObjectLibrary(); + if (!objLib.empty() && emitted.insert(objLib).second) { + if (depender->GetType() != cmState::EXECUTABLE && + depender->GetType() != cmState::STATIC_LIBRARY && + depender->GetType() != cmState::SHARED_LIBRARY && + depender->GetType() != cmState::MODULE_LIBRARY) { + this->GlobalGenerator->GetCMakeInstance()->IssueMessage( + cmake::FATAL_ERROR, + "Only executables and non-OBJECT libraries may " + "reference target objects.", + depender->GetBacktrace()); + return; + } + const_cast<cmGeneratorTarget*>(depender)->Target->AddUtility(objLib); + } + } + + cmLinkImplementation const* impl = depender->GetLinkImplementation(*it); + + // A target should not depend on itself. + emitted.insert(depender->GetName()); + for (std::vector<cmLinkImplItem>::const_iterator lib = + impl->Libraries.begin(); + lib != impl->Libraries.end(); ++lib) { + // Don't emit the same library twice for this target. + if (emitted.insert(*lib).second) { + this->AddTargetDepend(depender_index, *lib, true); + this->AddInterfaceDepends(depender_index, *lib, emitted); + } + } + } + } + + // Loop over all utility dependencies. + { + std::set<cmLinkItem> const& tutils = depender->GetUtilityItems(); + std::set<std::string> emitted; + // A target should not depend on itself. + emitted.insert(depender->GetName()); + for (std::set<cmLinkItem>::const_iterator util = tutils.begin(); + util != tutils.end(); ++util) { + // Don't emit the same utility twice for this target. + if (emitted.insert(*util).second) { + this->AddTargetDepend(depender_index, *util, false); + } + } + } +} + +void cmComputeTargetDepends::AddInterfaceDepends( + int depender_index, const cmGeneratorTarget* dependee, + const std::string& config, std::set<std::string>& emitted) +{ + cmGeneratorTarget const* depender = this->Targets[depender_index]; + if (cmLinkInterface const* iface = + dependee->GetLinkInterface(config, depender)) { + for (std::vector<cmLinkItem>::const_iterator lib = + iface->Libraries.begin(); + lib != iface->Libraries.end(); ++lib) { + // Don't emit the same library twice for this target. + if (emitted.insert(*lib).second) { + this->AddTargetDepend(depender_index, *lib, true); + this->AddInterfaceDepends(depender_index, *lib, emitted); + } + } + } +} + +void cmComputeTargetDepends::AddInterfaceDepends( + int depender_index, cmLinkItem const& dependee_name, + std::set<std::string>& emitted) +{ + cmGeneratorTarget const* depender = this->Targets[depender_index]; + cmGeneratorTarget const* dependee = dependee_name.Target; + // Skip targets that will not really be linked. This is probably a + // name conflict between an external library and an executable + // within the project. + if (dependee && dependee->GetType() == cmState::EXECUTABLE && + !dependee->IsExecutableWithExports()) { + dependee = CM_NULLPTR; + } + + if (dependee) { + this->AddInterfaceDepends(depender_index, dependee, "", emitted); + std::vector<std::string> configs; + depender->Makefile->GetConfigurations(configs); + for (std::vector<std::string>::const_iterator it = configs.begin(); + it != configs.end(); ++it) { + // A target should not depend on itself. + emitted.insert(depender->GetName()); + this->AddInterfaceDepends(depender_index, dependee, *it, emitted); + } + } +} + +void cmComputeTargetDepends::AddTargetDepend(int depender_index, + cmLinkItem const& dependee_name, + bool linking) +{ + // Get the depender. + cmGeneratorTarget const* depender = this->Targets[depender_index]; + + // Check the target's makefile first. + cmGeneratorTarget const* dependee = dependee_name.Target; + + if (!dependee && !linking && + (depender->GetType() != cmState::GLOBAL_TARGET)) { + cmake::MessageType messageType = cmake::AUTHOR_WARNING; + bool issueMessage = false; + std::ostringstream e; + switch (depender->GetPolicyStatusCMP0046()) { + case cmPolicies::WARN: + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0046) << "\n"; + issueMessage = true; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + issueMessage = true; + messageType = cmake::FATAL_ERROR; + } + if (issueMessage) { + cmake* cm = this->GlobalGenerator->GetCMakeInstance(); + + e << "The dependency target \"" << dependee_name << "\" of target \"" + << depender->GetName() << "\" does not exist."; + + cmListFileBacktrace const* backtrace = + depender->GetUtilityBacktrace(dependee_name); + if (backtrace) { + cm->IssueMessage(messageType, e.str(), *backtrace); + } else { + cm->IssueMessage(messageType, e.str()); + } + } + } + + // Skip targets that will not really be linked. This is probably a + // name conflict between an external library and an executable + // within the project. + if (linking && dependee && dependee->GetType() == cmState::EXECUTABLE && + !dependee->IsExecutableWithExports()) { + dependee = CM_NULLPTR; + } + + if (dependee) { + this->AddTargetDepend(depender_index, dependee, linking); + } +} + +void cmComputeTargetDepends::AddTargetDepend(int depender_index, + const cmGeneratorTarget* dependee, + bool linking) +{ + if (dependee->IsImported() || + dependee->GetType() == cmState::INTERFACE_LIBRARY) { + // Skip IMPORTED and INTERFACE targets but follow their utility + // dependencies. + std::set<cmLinkItem> const& utils = dependee->GetUtilityItems(); + for (std::set<cmLinkItem>::const_iterator i = utils.begin(); + i != utils.end(); ++i) { + if (cmGeneratorTarget const* transitive_dependee = i->Target) { + this->AddTargetDepend(depender_index, transitive_dependee, false); + } + } + } else { + // Lookup the index for this target. All targets should be known by + // this point. + std::map<cmGeneratorTarget const*, int>::const_iterator tii = + this->TargetIndex.find(dependee); + assert(tii != this->TargetIndex.end()); + int dependee_index = tii->second; + + // Add this entry to the dependency graph. + this->InitialGraph[depender_index].push_back( + cmGraphEdge(dependee_index, !linking)); + } +} + +void cmComputeTargetDepends::DisplayGraph(Graph const& graph, + const std::string& name) +{ + fprintf(stderr, "The %s target dependency graph is:\n", name.c_str()); + int n = static_cast<int>(graph.size()); + for (int depender_index = 0; depender_index < n; ++depender_index) { + EdgeList const& nl = graph[depender_index]; + cmGeneratorTarget const* depender = this->Targets[depender_index]; + fprintf(stderr, "target %d is [%s]\n", depender_index, + depender->GetName().c_str()); + for (EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) { + int dependee_index = *ni; + cmGeneratorTarget const* dependee = this->Targets[dependee_index]; + fprintf(stderr, " depends on target %d [%s] (%s)\n", dependee_index, + dependee->GetName().c_str(), ni->IsStrong() ? "strong" : "weak"); + } + } + fprintf(stderr, "\n"); +} + +void cmComputeTargetDepends::DisplayComponents( + cmComputeComponentGraph const& ccg) +{ + fprintf(stderr, "The strongly connected components are:\n"); + std::vector<NodeList> const& components = ccg.GetComponents(); + int n = static_cast<int>(components.size()); + for (int c = 0; c < n; ++c) { + NodeList const& nl = components[c]; + fprintf(stderr, "Component (%d):\n", c); + for (NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) { + int i = *ni; + fprintf(stderr, " contains target %d [%s]\n", i, + this->Targets[i]->GetName().c_str()); + } + } + fprintf(stderr, "\n"); +} + +bool cmComputeTargetDepends::CheckComponents( + cmComputeComponentGraph const& ccg) +{ + // All non-trivial components should consist only of static + // libraries. + 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. + NodeList const& nl = components[c]; + + // Skip trivial components. + if (nl.size() < 2) { + continue; + } + + // Immediately complain if no cycles are allowed at all. + if (this->NoCycles) { + this->ComplainAboutBadComponent(ccg, c); + return false; + } + + // Make sure the component is all STATIC_LIBRARY targets. + for (NodeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) { + if (this->Targets[*ni]->GetType() != cmState::STATIC_LIBRARY) { + this->ComplainAboutBadComponent(ccg, c); + return false; + } + } + } + return true; +} + +void cmComputeTargetDepends::ComplainAboutBadComponent( + cmComputeComponentGraph const& ccg, int c, bool strong) +{ + // Construct the error message. + std::ostringstream e; + e << "The inter-target dependency graph contains the following " + << "strongly connected component (cycle):\n"; + 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; + cmGeneratorTarget const* depender = this->Targets[i]; + + // Describe the depender. + e << " \"" << depender->GetName() << "\" of type " + << cmState::GetTargetTypeName(depender->GetType()) << "\n"; + + // List its dependencies that are inside the component. + EdgeList const& nl = this->InitialGraph[i]; + for (EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) { + int j = *ni; + if (cmap[j] == c) { + cmGeneratorTarget const* dependee = this->Targets[j]; + e << " depends on \"" << dependee->GetName() << "\"" + << " (" << (ni->IsStrong() ? "strong" : "weak") << ")\n"; + } + } + } + if (strong) { + // Custom command executable dependencies cannot occur within a + // component of static libraries. The cycle must appear in calls + // to add_dependencies. + e << "The component contains at least one cycle consisting of strong " + << "dependencies (created by add_dependencies) that cannot be broken."; + } else if (this->NoCycles) { + e << "The GLOBAL_DEPENDS_NO_CYCLES global property is enabled, so " + << "cyclic dependencies are not allowed even among static libraries."; + } else { + e << "At least one of these targets is not a STATIC_LIBRARY. " + << "Cyclic dependencies are allowed only among static libraries."; + } + cmSystemTools::Error(e.str().c_str()); +} + +bool cmComputeTargetDepends::IntraComponent(std::vector<int> const& cmap, + int c, int i, int* head, + std::set<int>& emitted, + std::set<int>& visited) +{ + if (!visited.insert(i).second) { + // Cycle in utility depends! + return false; + } + if (emitted.insert(i).second) { + // Honor strong intra-component edges in the final order. + EdgeList const& el = this->InitialGraph[i]; + for (EdgeList::const_iterator ei = el.begin(); ei != el.end(); ++ei) { + int j = *ei; + if (cmap[j] == c && ei->IsStrong()) { + this->FinalGraph[i].push_back(cmGraphEdge(j, true)); + if (!this->IntraComponent(cmap, c, j, head, emitted, visited)) { + return false; + } + } + } + + // Prepend to a linear linked-list of intra-component edges. + if (*head >= 0) { + this->FinalGraph[i].push_back(cmGraphEdge(*head, false)); + } else { + this->ComponentTail[c] = i; + } + *head = i; + } + return true; +} + +bool cmComputeTargetDepends::ComputeFinalDepends( + cmComputeComponentGraph const& ccg) +{ + // 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()); + + // Choose intra-component edges to linearize dependencies. + std::vector<int> const& cmap = ccg.GetComponentMap(); + this->ComponentHead.resize(components.size()); + this->ComponentTail.resize(components.size()); + int nc = static_cast<int>(components.size()); + for (int c = 0; c < nc; ++c) { + int head = -1; + std::set<int> emitted; + NodeList const& nl = components[c]; + for (NodeList::const_reverse_iterator ni = nl.rbegin(); ni != nl.rend(); + ++ni) { + std::set<int> visited; + if (!this->IntraComponent(cmap, c, *ni, &head, emitted, visited)) { + // Cycle in add_dependencies within component! + this->ComplainAboutBadComponent(ccg, c, true); + return false; + } + } + this->ComponentHead[c] = head; + } + + // Convert inter-component edges to connect component tails to heads. + int n = static_cast<int>(cgraph.size()); + for (int depender_component = 0; depender_component < n; + ++depender_component) { + int depender_component_tail = this->ComponentTail[depender_component]; + EdgeList const& nl = cgraph[depender_component]; + for (EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) { + int dependee_component = *ni; + int dependee_component_head = this->ComponentHead[dependee_component]; + this->FinalGraph[depender_component_tail].push_back( + cmGraphEdge(dependee_component_head, ni->IsStrong())); + } + } + return true; +} |