Coverage Report

Created: 2026-03-12 06:35

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