Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmComputeTargetDepends.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmComputeTargetDepends.h"
4
5
#include <cassert>
6
#include <cstddef>
7
#include <cstdio>
8
#include <memory>
9
#include <sstream>
10
#include <utility>
11
12
#include "cmComputeComponentGraph.h"
13
#include "cmGeneratorTarget.h"
14
#include "cmGlobalGenerator.h"
15
#include "cmLinkItem.h"
16
#include "cmListFileCache.h"
17
#include "cmLocalGenerator.h"
18
#include "cmMakefile.h"
19
#include "cmMessageType.h"
20
#include "cmRange.h"
21
#include "cmSourceFile.h"
22
#include "cmSourceFileLocationKind.h"
23
#include "cmState.h"
24
#include "cmStateTypes.h"
25
#include "cmStringAlgorithms.h"
26
#include "cmSystemTools.h"
27
#include "cmTarget.h"
28
#include "cmTargetDepend.h"
29
#include "cmValue.h"
30
#include "cmake.h"
31
32
/*
33
34
This class is meant to analyze inter-target dependencies globally
35
during the generation step.  The goal is to produce a set of direct
36
dependencies for each target such that no cycles are left and the
37
build order is safe.
38
39
For most target types cyclic dependencies are not allowed.  However
40
STATIC libraries may depend on each other in a cyclic fashion.  In
41
general the directed dependency graph forms a directed-acyclic-graph
42
of strongly connected components.  All strongly connected components
43
should consist of only STATIC_LIBRARY targets.
44
45
In order to safely break dependency cycles we must preserve all other
46
dependencies passing through the corresponding strongly connected component.
47
The approach taken by this class is as follows:
48
49
  - Collect all targets and form the original dependency graph
50
  - Run Tarjan's algorithm to extract the strongly connected components
51
    (error if any member of a non-trivial component is not STATIC)
52
  - The original dependencies imply a DAG on the components.
53
    Use the implied DAG to construct a final safe set of dependencies.
54
55
The final dependency set is constructed as follows:
56
57
  - For each connected component targets are placed in an arbitrary
58
    order.  Each target depends on the target following it in the order.
59
    The first target is designated the head and the last target the tail.
60
    (most components will be just 1 target anyway)
61
62
  - Original dependencies between targets in different components are
63
    converted to connect the depender's component tail to the
64
    dependee's component head.
65
66
In most cases this will reproduce the original dependencies.  However
67
when there are cycles of static libraries they will be broken in a
68
safe manner.
69
70
For example, consider targets A0, A1, A2, B0, B1, B2, and C with these
71
dependencies:
72
73
  A0 -> A1 -> A2 -> A0  ,  B0 -> B1 -> B2 -> B0 -> A0  ,  C -> B0
74
75
Components may be identified as
76
77
  Component 0: A0, A1, A2
78
  Component 1: B0, B1, B2
79
  Component 2: C
80
81
Intra-component dependencies are:
82
83
  0: A0 -> A1 -> A2   , head=A0, tail=A2
84
  1: B0 -> B1 -> B2   , head=B0, tail=B2
85
  2: head=C, tail=C
86
87
The inter-component dependencies are converted as:
88
89
  B0 -> A0  is component 1->0 and becomes  B2 -> A0
90
  C  -> B0  is component 2->1 and becomes  C  -> B0
91
92
This leads to the final target dependencies:
93
94
  C -> B0 -> B1 -> B2 -> A0 -> A1 -> A2
95
96
These produce a safe build order since C depends directly or
97
transitively on all the static libraries it links.
98
99
*/
100
101
cmComputeTargetDepends::cmComputeTargetDepends(cmGlobalGenerator* gg)
102
0
{
103
0
  this->GlobalGenerator = gg;
104
0
  cmake* cm = this->GlobalGenerator->GetCMakeInstance();
105
0
  this->DebugMode =
106
0
    cm->GetState()->GetGlobalPropertyAsBool("GLOBAL_DEPENDS_DEBUG_MODE");
107
0
  this->NoCycles =
108
0
    cm->GetState()->GetGlobalPropertyAsBool("GLOBAL_DEPENDS_NO_CYCLES");
109
0
}
110
111
0
cmComputeTargetDepends::~cmComputeTargetDepends() = default;
112
113
bool cmComputeTargetDepends::Compute()
114
0
{
115
  // Build the original graph.
116
0
  this->CollectTargets();
117
0
  this->CollectDepends();
118
0
  if (this->DebugMode) {
119
0
    this->DisplayGraph(this->InitialGraph, "initial");
120
0
  }
121
0
  cmComputeComponentGraph ccg1(this->InitialGraph);
122
0
  ccg1.Compute();
123
0
  if (!this->CheckComponents(ccg1)) {
124
0
    return false;
125
0
  }
126
127
  // Compute the intermediate graph.
128
0
  this->CollectSideEffects();
129
0
  this->ComputeIntermediateGraph();
130
0
  if (this->DebugMode) {
131
0
    this->DisplaySideEffects();
132
0
    this->DisplayGraph(this->IntermediateGraph, "intermediate");
133
0
  }
134
135
  // Identify components.
136
0
  cmComputeComponentGraph ccg2(this->IntermediateGraph);
137
0
  ccg2.Compute();
138
0
  if (this->DebugMode) {
139
0
    this->DisplayComponents(ccg2, "intermediate");
140
0
  }
141
0
  if (!this->CheckComponents(ccg2)) {
142
0
    return false;
143
0
  }
144
145
  // Compute the final dependency graph.
146
0
  if (!this->ComputeFinalDepends(ccg2)) {
147
0
    return false;
148
0
  }
149
0
  if (this->DebugMode) {
150
0
    this->DisplayGraph(this->FinalGraph, "final");
151
0
  }
152
153
0
  return true;
154
0
}
155
156
void cmComputeTargetDepends::GetTargetDirectDepends(cmGeneratorTarget const* t,
157
                                                    cmTargetDependSet& deps)
