Coverage Report

Created: 2026-07-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGraphVizWriter.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 "cmGraphVizWriter.h"
4
5
#include <algorithm>
6
#include <iostream>
7
#include <memory>
8
#include <set>
9
#include <unordered_set>
10
#include <utility>
11
12
#include <cm/memory>
13
14
#include "cmsys/RegularExpression.hxx"
15
#include "cmsys/String.h"
16
17
#include "cmGeneratedFileStream.h"
18
#include "cmGeneratorTarget.h"
19
#include "cmGlobalGenerator.h"
20
#include "cmLinkItem.h"
21
#include "cmList.h"
22
#include "cmLocalGenerator.h"
23
#include "cmMakefile.h"
24
#include "cmState.h"
25
#include "cmStateSnapshot.h"
26
#include "cmStringAlgorithms.h"
27
#include "cmSystemTools.h"
28
#include "cmTargetTypes.h"
29
#include "cmValue.h"
30
#include "cmake.h"
31
32
namespace {
33
34
char const* const GRAPHVIZ_EDGE_STYLE_PUBLIC = "solid";
35
char const* const GRAPHVIZ_EDGE_STYLE_INTERFACE = "dashed";
36
char const* const GRAPHVIZ_EDGE_STYLE_PRIVATE = "dotted";
37
38
char const* const GRAPHVIZ_NODE_SHAPE_EXECUTABLE = "egg"; // egg-xecutable
39
40
// Normal libraries.
41
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_STATIC = "octagon";
42
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_SHARED = "doubleoctagon";
43
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_MODULE = "tripleoctagon";
44
45
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_INTERFACE = "pentagon";
46
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_OBJECT = "hexagon";
47
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN = "septagon";
48
49
char const* const GRAPHVIZ_NODE_SHAPE_UTILITY = "box";
50
51
char const* getShapeForTarget(cmLinkItem const& item)
52
0
{
53
0
  if (!item.Target) {
54
0
    return GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN;
55
0
  }
56
57
0
  switch (item.Target->GetType()) {
58
0
    case cm::TargetType::EXECUTABLE:
59
0
      return GRAPHVIZ_NODE_SHAPE_EXECUTABLE;
60
0
    case cm::TargetType::STATIC_LIBRARY:
61
0
      return GRAPHVIZ_NODE_SHAPE_LIBRARY_STATIC;
62
0
    case cm::TargetType::SHARED_LIBRARY:
63
0
      return GRAPHVIZ_NODE_SHAPE_LIBRARY_SHARED;
64
0
    case cm::TargetType::MODULE_LIBRARY:
65
0
      return GRAPHVIZ_NODE_SHAPE_LIBRARY_MODULE;
66
0
    case cm::TargetType::OBJECT_LIBRARY:
67
0
      return GRAPHVIZ_NODE_SHAPE_LIBRARY_OBJECT;
68
0
    case cm::TargetType::UTILITY:
69
0
      return GRAPHVIZ_NODE_SHAPE_UTILITY;
70
0
    case cm::TargetType::INTERFACE_LIBRARY:
71
0
      return GRAPHVIZ_NODE_SHAPE_LIBRARY_INTERFACE;
72
0
    case cm::TargetType::UNKNOWN_LIBRARY:
73
0
    default:
74
0
      return GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN;
75
0
  }
76
0
}
77
78
struct DependeesDir
79
{
80
  template <typename T>
81
  static cmLinkItem const& src(T const& con)
82
0
  {
83
0
    return con.src;
84
0
  }
85
86
  template <typename T>
87
  static cmLinkItem const& dst(T const& con)
88
0
  {
89
0
    return con.dst;
90
0
  }
91
};
92
93
struct DependersDir
94
{
95
  template <typename T>
96
  static cmLinkItem const& src(T const& con)
97
0
  {
98
0
    return con.dst;
99
0
  }
100
101
  template <typename T>
102
  static cmLinkItem const& dst(T const& con)
103
0
  {
104
0
    return con.src;
105
0
  }
106
};
107
}
108
109
cmGraphVizWriter::cmGraphVizWriter(std::string const& fileName,
110
                                   cmGlobalGenerator const* globalGenerator)
