Coverage Report

Created: 2026-06-15 07:03

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
      // Build link dependencies before the current target.
223
0
      if (cmLinkImplementation const* impl = depender->GetLinkImplementation(
224
0
            it, cmGeneratorTarget::UseTo::Link)) {
225
0
        for (cmLinkItem const& lib : impl->Libraries) {
226
          // Don't emit the same library twice for this target.
227
0
          if (emitted.insert(lib).second) {
228
0
            this->AddTargetDepend(depender_index, lib, true, false, emitted);
229
0
            this->AddInterfaceDepends(depender_index, lib, it, emitted);
230
0
          }
231
0
        }
232
0
        for (cmLinkItem const& obj : impl->Objects) {
233
0
          if (cmSourceFile const* o = depender->Makefile->GetSource(
234
0
                obj.AsStr(), cmSourceFileLocationKind::Known)) {
235
0
            this->AddObjectDepends(depender_index, o, emitted);
236
0
          }
237
0
        }
238
0
      }
239
240
      // Build compile dependencies before the current target.
241
0
      if (cmLinkImplementation const* impl = depender->GetLinkImplementation(
242
0
            it, cmGeneratorTarget::UseTo::Compile)) {
243
0
        for (cmLinkItem const& lib : impl->Libraries) {
244
          // Don't emit the same library twice for this target.
245
0
          if (emitted.insert(lib).second) {
246
0
            this->AddTargetDepend(depender_index, lib, true, false, emitted);
247
0
            this->AddInterfaceDepends(depender_index, lib, it, emitted);
248
0
          }
249
0
        }
250
0
      }
251
252
      // Add dependencies on object libraries not otherwise handled above.
253
0
      std::vector<cmSourceFile const*> objectFiles;
254
0
      depender->GetExternalObjects(objectFiles, it);
255
0
      for (cmSourceFile const* o : objectFiles) {
256
0
        this->AddObjectDepends(depender_index, o, emitted);
257
0
      }
258
0
    }
259
0
  }
260
261
  // Loop over all utility dependencies.
262
0
  {
263
0
    std::set<cmLinkItem> const& tutils = depender->GetUtilityItems();
264
0
    std::set<cmLinkItem> emitted;
265
    // A target should not depend on itself.
266
0
    emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace()));
267
0
    emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace()));
268
0
    for (cmLinkItem const& litem : tutils) {
269
      // Don't emit the same utility twice for this target.
270
0
      if (emitted.insert(litem).second) {
271
0
        this->AddTargetDepend(depender_index, litem, false, litem.Cross,
272
0
                              emitted);
273
0
      }
274
0
    }
275
0
  }
276
0
}
277
278
void cmComputeTargetDepends::AddInterfaceDepends(
279
  size_t depender_index, cmGeneratorTarget const* dependee,
280
  cmListFileBacktrace const& dependee_backtrace, std::string const& config,
281
  std::set<cmLinkItem>& emitted)
282
0
{
283
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
284
0
  if (cmLinkInterface const* iface =
285
0
        dependee->GetLinkInterface(config, depender)) {
286
0
    for (cmLinkItem const& lib : iface->Libraries) {
287
      // Don't emit the same library twice for this target.
288
0
      if (emitted.insert(lib).second) {
289
        // Inject the backtrace of the original link dependency whose
290
        // link interface we are adding.  This indicates the line of
291
        // code in the project that caused this dependency to be added.
292
0
        cmLinkItem libBT = lib;
293
0
        libBT.Backtrace = dependee_backtrace;
294
0
        this->AddTargetDepend(depender_index, libBT, true, false, emitted);
295
0
        this->AddInterfaceDepends(depender_index, libBT, config, emitted);
296
0
      }
297
0
    }
298
0
    for (cmLinkItem const& obj : iface->Objects) {
299
0
      if (cmSourceFile const* o = depender->Makefile->GetSource(
300
0
            obj.AsStr(), cmSourceFileLocationKind::Known)) {
301
0
        this->AddObjectDepends(depender_index, o, emitted);
302
0
      }
303
0
    }
304
0
  }
305
0
}
306
307
void cmComputeTargetDepends::AddInterfaceDepends(
308
  size_t depender_index, cmLinkItem const& dependee_name,
309
  std::string const& config, std::set<cmLinkItem>& emitted)