158
0
{
159
  // Lookup the index for this target.  All targets should be known by
160
  // this point.
161
0
  auto tii = this->TargetIndex.find(t);
162
0
  assert(tii != this->TargetIndex.end());
163
0
  size_t i = tii->second;
164
165
  // Get its final dependencies.
166
0
  EdgeList const& nl = this->FinalGraph[i];
167
0
  for (cmGraphEdge const& ni : nl) {
168
0
    cmGeneratorTarget const* dep = this->Targets[ni];
169
0
    auto di = deps.insert(dep).first;
170
0
    di->SetType(ni.IsStrong());
171
0
    di->SetCross(ni.IsCross());
172
0
    di->SetBacktrace(ni.GetBacktrace());
173
0
  }
174
0
}
175
176
void cmComputeTargetDepends::CollectTargets()
177
0
{
178
  // Collect all targets from all generators.
179
0
  auto const& lgens = this->GlobalGenerator->GetLocalGenerators();
180
0
  for (auto const& lgen : lgens) {
181
0
    for (auto const& ti : lgen->GetGeneratorTargets()) {
182
0
      size_t index = this->Targets.size();
183
0
      this->TargetIndex[ti.get()] = index;
184
0
      this->Targets.push_back(ti.get());
185
0
    }
186
0
  }
187
0
}
188
189
void cmComputeTargetDepends::CollectDepends()
190
0
{
191
  // Allocate the dependency graph adjacency lists.
192
0
  this->InitialGraph.resize(this->Targets.size());
193
194
  // Compute each dependency list.
195
0
  for (size_t i = 0; i < this->Targets.size(); ++i) {
196
0
    this->CollectTargetDepends(i);
197
0
  }
198
0
}
199
200
void cmComputeTargetDepends::CollectTargetDepends(size_t depender_index)
201
0
{
202
  // Get the depender.
203
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
204
0
  if (!depender->IsInBuildSystem()) {
205
0
    return;
206
0
  }
207
208
  // Loop over all targets linked directly in all configs.
209
  // We need to make targets depend on the union of all config-specific
210
  // dependencies in all targets, because the generated build-systems can't
211
  // deal with config-specific dependencies.
212
0
  {
213
0
    std::set<cmLinkItem> emitted;
214
215
0
    std::vector<std::string> const& configs =
216
0
      depender->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
217
0
    for (std::string const& it : configs) {
218
      // A target should not depend on itself.
219
0
      emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace()));
220
0
      emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace()));