111
0
  : FileName(fileName)
112
0
  , GlobalFileStream(fileName)
113
0
  , GraphName(globalGenerator->GetSafeGlobalSetting("CMAKE_PROJECT_NAME"))
114
0
  , GraphHeader("node [\n  fontsize = \"12\"\n];")
115
0
  , GraphNodePrefix("node")
116
0
  , GlobalGenerator(globalGenerator)
117
0
{
118
0
}
119
120
cmGraphVizWriter::~cmGraphVizWriter()
121
0
{
122
0
  this->WriteFooter(this->GlobalFileStream);
123
0
}
124
125
void cmGraphVizWriter::VisitGraph(std::string const&)
126
0
{
127
0
  this->WriteHeader(this->GlobalFileStream, this->GraphName);
128
0
  this->WriteLegend(this->GlobalFileStream);
129
0
}
130
131
void cmGraphVizWriter::OnItem(cmLinkItem const& item)
132
0
{
133
0
  if (this->ItemExcluded(item)) {
134
0
    return;
135
0
  }
136
137
0
  this->NodeNames[item.AsStr()] =
138
0
    cmStrCat(this->GraphNodePrefix, this->NextNodeId);
139
0
  ++this->NextNodeId;
140
141
0
  this->WriteNode(this->GlobalFileStream, item);
142
0
}
143
144
std::unique_ptr<cmGeneratedFileStream> cmGraphVizWriter::CreateTargetFile(
145
  cmLinkItem const& item, std::string const& fileNameSuffix)
146
0
{
147
0
  auto const pathSafeItemName = PathSafeString(item.AsStr());
148
0
  auto const perTargetFileName =
149
0
    cmStrCat(this->FileName, '.', pathSafeItemName, fileNameSuffix);
150
0
  auto perTargetFileStream =
151
0
    cm::make_unique<cmGeneratedFileStream>(perTargetFileName);
152
153
0
  this->WriteHeader(*perTargetFileStream, item.AsStr());
154
0
  this->WriteNode(*perTargetFileStream, item);
155
156
0
  return perTargetFileStream;
157
0
}
158
159
void cmGraphVizWriter::OnDirectLink(cmLinkItem const& depender,
160
                                    cmLinkItem const& dependee,
161
                                    DependencyType dt)
162
0
{
163
0
  this->VisitLink(depender, dependee, true, GetEdgeStyle(dt));
164
0
}
165
166
void cmGraphVizWriter::OnIndirectLink(cmLinkItem const& depender,
167
                                      cmLinkItem const& dependee)
168
0
{
169
0
  this->VisitLink(depender, dependee, false);
170
0
}
171
172
void cmGraphVizWriter::VisitLink(cmLinkItem const& depender,
173
                                 cmLinkItem const& dependee, bool isDirectLink,
174
                                 std::string const& scopeType)
175
0
{
176
0
  if (this->ItemExcluded(depender) || this->ItemExcluded(dependee)) {
177
0
    return;
178
0
  }
179
180
0
  if (!isDirectLink) {
181
0
    return;
182
0
  }
183
184
  // write global data directly
185
0
  this->WriteConnection(this->GlobalFileStream, depender, dependee, scopeType);
186
187
0
  if (this->GeneratePerTarget) {
188
0
    this->PerTargetConnections[depender].emplace_back(depender, dependee,
189
0
                                                      scopeType);
190
0
  }
191
192
0
  if (this->GenerateDependers) {
193
0
    this->TargetDependersConnections[dependee].emplace_back(dependee, depender,
194
0
                                                            scopeType);
195
0
  }
196
0
}
197
198
void cmGraphVizWriter::ReadSettings(
199
  std::string const& settingsFileName,
200
  std::string const& fallbackSettingsFileName)
