Coverage Report

Created: 2026-07-14 07:09

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 "cmStringAlgorithms.h"
25
#include "cmSystemTools.h"
26
#include "cmTarget.h"
27
#include "cmTargetDepend.h"
28
#include "cmTargetTypes.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() == cm::TargetType::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() != cm::TargetType::EXECUTABLE &&
343
0
        depender->GetType() != cm::TargetType::STATIC_LIBRARY &&
344
0
        depender->GetType() != cm::TargetType::SHARED_LIBRARY &&
345
0
        depender->GetType() != cm::TargetType::MODULE_LIBRARY &&
346
0
        depender->GetType() != cm::TargetType::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() != cm::TargetType::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 &&
382
0
      dependee->GetType() == cm::TargetType::EXECUTABLE &&
383
0
      !dependee->IsExecutableWithExports()) {
384
0
    dependee = nullptr;
385
0
  }
386
387
0
  if (dependee) {
388
0
    this->AddTargetDepend(depender_index, dependee, dependee_name.Backtrace,
389
0
                          linking, cross, emitted);
390
0
  }
391
0
}
392
393
void cmComputeTargetDepends::AddTargetDepend(
394
  size_t depender_index, cmGeneratorTarget const* dependee,
395
  cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross,
396
  std::set<cmLinkItem>& emitted)
397
0
{
398
0
  if (!dependee->IsInBuildSystem()) {
399
    // Skip targets that are not in the buildsystem but follow their
400
    // utility dependencies.
401
0
    std::set<cmLinkItem> const& utils = dependee->GetUtilityItems();
402
0
    for (cmLinkItem const& i : utils) {
403
0
      if (emitted.insert(i).second) {
404
0
        if (cmGeneratorTarget const* transitive_dependee = i.Target) {
405
0
          this->AddTargetDepend(depender_index, transitive_dependee,
406
0
                                i.Backtrace, false, i.Cross, emitted);
407
0
        }
408
0
      }
409
0
    }
410
0
  } else {
411
    // Lookup the index for this target.  All targets should be known by
412
    // this point.
413
0
    auto tii = this->TargetIndex.find(dependee);
414
0
    assert(tii != this->TargetIndex.end());
415
0
    size_t dependee_index = tii->second;
416
417
    // Add this entry to the dependency graph.
418
0
    this->InitialGraph[depender_index].emplace_back(dependee_index, !linking,
419
0
                                                    cross, dependee_backtrace);
420
0
  }
421
0
}
422
423
void cmComputeTargetDepends::CollectSideEffects()
424
0
{
425
0
  this->SideEffects.resize(0);
426
0
  this->SideEffects.resize(this->InitialGraph.size());
427
428
0
  size_t n = this->InitialGraph.size();
429
0
  std::set<size_t> visited;
430
0
  for (size_t i = 0; i < n; ++i) {
431
0
    this->CollectSideEffectsForTarget(visited, i);
432
0
  }
433
0
}
434
435
void cmComputeTargetDepends::CollectSideEffectsForTarget(
436
  std::set<size_t>& visited, size_t depender_index)
437
0
{
438
0
  if (!visited.count(depender_index)) {
439
0
    auto& se = this->SideEffects[depender_index];
440
0
    visited.insert(depender_index);
441
0
    this->Targets[depender_index]->AppendCustomCommandSideEffects(
442
0
      se.CustomCommandSideEffects);
443
0
    this->Targets[depender_index]->AppendLanguageSideEffects(
444
0
      se.LanguageSideEffects);
445
446
0
    for (auto const& edge : this->InitialGraph[depender_index]) {
447
0
      this->CollectSideEffectsForTarget(visited, edge);
448
0
      auto const& dse = this->SideEffects[edge];
449
0
      se.CustomCommandSideEffects.insert(dse.CustomCommandSideEffects.cbegin(),
450
0
                                         dse.CustomCommandSideEffects.cend());
451
0
      for (auto const& it : dse.LanguageSideEffects) {
452
0
        se.LanguageSideEffects[it.first].insert(it.second.cbegin(),
453
0
                                                it.second.cend());
454
0
      }
455
0
    }
456
0
  }
457
0
}
458
459
void cmComputeTargetDepends::ComputeIntermediateGraph()
460
0
{
461
0
  this->IntermediateGraph.resize(0);
462
0
  this->IntermediateGraph.resize(this->InitialGraph.size());
463
464
0
  size_t n = this->InitialGraph.size();
465
0
  for (size_t i = 0; i < n; ++i) {
466
0
    auto const& initialEdges = this->InitialGraph[i];
467
0
    auto& intermediateEdges = this->IntermediateGraph[i];
468
0
    cmGeneratorTarget const* gt = this->Targets[i];
469
0
    if (gt->GetType() != cm::TargetType::STATIC_LIBRARY &&
470
0
        gt->GetType() != cm::TargetType::OBJECT_LIBRARY) {
471
0
      intermediateEdges = initialEdges;
472
0
    } else {
473
0
      if (cmValue optimizeDependencies =
474
0
            gt->GetProperty("OPTIMIZE_DEPENDENCIES")) {
475
0
        if (optimizeDependencies.IsOn()) {
476
0
          this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges);
477
0
        } else {
478
0
          intermediateEdges = initialEdges;
479
0
        }
480
0
      } else {
481
0
        intermediateEdges = initialEdges;
482
0
      }
483
0
    }
484
0
  }