221
222
0
      if (cmLinkImplementation const* impl = depender->GetLinkImplementation(
223
0
            it, cmGeneratorTarget::UseTo::Link)) {
224
0
        for (cmLinkItem const& lib : impl->Libraries) {
225
          // Don't emit the same library twice for this target.
226
0
          if (emitted.insert(lib).second) {
227
0
            this->AddTargetDepend(depender_index, lib, true, false, emitted);
228
0
            this->AddInterfaceDepends(depender_index, lib, it, emitted);
229
0
          }
230
0
        }
231
0
        for (cmLinkItem const& obj : impl->Objects) {
232
0
          if (cmSourceFile const* o = depender->Makefile->GetSource(
233
0
                obj.AsStr(), cmSourceFileLocationKind::Known)) {
234
0
            this->AddObjectDepends(depender_index, o, emitted);
235
0
          }
236
0
        }
237
0
      }
238
239
      // Add dependencies on object libraries not otherwise handled above.
240
0
      std::vector<cmSourceFile const*> objectFiles;
241
0
      depender->GetExternalObjects(objectFiles, it);
242
0
      for (cmSourceFile const* o : objectFiles) {
243
0
        this->AddObjectDepends(depender_index, o, emitted);
244
0
      }
245
0
    }
246
0
  }
247
248
  // Loop over all utility dependencies.
249
0
  {
250
0
    std::set<cmLinkItem> const& tutils = depender->GetUtilityItems();
251
0
    std::set<cmLinkItem> emitted;
252
    // A target should not depend on itself.
253
0
    emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace()));
254
0
    emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace()));
255
0
    for (cmLinkItem const& litem : tutils) {
256
      // Don't emit the same utility twice for this target.
257
0
      if (emitted.insert(litem).second) {
258
0
        this->AddTargetDepend(depender_index, litem, false, litem.Cross,
259
0
                              emitted);
260
0
      }
261
0
    }
262
0
  }
263
0
}
264
265
void cmComputeTargetDepends::AddInterfaceDepends(
266
  size_t depender_index, cmGeneratorTarget const* dependee,
267
  cmListFileBacktrace const& dependee_backtrace, std::string const& config,
268
  std::set<cmLinkItem>& emitted)
269
0
{
270
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
271
0
  if (cmLinkInterface const* iface =
272
0
        dependee->GetLinkInterface(config, depender)) {
273
0
    for (cmLinkItem const& lib : iface->Libraries) {
274
      // Don't emit the same library twice for this target.
275
0
      if (emitted.insert(lib).second) {
276
        // Inject the backtrace of the original link dependency whose
277
        // link interface we are adding.  This indicates the line of
278
        // code in the project that caused this dependency to be added.
279
0
        cmLinkItem libBT = lib;
280
0
        libBT.Backtrace = dependee_backtrace;
281
0
        this->AddTargetDepend(depender_index, libBT, true, false, emitted);
282
0
        this->AddInterfaceDepends(depender_index, libBT, config, emitted);
283
0
      }
284
0
    }
285
0
    for (cmLinkItem const& obj : iface->Objects) {
286
0
      if (cmSourceFile const* o = depender->Makefile->GetSource(
287
0
            obj.AsStr(), cmSourceFileLocationKind::Known)) {
288
0
        this->AddObjectDepends(depender_index, o, emitted);
289
0
      }
290
0
    }
291
0
  }
292
0
}
293
294
void cmComputeTargetDepends::AddInterfaceDepends(
295
  size_t depender_index, cmLinkItem const& dependee_name,
296
  std::string const& config, std::set<cmLinkItem>& emitted)