310
0
{
311
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
312
0
  cmGeneratorTarget const* dependee = dependee_name.Target;
313
  // Skip targets that will not really be linked.  This is probably a
314
  // name conflict between an external library and an executable
315
  // within the project.
316
0
  if (dependee && dependee->GetType() == cmStateEnums::EXECUTABLE &&
317
0
      !dependee->IsExecutableWithExports()) {
318
0
    dependee = nullptr;
319
0
  }
320
321
0
  if (dependee) {
322
    // A target should not depend on itself.
323
0
    emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace()));
324
0
    emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace()));
325
0
    this->AddInterfaceDepends(depender_index, dependee,
326
0
                              dependee_name.Backtrace, config, emitted);
327
0
  }
328
0
}
329
330
void cmComputeTargetDepends::AddObjectDepends(size_t depender_index,
331
                                              cmSourceFile const* o,
332
                                              std::set<cmLinkItem>& emitted)
333
0
{
334
0
  std::string const& objLib = o->GetObjectLibrary();
335
0
  if (objLib.empty()) {
336
0
    return;
337
0
  }
338
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
339
0
  cmLinkItem const& objItem =
340
0
    depender->ResolveLinkItem(BT<std::string>(objLib));
341
0
  if (emitted.insert(objItem).second) {
342
0
    if (depender->GetType() != cmStateEnums::EXECUTABLE &&
343
0
        depender->GetType() != cmStateEnums::STATIC_LIBRARY &&
344
0
        depender->GetType() != cmStateEnums::SHARED_LIBRARY &&
345
0
        depender->GetType() != cmStateEnums::MODULE_LIBRARY &&
346
0
        depender->GetType() != cmStateEnums::OBJECT_LIBRARY) {
347
0
      this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
348
0
        MessageType::FATAL_ERROR,
349
0
        "Only executables and libraries may reference target objects.",
350
0
        depender->GetBacktrace());
351
0
      return;
352
0
    }
353
0
    const_cast<cmGeneratorTarget*>(depender)->Target->AddUtility(objLib,
354
0
                                                                 false);
355
0
  }
356
0
}
357
358
void cmComputeTargetDepends::AddTargetDepend(size_t depender_index,
359
                                             cmLinkItem const& dependee_name,
360
                                             bool linking, bool cross,
361
                                             std::set<cmLinkItem>& emitted)
362
0
{
363
  // Get the depender.
364
0
  cmGeneratorTarget const* depender = this->Targets[depender_index];
365
366
  // Check the target's makefile first.
367
0
  cmGeneratorTarget const* dependee = dependee_name.Target;
368
369
0
  if (!dependee && !linking &&
370
0
      (depender->GetType() != cmStateEnums::GLOBAL_TARGET)) {
371
0
    this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
372
0
      MessageType::FATAL_ERROR,
373
0
      cmStrCat("The dependency target \"", dependee_name.AsStr(),
374
0
               "\" of target \"", depender->GetName(), "\" does not exist."),
375
0
      dependee_name.Backtrace);
376
0
  }
377
378
  // Skip targets that will not really be linked.  This is probably a
379
  // name conflict between an external library and an executable
380
  // within the project.
381
0
  if (linking && dependee && dependee->GetType() == cmStateEnums::EXECUTABLE &&
382
0
      !dependee->IsExecutableWithExports()) {
383
0
    dependee = nullptr;
384
0
  }
385
386
0
  if (dependee) {
387
0
    this->AddTargetDepend(depender_index, dependee, dependee_name.Backtrace,
388
0
                          linking, cross, emitted);
389
0
  }
390
0
}
391
392
void cmComputeTargetDepends::AddTargetDepend(
393
  size_t depender_index, cmGeneratorTarget const* dependee,
394
  cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross,
395
  std::set<cmLinkItem>& emitted)