485
0
}
486
487
void cmComputeTargetDepends::OptimizeLinkDependencies(
488
  cmGeneratorTarget const* gt, cmGraphEdgeList& outputEdges,
489
  cmGraphEdgeList const& inputEdges)
490
0
{
491
0
  std::set<size_t> emitted;
492
0
  for (auto const& edge : inputEdges) {
493
0
    if (edge.IsStrong()) {
494
      // Preserve strong edges
495
0
      outputEdges.push_back(edge);
496
0
    } else {
497
0
      auto const& dse = this->SideEffects[edge];
498
499
      // Add edges that have custom command side effects
500
0
      for (cmGeneratorTarget const* dep : dse.CustomCommandSideEffects) {
501
0
        auto index = this->TargetIndex[dep];
502
0
        if (!emitted.count(index)) {
503
0
          emitted.insert(index);
504
0
          outputEdges.push_back(
505
0
            cmGraphEdge(index, false, edge.IsCross(), edge.GetBacktrace()));
506
0
        }
507
0
      }
508
509
      // Add edges that have language side effects for languages we
510
      // care about
511
0
      for (auto const& lang : gt->GetAllConfigCompileLanguages()) {
512
0
        auto it = dse.LanguageSideEffects.find(lang);
513
0
        if (it != dse.LanguageSideEffects.end()) {
514
0
          for (cmGeneratorTarget const* dep : it->second) {
515
0
            auto index = this->TargetIndex[dep];
516
0
            if (!emitted.count(index)) {
517
0
              emitted.insert(index);
518
0
              outputEdges.push_back(cmGraphEdge(index, false, edge.IsCross(),
519
0
                                                edge.GetBacktrace()));
520
0
            }
521
0
          }
522
0
        }
523
0
      }
524
0
    }
525
0
  }
526
0
}
527
528
void cmComputeTargetDepends::DisplayGraph(Graph const& graph,
529
                                          std::string const& name)
530
0
{
531
0
  fprintf(stderr, "The %s target dependency graph is:\n", name.c_str());
532
0
  size_t n = graph.size();
533
0
  for (size_t depender_index = 0; depender_index < n; ++depender_index) {
534
0
    EdgeList const& nl = graph[depender_index];
535
0
    cmGeneratorTarget const* depender = this->Targets[depender_index];
536
0
    fprintf(stderr, "target %zu is [%s]\n", depender_index,
537
0
            depender->GetName().c_str());
538
0
    for (cmGraphEdge const& ni : nl) {
539
0
      size_t dependee_index = ni;
540
0
      cmGeneratorTarget const* dependee = this->Targets[dependee_index];
541
0
      fprintf(stderr, "  depends on target %zu [%s] (%s)\n", dependee_index,
542
0
              dependee->GetName().c_str(), ni.IsStrong() ? "strong" : "weak");
543
0
    }
544
0
  }
545
0
  fprintf(stderr, "\n");
546
0
}
547
548
void cmComputeTargetDepends::DisplaySideEffects()
549
0
{
550
0
  fprintf(stderr, "The side effects are:\n");
551
0
  size_t n = this->SideEffects.size();
552
0
  for (size_t depender_index = 0; depender_index < n; ++depender_index) {
553
0
    cmGeneratorTarget const* depender = this->Targets[depender_index];
554
0
    fprintf(stderr, "target %zu is [%s]\n", depender_index,
555
0
            depender->GetName().c_str());
556
0
    if (!this->SideEffects[depender_index].CustomCommandSideEffects.empty()) {
557
0
      fprintf(stderr, "  custom commands\n");
558
0
      for (auto const* gt :
559
0
           this->SideEffects[depender_index].CustomCommandSideEffects) {
560
0
        fprintf(stderr, "    from target %zu [%s]\n", this->TargetIndex[gt],
561
0
                gt->GetName().c_str());
562
0
      }
563
0
    }
564
0
    for (auto const& it :
565
0
         this->SideEffects[depender_index].LanguageSideEffects) {
566
0
      fprintf(stderr, "  language %s\n", it.first.c_str());
567
0
      for (auto const* gt : it.second) {
568
0
        fprintf(stderr, "    from target %zu [%s]\n", this->TargetIndex[gt],
569
0
                gt->GetName().c_str());
570
0
      }
571
0
    }
572
0
  }
573
0
  fprintf(stderr, "\n");
574
0
}
575
576
void cmComputeTargetDepends::DisplayComponents(
577
  cmComputeComponentGraph const& ccg, std::string const& name)