297
0
{
298
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
299
0
  cmGeneratorTarget const* dependee = dependee_name.Target;
300
  // Skip targets that will not really be linked.  This is probably a
301
  // name conflict between an external library and an executable
302
  // within the project.
303
0
  if (dependee && dependee->GetType() == cmStateEnums::EXECUTABLE &&
304
0
      !dependee->IsExecutableWithExports()) {
305
0
    dependee = nullptr;
306
0
  }
307
308
0
  if (dependee) {
309
    // A target should not depend on itself.
310
0
    emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace()));
311
0
    emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace()));
312
0
    this->AddInterfaceDepends(depender_index, dependee,
313
0
                              dependee_name.Backtrace, config, emitted);
314
0
  }
315
0
}
316
317
void cmComputeTargetDepends::AddObjectDepends(size_t depender_index,
318
                                              cmSourceFile const* o,
319
                                              std::set<cmLinkItem>& emitted)
320
0
{
321
0
  std::string const& objLib = o->GetObjectLibrary();
322
0
  if (objLib.empty()) {
323
0
    return;
324
0
  }
325
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
326
0
  cmLinkItem const& objItem =
327
0
    depender->ResolveLinkItem(BT<std::string>(objLib));
328
0
  if (emitted.insert(objItem).second) {
329
0
    if (depender->GetType() != cmStateEnums::EXECUTABLE &&
330
0
        depender->GetType() != cmStateEnums::STATIC_LIBRARY &&
331
0
        depender->GetType() != cmStateEnums::SHARED_LIBRARY &&
332
0
        depender->GetType() != cmStateEnums::MODULE_LIBRARY &&
333
0
        depender->GetType() != cmStateEnums::OBJECT_LIBRARY) {
334
0
      this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
335
0
        MessageType::FATAL_ERROR,
336
0
        "Only executables and libraries may reference target objects.",
337
0
        depender->GetBacktrace());
338
0
      return;
339
0
    }
340
0
    const_cast<cmGeneratorTarget*>(depender)->Target->AddUtility(objLib,
341
0
                                                                 false);
342
0
  }
343
0
}
344
345
void cmComputeTargetDepends::AddTargetDepend(size_t depender_index,
346
                                             cmLinkItem const& dependee_name,
347
                                             bool linking, bool cross,
348
                                             std::set<cmLinkItem>& emitted)
349
0
{
350
  // Get the depender.
351
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
352
353
  // Check the target's makefile first.
354
0
  cmGeneratorTarget const* dependee = dependee_name.Target;
355
356
0
  if (!dependee && !linking &&
357
0
      (depender->GetType() != cmStateEnums::GLOBAL_TARGET)) {
358
0
    this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
359
0
      MessageType::FATAL_ERROR,
360
0
      cmStrCat("The dependency target \"", dependee_name.AsStr(),
361
0
               "\" of target \"", depender->GetName(), "\" does not exist."),
362
0
      dependee_name.Backtrace);
363
0
  }
364
365
  // Skip targets that will not really be linked.  This is probably a
366
  // name conflict between an external library and an executable
367
  // within the project.
368
0
  if (linking && dependee && dependee->GetType() == cmStateEnums::EXECUTABLE &&
369
0
      !dependee->IsExecutableWithExports()) {
370
0
    dependee = nullptr;
371
0
  }
372
373
0
  if (dependee) {
374
0
    this->AddTargetDepend(depender_index, dependee, dependee_name.Backtrace,
375
0
                          linking, cross, emitted);
376
0
  }
377
0
}
378
379
void cmComputeTargetDepends::AddTargetDepend(
380
  size_t depender_index, cmGeneratorTarget const* dependee,
381
  cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross,
382
  std::set<cmLinkItem>& emitted)