396
0
{
397
0
  if (!dependee->IsInBuildSystem()) {
398
    // Skip targets that are not in the buildsystem but follow their
399
    // utility dependencies.
400
0
    std::set<cmLinkItem> const& utils = dependee->GetUtilityItems();
401
0
    for (cmLinkItem const& i : utils) {
402
0
      if (emitted.insert(i).second) {
403
0
        if (cmGeneratorTarget const* transitive_dependee = i.Target) {
404
0
          this->AddTargetDepend(depender_index, transitive_dependee,
405
0
                                i.Backtrace, false, i.Cross, emitted);
406
0
        }
407
0
      }
408
0
    }
409
0
  } else {
410
    // Lookup the index for this target.  All targets should be known by
411
    // this point.
412
0
    auto tii = this->TargetIndex.find(dependee);
413
0
    assert(tii != this->TargetIndex.end());
414
0
    size_t dependee_index = tii->second;
415
416
    // Add this entry to the dependency graph.
417
0
    this->InitialGraph[depender_index].emplace_back(dependee_index, !linking,
418
0
                                                    cross, dependee_backtrace);
419
0
  }
420
0
}
421
422
void cmComputeTargetDepends::CollectSideEffects()
423
0
{
424
0
  this->SideEffects.resize(0);
425
0
  this->SideEffects.resize(this->InitialGraph.size());
426
427
0
  size_t n = this->InitialGraph.size();
428
0
  std::set<size_t> visited;
429
0
  for (size_t i = 0; i < n; ++i) {
430
0
    this->CollectSideEffectsForTarget(visited, i);
431
0
  }
432
0
}
433
434
void cmComputeTargetDepends::CollectSideEffectsForTarget(
435
  std::set<size_t>& visited, size_t depender_index)
436
0
{
437
0
  if (!visited.count(depender_index)) {
438
0
    auto& se = this->SideEffects[depender_index];
439
0
    visited.insert(depender_index);
440
0
    this->Targets[depender_index]->AppendCustomCommandSideEffects(
441
0
      se.CustomCommandSideEffects);
442
0
    this->Targets[depender_index]->AppendLanguageSideEffects(
443
0
      se.LanguageSideEffects);
444
445
0
    for (auto const& edge : this->InitialGraph[depender_index]) {
446
0
      this->CollectSideEffectsForTarget(visited, edge);
447
0
      auto const& dse = this->SideEffects[edge];
448
0
      se.CustomCommandSideEffects.insert(dse.CustomCommandSideEffects.cbegin(),
449
0
                                         dse.CustomCommandSideEffects.cend());
450
0
      for (auto const& it : dse.LanguageSideEffects) {
451
0
        se.LanguageSideEffects[it.first].insert(it.second.cbegin(),
452
0
                                                it.second.cend());
453
0
      }
454
0
    }
455
0
  }
456
0
}
457
458
void cmComputeTargetDepends::ComputeIntermediateGraph()
459
0
{
460
0
  this->IntermediateGraph.resize(0);
461
0
  this->IntermediateGraph.resize(this->InitialGraph.size());
462
463
0
  size_t n = this->InitialGraph.size();
464
0
  for (size_t i = 0; i < n; ++i) {
465
0
    auto const& initialEdges = this->InitialGraph[i];
466
0
    auto& intermediateEdges = this->IntermediateGraph[i];
467
0
    cmGeneratorTarget const* gt = this->Targets[i];
468
0
    if (gt->GetType() != cmStateEnums::STATIC_LIBRARY &&
469
0
        gt->GetType() != cmStateEnums::OBJECT_LIBRARY) {
470
0
      intermediateEdges = initialEdges;
471
0
    } else {
472
0
      if (cmValue optimizeDependencies =
473
0
            gt->GetProperty("OPTIMIZE_DEPENDENCIES")) {
474
0
        if (optimizeDependencies.IsOn()) {
475
0
          this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges);
476
0
        } else {
477
0
          intermediateEdges = initialEdges;
478
0
        }
479
0
      } else {
480
0
        intermediateEdges = initialEdges;
481
0
      }
482
0
    }
483
0
  }
484
0
}
485
486
void cmComputeTargetDepends::OptimizeLinkDependencies(
487
  cmGeneratorTarget const* gt, cmGraphEdgeList& outputEdges,
488
  cmGraphEdgeList const& inputEdges)
