| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmComputeTargetDepends.h" | |
| #include <cassert> | |
| #include <cstddef> | |
| #include <cstdio> | |
| #include <memory> | |
| #include <sstream> | |
| #include <utility> | |
| #include "cmComputeComponentGraph.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmLinkItem.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmRange.h" | |
| #include "cmSourceFile.h" | |
| #include "cmSourceFileLocationKind.h" | |
| #include "cmState.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetDepend.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| #include "cmake.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() = default; | |
| bool cmComputeTargetDepends::Compute() | |
| { | |
| // Build the original graph. | |
| this->CollectTargets(); | |
| this->CollectDepends(); | |
| if (this->DebugMode) { | |
| this->DisplayGraph(this->InitialGraph, "initial"); | |
| } | |
| cmComputeComponentGraph ccg1(this->InitialGraph); | |
| ccg1.Compute(); | |
| if (!this->CheckComponents(ccg1)) { | |
| return false; | |
| } | |
| // Compute the intermediate graph. | |
| this->CollectSideEffects(); | |
| this->ComputeIntermediateGraph(); | |
| if (this->DebugMode) { | |
| this->DisplaySideEffects(); | |
| this->DisplayGraph(this->IntermediateGraph, "intermediate"); | |
| } | |
| // Identify components. | |
| cmComputeComponentGraph ccg2(this->IntermediateGraph); | |
| ccg2.Compute(); | |
| if (this->DebugMode) { | |
| this->DisplayComponents(ccg2, "intermediate"); | |
| } | |
| if (!this->CheckComponents(ccg2)) { | |
| return false; | |
| } | |
| // Compute the final dependency graph. | |
| if (!this->ComputeFinalDepends(ccg2)) { | |
| 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. | |
| auto tii = this->TargetIndex.find(t); | |
| assert(tii != this->TargetIndex.end()); | |
| size_t i = tii->second; | |
| // Get its final dependencies. | |
| EdgeList const& nl = this->FinalGraph[i]; | |
| for (cmGraphEdge const& ni : nl) { | |
| cmGeneratorTarget const* dep = this->Targets[ni]; | |
| auto di = deps.insert(dep).first; | |
| di->SetType(ni.IsStrong()); | |
| di->SetCross(ni.IsCross()); | |
| di->SetBacktrace(ni.GetBacktrace()); | |
| } | |
| } | |
| void cmComputeTargetDepends::CollectTargets() | |
| { | |
| // Collect all targets from all generators. | |
| auto const& lgens = this->GlobalGenerator->GetLocalGenerators(); | |
| for (auto const& lgen : lgens) { | |
| for (auto const& ti : lgen->GetGeneratorTargets()) { | |
| size_t index = this->Targets.size(); | |
| this->TargetIndex[ti.get()] = index; | |
| this->Targets.push_back(ti.get()); | |
| } | |
| } | |
| } | |
| void cmComputeTargetDepends::CollectDepends() | |
| { | |
| // Allocate the dependency graph adjacency lists. | |
| this->InitialGraph.resize(this->Targets.size()); | |
| // Compute each dependency list. | |
| for (size_t i = 0; i < this->Targets.size(); ++i) { | |
| this->CollectTargetDepends(i); | |
| } | |
| } | |
| void cmComputeTargetDepends::CollectTargetDepends(size_t depender_index) | |
| { | |
| // Get the depender. | |
| cmGeneratorTarget const* depender = this->Targets[depender_index]; | |
| if (!depender->IsInBuildSystem()) { | |
| 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<cmLinkItem> emitted; | |
| std::vector<std::string> const& configs = | |
| depender->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| for (std::string const& it : configs) { | |
| // A target should not depend on itself. | |
| emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); | |
| emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); | |
| // Build link dependencies before the current target. | |
| if (cmLinkImplementation const* impl = depender->GetLinkImplementation( | |
| it, cmGeneratorTarget::UseTo::Link)) { | |
| for (cmLinkItem const& lib : impl->Libraries) { | |
| // Don't emit the same library twice for this target. | |
| if (emitted.insert(lib).second) { | |
| this->AddTargetDepend(depender_index, lib, true, false, emitted); | |
| this->AddInterfaceDepends(depender_index, lib, it, emitted); | |
| } | |
| } | |
| for (cmLinkItem const& obj : impl->Objects) { | |
| if (cmSourceFile const* o = depender->Makefile->GetSource( | |
| obj.AsStr(), cmSourceFileLocationKind::Known)) { | |
| this->AddObjectDepends(depender_index, o, emitted); | |
| } | |
| } | |
| } | |
| // Build compile dependencies before the current target. | |
| if (cmLinkImplementation const* impl = depender->GetLinkImplementation( | |
| it, cmGeneratorTarget::UseTo::Compile)) { | |
| for (cmLinkItem const& lib : impl->Libraries) { | |
| // Don't emit the same library twice for this target. | |
| if (emitted.insert(lib).second) { | |
| this->AddTargetDepend(depender_index, lib, true, false, emitted); | |
| this->AddInterfaceDepends(depender_index, lib, it, emitted); | |
| } | |
| } | |
| } | |
| // Add dependencies on object libraries not otherwise handled above. | |
| std::vector<cmSourceFile const*> objectFiles; | |
| depender->GetExternalObjects(objectFiles, it); | |
| for (cmSourceFile const* o : objectFiles) { | |
| this->AddObjectDepends(depender_index, o, emitted); | |
| } | |
| } | |
| } | |
| // Loop over all utility dependencies. | |
| { | |
| std::set<cmLinkItem> const& tutils = depender->GetUtilityItems(); | |
| std::set<cmLinkItem> emitted; | |
| // A target should not depend on itself. | |
| emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); | |
| emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); | |
| for (cmLinkItem const& litem : tutils) { | |
| // Don't emit the same utility twice for this target. | |
| if (emitted.insert(litem).second) { | |
| this->AddTargetDepend(depender_index, litem, false, litem.Cross, | |
| emitted); | |
| } | |
| } | |
| } | |
| } | |
| void cmComputeTargetDepends::AddInterfaceDepends( | |
| size_t depender_index, cmGeneratorTarget const* dependee, | |
| cmListFileBacktrace const& dependee_backtrace, std::string const& config, | |
| std::set<cmLinkItem>& emitted) | |
| { | |
| cmGeneratorTarget const* depender = this->Targets[depender_index]; | |
| if (cmLinkInterface const* iface = | |
| dependee->GetLinkInterface(config, depender)) { | |
| for (cmLinkItem const& lib : iface->Libraries) { | |
| // Don't emit the same library twice for this target. | |
| if (emitted.insert(lib).second) { | |
| // Inject the backtrace of the original link dependency whose | |
| // link interface we are adding. This indicates the line of | |
| // code in the project that caused this dependency to be added. | |
| cmLinkItem libBT = lib; | |
| libBT.Backtrace = dependee_backtrace; | |
| this->AddTargetDepend(depender_index, libBT, true, false, emitted); | |
| this->AddInterfaceDepends(depender_index, libBT, config, emitted); | |
| } | |
| } | |
| for (cmLinkItem const& obj : iface->Objects) { | |
| if (cmSourceFile const* o = depender->Makefile->GetSource( | |
| obj.AsStr(), cmSourceFileLocationKind::Known)) { | |
| this->AddObjectDepends(depender_index, o, emitted); | |
| } | |
| } | |
| } | |
| } | |
| void cmComputeTargetDepends::AddInterfaceDepends( | |
| size_t depender_index, cmLinkItem const& dependee_name, | |
| std::string const& config, std::set<cmLinkItem>& 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() == cm::TargetType::EXECUTABLE && | |
| !dependee->IsExecutableWithExports()) { | |
| dependee = nullptr; | |
| } | |
| if (dependee) { | |
| // A target should not depend on itself. | |
| emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); | |
| emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); | |
| this->AddInterfaceDepends(depender_index, dependee, | |
| dependee_name.Backtrace, config, emitted); | |
| } | |
| } | |
| void cmComputeTargetDepends::AddObjectDepends(size_t depender_index, | |
| cmSourceFile const* o, | |
| std::set<cmLinkItem>& emitted) | |
| { | |
| std::string const& objLib = o->GetObjectLibrary(); | |
| if (objLib.empty()) { | |
| return; | |
| } | |
| cmGeneratorTarget const* depender = this->Targets[depender_index]; | |
| cmLinkItem const& objItem = | |
| depender->ResolveLinkItem(BT<std::string>(objLib)); | |
| if (emitted.insert(objItem).second) { | |
| if (depender->GetType() != cm::TargetType::EXECUTABLE && | |
| depender->GetType() != cm::TargetType::STATIC_LIBRARY && | |
| depender->GetType() != cm::TargetType::SHARED_LIBRARY && | |
| depender->GetType() != cm::TargetType::MODULE_LIBRARY && | |
| depender->GetType() != cm::TargetType::OBJECT_LIBRARY) { | |
| this->GlobalGenerator->GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "Only executables and libraries may reference target objects.", | |
| depender->GetBacktrace()); | |
| return; | |
| } | |
| const_cast<cmGeneratorTarget*>(depender)->Target->AddUtility(objLib, | |
| false); | |
| } | |
| } | |
| void cmComputeTargetDepends::AddTargetDepend(size_t depender_index, | |
| cmLinkItem const& dependee_name, | |
| bool linking, bool cross, | |
| std::set<cmLinkItem>& emitted) | |
| { | |
| // 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() != cm::TargetType::GLOBAL_TARGET)) { | |
| this->GlobalGenerator->GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The dependency target \"", dependee_name.AsStr(), | |
| "\" of target \"", depender->GetName(), "\" does not exist."), | |
| dependee_name.Backtrace); | |
| } | |
| // 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() == cm::TargetType::EXECUTABLE && | |
| !dependee->IsExecutableWithExports()) { | |
| dependee = nullptr; | |
| } | |
| if (dependee) { | |
| this->AddTargetDepend(depender_index, dependee, dependee_name.Backtrace, | |
| linking, cross, emitted); | |
| } | |
| } | |
| void cmComputeTargetDepends::AddTargetDepend( | |
| size_t depender_index, cmGeneratorTarget const* dependee, | |
| cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross, | |
| std::set<cmLinkItem>& emitted) | |
| { | |
| if (!dependee->IsInBuildSystem()) { | |
| // Skip targets that are not in the buildsystem but follow their | |
| // utility dependencies. | |
| std::set<cmLinkItem> const& utils = dependee->GetUtilityItems(); | |
| for (cmLinkItem const& i : utils) { | |
| if (emitted.insert(i).second) { | |
| if (cmGeneratorTarget const* transitive_dependee = i.Target) { | |
| this->AddTargetDepend(depender_index, transitive_dependee, | |
| i.Backtrace, false, i.Cross, emitted); | |
| } | |
| } | |
| } | |
| } else { | |
| // Lookup the index for this target. All targets should be known by | |
| // this point. | |
| auto tii = this->TargetIndex.find(dependee); | |
| assert(tii != this->TargetIndex.end()); | |
| size_t dependee_index = tii->second; | |
| // Add this entry to the dependency graph. | |
| this->InitialGraph[depender_index].emplace_back(dependee_index, !linking, | |
| cross, dependee_backtrace); | |
| } | |
| } | |
| void cmComputeTargetDepends::CollectSideEffects() | |
| { | |
| this->SideEffects.resize(0); | |
| this->SideEffects.resize(this->InitialGraph.size()); | |
| size_t n = this->InitialGraph.size(); | |
| std::set<size_t> visited; | |
| for (size_t i = 0; i < n; ++i) { | |
| this->CollectSideEffectsForTarget(visited, i); | |
| } | |
| } | |
| void cmComputeTargetDepends::CollectSideEffectsForTarget( | |
| std::set<size_t>& visited, size_t depender_index) | |
| { | |
| if (!visited.count(depender_index)) { | |
| auto& se = this->SideEffects[depender_index]; | |
| visited.insert(depender_index); | |
| this->Targets[depender_index]->AppendCustomCommandSideEffects( | |
| se.CustomCommandSideEffects); | |
| this->Targets[depender_index]->AppendLanguageSideEffects( | |
| se.LanguageSideEffects); | |
| for (auto const& edge : this->InitialGraph[depender_index]) { | |
| this->CollectSideEffectsForTarget(visited, edge); | |
| auto const& dse = this->SideEffects[edge]; | |
| se.CustomCommandSideEffects.insert(dse.CustomCommandSideEffects.cbegin(), | |
| dse.CustomCommandSideEffects.cend()); | |
| for (auto const& it : dse.LanguageSideEffects) { | |
| se.LanguageSideEffects[it.first].insert(it.second.cbegin(), | |
| it.second.cend()); | |
| } | |
| } | |
| } | |
| } | |
| void cmComputeTargetDepends::ComputeIntermediateGraph() | |
| { | |
| this->IntermediateGraph.resize(0); | |
| this->IntermediateGraph.resize(this->InitialGraph.size()); | |
| size_t n = this->InitialGraph.size(); | |
| for (size_t i = 0; i < n; ++i) { | |
| auto const& initialEdges = this->InitialGraph[i]; | |
| auto& intermediateEdges = this->IntermediateGraph[i]; | |
| cmGeneratorTarget const* gt = this->Targets[i]; | |
| if (gt->GetType() != cm::TargetType::STATIC_LIBRARY && | |
| gt->GetType() != cm::TargetType::OBJECT_LIBRARY) { | |
| intermediateEdges = initialEdges; | |
| } else { | |
| if (cmValue optimizeDependencies = | |
| gt->GetProperty("OPTIMIZE_DEPENDENCIES")) { | |
| if (optimizeDependencies.IsOn()) { | |
| this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges); | |
| } else { | |
| intermediateEdges = initialEdges; | |
| } | |
| } else { | |
| intermediateEdges = initialEdges; | |
| } | |
| } | |
| } | |
| } | |
| void cmComputeTargetDepends::OptimizeLinkDependencies( | |
| cmGeneratorTarget const* gt, cmGraphEdgeList& outputEdges, | |
| cmGraphEdgeList const& inputEdges) | |
| { | |
| std::set<size_t> emitted; | |
| for (auto const& edge : inputEdges) { | |
| if (edge.IsStrong()) { | |
| // Preserve strong edges | |
| outputEdges.push_back(edge); | |
| } else { | |
| auto const& dse = this->SideEffects[edge]; | |
| // Add edges that have custom command side effects | |
| for (cmGeneratorTarget const* dep : dse.CustomCommandSideEffects) { | |
| auto index = this->TargetIndex[dep]; | |
| if (!emitted.count(index)) { | |
| emitted.insert(index); | |
| outputEdges.push_back( | |
| cmGraphEdge(index, false, edge.IsCross(), edge.GetBacktrace())); | |
| } | |
| } | |
| // Add edges that have language side effects for languages we | |
| // care about | |
| for (auto const& lang : gt->GetAllConfigCompileLanguages()) { | |
| auto it = dse.LanguageSideEffects.find(lang); | |
| if (it != dse.LanguageSideEffects.end()) { | |
| for (cmGeneratorTarget const* dep : it->second) { | |
| auto index = this->TargetIndex[dep]; | |
| if (!emitted.count(index)) { | |
| emitted.insert(index); | |
| outputEdges.push_back(cmGraphEdge(index, false, edge.IsCross(), | |
| edge.GetBacktrace())); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void cmComputeTargetDepends::DisplayGraph(Graph const& graph, | |
| std::string const& name) | |
| { | |
| fprintf(stderr, "The %s target dependency graph is:\n", name.c_str()); | |
| size_t n = graph.size(); | |
| for (size_t depender_index = 0; depender_index < n; ++depender_index) { | |
| EdgeList const& nl = graph[depender_index]; | |
| cmGeneratorTarget const* depender = this->Targets[depender_index]; | |
| fprintf(stderr, "target %zu is [%s]\n", depender_index, | |
| depender->GetName().c_str()); | |
| for (cmGraphEdge const& ni : nl) { | |
| size_t dependee_index = ni; | |
| cmGeneratorTarget const* dependee = this->Targets[dependee_index]; | |
| fprintf(stderr, " depends on target %zu [%s] (%s)\n", dependee_index, | |
| dependee->GetName().c_str(), ni.IsStrong() ? "strong" : "weak"); | |
| } | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| void cmComputeTargetDepends::DisplaySideEffects() | |
| { | |
| fprintf(stderr, "The side effects are:\n"); | |
| size_t n = this->SideEffects.size(); | |
| for (size_t depender_index = 0; depender_index < n; ++depender_index) { | |
| cmGeneratorTarget const* depender = this->Targets[depender_index]; | |
| fprintf(stderr, "target %zu is [%s]\n", depender_index, | |
| depender->GetName().c_str()); | |
| if (!this->SideEffects[depender_index].CustomCommandSideEffects.empty()) { | |
| fprintf(stderr, " custom commands\n"); | |
| for (auto const* gt : | |
| this->SideEffects[depender_index].CustomCommandSideEffects) { | |
| fprintf(stderr, " from target %zu [%s]\n", this->TargetIndex[gt], | |
| gt->GetName().c_str()); | |
| } | |
| } | |
| for (auto const& it : | |
| this->SideEffects[depender_index].LanguageSideEffects) { | |
| fprintf(stderr, " language %s\n", it.first.c_str()); | |
| for (auto const* gt : it.second) { | |
| fprintf(stderr, " from target %zu [%s]\n", this->TargetIndex[gt], | |
| gt->GetName().c_str()); | |
| } | |
| } | |
| } | |
| fprintf(stderr, "\n"); | |
| } | |
| void cmComputeTargetDepends::DisplayComponents( | |
| cmComputeComponentGraph const& ccg, std::string const& name) | |
| { | |
| fprintf(stderr, "The strongly connected components for the %s graph are:\n", | |
| name.c_str()); | |
| std::vector<NodeList> const& components = ccg.GetComponents(); | |
| size_t n = components.size(); | |
| for (size_t c = 0; c < n; ++c) { | |
| NodeList const& nl = components[c]; | |
| fprintf(stderr, "Component (%zu):\n", c); | |
| for (size_t i : nl) { | |
| fprintf(stderr, " contains target %zu [%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(); | |
| size_t nc = components.size(); | |
| for (size_t 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 (size_t ni : nl) { | |
| if (this->Targets[ni]->GetType() != cm::TargetType::STATIC_LIBRARY) { | |
| this->ComplainAboutBadComponent(ccg, c); | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| void cmComputeTargetDepends::ComplainAboutBadComponent( | |
| cmComputeComponentGraph const& ccg, size_t 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<size_t> const& cmap = ccg.GetComponentMap(); | |
| NodeList const& cl = components[c]; | |
| for (size_t i : cl) { | |
| // Get the depender. | |
| 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 (cmGraphEdge const& ni : nl) { | |
| size_t 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()); | |
| } | |
| bool cmComputeTargetDepends::IntraComponent(std::vector<size_t> const& cmap, | |
| size_t c, size_t i, size_t* head, | |
| std::set<size_t>& emitted, | |
| std::set<size_t>& 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 (cmGraphEdge const& edge : el) { | |
| size_t j = edge; | |
| if (cmap[j] == c && edge.IsStrong()) { | |
| this->FinalGraph[i].emplace_back(j, true, edge.IsCross(), | |
| edge.GetBacktrace()); | |
| if (!this->IntraComponent(cmap, c, j, head, emitted, visited)) { | |
| return false; | |
| } | |
| } | |
| } | |
| // Prepend to a linear linked-list of intra-component edges. | |
| if (*head != cmComputeComponentGraph::INVALID_COMPONENT) { | |
| this->FinalGraph[i].emplace_back(*head, false, false, | |
| cmListFileBacktrace()); | |
| } 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<size_t> const& cmap = ccg.GetComponentMap(); | |
| this->ComponentHead.resize(components.size()); | |
| this->ComponentTail.resize(components.size()); | |
| size_t nc = components.size(); | |
| for (size_t c = 0; c < nc; ++c) { | |
| size_t head = cmComputeComponentGraph::INVALID_COMPONENT; | |
| std::set<size_t> emitted; | |
| NodeList const& nl = components[c]; | |
| for (size_t ni : cmReverseRange(nl)) { | |
| std::set<size_t> 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. | |
| size_t n = cgraph.size(); | |
| for (size_t depender_component = 0; depender_component < n; | |
| ++depender_component) { | |
| size_t depender_component_tail = this->ComponentTail[depender_component]; | |
| EdgeList const& nl = cgraph[depender_component]; | |
| for (cmGraphEdge const& ni : nl) { | |
| size_t dependee_component = ni; | |
| size_t dependee_component_head = this->ComponentHead[dependee_component]; | |
| this->FinalGraph[depender_component_tail].emplace_back( | |
| dependee_component_head, ni.IsStrong(), ni.IsCross(), | |
| ni.GetBacktrace()); | |
| } | |
| } | |
| return true; | |
| } |