383
0
{
384
0
  if (!dependee->IsInBuildSystem()) {
385
    // Skip targets that are not in the buildsystem but follow their
386
    // utility dependencies.
387
0
    std::set<cmLinkItem> const& utils = dependee->GetUtilityItems();
388
0
    for (cmLinkItem const& i : utils) {
389
0
      if (emitted.insert(i).second) {
390
0
        if (cmGeneratorTarget const* transitive_dependee = i.Target) {
391
0
          this->AddTargetDepend(depender_index, transitive_dependee,
392
0
                                i.Backtrace, false, i.Cross, emitted);
393
0
        }
394
0
      }
395
0
    }
396
0
  } else {
397
    // Lookup the index for this target.  All targets should be known by
398
    // this point.
399
0
    auto tii = this->TargetIndex.find(dependee);
400
0
    assert(tii != this->TargetIndex.end());
401
0
    size_t dependee_index = tii->second;
402
403
    // Add this entry to the dependency graph.
404
0
    this->InitialGraph[depender_index].emplace_back(dependee_index, !linking,
405
0
                                                    cross, dependee_backtrace);
406
0
  }
407
0
}
408
409
void cmComputeTargetDepends::CollectSideEffects()
410
0
{
411
0
  this->SideEffects.resize(0);
412
0
  this->SideEffects.resize(this->InitialGraph.size());
413
414
0
  size_t n = this->InitialGraph.size();
415
0
  std::set<size_t> visited;
416
0
  for (size_t i = 0; i < n; ++i) {
417
0
    this->CollectSideEffectsForTarget(visited, i);
418
0
  }
419
0
}
420
421
void cmComputeTargetDepends::CollectSideEffectsForTarget(
422
  std::set<size_t>& visited, size_t depender_index)
423
0
{
424
0
  if (!visited.count(depender_index)) {
425
0
    auto& se = this->SideEffects[depender_index];
426
0
    visited.insert(depender_index);
427
0
    this->Targets[depender_index]->AppendCustomCommandSideEffects(
428
0
      se.CustomCommandSideEffects);
429
0
    this->Targets[depender_index]->AppendLanguageSideEffects(
430
0
      se.LanguageSideEffects);
431
432
0
    for (auto const& edge : this->InitialGraph[depender_index]) {
433
0
      this->CollectSideEffectsForTarget(visited, edge);
434
0
      auto const& dse = this->SideEffects[edge];
435
0
      se.CustomCommandSideEffects.insert(dse.CustomCommandSideEffects.cbegin(),
436
0
                                         dse.CustomCommandSideEffects.cend());
437
0
      for (auto const& it : dse.LanguageSideEffects) {
438
0
        se.LanguageSideEffects[it.first].insert(it.second.cbegin(),
439
0
                                                it.second.cend());
440
0
      }
441
0
    }
442
0
  }
443
0
}
444
445
void cmComputeTargetDepends::ComputeIntermediateGraph()
446
0
{
447
0
  this->IntermediateGraph.resize(0);
448
0
  this->IntermediateGraph.resize(this->InitialGraph.size());
449
450
0
  size_t n = this->InitialGraph.size();
451
0
  for (size_t i = 0; i < n; ++i) {
452
0
    auto const& initialEdges = this->InitialGraph[i];
453
0
    auto& intermediateEdges = this->IntermediateGraph[i];
454
0
    cmGeneratorTarget const* gt = this->Targets[i];
455
0
    if (gt->GetType() != cmStateEnums::STATIC_LIBRARY &&
456
0
        gt->GetType() != cmStateEnums::OBJECT_LIBRARY) {
457
0
      intermediateEdges = initialEdges;
458
0
    } else {
459
0
      if (cmValue optimizeDependencies =
460
0
            gt->GetProperty("OPTIMIZE_DEPENDENCIES")) {
461
0
        if (optimizeDependencies.IsOn()) {
462
0
          this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges);
463
0
        } else {
464
0
          intermediateEdges = initialEdges;
465
0
        }
466
0
      } else {
467
0
        intermediateEdges = initialEdges;
468
0
      }
469
0
    }
470
0
  }
471
0
}
472
473
void cmComputeTargetDepends::OptimizeLinkDependencies(
474
  cmGeneratorTarget const* gt, cmGraphEdgeList& outputEdges,
475
  cmGraphEdgeList const& inputEdges)