201
0
{
202
0
  cmake cm(cmState::Role::Script);
203
0
  cm.GetCurrentSnapshot().SetDefaultDefinitions();
204
0
  cmGlobalGenerator ggi(&cm);
205
0
  cmMakefile mf(&ggi, cm.GetCurrentSnapshot());
206
0
  std::unique_ptr<cmLocalGenerator> lg(ggi.CreateLocalGenerator(&mf));
207
208
0
  std::string inFileName = settingsFileName;
209
0
  if (!cmSystemTools::FileExists(inFileName)) {
210
0
    inFileName = fallbackSettingsFileName;
211
0
    if (!cmSystemTools::FileExists(inFileName)) {
212
0
      return;
213
0
    }
214
0
  }
215
216
0
  if (!mf.ReadListFile(inFileName)) {
217
0
    cmSystemTools::Error("Problem opening GraphViz options file: " +
218
0
                         inFileName);
219
0
    return;
220
0
  }
221
222
0
  std::cout << "Reading GraphViz options file: " << inFileName << std::endl;
223
224
0
#define set_if_set(var, cmakeDefinition)                                      \
225
0
  do {                                                                        \
226
0
    cmValue value = mf.GetDefinition(cmakeDefinition);                        \
227
0
    if (value) {                                                              \
228
0
      (var) = *value;                                                         \
229
0
    }                                                                         \
230
0
  } while (false)
231
232
0
  set_if_set(this->GraphName, "GRAPHVIZ_GRAPH_NAME");
233
0
  set_if_set(this->GraphHeader, "GRAPHVIZ_GRAPH_HEADER");
234
0
  set_if_set(this->GraphNodePrefix, "GRAPHVIZ_NODE_PREFIX");
235
236
0
#define set_bool_if_set(var, cmakeDefinition)                                 \
237
0
  do {                                                                        \
238
0
    cmValue value = mf.GetDefinition(cmakeDefinition);                        \
239
0
    if (value) {                                                              \
240
0
      (var) = cmIsOn(*value);                                                 \
241
0
    }                                                                         \
242
0
  } while (false)
243
244
0
  set_bool_if_set(this->GenerateForExecutables, "GRAPHVIZ_EXECUTABLES");
245
0
  set_bool_if_set(this->GenerateForStaticLibs, "GRAPHVIZ_STATIC_LIBS");
246
0
  set_bool_if_set(this->GenerateForSharedLibs, "GRAPHVIZ_SHARED_LIBS");
247
0
  set_bool_if_set(this->GenerateForModuleLibs, "GRAPHVIZ_MODULE_LIBS");
248
0
  set_bool_if_set(this->GenerateForInterfaceLibs, "GRAPHVIZ_INTERFACE_LIBS");
249
0
  set_bool_if_set(this->GenerateForObjectLibs, "GRAPHVIZ_OBJECT_LIBS");
250
0
  set_bool_if_set(this->GenerateForUnknownLibs, "GRAPHVIZ_UNKNOWN_LIBS");
251
0
  set_bool_if_set(this->GenerateForCustomTargets, "GRAPHVIZ_CUSTOM_TARGETS");
252
0
  set_bool_if_set(this->GenerateForExternals, "GRAPHVIZ_EXTERNAL_LIBS");
253
0
  set_bool_if_set(this->GeneratePerTarget, "GRAPHVIZ_GENERATE_PER_TARGET");
254
0
  set_bool_if_set(this->GenerateDependers, "GRAPHVIZ_GENERATE_DEPENDERS");
255
256
0
  std::string ignoreTargetsRegexes;
257
0
  set_if_set(ignoreTargetsRegexes, "GRAPHVIZ_IGNORE_TARGETS");
258
259
0
  this->TargetsToIgnoreRegex.clear();
260
0
  if (!ignoreTargetsRegexes.empty()) {
261
0
    cmList ignoreTargetsRegExList{ ignoreTargetsRegexes };
262
0
    for (std::string const& currentRegexString : ignoreTargetsRegExList) {
263
0
      cmsys::RegularExpression currentRegex;
264
0
      if (!currentRegex.compile(currentRegexString)) {
265
0
        std::cerr << "Could not compile bad regex \"" << currentRegexString
266
0
                  << "\"" << std::endl;
267
0
      }
268
0
      this->TargetsToIgnoreRegex.push_back(std::move(currentRegex));
269
0
    }
270
0
  }
271
0
}
272
273
void cmGraphVizWriter::Write()
274
0
{
275
0
  auto const* gg = this->GlobalGenerator;
276
277
0
  this->VisitGraph(gg->GetName());
278
279
  // We want to traverse in a determined order, such that the output is always
280
  // the same for a given project (this makes tests reproducible, etc.)
281
0
  std::set<cmGeneratorTarget const*, cmGeneratorTarget::StrictTargetComparison>
282
0
    sortedGeneratorTargets;
283
284
0
  for (auto const& lg : gg->GetLocalGenerators()) {
285
0
    for (auto const& gt : lg->GetGeneratorTargets()) {
286
      // Reserved targets have inconsistent names across platforms (e.g. 'all'
287
      // vs. 'ALL_BUILD'), which can disrupt the traversal ordering.
288
      // We don't need or want them anyway.
289
0
      if (!cmGlobalGenerator::IsReservedTarget(gt->GetName()) &&
290
0
          !cmHasLiteralPrefix(gt->GetName(), "__cmake_")) {
291
0
        sortedGeneratorTargets.insert(gt.get());
292
0
      }
293
0
    }
294
0
  }
295
296
  // write global data and collect all connection data for per target graphs
297
0
  for (auto const* const gt : sortedGeneratorTargets) {
298
0
    auto item = cmLinkItem(gt, false, gt->GetBacktrace());
299
0
    this->VisitItem(item);
300
0
  }
301
302
0
  if (this->GeneratePerTarget) {
303
0
    this->WritePerTargetConnections<DependeesDir>(this->PerTargetConnections);
304
0
  }
305
306
0
  if (this->GenerateDependers) {
307
0
    this->WritePerTargetConnections<DependersDir>(
308
0
      this->TargetDependersConnections, ".dependers");
309
0
  }
310
0
}
311
312
void cmGraphVizWriter::FindAllConnections(ConnectionsMap const& connectionMap,
313
                                          cmLinkItem const& rootItem,
314
                                          Connections& extendedCons,
315
                                          std::set<cmLinkItem>& visitedItems)