489
0
{
490
0
  std::set<size_t> emitted;
491
0
  for (auto const& edge : inputEdges) {
492
0
    if (edge.IsStrong()) {
493
      // Preserve strong edges
494
0
      outputEdges.push_back(edge);
495
0
    } else {
496
0
      auto const& dse = this->SideEffects[edge];
497
498
      // Add edges that have custom command side effects
499
0
      for (cmGeneratorTarget const* dep : dse.CustomCommandSideEffects) {
500
0
        auto index = this->TargetIndex[dep];
501
0
        if (!emitted.count(index)) {
502
0
          emitted.insert(index);
503
0
          outputEdges.push_back(
504
0
            cmGraphEdge(index, false, edge.IsCross(), edge.GetBacktrace()));
505
0
        }
506
0
      }
507
508
      // Add edges that have language side effects for languages we
509
      // care about
510
0
      for (auto const& lang : gt->GetAllConfigCompileLanguages()) {
511
0
        auto it = dse.LanguageSideEffects.find(lang);
512
0
        if (it != dse.LanguageSideEffects.end()) {
513
0
          for (cmGeneratorTarget const* dep : it->second) {
514
0
            auto index = this->TargetIndex[dep];
515
0
            if (!emitted.count(index)) {
516
0
              emitted.insert(index);
517
0
              outputEdges.push_back(cmGraphEdge(index, false, edge.IsCross(),
518
0
                                                edge.GetBacktrace()));
519
0
            }
520
0
          }
521
0
        }
522
0
      }
523
0
    }
524
0
  }
525
0
}
526
527
void cmComputeTargetDepends::DisplayGraph(Graph const& graph,
528
                                          std::string const& name)
529
0
{
530
0
  fprintf(stderr, "The %s target dependency graph is:\n", name.c_str());
531
0
  size_t n = graph.size();
532
0
  for (size_t depender_index = 0; depender_index < n; ++depender_index) {
533
0
    EdgeList const& nl = graph[depender_index];
534
0
    cmGeneratorTarget const* depender = this->Targets[depender_index];
535
0
    fprintf(stderr, "target %zu is [%s]\n", depender_index,
536
0
            depender->GetName().c_str());
537
0
    for (cmGraphEdge const& ni : nl) {
538
0
      size_t dependee_index = ni;
539
0
      cmGeneratorTarget const* dependee = this->Targets[dependee_index];
540
0
      fprintf(stderr, "  depends on target %zu [%s] (%s)\n", dependee_index,
541
0
              dependee->GetName().c_str(), ni.IsStrong() ? "strong" : "weak");
542
0
    }
543
0
  }
544
0
  fprintf(stderr, "\n");
545
0
}
546
547
void cmComputeTargetDepends::DisplaySideEffects()
548
0
{
549
0
  fprintf(stderr, "The side effects are:\n");
550
0
  size_t n = this->SideEffects.size();
551
0
  for (size_t depender_index = 0; depender_index < n; ++depender_index) {
552
0
    cmGeneratorTarget const* depender = this->Targets[depender_index];
553
0
    fprintf(stderr, "target %zu is [%s]\n", depender_index,
554
0
            depender->GetName().c_str());
555
0
    if (!this->SideEffects[depender_index].CustomCommandSideEffects.empty()) {
556
0
      fprintf(stderr, "  custom commands\n");
557
0
      for (auto const* gt :
558
0
           this->SideEffects[depender_index].CustomCommandSideEffects) {
559
0
        fprintf(stderr, "    from target %zu [%s]\n", this->TargetIndex[gt],
560
0
                gt->GetName().c_str());
561
0
      }
562
0
    }
563
0
    for (auto const& it :
564
0
         this->SideEffects[depender_index].LanguageSideEffects) {
565
0
      fprintf(stderr, "  language %s\n", it.first.c_str());
566
0
      for (auto const* gt : it.second) {
567
0
        fprintf(stderr, "    from target %zu [%s]\n", this->TargetIndex[gt],
568
0
                gt->GetName().c_str());
569
0
      }
570
0
    }
571
0
  }
572
0
  fprintf(stderr, "\n");
573
0
}
574
575
void cmComputeTargetDepends::DisplayComponents(
576
  cmComputeComponentGraph const& ccg, std::string const& name)