476
0
{
477
0
  std::set<size_t> emitted;
478
0
  for (auto const& edge : inputEdges) {
479
0
    if (edge.IsStrong()) {
480
      // Preserve strong edges
481
0
      outputEdges.push_back(edge);
482
0
    } else {
483
0
      auto const& dse = this->SideEffects[edge];
484
485
      // Add edges that have custom command side effects
486
0
      for (cmGeneratorTarget const* dep : dse.CustomCommandSideEffects) {
487
0
        auto index = this->TargetIndex[dep];
488
0
        if (!emitted.count(index)) {
489
0
          emitted.insert(index);
490
0
          outputEdges.push_back(
491
0
            cmGraphEdge(index, false, edge.IsCross(), edge.GetBacktrace()));
492
0
        }
493
0
      }
494
495
      // Add edges that have language side effects for languages we
496
      // care about
497
0
      for (auto const& lang : gt->GetAllConfigCompileLanguages()) {
498
0
        auto it = dse.LanguageSideEffects.find(lang);
499
0
        if (it != dse.LanguageSideEffects.end()) {
500
0
          for (cmGeneratorTarget const* dep : it->second) {
501
0
            auto index = this->TargetIndex[dep];
502
0
            if (!emitted.count(index)) {
503
0
              emitted.insert(index);
504
0
              outputEdges.push_back(cmGraphEdge(index, false, edge.IsCross(),
505
0
                                                edge.GetBacktrace()));
506
0
            }
507
0
          }
508
0
        }
509
0
      }
510
0
    }
511
0
  }
512
0
}
513
514
void cmComputeTargetDepends::DisplayGraph(Graph const& graph,
515
                                          std::string const& name)
516
0
{
517
0
  fprintf(stderr, "The %s target dependency graph is:\n", name.c_str());
518
0
  size_t n = graph.size();
519
0
  for (size_t depender_index = 0; depender_index < n; ++depender_index) {
520
0
    EdgeList const& nl = graph[depender_index];
521
0
    cmGeneratorTarget const* depender = this->Targets[depender_index];
522
0
    fprintf(stderr, "target %zu is [%s]\n", depender_index,
523
0
            depender->GetName().c_str());
524
0
    for (cmGraphEdge const& ni : nl) {
525
0
      size_t dependee_index = ni;
526
0
      cmGeneratorTarget const* dependee = this->Targets[dependee_index];
527
0
      fprintf(stderr, "  depends on target %zu [%s] (%s)\n", dependee_index,
528
0
              dependee->GetName().c_str(), ni.IsStrong() ? "strong" : "weak");
529
0
    }
530
0
  }
531
0
  fprintf(stderr, "\n");
532
0
}
533
534
void cmComputeTargetDepends::DisplaySideEffects()
535
0
{
536
0
  fprintf(stderr, "The side effects are:\n");
537
0
  size_t n = this->SideEffects.size();
538
0
  for (size_t depender_index = 0; depender_index < n; ++depender_index) {
539
0
    cmGeneratorTarget const* depender = this->Targets[depender_index];
540
0
    fprintf(stderr, "target %zu is [%s]\n", depender_index,
541
0
            depender->GetName().c_str());
542
0
    if (!this->SideEffects[depender_index].CustomCommandSideEffects.empty()) {
543
0
      fprintf(stderr, "  custom commands\n");
544
0
      for (auto const* gt :
545
0
           this->SideEffects[depender_index].CustomCommandSideEffects) {
546
0
        fprintf(stderr, "    from target %zu [%s]\n", this->TargetIndex[gt],
547
0
                gt->GetName().c_str());
548
0
      }
549
0
    }
550
0
    for (auto const& it :
551
0
         this->SideEffects[depender_index].LanguageSideEffects) {
552
0
      fprintf(stderr, "  language %s\n", it.first.c_str());
553
0
      for (auto const* gt : it.second) {
554
0
        fprintf(stderr, "    from target %zu [%s]\n", this->TargetIndex[gt],
555
0
                gt->GetName().c_str());
556
0
      }
557
0
    }
558
0
  }
559
0
  fprintf(stderr, "\n");
560
0
}
561
562
void cmComputeTargetDepends::DisplayComponents(
563
  cmComputeComponentGraph const& ccg, std::string const& name)