578
0
{
579
0
  fprintf(stderr, "The strongly connected components for the %s graph are:\n",
580
0
          name.c_str());
581
0
  std::vector<NodeList> const& components = ccg.GetComponents();
582
0
  size_t n = components.size();
583
0
  for (size_t c = 0; c < n; ++c) {
584
0
    NodeList const& nl = components[c];
585
0
    fprintf(stderr, "Component (%zu):\n", c);
586
0
    for (size_t i : nl) {
587
0
      fprintf(stderr, "  contains target %zu [%s]\n", i,
588
0
              this->Targets[i]->GetName().c_str());
589
0
    }
590
0
  }
591
0
  fprintf(stderr, "\n");
592
0
}
593
594
bool cmComputeTargetDepends::CheckComponents(
595
  cmComputeComponentGraph const& ccg)
596
0
{
597
  // All non-trivial components should consist only of static
598
  // libraries.
599
0
  std::vector<NodeList> const& components = ccg.GetComponents();
600
0
  size_t nc = components.size();
601
0
  for (size_t c = 0; c < nc; ++c) {
602
    // Get the current component.
603
0
    NodeList const& nl = components[c];
604
605
    // Skip trivial components.
606
0
    if (nl.size() < 2) {
607
0
      continue;
608
0
    }
609
610
    // Immediately complain if no cycles are allowed at all.
611
0
    if (this->NoCycles) {
612
0
      this->ComplainAboutBadComponent(ccg, c);
613
0
      return false;
614
0
    }
615
616
    // Make sure the component is all STATIC_LIBRARY targets.
617
0
    for (size_t ni : nl) {
618
0
      if (this->Targets[ni]->GetType() != cm::TargetType::STATIC_LIBRARY) {
619
0
        this->ComplainAboutBadComponent(ccg, c);
620
0
        return false;
621
0
      }
622
0
    }
623
0
  }
624
0
  return true;
625
0
}
626
627
void cmComputeTargetDepends::ComplainAboutBadComponent(
628
  cmComputeComponentGraph const& ccg, size_t c, bool strong)
629
0
{
630
  // Construct the error message.
631
0
  std::ostringstream e;
632
0
  e << "The inter-target dependency graph contains the following "
633
0
    << "strongly connected component (cycle):\n";
634
0
  std::vector<NodeList> const& components = ccg.GetComponents();
635
0
  std::vector<size_t> const& cmap = ccg.GetComponentMap();
636
0
  NodeList const& cl = components[c];
637
0
  for (size_t i : cl) {
638
    // Get the depender.
639
0
    cmGeneratorTarget const* depender = this->Targets[i];
640
641
    // Describe the depender.
642
0
    e << "  \"" << depender->GetName() << "\" of type "
643
0
      << cmState::GetTargetTypeName(depender->GetType()) << "\n";
644
645
    // List its dependencies that are inside the component.
646
0
    EdgeList const& nl = this->InitialGraph[i];
647
0
    for (cmGraphEdge const& ni : nl) {
648
0
      size_t j = ni;
649
0
      if (cmap[j] == c) {
650
0
        cmGeneratorTarget const* dependee = this->Targets[j];
651
0
        e << "    depends on \"" << dependee->GetName() << "\""
652
0
          << " (" << (ni.IsStrong() ? "strong" : "weak") << ")\n";
653
0
      }
654
0
    }
655
0
  }
656
0
  if (strong) {
657
    // Custom command executable dependencies cannot occur within a
658
    // component of static libraries.  The cycle must appear in calls
659
    // to add_dependencies.
660
0
    e << "The component contains at least one cycle consisting of strong "
661
0
      << "dependencies (created by add_dependencies) that cannot be broken.";
662
0
  } else if (this->NoCycles) {
663
0
    e << "The GLOBAL_DEPENDS_NO_CYCLES global property is enabled, so "
664
0
      << "cyclic dependencies are not allowed even among static libraries.";
665
0
  } else {
666
0
    e << "At least one of these targets is not a STATIC_LIBRARY.  "
667
0
      << "Cyclic dependencies are allowed only among static libraries.";
668
0
  }
669
0
  cmSystemTools::Error(e.str());
670
0
}
671
672
bool cmComputeTargetDepends::IntraComponent(std::vector<size_t> const& cmap,
673
                                            size_t c, size_t i, size_t* head,
674
                                            std::set<size_t>& emitted,
675
                                            std::set<size_t>& visited)