577
0
{
578
0
  fprintf(stderr, "The strongly connected components for the %s graph are:\n",
579
0
          name.c_str());
580
0
  std::vector<NodeList> const& components = ccg.GetComponents();
581
0
  size_t n = components.size();
582
0
  for (size_t c = 0; c < n; ++c) {
583
0
    NodeList const& nl = components[c];
584
0
    fprintf(stderr, "Component (%zu):\n", c);
585
0
    for (size_t i : nl) {
586
0
      fprintf(stderr, "  contains target %zu [%s]\n", i,
587
0
              this->Targets[i]->GetName().c_str());
588
0
    }
589
0
  }
590
0
  fprintf(stderr, "\n");
591
0
}
592
593
bool cmComputeTargetDepends::CheckComponents(
594
  cmComputeComponentGraph const& ccg)
595
0
{
596
  // All non-trivial components should consist only of static
597
  // libraries.
598
0
  std::vector<NodeList> const& components = ccg.GetComponents();
599
0
  size_t nc = components.size();
600
0
  for (size_t c = 0; c < nc; ++c) {
601
    // Get the current component.
602
0
    NodeList const& nl = components[c];
603
604
    // Skip trivial components.
605
0
    if (nl.size() < 2) {
606
0
      continue;
607
0
    }
608
609
    // Immediately complain if no cycles are allowed at all.
610
0
    if (this->NoCycles) {
611
0
      this->ComplainAboutBadComponent(ccg, c);
612
0
      return false;
613
0
    }
614
615
    // Make sure the component is all STATIC_LIBRARY targets.
616
0
    for (size_t ni : nl) {
617
0
      if (this->Targets[ni]->GetType() != cmStateEnums::STATIC_LIBRARY) {
618
0
        this->ComplainAboutBadComponent(ccg, c);
619
0
        return false;
620
0
      }
621
0
    }
622
0
  }
623
0
  return true;
624
0
}
625
626
void cmComputeTargetDepends::ComplainAboutBadComponent(
627
  cmComputeComponentGraph const& ccg, size_t c, bool strong)
628
0
{
629
  // Construct the error message.
630
0
  std::ostringstream e;
631
0
  e << "The inter-target dependency graph contains the following "
632
0
    << "strongly connected component (cycle):\n";
633
0
  std::vector<NodeList> const& components = ccg.GetComponents();
634
0
  std::vector<size_t> const& cmap = ccg.GetComponentMap();
635
0
  NodeList const& cl = components[c];
636
0
  for (size_t i : cl) {
637
    // Get the depender.
638
0
    cmGeneratorTarget const* depender = this->Targets[i];
639
640
    // Describe the depender.
641
0
    e << "  \"" << depender->GetName() << "\" of type "
642
0
      << cmState::GetTargetTypeName(depender->GetType()) << "\n";
643
644
    // List its dependencies that are inside the component.
645
0
    EdgeList const& nl = this->InitialGraph[i];
646
0
    for (cmGraphEdge const& ni : nl) {
647
0
      size_t j = ni;
648
0
      if (cmap[j] == c) {
649
0
        cmGeneratorTarget const* dependee = this->Targets[j];
650
0
        e << "    depends on \"" << dependee->GetName() << "\""
651
0
          << " (" << (ni.IsStrong() ? "strong" : "weak") << ")\n";
652
0
      }
653
0
    }
654
0
  }
655
0
  if (strong) {
656
    // Custom command executable dependencies cannot occur within a
657
    // component of static libraries.  The cycle must appear in calls
658
    // to add_dependencies.
659
0
    e << "The component contains at least one cycle consisting of strong "
660
0
      << "dependencies (created by add_dependencies) that cannot be broken.";
661
0
  } else if (this->NoCycles) {
662
0
    e << "The GLOBAL_DEPENDS_NO_CYCLES global property is enabled, so "
663
0
      << "cyclic dependencies are not allowed even among static libraries.";
664
0
  } else {
665
0
    e << "At least one of these targets is not a STATIC_LIBRARY.  "
666
0
      << "Cyclic dependencies are allowed only among static libraries.";
667
0
  }
668
0
  cmSystemTools::Error(e.str());
669
0
}
670
671
bool cmComputeTargetDepends::IntraComponent(std::vector<size_t> const& cmap,
672
                                            size_t c, size_t i, size_t* head,
673
                                            std::set<size_t>& emitted,
674
                                            std::set<size_t>& visited)