564
0
{
565
0
  fprintf(stderr, "The strongly connected components for the %s graph are:\n",
566
0
          name.c_str());
567
0
  std::vector<NodeList> const& components = ccg.GetComponents();
568
0
  size_t n = components.size();
569
0
  for (size_t c = 0; c < n; ++c) {
570
0
    NodeList const& nl = components[c];
571
0
    fprintf(stderr, "Component (%zu):\n", c);
572
0
    for (size_t i : nl) {
573
0
      fprintf(stderr, "  contains target %zu [%s]\n", i,
574
0
              this->Targets[i]->GetName().c_str());
575
0
    }
576
0
  }
577
0
  fprintf(stderr, "\n");
578
0
}
579
580
bool cmComputeTargetDepends::CheckComponents(
581
  cmComputeComponentGraph const& ccg)
582
0
{
583
  // All non-trivial components should consist only of static
584
  // libraries.
585
0
  std::vector<NodeList> const& components = ccg.GetComponents();
586
0
  size_t nc = components.size();
587
0
  for (size_t c = 0; c < nc; ++c) {
588
    // Get the current component.
589
0
    NodeList const& nl = components[c];
590
591
    // Skip trivial components.
592
0
    if (nl.size() < 2) {
593
0
      continue;
594
0
    }
595
596
    // Immediately complain if no cycles are allowed at all.
597
0
    if (this->NoCycles) {
598
0
      this->ComplainAboutBadComponent(ccg, c);
599
0
      return false;
600
0
    }
601
602
    // Make sure the component is all STATIC_LIBRARY targets.
603
0
    for (size_t ni : nl) {
604
0
      if (this->Targets[ni]->GetType() != cmStateEnums::STATIC_LIBRARY) {
605
0
        this->ComplainAboutBadComponent(ccg, c);
606
0
        return false;
607
0
      }
608
0
    }
609
0
  }
610
0
  return true;
611
0
}
612
613
void cmComputeTargetDepends::ComplainAboutBadComponent(
614
  cmComputeComponentGraph const& ccg, size_t c, bool strong)
615
0
{
616
  // Construct the error message.
617
0
  std::ostringstream e;
618
0
  e << "The inter-target dependency graph contains the following "
619
0
    << "strongly connected component (cycle):\n";
620
0
  std::vector<NodeList> const& components = ccg.GetComponents();
621
0
  std::vector<size_t> const& cmap = ccg.GetComponentMap();
622
0
  NodeList const& cl = components[c];
623
0
  for (size_t i : cl) {
624
    // Get the depender.
625
0
    cmGeneratorTarget const* depender = this->Targets[i];
626
627
    // Describe the depender.
628
0
    e << "  \"" << depender->GetName() << "\" of type "
629
0
      << cmState::GetTargetTypeName(depender->GetType()) << "\n";
630
631
    // List its dependencies that are inside the component.
632
0
    EdgeList const& nl = this->InitialGraph[i];
633
0
    for (cmGraphEdge const& ni : nl) {
634
0
      size_t j = ni;
635
0
      if (cmap[j] == c) {
636
0
        cmGeneratorTarget const* dependee = this->Targets[j];
637
0
        e << "    depends on \"" << dependee->GetName() << "\""
638
0
          << " (" << (ni.IsStrong() ? "strong" : "weak") << ")\n";
639
0
      }
640
0
    }
641
0
  }
642
0
  if (strong) {
643
    // Custom command executable dependencies cannot occur within a
644
    // component of static libraries.  The cycle must appear in calls
645
    // to add_dependencies.
646
0
    e << "The component contains at least one cycle consisting of strong "
647
0
      << "dependencies (created by add_dependencies) that cannot be broken.";
648
0
  } else if (this->NoCycles) {
649
0
    e << "The GLOBAL_DEPENDS_NO_CYCLES global property is enabled, so "
650
0
      << "cyclic dependencies are not allowed even among static libraries.";
651
0
  } else {
652
0
    e << "At least one of these targets is not a STATIC_LIBRARY.  "
653
0
      << "Cyclic dependencies are allowed only among static libraries.";
654
0
  }
655
0
  cmSystemTools::Error(e.str());
656
0
}
657
658
bool cmComputeTargetDepends::IntraComponent(std::vector<size_t> const& cmap,
659
                                            size_t c, size_t i, size_t* head,
660
                                            std::set<size_t>& emitted,
661
                                            std::set<size_t>& visited)
