|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
file Copyright.txt 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 "cmPolicies.h"
|
|
|
|
#include "cmRange.h"
|
|
|
|
#include "cmSourceFile.h"
|
|
|
|
#include "cmSourceFileLocationKind.h"
|
|
|
|
#include "cmState.h"
|
|
|
|
#include "cmStateTypes.h"
|
|
|
|
#include "cmSystemTools.h"
|
|
|
|
#include "cmTarget.h"
|
|
|
|
#include "cmTargetDepend.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 (const auto& lgen : lgens) {
|
|
|
|
for (const auto& 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()));
|
|
|
|
|
|
|
|
if (cmLinkImplementation const* impl = depender->GetLinkImplementation(
|
|
|
|
it, cmGeneratorTarget::UseTo::Link)) {
|
|
|
|
for (cmLinkImplItem 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);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmComputeTargetDepends::AddInterfaceDepends(
|
|
|
|
size_t depender_index, const cmGeneratorTarget* dependee,
|
|
|
|
cmListFileBacktrace const& dependee_backtrace, const std::string& 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);
|
|
|
|
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,
|
|
|
|
const std::string& 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() == cmStateEnums::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() != cmStateEnums::EXECUTABLE &&
|
|
|
|
depender->GetType() != cmStateEnums::STATIC_LIBRARY &&
|
|
|
|
depender->GetType() != cmStateEnums::SHARED_LIBRARY &&
|
|
|
|
depender->GetType() != cmStateEnums::MODULE_LIBRARY &&
|
|
|
|
depender->GetType() != cmStateEnums::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)
|
|
|
|
{
|
|
|
|
// 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() != cmStateEnums::GLOBAL_TARGET)) {
|
|
|
|
MessageType messageType = MessageType::AUTHOR_WARNING;
|
|
|
|
bool issueMessage = false;
|
|
|
|
std::ostringstream e;
|
|
|
|
switch (depender->GetPolicyStatusCMP0046()) {
|
|
|
|
case cmPolicies::WARN:
|
|
|
|
e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0046) << "\n";
|
|
|
|
issueMessage = true;
|
|
|
|
CM_FALLTHROUGH;
|
|
|
|
case cmPolicies::OLD:
|
|
|
|
break;
|
|
|
|
case cmPolicies::NEW:
|
|
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
|
|
issueMessage = true;
|
|
|
|
messageType = MessageType::FATAL_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (issueMessage) {
|
|
|
|
cmake* cm = this->GlobalGenerator->GetCMakeInstance();
|
|
|
|
|
|
|
|
e << "The dependency target \"" << dependee_name << "\" of target \""
|
|
|
|
<< depender->GetName() << "\" does not exist.";
|
|
|
|
|
|
|
|
cm->IssueMessage(messageType, e.str(), 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() == cmStateEnums::EXECUTABLE &&
|
|
|
|
!dependee->IsExecutableWithExports()) {
|
|
|
|
dependee = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dependee) {
|
|
|
|
this->AddTargetDepend(depender_index, dependee, dependee_name.Backtrace,
|
|
|
|
linking, cross);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmComputeTargetDepends::AddTargetDepend(
|
|
|
|
size_t depender_index, cmGeneratorTarget const* dependee,
|
|
|
|
cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross)
|
|
|
|
{
|
|
|
|
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 (cmGeneratorTarget const* transitive_dependee = i.Target) {
|
|
|
|
this->AddTargetDepend(depender_index, transitive_dependee, i.Backtrace,
|
|
|
|
false, i.Cross);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} 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() != cmStateEnums::STATIC_LIBRARY &&
|
|
|
|
gt->GetType() != cmStateEnums::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,
|
|
|
|
const std::string& 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, const std::string& 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() != cmStateEnums::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;
|
|
|
|
}
|