675
0
{
676
0
  if (!visited.insert(i).second) {
677
    // Cycle in utility depends!
678
0
    return false;
679
0
  }
680
0
  if (emitted.insert(i).second) {
681
    // Honor strong intra-component edges in the final order.
682
0
    EdgeList const& el = this->InitialGraph[i];
683
0
    for (cmGraphEdge const& edge : el) {
684
0
      size_t j = edge;
685
0
      if (cmap[j] == c && edge.IsStrong()) {
686
0
        this->FinalGraph[i].emplace_back(j, true, edge.IsCross(),
687
0
                                         edge.GetBacktrace());
688
0
        if (!this->IntraComponent(cmap, c, j, head, emitted, visited)) {
689
0
          return false;
690
0
        }
691
0
      }
692
0
    }
693
694
    // Prepend to a linear linked-list of intra-component edges.
695
0
    if (*head != cmComputeComponentGraph::INVALID_COMPONENT) {
696
0
      this->FinalGraph[i].emplace_back(*head, false, false,
697
0
                                       cmListFileBacktrace());
698
0
    } else {
699
0
      this->ComponentTail[c] = i;
700
0
    }
701
0
    *head = i;
702
0
  }
703
0
  return true;
704
0
}
705
706
bool cmComputeTargetDepends::ComputeFinalDepends(
707
  cmComputeComponentGraph const& ccg)
708
0
{
709
  // Get the component graph information.
710
0
  std::vector<NodeList> const& components = ccg.GetComponents();
711
0
  Graph const& cgraph = ccg.GetComponentGraph();
712
713
  // Allocate the final graph.
714
0
  this->FinalGraph.resize(0);
715
0
  this->FinalGraph.resize(this->InitialGraph.size());
716
717
  // Choose intra-component edges to linearize dependencies.
718
0
  std::vector<size_t> const& cmap = ccg.GetComponentMap();
719
0
  this->ComponentHead.resize(components.size());
720
0
  this->ComponentTail.resize(components.size());
721
0
  size_t nc = components.size();
722
0
  for (size_t c = 0; c < nc; ++c) {
723
0
    size_t head = cmComputeComponentGraph::INVALID_COMPONENT;
724
0
    std::set<size_t> emitted;
725
0
    NodeList const& nl = components[c];
726
0
    for (size_t ni : cmReverseRange(nl)) {
727
0
      std::set<size_t> visited;
728
0
      if (!this->IntraComponent(cmap, c, ni, &head, emitted, visited)) {
729
        // Cycle in add_dependencies within component!
730
0
        this->ComplainAboutBadComponent(ccg, c, true);
731
0
        return false;
732
0
      }
733
0
    }
734
0
    this->ComponentHead[c] = head;
735
0
  }
736
737
  // Convert inter-component edges to connect component tails to heads.
738
0
  size_t n = cgraph.size();
739
0
  for (size_t depender_component = 0; depender_component < n;
740
0
       ++depender_component) {
741
0
    size_t depender_component_tail = this->ComponentTail[depender_component];
742
0
    EdgeList const& nl = cgraph[depender_component];
743
0
    for (cmGraphEdge const& ni : nl) {
744
0
      size_t dependee_component = ni;
745
0
      size_t dependee_component_head = this->ComponentHead[dependee_component];
746
0
      this->FinalGraph[depender_component_tail].emplace_back(
747
0
        dependee_component_head, ni.IsStrong(), ni.IsCross(),
748
0
        ni.GetBacktrace());
749
0
    }
750
0
  }
751
0
  return true;
752
0
}