316
0
{
317
  // some "targets" are not in map, e.g. linker flags as -lm or
318
  // targets without dependency.
319
  // in both cases we are finished with traversing the graph
320
0
  if (connectionMap.find(rootItem) == connectionMap.cend()) {
321
0
    return;
322
0
  }
323
324
0
  Connections const& origCons = connectionMap.at(rootItem);
325
326
0
  for (Connection const& con : origCons) {
327
0
    extendedCons.emplace_back(con);
328
0
    cmLinkItem const& dstItem = con.dst;
329
0
    bool const visited = visitedItems.find(dstItem) != visitedItems.cend();
330
0
    if (!visited) {
331
0
      visitedItems.insert(dstItem);
332
0
      this->FindAllConnections(connectionMap, dstItem, extendedCons,
333
0
                               visitedItems);
334
0
    }
335
0
  }
336
0
}
337
338
void cmGraphVizWriter::FindAllConnections(ConnectionsMap const& connectionMap,
339
                                          cmLinkItem const& rootItem,
340
                                          Connections& extendedCons)
341
0
{
342
0
  std::set<cmLinkItem> visitedItems = { rootItem };
343
0
  this->FindAllConnections(connectionMap, rootItem, extendedCons,
344
0
                           visitedItems);
345
0
}
346
347
template <typename DirFunc>
348
void cmGraphVizWriter::WritePerTargetConnections(
349
  ConnectionsMap const& connections, std::string const& fileNameSuffix)