676
0
{
677
0
  if (!visited.insert(i).second) {
678
    // Cycle in utility depends!
679
0
    return false;
680
0
  }
681
0
  if (emitted.insert(i).second) {
682
    // Honor strong intra-component edges in the final order.
683
0
    EdgeList const& el = this->InitialGraph[i];
684
0
    for (cmGraphEdge const& edge : el) {
685
0
      size_t j = edge;
686
0
      if (cmap[j] == c && edge.IsStrong()) {
687
0
        this->FinalGraph[i].emplace_back(j, true, edge.IsCross(),
688
0
                                         edge.GetBacktrace());
689
0
        if (!this->IntraComponent(cmap, c, j, head, emitted, visited)) {
690
0
          return false;
691
0
        }
692
0
      }
693
0
    }
694
695
    // Prepend to a linear linked-list of intra-component edges.
696
0
    if (*head != cmComputeComponentGraph::INVALID_COMPONENT) {
697
0
      this->FinalGraph[i].emplace_back(*head, false, false,
698
0
                                       cmListFileBacktrace());
699
0
    } else {
700
0
      this->ComponentTail[c] = i;
701
0
    }
702
0
    *head = i;
703
0
  }
704
0
  return true;
705
0
}
706
707
bool cmComputeTargetDepends::ComputeFinalDepends(
708
  cmComputeComponentGraph const& ccg)
709
0
{
710
  // Get the component graph information.
711
0
  std::vector<NodeList> const& components = ccg.GetComponents();
712
0
  Graph const& cgraph = ccg.GetComponentGraph();
713
714
  // Allocate the final graph.
715
0
  this->FinalGraph.resize(0);
716
0
  this->FinalGraph.resize(this->InitialGraph.size());
717
718
  // Choose intra-component edges to linearize dependencies.
719
0
  std::vector<size_t> const& cmap = ccg.GetComponentMap();
720
0
  this->ComponentHead.resize(components.size());
721
0
  this->ComponentTail.resize(components.size());
722
0
  size_t nc = components.size();
723
0
  for (size_t c = 0; c < nc; ++c) {
724
0
    size_t head = cmComputeComponentGraph::INVALID_COMPONENT;
725
0
    std::set<size_t> emitted;
726
0
    NodeList const& nl = components[c];
727
0
    for (size_t ni : cmReverseRange(nl)) {
728
0
      std::set<size_t> visited;
729
0
      if (!this->IntraComponent(cmap, c, ni, &head, emitted, visited)) {
730
        // Cycle in add_dependencies within component!
731
0
        this->ComplainAboutBadComponent(ccg, c, true);
732
0
        return false;
733
0
      }
734
0
    }
735
0
    this->ComponentHead[c] = head;
736
0
  }
737
738
  // Convert inter-component edges to connect component tails to heads.
739
0
  size_t n = cgraph.size();
740
0
  for (size_t depender_component = 0; depender_component < n;
741
0
       ++depender_component) {
742
0
    size_t depender_component_tail = this->ComponentTail[depender_component];
743
0
    EdgeList const& nl = cgraph[depender_component];
744
0
    for (cmGraphEdge const& ni : nl) {
745
0
      size_t dependee_component = ni;
746
0
      size_t dependee_component_head = this->ComponentHead[dependee_component];
747
0
      this->FinalGraph[depender_component_tail].emplace_back(
748
0
        dependee_component_head, ni.IsStrong(), ni.IsCross(),
749
0
        ni.GetBacktrace());
750
0
    }
751
0
  }
752
0
  return true;
753
0
}