662
0
{
663
0
  if (!visited.insert(i).second) {
664
    // Cycle in utility depends!
665
0
    return false;
666
0
  }
667
0
  if (emitted.insert(i).second) {
668
    // Honor strong intra-component edges in the final order.
669
0
    EdgeList const& el = this->InitialGraph[i];
670
0
    for (cmGraphEdge const& edge : el) {
671
0
      size_t j = edge;
672
0
      if (cmap[j] == c && edge.IsStrong()) {
673
0
        this->FinalGraph[i].emplace_back(j, true, edge.IsCross(),
674
0
                                         edge.GetBacktrace());
675
0
        if (!this->IntraComponent(cmap, c, j, head, emitted, visited)) {
676
0
          return false;
677
0
        }
678
0
      }
679
0
    }
680
681
    // Prepend to a linear linked-list of intra-component edges.
682
0
    if (*head != cmComputeComponentGraph::INVALID_COMPONENT) {
683
0
      this->FinalGraph[i].emplace_back(*head, false, false,
684
0
                                       cmListFileBacktrace());
685
0
    } else {
686
0
      this->ComponentTail[c] = i;
687
0
    }
688
0
    *head = i;
689
0
  }
690
0
  return true;
691
0
}
692
693
bool cmComputeTargetDepends::ComputeFinalDepends(
694
  cmComputeComponentGraph const& ccg)
695
0
{
696
  // Get the component graph information.
697
0
  std::vector<NodeList> const& components = ccg.GetComponents();
698
0
  Graph const& cgraph = ccg.GetComponentGraph();
699
700
  // Allocate the final graph.
701
0
  this->FinalGraph.resize(0);
702
0
  this->FinalGraph.resize(this->InitialGraph.size());
703
704
  // Choose intra-component edges to linearize dependencies.
705
0
  std::vector<size_t> const& cmap = ccg.GetComponentMap();
706
0
  this->ComponentHead.resize(components.size());
707
0
  this->ComponentTail.resize(components.size());
708
0
  size_t nc = components.size();
709
0
  for (size_t c = 0; c < nc; ++c) {
710
0
    size_t head = cmComputeComponentGraph::INVALID_COMPONENT;
711
0
    std::set<size_t> emitted;
712
0
    NodeList const& nl = components[c];
713
0
    for (size_t ni : cmReverseRange(nl)) {
714
0
      std::set<size_t> visited;
715
0
      if (!this->IntraComponent(cmap, c, ni, &head, emitted, visited)) {
716
        // Cycle in add_dependencies within component!
717
0
        this->ComplainAboutBadComponent(ccg, c, true);
718
0
        return false;
719
0
      }
720
0
    }
721
0
    this->ComponentHead[c] = head;
722
0
  }
723
724
  // Convert inter-component edges to connect component tails to heads.
725
0
  size_t n = cgraph.size();
726
0
  for (size_t depender_component = 0; depender_component < n;
727
0
       ++depender_component) {
728
0
    size_t depender_component_tail = this->ComponentTail[depender_component];
729
0
    EdgeList const& nl = cgraph[depender_component];
730
0
    for (cmGraphEdge const& ni : nl) {
731
0
      size_t dependee_component = ni;
732
0
      size_t dependee_component_head = this->ComponentHead[dependee_component];
733
0
      this->FinalGraph[depender_component_tail].emplace_back(
734
0
        dependee_component_head, ni.IsStrong(), ni.IsCross(),
735
0
        ni.GetBacktrace());
736
0
    }
737
0
  }
738
0
  return true;
739
0
}