350
0
{
351
  // the per target connections must be extended by indirect dependencies
352
0
  ConnectionsMap extendedConnections;
353
0
  for (auto const& conPerTarget : connections) {
354
0
    cmLinkItem const& rootItem = conPerTarget.first;
355
0
    Connections& extendedCons = extendedConnections[conPerTarget.first];
356
0
    this->FindAllConnections(connections, rootItem, extendedCons);
357
0
  }
358
359
0
  for (auto const& conPerTarget : extendedConnections) {
360
0
    cmLinkItem const& rootItem = conPerTarget.first;
361
362
    // some of the nodes are excluded completely and are not written
363
0
    if (this->ItemExcluded(rootItem)) {
364
0
      continue;
365
0
    }
366
367
0
    Connections const& cons = conPerTarget.second;
368
369
0
    std::unique_ptr<cmGeneratedFileStream> fileStream =
370
0
      this->CreateTargetFile(rootItem, fileNameSuffix);
371
372
    // avoid write same node multiple times
373
0
    std::unordered_set<std::string> writtenNodes = { rootItem.AsStr() };
374
0
    for (Connection const& con : cons) {
375
0
      cmLinkItem const& src = DirFunc::src(con);
376
0
      cmLinkItem const& dst = DirFunc::dst(con);
377
0
      if (writtenNodes.emplace(con.dst.AsStr()).second) {
378
0
        this->WriteNode(*fileStream, con.dst);
379
0
      }
380
0
      this->WriteConnection(*fileStream, src, dst, con.scopeType);
381
0
    }
382
383
0
    this->WriteFooter(*fileStream);
384
0
  }
385
0
}
Unexecuted instantiation: cmGraphVizWriter.cxx:void cmGraphVizWriter::WritePerTargetConnections<(anonymous namespace)::DependeesDir>(std::__1::map<cmLinkItem, std::__1::vector<cmGraphVizWriter::Connection, std::__1::allocator<cmGraphVizWriter::Connection> >, std::__1::less<cmLinkItem>, std::__1::allocator<std::__1::pair<cmLinkItem const, std::__1::vector<cmGraphVizWriter::Connection, std::__1::allocator<cmGraphVizWriter::Connection> > > > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: cmGraphVizWriter.cxx:void cmGraphVizWriter::WritePerTargetConnections<(anonymous namespace)::DependersDir>(std::__1::map<cmLinkItem, std::__1::vector<cmGraphVizWriter::Connection, std::__1::allocator<cmGraphVizWriter::Connection> >, std::__1::less<cmLinkItem>, std::__1::allocator<std::__1::pair<cmLinkItem const, std::__1::vector<cmGraphVizWriter::Connection, std::__1::allocator<cmGraphVizWriter::Connection> > > > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
386
387
void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& fs,
388
                                   std::string const& name)
389
0
{
390
0
  auto const escapedGraphName = EscapeForDotFile(name);
391
0
  fs << "digraph \"" << escapedGraphName << "\" {\n"
392
0
     << this->GraphHeader << '\n';
393
0
}
394
395
void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& fs)
396
0
{
397
0
  fs << "}\n";
398
0
}
399
400
void cmGraphVizWriter::WriteLegend(cmGeneratedFileStream& fs)
401
0
{
402
  // Note that the subgraph name must start with "cluster", as done here, to
403
  // make Graphviz layout engines do the right thing and keep the nodes
404
  // together.
405
  /* clang-format off */
406
0
  fs << "subgraph clusterLegend {\n"
407
0
        "  label = \"Legend\";\n"
408
        // Set the color of the box surrounding the legend.
409
0
        "  color = black;\n"
410
        // We use invisible edges just to enforce the layout.
411
0
        "  edge [ style = invis ];\n"
412
        // Nodes.
413
0
        "  legendNode0 [ label = \"Executable\", shape = "
414
0
     << GRAPHVIZ_NODE_SHAPE_EXECUTABLE << " ];\n"
415
0
        "  legendNode1 [ label = \"Static Library\", shape = "
416
0
     << GRAPHVIZ_NODE_SHAPE_LIBRARY_STATIC << " ];\n"
417
0
        "  legendNode2 [ label = \"Shared Library\", shape = "
418
0
     << GRAPHVIZ_NODE_SHAPE_LIBRARY_SHARED << " ];\n"
419
0
        "  legendNode3 [ label = \"Module Library\", shape = "
420
0
     << GRAPHVIZ_NODE_SHAPE_LIBRARY_MODULE << " ];\n"
421
0
        "  legendNode4 [ label = \"Interface Library\", shape = "
422
0
     << GRAPHVIZ_NODE_SHAPE_LIBRARY_INTERFACE << " ];\n"
423
0
        "  legendNode5 [ label = \"Object Library\", shape = "
424
0
     << GRAPHVIZ_NODE_SHAPE_LIBRARY_OBJECT << " ];\n"
425
0
        "  legendNode6 [ label = \"Unknown Library\", shape = "
426
0
     << GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN << " ];\n"
427
0
        "  legendNode7 [ label = \"Custom Target\", shape = "
428
0
     << GRAPHVIZ_NODE_SHAPE_UTILITY << " ];\n"
429
        // Edges.
430
        // Some of those are dummy (invisible) edges to enforce a layout.
431
0
        "  legendNode0 -> legendNode1 [ style = "
432
0
     << GRAPHVIZ_EDGE_STYLE_PUBLIC << " ];\n"
433
0
        "  legendNode0 -> legendNode2 [ style = "
434
0
     << GRAPHVIZ_EDGE_STYLE_PUBLIC << " ];\n"
435
0
        "  legendNode0 -> legendNode3;\n"
436
0
        "  legendNode1 -> legendNode4 [ label = \"Interface\", style = "
437
0
     << GRAPHVIZ_EDGE_STYLE_INTERFACE << " ];\n"
438
0
        "  legendNode2 -> legendNode5 [ label = \"Private\", style = "
439
0
     << GRAPHVIZ_EDGE_STYLE_PRIVATE << " ];\n"
440
0
        "  legendNode3 -> legendNode6 [ style = "
441
0
     << GRAPHVIZ_EDGE_STYLE_PUBLIC << " ];\n"
442
0
        "  legendNode0 -> legendNode7;\n"
443
0
        "}\n";
444
  /* clang-format off */
445
0
}
446
447
void cmGraphVizWriter::WriteNode(cmGeneratedFileStream& fs,
448
                                 cmLinkItem const& item)
449
0
{
450
0
  auto const& itemName = item.AsStr();
451
0
  auto const& nodeName = this->NodeNames[itemName];
452
453
0
  auto const itemNameWithAliases = this->ItemNameWithAliases(itemName);
454
0
  auto const escapedLabel = EscapeForDotFile(itemNameWithAliases);
455
456
0
  fs << "    \"" << nodeName << "\" [ label = \"" << escapedLabel
457
0
     << "\", shape = " << getShapeForTarget(item) << " ];\n";
458
0
}
459
460
void cmGraphVizWriter::WriteConnection(cmGeneratedFileStream& fs,
461
                                       cmLinkItem const& depender,
462
                                       cmLinkItem const& dependee,
463
                                       std::string const& edgeStyle)
464
0
{
465
0
  auto const& dependerName = depender.AsStr();
466
0
  auto const& dependeeName = dependee.AsStr();
467
468
0
  fs << "    \"" << this->NodeNames[dependerName] << "\" -> \""
469
0
     << this->NodeNames[dependeeName] << "\" "
470
0
     << edgeStyle
471
0
     << " // " << dependerName << " -> " << dependeeName << '\n';
472
0
}
473
474
bool cmGraphVizWriter::ItemExcluded(cmLinkItem const& item)
475
0
{
476
0
  auto const itemName = item.AsStr();
477
478
0
  if (this->ItemNameFilteredOut(itemName)) {
479
0
    return true;
480
0
  }
481
482
0
  if (!item.Target) {
483
0
    return !this->GenerateForExternals;
484
0
  }
485
486
0
  if (item.Target->GetType() == cm::TargetType::UTILITY) {
487
0
    if (cmHasLiteralPrefix(itemName, "Nightly") ||
488
0
        cmHasLiteralPrefix(itemName, "Continuous") ||
489
0
        cmHasLiteralPrefix(itemName, "Experimental")) {
490
0
      return true;
491
0
    }
492
0
  }
493
494
0
  if (item.Target->IsImported() && !this->GenerateForExternals) {
495
0
    return true;
496
0
  }
497
498
0
  return !this->TargetTypeEnabled(item.Target->GetType());
499
0
}
500
501
bool cmGraphVizWriter::ItemNameFilteredOut(std::string const& itemName)
502
0
{
503
0
  if (itemName == ">") {
504
    // FIXME: why do we even receive such a target here?
505
0
    return true;
506
0
  }
507
508
0
  if (cmGlobalGenerator::IsReservedTarget(itemName)) {
509
0
    return true;
510
0
  }
511
512
0
  for (cmsys::RegularExpression& regEx : this->TargetsToIgnoreRegex) {
513
0
    if (regEx.is_valid()) {
514
0
      if (regEx.find(itemName)) {
515
0
        return true;
516
0
      }
517
0
    }
518
0
  }
519
520
0
  return false;
521
0
}
522
523
bool cmGraphVizWriter::TargetTypeEnabled(
524
  cm::TargetType targetType) const
525
0
{
526
0
  switch (targetType) {
527
0
    case cm::TargetType::EXECUTABLE:
528
0
      return this->GenerateForExecutables;
529
0
    case cm::TargetType::STATIC_LIBRARY:
530
0
      return this->GenerateForStaticLibs;
531
0
    case cm::TargetType::SHARED_LIBRARY:
532
0
      return this->GenerateForSharedLibs;
533
0
    case cm::TargetType::MODULE_LIBRARY:
534
0
      return this->GenerateForModuleLibs;
535
0
    case cm::TargetType::INTERFACE_LIBRARY:
536
0
      return this->GenerateForInterfaceLibs;
537
0
    case cm::TargetType::OBJECT_LIBRARY:
538
0
      return this->GenerateForObjectLibs;
539
0
    case cm::TargetType::UNKNOWN_LIBRARY:
540
0
      return this->GenerateForUnknownLibs;
541
0
    case cm::TargetType::UTILITY:
542
0
      return this->GenerateForCustomTargets;
543
0
    case cm::TargetType::GLOBAL_TARGET:
544
      // Built-in targets like edit_cache, etc.
545
      // We don't need/want those in the dot file.
546
0
      return false;
547
0
    default:
548
0
      break;
549
0
  }
550
0
  return false;
551
0
}
552
553
std::string cmGraphVizWriter::ItemNameWithAliases(
554
  std::string const& itemName) const
555
0
{
556
0
  std::vector<std::string> items;
557
0
  for (auto const& lg : this->GlobalGenerator->GetLocalGenerators()) {
558
0
    for (auto const& aliasTargets : lg->GetMakefile()->GetAliasTargets()) {
559
0
      if (aliasTargets.second == itemName) {
560
0
        items.push_back(aliasTargets.first);
561
0
      }
562
0
    }
563
0
  }
564
565
0
  std::sort(items.begin(), items.end());
566
0
  items.erase(std::unique(items.begin(), items.end()), items.end());
567
568
0
  auto nameWithAliases = itemName;
569
0
  for(auto const& item : items) {
570
0
    nameWithAliases += "\\n(" + item + ")";
571
0
  }
572
573
0
  return nameWithAliases;
574
0
}
575
576
std::string cmGraphVizWriter::GetEdgeStyle(DependencyType dt)
577
0
{
578
0
  std::string style;
579
0
  switch (dt) {
580
0
    case DependencyType::LinkPrivate:
581
0
      style = "[ style = " + std::string(GRAPHVIZ_EDGE_STYLE_PRIVATE) + " ]";
582
0
      break;
583
0
    case DependencyType::LinkInterface:
584
0
      style = "[ style = " + std::string(GRAPHVIZ_EDGE_STYLE_INTERFACE) + " ]";
585
0
      break;
586
0
    default:
587
0
      break;
588
0
  }
589
0
  return style;
590
0
}
591
592
std::string cmGraphVizWriter::EscapeForDotFile(std::string const& str)
593
0
{
594
0
  return cmSystemTools::EscapeChars(str.data(), "\"");
595
0
}
596
597
std::string cmGraphVizWriter::PathSafeString(std::string const& str)
598
0
{
599
0
  std::string pathSafeStr;
600
601
  // We'll only keep alphanumerical characters, plus the following ones that
602
  // are common, and safe on all platforms:
603
0
  auto const extra_chars = std::set<char>{ '.', '-', '_' };
604
605
0
  for (char c : str) {
606
0
    if (cmsysString_isalnum(c) || extra_chars.find(c) != extra_chars.cend()) {
607
0
      pathSafeStr += c;
608
0
    }
609
0
  }
610
611
0
  return pathSafeStr;
612
0
}