Graphviz: added test suite, fixes, enhancements

* Added a fairly comprehensive test suite
* Separated the graph traversal logic from the Graphviz generation
  code by introducing a new class, cmLinkItemsGraphVisitor{.h,cxx}
* Made the graph traversal logic less ad-hoc by using existing
  methods in the GlobalGenerator; this fixed a few bugs
* Added support for new target types: custom targets, object
  and unknown libraries
* Improved support for ALIAS libraries by showing the alias(es)
  in the graph
* Introduced new flags to control those new libraries (consistent
  with existing flags)
* Updated the documentation
* Removed useless setting to set graph type in dot file
* Improved the node/edge shapes (nicer, more consistent)
* Added a legend to the graph
* Some refactoring and cleanup of the Graphviz generation code
* Added test and fix for issue 19746
This commit is contained in:
Corentin Plouet 2019-10-08 13:45:30 +11:00
parent 4c29297495
commit 553658393c
50 changed files with 1724 additions and 533 deletions

View File

@ -5,119 +5,145 @@
CMakeGraphVizOptions
--------------------
The builtin graphviz support of CMake.
The builtin Graphviz support of CMake.
Variables specific to the graphviz support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Generating Graphviz files
^^^^^^^^^^^^^^^^^^^^^^^^^
CMake
can generate `graphviz <http://www.graphviz.org/>`_ files, showing the dependencies between the
targets in a project and also external libraries which are linked
against. When CMake is run with the ``--graphviz=foo.dot`` option, it will
produce:
CMake can generate `Graphviz <https://www.graphviz.org/>`_ files showing the
dependencies between the targets in a project, as well as external libraries
which are linked against.
* a ``foo.dot`` file showing all dependencies in the project
* a ``foo.dot.<target>`` file for each target, file showing on which other targets the respective target depends
* a ``foo.dot.<target>.dependers`` file, showing which other targets depend on the respective target
When running CMake with the ``--graphviz=foo.dot`` option, it produces:
The different dependency types ``PUBLIC``, ``PRIVATE`` and ``INTERFACE``
* a ``foo.dot`` file, showing all dependencies in the project
* a ``foo.dot.<target>`` file for each target, showing on which other targets
it depends
* a ``foo.dot.<target>.dependers`` file for each target, showing which other
targets depend on it
Those .dot files can be converted to images using the *dot* command from the
Graphviz package:
.. code-block:: shell
dot -Tpng -o foo.png foo.dot
The different dependency types ``PUBLIC``, ``INTERFACE`` and ``PRIVATE``
are represented as solid, dashed and dotted edges.
This can result in huge graphs. Using the file
``CMakeGraphVizOptions.cmake`` the look and content of the generated
graphs can be influenced. This file is searched first in
:variable:`CMAKE_BINARY_DIR` and then in :variable:`CMAKE_SOURCE_DIR`. If found, it is
read and the variables set in it are used to adjust options for the
generated graphviz files.
Variables specific to the Graphviz support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. variable:: GRAPHVIZ_GRAPH_TYPE
The graph type.
* Mandatory : NO
* Default : "digraph"
Valid graph types are:
* "graph" : Nodes are joined with lines
* "digraph" : Nodes are joined with arrows showing direction
* "strict graph" : Like "graph" but max one line between each node
* "strict digraph" : Like "graph" but max one line between each node in each direction
The resulting graphs can be huge. The look and content of the generated graphs
can be controlled using the file ``CMakeGraphVizOptions.cmake``. This file is
first searched in :variable:`CMAKE_BINARY_DIR`, and then in
:variable:`CMAKE_SOURCE_DIR`. If found, the variables set in it are used to
adjust options for the generated Graphviz files.
.. variable:: GRAPHVIZ_GRAPH_NAME
The graph name.
* Mandatory : NO
* Default : "GG"
* Mandatory: NO
* Default: value of :variable:`CMAKE_PROJECT_NAME`
.. variable:: GRAPHVIZ_GRAPH_HEADER
The header written at the top of the graphviz file.
The header written at the top of the Graphviz files.
* Mandatory : NO
* Default : "node [n fontsize = "12"];"
* Mandatory: NO
* Default: "node [ fontsize = "12" ];"
.. variable:: GRAPHVIZ_NODE_PREFIX
The prefix for each node in the graphviz file.
The prefix for each node in the Graphviz files.
* Mandatory : NO
* Default : "node"
* Mandatory: NO
* Default: "node"
.. variable:: GRAPHVIZ_EXECUTABLES
Set this to FALSE to exclude executables from the generated graphs.
Set to FALSE to exclude executables from the generated graphs.
* Mandatory : NO
* Default : TRUE
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_STATIC_LIBS
Set this to FALSE to exclude static libraries from the generated graphs.
Set to FALSE to exclude static libraries from the generated graphs.
* Mandatory : NO
* Default : TRUE
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_SHARED_LIBS
Set this to FALSE to exclude shared libraries from the generated graphs.
Set to FALSE to exclude shared libraries from the generated graphs.
* Mandatory : NO
* Default : TRUE
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_MODULE_LIBS
Set this to FALSE to exclude module libraries from the generated graphs.
Set to FALSE to exclude module libraries from the generated graphs.
* Mandatory : NO
* Default : TRUE
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_INTERFACE_LIBS
Set to FALSE to exclude interface libraries from the generated graphs.
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_OBJECT_LIBS
Set to FALSE to exclude object libraries from the generated graphs.
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_UNKNOWN_LIBS
Set to FALSE to exclude unknown libraries from the generated graphs.
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_EXTERNAL_LIBS
Set this to FALSE to exclude external libraries from the generated graphs.
Set to FALSE to exclude external libraries from the generated graphs.
* Mandatory : NO
* Default : TRUE
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_CUSTOM_TARGETS
Set to TRUE to include custom targets in the generated graphs.
* Mandatory: NO
* Default: FALSE
.. variable:: GRAPHVIZ_IGNORE_TARGETS
A list of regular expressions for ignoring targets.
A list of regular expressions for names of targets to exclude from the
generated graphs.
* Mandatory : NO
* Default : empty
* Mandatory: NO
* Default: empty
.. variable:: GRAPHVIZ_GENERATE_PER_TARGET
Set this to FALSE to exclude per target graphs ``foo.dot.<target>``.
Set to FALSE to not generate per-target graphs ``foo.dot.<target>``.
* Mandatory : NO
* Default : TRUE
* Mandatory: NO
* Default: TRUE
.. variable:: GRAPHVIZ_GENERATE_DEPENDERS
Set this to FALSE to exclude depender graphs ``foo.dot.<target>.dependers``.
Set to FALSE to not generate depender graphs ``foo.dot.<target>.dependers``.
* Mandatory : NO
* Default : TRUE
* Mandatory: NO
* Default: TRUE
#]=======================================================================]

View File

@ -289,6 +289,8 @@ set(SRCS
cmGeneratorExpression.h
cmGeneratorTarget.cxx
cmGeneratorTarget.h
cmLinkItemGraphVisitor.cxx
cmLinkItemGraphVisitor.h
cmGetPipes.cxx
cmGetPipes.h
cmGlobalCommonGenerator.cxx

View File

@ -2,176 +2,192 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmGraphVizWriter.h"
#include <cstddef>
#include <cctype>
#include <iostream>
#include <memory>
#include <sstream>
#include <set>
#include <utility>
#include <cm/memory>
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmState.h"
#include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmake.h"
namespace {
enum LinkLibraryScopeType
{
LLT_SCOPE_PUBLIC,
LLT_SCOPE_PRIVATE,
LLT_SCOPE_INTERFACE
};
const char* const GRAPHVIZ_PRIVATE_EDEGE_STYLE = "dashed";
const char* const GRAPHVIZ_INTERFACE_EDEGE_STYLE = "dotted";
char const* const GRAPHVIZ_EDGE_STYLE_PUBLIC = "solid";
char const* const GRAPHVIZ_EDGE_STYLE_INTERFACE = "dashed";
char const* const GRAPHVIZ_EDGE_STYLE_PRIVATE = "dotted";
std::string getLinkLibraryStyle(const LinkLibraryScopeType& type)
{
std::string style;
switch (type) {
case LLT_SCOPE_PRIVATE:
style = "[style = " + std::string(GRAPHVIZ_PRIVATE_EDEGE_STYLE) + "]";
break;
case LLT_SCOPE_INTERFACE:
style = "[style = " + std::string(GRAPHVIZ_INTERFACE_EDEGE_STYLE) + "]";
break;
default:
break;
}
return style;
}
char const* const GRAPHVIZ_NODE_SHAPE_EXECUTABLE = "egg"; // egg-xecutable
const char* getShapeForTarget(const cmGeneratorTarget* target)
// Normal libraries.
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_STATIC = "octagon";
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_SHARED = "doubleoctagon";
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_MODULE = "tripleoctagon";
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_INTERFACE = "pentagon";
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_OBJECT = "hexagon";
char const* const GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN = "septagon";
char const* const GRAPHVIZ_NODE_SHAPE_UTILITY = "box";
const char* getShapeForTarget(const cmLinkItem& item)
{
if (!target) {
return "ellipse";
if (item.Target == nullptr) {
return GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN;
}
switch (target->GetType()) {
switch (item.Target->GetType()) {
case cmStateEnums::EXECUTABLE:
return "house";
return GRAPHVIZ_NODE_SHAPE_EXECUTABLE;
case cmStateEnums::STATIC_LIBRARY:
return "diamond";
return GRAPHVIZ_NODE_SHAPE_LIBRARY_STATIC;
case cmStateEnums::SHARED_LIBRARY:
return "polygon";
return GRAPHVIZ_NODE_SHAPE_LIBRARY_SHARED;
case cmStateEnums::MODULE_LIBRARY:
return "octagon";
return GRAPHVIZ_NODE_SHAPE_LIBRARY_MODULE;
case cmStateEnums::OBJECT_LIBRARY:
return GRAPHVIZ_NODE_SHAPE_LIBRARY_OBJECT;
case cmStateEnums::UTILITY:
return GRAPHVIZ_NODE_SHAPE_UTILITY;
case cmStateEnums::INTERFACE_LIBRARY:
return GRAPHVIZ_NODE_SHAPE_LIBRARY_INTERFACE;
case cmStateEnums::UNKNOWN_LIBRARY:
default:
break;
return GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN;
}
return "box";
}
std::map<std::string, LinkLibraryScopeType> getScopedLinkLibrariesFromTarget(
cmTarget* Target, const cmGlobalGenerator* globalGenerator)
{
char sep = ';';
std::map<std::string, LinkLibraryScopeType> tokens;
size_t start = 0;
size_t end = 0;
const char* pInterfaceLinkLibraries =
Target->GetProperty("INTERFACE_LINK_LIBRARIES");
const char* pLinkLibraries = Target->GetProperty("LINK_LIBRARIES");
if (!pInterfaceLinkLibraries && !pLinkLibraries) {
return tokens; // target is not linked against any other libraries
}
// make sure we don't touch a null-ptr
auto interfaceLinkLibraries =
std::string(pInterfaceLinkLibraries ? pInterfaceLinkLibraries : "");
auto linkLibraries = std::string(pLinkLibraries ? pLinkLibraries : "");
// first extract interfaceLinkLibraries
while (start < interfaceLinkLibraries.length()) {
if ((end = interfaceLinkLibraries.find(sep, start)) == std::string::npos) {
end = interfaceLinkLibraries.length();
}
std::string element = interfaceLinkLibraries.substr(start, end - start);
if (globalGenerator->IsAlias(element)) {
const auto tgt = globalGenerator->FindTarget(element);
if (tgt) {
element = tgt->GetName();
}
}
if (std::string::npos == element.find("$<LINK_ONLY:", 0)) {
// we assume first, that this library is an interface library.
// if we find it again in the linklibraries property, we promote it to an
// public library.
tokens[element] = LLT_SCOPE_INTERFACE;
} else {
// this is an private linked static library.
// we take care of this case in the second iterator.
}
start = end + 1;
}
// second extract linkLibraries
start = 0;
while (start < linkLibraries.length()) {
if ((end = linkLibraries.find(sep, start)) == std::string::npos) {
end = linkLibraries.length();
}
std::string element = linkLibraries.substr(start, end - start);
if (globalGenerator->IsAlias(element)) {
const auto tgt = globalGenerator->FindTarget(element);
if (tgt) {
element = tgt->GetName();
}
}
if (tokens.find(element) == tokens.end()) {
// this library is not found in interfaceLinkLibraries but in
// linkLibraries.
// this results in a private linked library.
tokens[element] = LLT_SCOPE_PRIVATE;
} else if (LLT_SCOPE_INTERFACE == tokens[element]) {
// this library is found in interfaceLinkLibraries and linkLibraries.
// this results in a public linked library.
tokens[element] = LLT_SCOPE_PUBLIC;
} else {
// private and public linked libraries should not be changed anymore.
}
start = end + 1;
}
return tokens;
}
}
cmGraphVizWriter::cmGraphVizWriter(const cmGlobalGenerator* globalGenerator)
: GraphType("digraph")
, GraphName("GG")
cmGraphVizWriter::cmGraphVizWriter(std::string const& fileName,
const cmGlobalGenerator* globalGenerator)
: FileName(fileName)
, GlobalFileStream(fileName)
, GraphName(globalGenerator->GetSafeGlobalSetting("CMAKE_PROJECT_NAME"))
, GraphHeader("node [\n fontsize = \"12\"\n];")
, GraphNodePrefix("node")
, GlobalGenerator(globalGenerator)
, LocalGenerators(globalGenerator->GetLocalGenerators())
, NextNodeId(0)
, GenerateForExecutables(true)
, GenerateForStaticLibs(true)
, GenerateForSharedLibs(true)
, GenerateForModuleLibs(true)
, GenerateForInterface(true)
, GenerateForInterfaceLibs(true)
, GenerateForObjectLibs(true)
, GenerateForUnknownLibs(true)
, GenerateForCustomTargets(false)
, GenerateForExternals(true)
, GeneratePerTarget(true)
, GenerateDependers(true)
, HaveTargetsAndLibs(false)
{
}
cmGraphVizWriter::~cmGraphVizWriter()
{
this->WriteFooter(this->GlobalFileStream);
for (auto& fileStream : this->PerTargetFileStreams) {
this->WriteFooter(*fileStream.second);
}
for (auto& fileStream : this->TargetDependersFileStreams) {
this->WriteFooter(*fileStream.second);
}
}
void cmGraphVizWriter::VisitGraph(std::string const&)
{
this->WriteHeader(GlobalFileStream, this->GraphName);
this->WriteLegend(GlobalFileStream);
}
void cmGraphVizWriter::OnItem(cmLinkItem const& item)
{
if (this->ItemExcluded(item)) {
return;
}
NodeNames[item.AsStr()] = cmStrCat(GraphNodePrefix, NextNodeId);
++NextNodeId;
this->WriteNode(this->GlobalFileStream, item);
if (this->GeneratePerTarget) {
this->CreateTargetFile(this->PerTargetFileStreams, item);
}
if (this->GenerateDependers) {
this->CreateTargetFile(this->TargetDependersFileStreams, item,
".dependers");
}
}
void cmGraphVizWriter::CreateTargetFile(FileStreamMap& fileStreamMap,
cmLinkItem const& item,
std::string const& fileNameSuffix)
{
auto const pathSafeItemName = PathSafeString(item.AsStr());
auto const perTargetFileName =
cmStrCat(this->FileName, '.', pathSafeItemName, fileNameSuffix);
auto perTargetFileStream =
cm::make_unique<cmGeneratedFileStream>(perTargetFileName);
this->WriteHeader(*perTargetFileStream, item.AsStr());
this->WriteNode(*perTargetFileStream, item);
fileStreamMap.emplace(item.AsStr(), std::move(perTargetFileStream));
}
void cmGraphVizWriter::OnDirectLink(cmLinkItem const& depender,
cmLinkItem const& dependee,
DependencyType dt)
{
this->VisitLink(depender, dependee, true, GetEdgeStyle(dt));
}
void cmGraphVizWriter::OnIndirectLink(cmLinkItem const& depender,
cmLinkItem const& dependee)
{
this->VisitLink(depender, dependee, false);
}
void cmGraphVizWriter::VisitLink(cmLinkItem const& depender,
cmLinkItem const& dependee, bool isDirectLink,
std::string const& scopeType)
{
if (this->ItemExcluded(depender) || this->ItemExcluded(dependee)) {
return;
}
if (!isDirectLink) {
return;
}
this->WriteConnection(this->GlobalFileStream, depender, dependee, scopeType);
if (this->GeneratePerTarget) {
auto fileStream = PerTargetFileStreams[depender.AsStr()].get();
this->WriteNode(*fileStream, dependee);
this->WriteConnection(*fileStream, depender, dependee, scopeType);
}
if (this->GenerateDependers) {
auto fileStream = TargetDependersFileStreams[dependee.AsStr()].get();
this->WriteNode(*fileStream, depender);
this->WriteConnection(*fileStream, depender, dependee, scopeType);
}
}
void cmGraphVizWriter::ReadSettings(
const std::string& settingsFileName,
const std::string& fallbackSettingsFileName)
@ -208,7 +224,6 @@ void cmGraphVizWriter::ReadSettings(
} \
} while (false)
__set_if_set(this->GraphType, "GRAPHVIZ_GRAPH_TYPE");
__set_if_set(this->GraphName, "GRAPHVIZ_GRAPH_NAME");
__set_if_set(this->GraphHeader, "GRAPHVIZ_GRAPH_HEADER");
__set_if_set(this->GraphNodePrefix, "GRAPHVIZ_NODE_PREFIX");
@ -225,7 +240,10 @@ void cmGraphVizWriter::ReadSettings(
__set_bool_if_set(this->GenerateForStaticLibs, "GRAPHVIZ_STATIC_LIBS");
__set_bool_if_set(this->GenerateForSharedLibs, "GRAPHVIZ_SHARED_LIBS");
__set_bool_if_set(this->GenerateForModuleLibs, "GRAPHVIZ_MODULE_LIBS");
__set_bool_if_set(this->GenerateForInterface, "GRAPHVIZ_INTERFACE");
__set_bool_if_set(this->GenerateForInterfaceLibs, "GRAPHVIZ_INTERFACE_LIBS");
__set_bool_if_set(this->GenerateForObjectLibs, "GRAPHVIZ_OBJECT_LIBS");
__set_bool_if_set(this->GenerateForUnknownLibs, "GRAPHVIZ_UNKNOWN_LIBS");
__set_bool_if_set(this->GenerateForCustomTargets, "GRAPHVIZ_CUSTOM_TARGETS");
__set_bool_if_set(this->GenerateForExternals, "GRAPHVIZ_EXTERNAL_LIBS");
__set_bool_if_set(this->GeneratePerTarget, "GRAPHVIZ_GENERATE_PER_TARGET");
__set_bool_if_set(this->GenerateDependers, "GRAPHVIZ_GENERATE_DEPENDERS");
@ -248,329 +266,170 @@ void cmGraphVizWriter::ReadSettings(
}
}
// Iterate over all targets and write for each one a graph which shows
// which other targets depend on it.
void cmGraphVizWriter::WriteTargetDependersFiles(const std::string& fileName)
void cmGraphVizWriter::Write()
{
if (!this->GenerateDependers) {
return;
}
auto gg = this->GlobalGenerator;
this->CollectTargetsAndLibs();
this->VisitGraph(gg->GetName());
for (auto const& ptr : this->TargetPtrs) {
if (ptr.second == nullptr) {
continue;
}
// We want to traverse in a determined order, such that the output is always
// the same for a given project (this makes tests reproducible, etc.)
std::set<cmGeneratorTarget const*, cmGeneratorTarget::StrictTargetComparison>
sortedGeneratorTargets;
if (!this->GenerateForTargetType(ptr.second->GetType())) {
continue;
}
std::string currentFilename =
cmStrCat(fileName, '.', ptr.first, ".dependers");
cmGeneratedFileStream str(currentFilename);
if (!str) {
return;
}
std::set<std::string> insertedConnections;
std::set<std::string> insertedNodes;
std::cout << "Writing " << currentFilename << "..." << std::endl;
this->WriteHeader(str);
this->WriteDependerConnections(ptr.first, insertedNodes,
insertedConnections, str);
this->WriteFooter(str);
}
}
// Iterate over all targets and write for each one a graph which shows
// on which targets it depends.
void cmGraphVizWriter::WritePerTargetFiles(const std::string& fileName)
{
if (!this->GeneratePerTarget) {
return;
}
this->CollectTargetsAndLibs();
for (auto const& ptr : this->TargetPtrs) {
if (ptr.second == nullptr) {
continue;
}
if (!this->GenerateForTargetType(ptr.second->GetType())) {
continue;
}
std::set<std::string> insertedConnections;
std::set<std::string> insertedNodes;
std::string currentFilename = cmStrCat(fileName, '.', ptr.first);
cmGeneratedFileStream str(currentFilename);
if (!str) {
return;
}
std::cout << "Writing " << currentFilename << "..." << std::endl;
this->WriteHeader(str);
this->WriteConnections(ptr.first, insertedNodes, insertedConnections, str);
this->WriteFooter(str);
}
}
void cmGraphVizWriter::WriteGlobalFile(const std::string& fileName)
{
this->CollectTargetsAndLibs();
cmGeneratedFileStream str(fileName);
if (!str) {
return;
}
this->WriteHeader(str);
std::cout << "Writing " << fileName << "..." << std::endl;
std::set<std::string> insertedConnections;
std::set<std::string> insertedNodes;
for (auto const& ptr : this->TargetPtrs) {
if (ptr.second == nullptr) {
continue;
}
if (!this->GenerateForTargetType(ptr.second->GetType())) {
continue;
}
this->WriteConnections(ptr.first, insertedNodes, insertedConnections, str);
}
this->WriteFooter(str);
}
void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& str) const
{
str << this->GraphType << " \"" << this->GraphName << "\" {" << std::endl;
str << this->GraphHeader << std::endl;
}
void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& str) const
{
str << "}" << std::endl;
}
void cmGraphVizWriter::WriteConnections(
const std::string& targetName, std::set<std::string>& insertedNodes,
std::set<std::string>& insertedConnections, cmGeneratedFileStream& str) const
{
auto targetPtrIt = this->TargetPtrs.find(targetName);
if (targetPtrIt == this->TargetPtrs.end()) // not found at all
{
return;
}
this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
if (targetPtrIt->second == nullptr) // it's an external library
{
return;
}
std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
std::map<std::string, LinkLibraryScopeType> ll =
getScopedLinkLibrariesFromTarget(targetPtrIt->second->Target,
GlobalGenerator);
for (auto const& llit : ll) {
const std::string& libName = llit.first;
auto libNameIt = this->TargetNamesNodes.find(libName);
// can happen e.g. if GRAPHVIZ_TARGET_IGNORE_REGEX is used
if (libNameIt == this->TargetNamesNodes.end()) {
continue;
}
std::string connectionName = cmStrCat(myNodeName, '-', libNameIt->second);
if (insertedConnections.find(connectionName) ==
insertedConnections.end()) {
insertedConnections.insert(connectionName);
this->WriteNode(libName, this->TargetPtrs.find(libName)->second,
insertedNodes, str);
str << " \"" << myNodeName << "\" -> \"" << libNameIt->second << "\"";
str << getLinkLibraryStyle(llit.second);
str << " // " << targetName << " -> " << libName << std::endl;
this->WriteConnections(libName, insertedNodes, insertedConnections, str);
}
}
}
void cmGraphVizWriter::WriteDependerConnections(
const std::string& targetName, std::set<std::string>& insertedNodes,
std::set<std::string>& insertedConnections, cmGeneratedFileStream& str) const
{
auto targetPtrIt = this->TargetPtrs.find(targetName);
if (targetPtrIt == this->TargetPtrs.end()) // not found at all
{
return;
}
this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
if (targetPtrIt->second == nullptr) // it's an external library
{
return;
}
std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
// now search who links against me
for (auto const& tptr : this->TargetPtrs) {
if (tptr.second == nullptr) {
continue;
}
if (!this->GenerateForTargetType(tptr.second->GetType())) {
continue;
}
// Now we have a target, check whether it links against targetName.
// If so, draw a connection, and then continue with dependers on that one.
std::map<std::string, LinkLibraryScopeType> ll =
getScopedLinkLibrariesFromTarget(tptr.second->Target, GlobalGenerator);
for (auto const& llit : ll) {
if (llit.first == targetName) {
// So this target links against targetName.
auto dependerNodeNameIt = this->TargetNamesNodes.find(tptr.first);
if (dependerNodeNameIt != this->TargetNamesNodes.end()) {
std::string connectionName =
cmStrCat(dependerNodeNameIt->second, '-', myNodeName);
if (insertedConnections.find(connectionName) ==
insertedConnections.end()) {
insertedConnections.insert(connectionName);
this->WriteNode(tptr.first, tptr.second, insertedNodes, str);
str << " \"" << dependerNodeNameIt->second << "\" -> \""
<< myNodeName << "\"";
str << " // " << targetName << " -> " << tptr.first << std::endl;
str << getLinkLibraryStyle(llit.second);
this->WriteDependerConnections(tptr.first, insertedNodes,
insertedConnections, str);
}
}
break;
for (cmLocalGenerator const* lg : gg->GetLocalGenerators()) {
for (cmGeneratorTarget const* gt : lg->GetGeneratorTargets()) {
// Reserved targets have inconsistent names across platforms (e.g. 'all'
// vs. 'ALL_BUILD'), which can disrupt the traversal ordering.
// We don't need or want them anyway.
if (!cmGlobalGenerator::IsReservedTarget(gt->GetName())) {
sortedGeneratorTargets.insert(gt);
}
}
}
}
void cmGraphVizWriter::WriteNode(const std::string& targetName,
const cmGeneratorTarget* target,
std::set<std::string>& insertedNodes,
cmGeneratedFileStream& str) const
{
if (insertedNodes.find(targetName) == insertedNodes.end()) {
insertedNodes.insert(targetName);
auto nameIt = this->TargetNamesNodes.find(targetName);
str << " \"" << nameIt->second << "\" [ label=\"" << targetName
<< "\" shape=\"" << getShapeForTarget(target) << "\"];" << std::endl;
for (auto const gt : sortedGeneratorTargets) {
auto item = cmLinkItem(gt, gt->GetBacktrace());
this->VisitItem(item);
}
}
void cmGraphVizWriter::CollectTargetsAndLibs()
void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& fs,
const std::string& name)
{
if (!this->HaveTargetsAndLibs) {
this->HaveTargetsAndLibs = true;
int cnt = this->CollectAllTargets();
if (this->GenerateForExternals) {
this->CollectAllExternalLibs(cnt);
}
}
auto const escapedGraphName = EscapeForDotFile(name);
fs << "digraph \"" << escapedGraphName << "\" {" << std::endl;
fs << this->GraphHeader << std::endl;
}
int cmGraphVizWriter::CollectAllTargets()
void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& fs)
{
int cnt = 0;
// First pass get the list of all cmake targets
for (cmLocalGenerator* lg : this->LocalGenerators) {
const std::vector<cmGeneratorTarget*>& targets = lg->GetGeneratorTargets();
for (cmGeneratorTarget* target : targets) {
const std::string& realTargetName = target->GetName();
if (this->IgnoreThisTarget(realTargetName)) {
// Skip ignored targets
continue;
}
// std::cout << "Found target: " << tit->first << std::endl;
std::ostringstream ostr;
ostr << this->GraphNodePrefix << cnt++;
this->TargetNamesNodes[realTargetName] = ostr.str();
this->TargetPtrs[realTargetName] = target;
fs << "}" << std::endl;
}
void cmGraphVizWriter::WriteLegend(cmGeneratedFileStream& fs)
{
// Note that the subgraph name must start with "cluster", as done here, to
// make Graphviz layout engines do the right thing and keep the nodes
// together.
fs << "subgraph clusterLegend {" << std::endl;
fs << " label = \"Legend\";" << std::endl;
// Set the color of the box surrounding the legend.
fs << " color = black;" << std::endl;
// We use invisible edges just to enforce the layout.
fs << " edge [ style = invis ];" << std::endl;
// Nodes.
fs << " legendNode0 [ label = \"Executable\", shape = "
<< GRAPHVIZ_NODE_SHAPE_EXECUTABLE << " ];" << std::endl;
fs << " legendNode1 [ label = \"Static Library\", shape = "
<< GRAPHVIZ_NODE_SHAPE_LIBRARY_STATIC << " ];" << std::endl;
fs << " legendNode2 [ label = \"Shared Library\", shape = "
<< GRAPHVIZ_NODE_SHAPE_LIBRARY_SHARED << " ];" << std::endl;
fs << " legendNode3 [ label = \"Module Library\", shape = "
<< GRAPHVIZ_NODE_SHAPE_LIBRARY_MODULE << " ];" << std::endl;
fs << " legendNode4 [ label = \"Interface Library\", shape = "
<< GRAPHVIZ_NODE_SHAPE_LIBRARY_INTERFACE << " ];" << std::endl;
fs << " legendNode5 [ label = \"Object Library\", shape = "
<< GRAPHVIZ_NODE_SHAPE_LIBRARY_OBJECT << " ];" << std::endl;
fs << " legendNode6 [ label = \"Unknown Library\", shape = "
<< GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN << " ];" << std::endl;
fs << " legendNode7 [ label = \"Custom Target\", shape = "
<< GRAPHVIZ_NODE_SHAPE_UTILITY << " ];" << std::endl;
// Edges.
// Some of those are dummy (invisible) edges to enforce a layout.
fs << " legendNode0 -> legendNode1 [ style = " << GRAPHVIZ_EDGE_STYLE_PUBLIC
<< " ];" << std::endl;
fs << " legendNode0 -> legendNode2 [ style = " << GRAPHVIZ_EDGE_STYLE_PUBLIC
<< " ];" << std::endl;
fs << " legendNode0 -> legendNode3;" << std::endl;
fs << " legendNode1 -> legendNode4 [ label = \"Interface\", style = "
<< GRAPHVIZ_EDGE_STYLE_INTERFACE << " ];" << std::endl;
fs << " legendNode2 -> legendNode5 [ label = \"Private\", style = "
<< GRAPHVIZ_EDGE_STYLE_PRIVATE << " ];" << std::endl;
fs << " legendNode3 -> legendNode6 [ style = " << GRAPHVIZ_EDGE_STYLE_PUBLIC
<< " ];" << std::endl;
fs << " legendNode0 -> legendNode7;" << std::endl;
fs << "}" << std::endl;
}
void cmGraphVizWriter::WriteNode(cmGeneratedFileStream& fs,
cmLinkItem const& item)
{
auto const& itemName = item.AsStr();
auto const& nodeName = this->NodeNames[itemName];
auto const itemNameWithAliases = ItemNameWithAliases(itemName);
auto const escapedLabel = EscapeForDotFile(itemNameWithAliases);
fs << " \"" << nodeName << "\" [ label = \"" << escapedLabel
<< "\", shape = " << getShapeForTarget(item) << " ];" << std::endl;
}
void cmGraphVizWriter::WriteConnection(cmGeneratedFileStream& fs,
cmLinkItem const& depender,
cmLinkItem const& dependee,
std::string const& edgeStyle)
{
auto const& dependerName = depender.AsStr();
auto const& dependeeName = dependee.AsStr();
fs << " \"" << this->NodeNames[dependerName] << "\" -> \""
<< this->NodeNames[dependeeName] << "\" ";
fs << edgeStyle;
fs << " // " << dependerName << " -> " << dependeeName << std::endl;
}
bool cmGraphVizWriter::ItemExcluded(cmLinkItem const& item)
{
auto const itemName = item.AsStr();
if (this->ItemNameFilteredOut(itemName)) {
return true;
}
if (item.Target == nullptr) {
return !this->GenerateForExternals;
}
if (item.Target->GetType() == cmStateEnums::UTILITY) {
if ((itemName.find("Nightly") == 0) ||
(itemName.find("Continuous") == 0) ||
(itemName.find("Experimental") == 0)) {
return true;
}
}
return cnt;
}
int cmGraphVizWriter::CollectAllExternalLibs(int cnt)
{
// Ok, now find all the stuff we link to that is not in cmake
for (cmLocalGenerator* lg : this->LocalGenerators) {
const std::vector<cmGeneratorTarget*>& targets = lg->GetGeneratorTargets();
for (cmGeneratorTarget* target : targets) {
const std::string& realTargetName = target->GetName();
if (this->IgnoreThisTarget(realTargetName)) {
// Skip ignored targets
continue;
}
const cmTarget::LinkLibraryVectorType* ll =
&(target->Target->GetOriginalLinkLibraries());
for (auto const& llit : *ll) {
std::string libName = llit.first;
if (this->IgnoreThisTarget(libName)) {
// Skip ignored targets
continue;
}
if (GlobalGenerator->IsAlias(libName)) {
const auto tgt = GlobalGenerator->FindTarget(libName);
if (tgt) {
libName = tgt->GetName();
}
}
auto tarIt = this->TargetPtrs.find(libName);
if (tarIt == this->TargetPtrs.end()) {
std::ostringstream ostr;
ostr << this->GraphNodePrefix << cnt++;
this->TargetNamesNodes[libName] = ostr.str();
this->TargetPtrs[libName] = nullptr;
// str << " \"" << ostr << "\" [ label=\"" << libName
// << "\" shape=\"ellipse\"];" << std::endl;
}
}
}
if (item.Target->IsImported() && !this->GenerateForExternals) {
return true;
}
return cnt;
return !this->TargetTypeEnabled(item.Target->GetType());
}
bool cmGraphVizWriter::IgnoreThisTarget(const std::string& name)
bool cmGraphVizWriter::ItemNameFilteredOut(std::string const& itemName)
{
if (itemName == ">") {
// FIXME: why do we even receive such a target here?
return true;
}
if (cmGlobalGenerator::IsReservedTarget(itemName)) {
return true;
}
for (cmsys::RegularExpression& regEx : this->TargetsToIgnoreRegex) {
if (regEx.is_valid()) {
if (regEx.find(name)) {
if (regEx.find(itemName)) {
return true;
}
}
@ -579,7 +438,7 @@ bool cmGraphVizWriter::IgnoreThisTarget(const std::string& name)
return false;
}
bool cmGraphVizWriter::GenerateForTargetType(
bool cmGraphVizWriter::TargetTypeEnabled(
cmStateEnums::TargetType targetType) const
{
switch (targetType) {
@ -592,9 +451,73 @@ bool cmGraphVizWriter::GenerateForTargetType(
case cmStateEnums::MODULE_LIBRARY:
return this->GenerateForModuleLibs;
case cmStateEnums::INTERFACE_LIBRARY:
return this->GenerateForInterface;
return this->GenerateForInterfaceLibs;
case cmStateEnums::OBJECT_LIBRARY:
return this->GenerateForObjectLibs;
case cmStateEnums::UNKNOWN_LIBRARY:
return this->GenerateForUnknownLibs;
case cmStateEnums::UTILITY:
return this->GenerateForCustomTargets;
case cmStateEnums::GLOBAL_TARGET:
// Built-in targets like edit_cache, etc.
// We don't need/want those in the dot file.
return false;
default:
break;
}
return false;
}
std::string cmGraphVizWriter::ItemNameWithAliases(
std::string const& itemName) const
{
auto nameWithAliases = itemName;
for (auto const& lg : this->GlobalGenerator->GetLocalGenerators()) {
for (auto const& aliasTargets : lg->GetMakefile()->GetAliasTargets()) {
if (aliasTargets.second == itemName) {
nameWithAliases += "\\n(" + aliasTargets.first + ")";
}
}
}
return nameWithAliases;
}
std::string cmGraphVizWriter::GetEdgeStyle(DependencyType dt)
{
std::string style;
switch (dt) {
case DependencyType::LinkPrivate:
style = "[ style = " + std::string(GRAPHVIZ_EDGE_STYLE_PRIVATE) + " ]";
break;
case DependencyType::LinkInterface:
style = "[ style = " + std::string(GRAPHVIZ_EDGE_STYLE_INTERFACE) + " ]";
break;
default:
break;
}
return style;
}
std::string cmGraphVizWriter::EscapeForDotFile(std::string const& str)
{
return cmSystemTools::EscapeChars(str.data(), "\"");
}
std::string cmGraphVizWriter::PathSafeString(std::string const& str)
{
std::string pathSafeStr;
// We'll only keep alphanumerical characters, plus the following ones that
// are common, and safe on all platforms:
auto const extra_chars = std::set<char>{ '.', '-', '_' };
for (char c : str) {
if (std::isalnum(c) || extra_chars.find(c) != extra_chars.cend()) {
pathSafeStr += c;
}
}
return pathSafeStr;
}

View File

@ -6,87 +6,106 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <map>
#include <set>
#include <memory>
#include <string>
#include <vector>
#include "cmsys/RegularExpression.hxx"
#include "cmGeneratedFileStream.h"
#include "cmLinkItemGraphVisitor.h"
#include "cmStateTypes.h"
class cmGeneratedFileStream;
class cmGeneratorTarget;
class cmLocalGenerator;
class cmLinkItem;
class cmGlobalGenerator;
/** This class implements writing files for graphviz (dot) for graphs
* representing the dependencies between the targets in the project. */
class cmGraphVizWriter
class cmGraphVizWriter : public cmLinkItemGraphVisitor
{
public:
cmGraphVizWriter(const cmGlobalGenerator* globalGenerator);
cmGraphVizWriter(std::string const& fileName,
const cmGlobalGenerator* globalGenerator);
~cmGraphVizWriter() override;
void VisitGraph(std::string const& name) override;
void OnItem(cmLinkItem const& item) override;
void OnDirectLink(cmLinkItem const& depender, cmLinkItem const& dependee,
DependencyType dt) override;
void OnIndirectLink(cmLinkItem const& depender,
cmLinkItem const& dependee) override;
void ReadSettings(const std::string& settingsFileName,
const std::string& fallbackSettingsFileName);
void WritePerTargetFiles(const std::string& fileName);
void WriteTargetDependersFiles(const std::string& fileName);
void Write();
void WriteGlobalFile(const std::string& fileName);
private:
using FileStreamMap =
std::map<std::string, std::unique_ptr<cmGeneratedFileStream>>;
protected:
void CollectTargetsAndLibs();
void VisitLink(cmLinkItem const& depender, cmLinkItem const& dependee,
bool isDirectLink, std::string const& scopeType = "");
int CollectAllTargets();
void WriteHeader(cmGeneratedFileStream& fs, std::string const& name);
int CollectAllExternalLibs(int cnt);
void WriteFooter(cmGeneratedFileStream& fs);
void WriteHeader(cmGeneratedFileStream& str) const;
void WriteLegend(cmGeneratedFileStream& fs);
void WriteConnections(const std::string& targetName,
std::set<std::string>& insertedNodes,
std::set<std::string>& insertedConnections,
cmGeneratedFileStream& str) const;
void WriteNode(cmGeneratedFileStream& fs, cmLinkItem const& item);
void WriteDependerConnections(const std::string& targetName,
std::set<std::string>& insertedNodes,
std::set<std::string>& insertedConnections,
cmGeneratedFileStream& str) const;
void CreateTargetFile(FileStreamMap& fileStreamMap, cmLinkItem const& target,
std::string const& fileNameSuffix = "");
void WriteNode(const std::string& targetName,
const cmGeneratorTarget* target,
std::set<std::string>& insertedNodes,
cmGeneratedFileStream& str) const;
void WriteConnection(cmGeneratedFileStream& fs,
cmLinkItem const& dependerTargetName,
cmLinkItem const& dependeeTargetName,
std::string const& edgeStyle);
void WriteFooter(cmGeneratedFileStream& str) const;
bool ItemExcluded(cmLinkItem const& item);
bool ItemNameFilteredOut(std::string const& itemName);
bool TargetTypeEnabled(cmStateEnums::TargetType targetType) const;
bool IgnoreThisTarget(const std::string& name);
std::string ItemNameWithAliases(std::string const& itemName) const;
bool GenerateForTargetType(cmStateEnums::TargetType targetType) const;
static std::string GetEdgeStyle(DependencyType dt);
static std::string EscapeForDotFile(std::string const& str);
static std::string PathSafeString(std::string const& str);
std::string FileName;
cmGeneratedFileStream GlobalFileStream;
FileStreamMap PerTargetFileStreams;
FileStreamMap TargetDependersFileStreams;
std::string GraphType;
std::string GraphName;
std::string GraphHeader;
std::string GraphNodePrefix;
std::vector<cmsys::RegularExpression> TargetsToIgnoreRegex;
const cmGlobalGenerator* GlobalGenerator;
const std::vector<cmLocalGenerator*>& LocalGenerators;
cmGlobalGenerator const* GlobalGenerator;
std::map<std::string, const cmGeneratorTarget*> TargetPtrs;
// maps from the actual target names to node names in dot:
std::map<std::string, std::string> TargetNamesNodes;
int NextNodeId;
// maps from the actual item names to node names in dot:
std::map<std::string, std::string> NodeNames;
bool GenerateForExecutables;
bool GenerateForStaticLibs;
bool GenerateForSharedLibs;
bool GenerateForModuleLibs;
bool GenerateForInterface;
bool GenerateForInterfaceLibs;
bool GenerateForObjectLibs;
bool GenerateForUnknownLibs;
bool GenerateForCustomTargets;
bool GenerateForExternals;
bool GeneratePerTarget;
bool GenerateDependers;
bool HaveTargetsAndLibs;
};
#endif

View File

@ -0,0 +1,142 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmLinkItemGraphVisitor.h"
#include <map>
#include <utility>
#include <vector>
#include "cmGeneratorTarget.h"
#include "cmLinkItem.h"
#include "cmMakefile.h"
void cmLinkItemGraphVisitor::VisitItem(cmLinkItem const& item)
{
if (this->ItemVisited(item)) {
return;
}
this->OnItem(item);
this->VisitLinks(item, item);
}
void cmLinkItemGraphVisitor::VisitLinks(cmLinkItem const& item,
cmLinkItem const& rootItem)
{
if (this->LinkVisited(item, rootItem)) {
return;
}
if (item.Target == nullptr) {
return;
}
for (auto const& config : item.Target->Makefile->GetGeneratorConfigs()) {
this->VisitLinks(item, rootItem, config);
}
}
void cmLinkItemGraphVisitor::VisitLinks(cmLinkItem const& item,
cmLinkItem const& rootItem,
std::string const& config)
{
auto const& target = *item.Target;
DependencyMap dependencies;
cmLinkItemGraphVisitor::GetDependencies(target, config, dependencies);
for (auto const& d : dependencies) {
auto const& dependency = d.second;
auto const& dependencyType = dependency.first;
auto const& dependee = dependency.second;
this->VisitItem(dependee);
if (this->LinkVisited(item, dependee)) {
continue;
}
this->OnDirectLink(item, dependee, dependencyType);
if (rootItem.AsStr() != item.AsStr()) {
this->OnIndirectLink(rootItem, dependee);
}
// Visit all the direct and indirect links.
this->VisitLinks(dependee, dependee);
this->VisitLinks(dependee, item);
this->VisitLinks(dependee, rootItem);
}
}
bool cmLinkItemGraphVisitor::ItemVisited(cmLinkItem const& item)
{
auto& collection = this->VisitedItems;
bool const visited = collection.find(item.AsStr()) != collection.cend();
if (!visited) {
collection.insert(item.AsStr());
}
return visited;
}
bool cmLinkItemGraphVisitor::LinkVisited(cmLinkItem const& depender,
cmLinkItem const& dependee)
{
auto const link = std::make_pair<>(depender.AsStr(), dependee.AsStr());
bool const linkVisited =
this->VisitedLinks.find(link) != this->VisitedLinks.cend();
if (!linkVisited) {
this->VisitedLinks.insert(link);
}
return linkVisited;
}
void cmLinkItemGraphVisitor::GetDependencies(cmGeneratorTarget const& target,
std::string const& config,
DependencyMap& dependencies)
{
auto implementationLibraries = target.GetLinkImplementationLibraries(config);
if (implementationLibraries != nullptr) {
for (auto const& lib : implementationLibraries->Libraries) {
auto const& name = lib.AsStr();
dependencies[name] = Dependency(DependencyType::LinkPrivate, lib);
}
}
auto interfaceLibraries =
target.GetLinkInterfaceLibraries(config, &target, true);
if (interfaceLibraries != nullptr) {
for (auto const& lib : interfaceLibraries->Libraries) {
auto const& name = lib.AsStr();
if (dependencies.find(name) != dependencies.cend()) {
dependencies[name] = Dependency(DependencyType::LinkPublic, lib);
} else {
dependencies[name] = Dependency(DependencyType::LinkInterface, lib);
}
}
}
std::vector<cmGeneratorTarget*> objectLibraries;
target.GetObjectLibrariesCMP0026(objectLibraries);
for (auto const& lib : objectLibraries) {
auto const& name = lib->GetName();
if (dependencies.find(name) == dependencies.cend()) {
auto objectItem = cmLinkItem(lib, lib->GetBacktrace());
dependencies[name] = Dependency(DependencyType::Object, objectItem);
}
}
auto const& utilityItems = target.GetUtilityItems();
for (auto const& item : utilityItems) {
auto const& name = item.AsStr();
if (dependencies.find(name) == dependencies.cend()) {
dependencies[name] = Dependency(DependencyType::Utility, item);
}
}
}

View File

@ -0,0 +1,75 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#ifndef cmLinkItemGraphVisitor_h
#define cmLinkItemGraphVisitor_h
#include <map>
#include <set>
#include <string>
#include <utility>
#include "cmLinkItem.h"
class cmGeneratorTarget;
/** \class cmLinkItemGraphVisitor
* \brief Visits a graph of linked items.
*
* Allows to visit items and dependency links (direct and indirect) between
* those items.
* This abstract class takes care of the graph traversal, making sure that:
* - it terminates even in the presence of cycles;
* - it visits every object once (and only once);
* - it visits the objects in the same order every time.
*
* Children classes only have to implement OnItem() etc. to handle whatever
* logic they care about.
*/
class cmLinkItemGraphVisitor
{
public:
virtual ~cmLinkItemGraphVisitor() = default;
virtual void VisitGraph(std::string const& name) = 0;
void VisitItem(cmLinkItem const& item);
protected:
enum class DependencyType
{
LinkInterface,
LinkPublic,
LinkPrivate,
Object,
Utility
};
virtual void OnItem(cmLinkItem const& item) = 0;
virtual void OnDirectLink(cmLinkItem const& depender,
cmLinkItem const& dependee, DependencyType dt) = 0;
virtual void OnIndirectLink(cmLinkItem const& depender,
cmLinkItem const& dependee) = 0;
private:
std::set<std::string> VisitedItems;
std::set<std::pair<std::string, std::string>> VisitedLinks;
void VisitLinks(cmLinkItem const& item, cmLinkItem const& rootItem);
void VisitLinks(cmLinkItem const& item, cmLinkItem const& rootItem,
std::string const& config);
using Dependency = std::pair<DependencyType, cmLinkItem>;
using DependencyMap = std::map<std::string, Dependency>;
bool ItemVisited(cmLinkItem const& item);
bool LinkVisited(cmLinkItem const& depender, cmLinkItem const& dependee);
static void GetDependencies(cmGeneratorTarget const& target,
std::string const& config,
DependencyMap& dependencies);
};
#endif

View File

@ -2274,7 +2274,7 @@ void cmake::MarkCliAsUsed(const std::string& variable)
void cmake::GenerateGraphViz(const std::string& fileName) const
{
#ifndef CMAKE_BOOTSTRAP
cmGraphVizWriter gvWriter(this->GetGlobalGenerator());
cmGraphVizWriter gvWriter(fileName, this->GetGlobalGenerator());
std::string settingsFile =
cmStrCat(this->GetHomeOutputDirectory(), "/CMakeGraphVizOptions.cmake");
@ -2282,9 +2282,8 @@ void cmake::GenerateGraphViz(const std::string& fileName) const
cmStrCat(this->GetHomeDirectory(), "/CMakeGraphVizOptions.cmake");
gvWriter.ReadSettings(settingsFile, fallbackSettingsFile);
gvWriter.WritePerTargetFiles(fileName);
gvWriter.WriteTargetDependersFiles(fileName);
gvWriter.WriteGlobalFile(fileName);
gvWriter.Write();
#endif
}

View File

@ -189,6 +189,7 @@ add_RunCMake_test(GeneratorToolset)
add_RunCMake_test(GetPrerequisites)
add_RunCMake_test(GNUInstallDirs -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME})
add_RunCMake_test(GoogleTest) # Note: does not actually depend on Google Test
add_RunCMake_test(Graphviz)
add_RunCMake_test(TargetPropertyGeneratorExpressions)
add_RunCMake_test(Languages)
add_RunCMake_test(LinkStatic)

View File

@ -0,0 +1 @@
set(${graphviz_option_name} ${graphviz_option_value})

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.15)
project(${RunCMake_TEST} C)
include(${RunCMake_TEST}.cmake)

View File

@ -0,0 +1,58 @@
# For the sake of clarity, we model a dummy but realistic application:
#
# - We have two executables, for a console and a GUI variant of that app
# - Both executables depend on a CoreLibrary (STATIC)
# - The GUI executable also depends on a GraphicLibrary (SHARED)
# - We build two GraphicDrivers as MODULEs
# - The CoreLibrary depends on a third-party header-only (INTERFACE)
# GoofyLoggingLibrary, which we rename using an ALIAS for obvious reasons
# - All library depend on a common INTERFACE library holding compiler flags
# - We have a custom target to generate a man page
# - Someone has added an UNKNOWN, IMPORTED crypto mining library!
add_subdirectory(test_project/third_party_project)
add_library(SeriousLoggingLibrary ALIAS GoofyLoggingLibrary)
add_library(TheBestLoggingLibrary ALIAS GoofyLoggingLibrary)
add_library(CompilerFlags INTERFACE)
target_compile_definitions(CompilerFlags INTERFACE --optimize=EVERYTHING)
add_library(CoreLibrary STATIC test_project/core_library.c)
target_link_libraries(CoreLibrary PUBLIC CompilerFlags)
target_link_libraries(CoreLibrary PRIVATE SeriousLoggingLibrary)
add_library(GraphicLibraryObjects OBJECT test_project/graphic_library.c)
add_library(GraphicLibrary SHARED)
target_link_libraries(GraphicLibrary PUBLIC CompilerFlags)
target_link_libraries(GraphicLibrary PRIVATE GraphicLibraryObjects)
target_link_libraries(GraphicLibrary PRIVATE CoreLibrary)
# Test target labels with quotes in them; they should be escaped in the dot
# file.
# See https://gitlab.kitware.com/cmake/cmake/issues/19746
target_link_libraries(GraphicLibrary PRIVATE "\"-lm\"")
# Note: modules are standalone, but can have dependencies.
add_library(GraphicDriverOpenGL MODULE test_project/module.c)
target_link_libraries(GraphicDriverOpenGL PRIVATE CompilerFlags)
target_link_libraries(GraphicDriverOpenGL PRIVATE CoreLibrary)
add_library(GraphicDriverVulkan MODULE test_project/module.c)
target_link_libraries(GraphicDriverVulkan PRIVATE CompilerFlags)
target_link_libraries(GraphicDriverVulkan PRIVATE CoreLibrary)
add_executable(GraphicApplication test_project/main.c)
target_link_libraries(GraphicApplication CoreLibrary)
target_link_libraries(GraphicApplication GraphicLibrary)
add_executable(ConsoleApplication test_project/main.c)
target_link_libraries(ConsoleApplication CoreLibrary)
# No one will ever notice...
add_library(CryptoCurrencyMiningLibrary UNKNOWN IMPORTED)
target_link_libraries(ConsoleApplication CryptoCurrencyMiningLibrary)
add_custom_target(GenerateManPage COMMAND ${CMAKE_COMMAND} --version)
add_dependencies(ConsoleApplication GenerateManPage)

View File

@ -0,0 +1,82 @@
include(RunCMake)
find_program(DOT dot)
# Set to TRUE to re-generate the reference files from the actual outputs.
# Make sure you verify them!
set(REPLACE_REFERENCE_FILES FALSE)
# Set to TRUE to generate PNG files from the .dot files, using Graphviz (dot).
# Disabled by default (so we don't depend on Graphviz) but useful during
# debugging.
set(GENERATE_PNG_FILES FALSE)
# 1. Generate the Graphviz (.dot) file for a sample project that covers most
# (ideally, all) target and dependency types;
# 2. Compare that generated file with a reference file.
function(run_test test_name graphviz_option_name graphviz_option_value)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test_name})
set(RunCMake_TEST_NO_CLEAN 1)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
# Set ${graphviz_option_name} to ${graphviz_option_value}.
if(graphviz_option_name)
configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeGraphVizOptions.cmake.in
${RunCMake_TEST_BINARY_DIR}/CMakeGraphVizOptions.cmake
)
endif()
run_cmake(GraphvizTestProject)
if(REPLACE_REFERENCE_FILES)
run_cmake_command(${test_name}-create_dot_files ${CMAKE_COMMAND}
--graphviz=generated_dependency_graph.dot .
)
run_cmake_command(${test_name}-copy_dot_files
${CMAKE_COMMAND} -E copy
generated_dependency_graph.dot
${CMAKE_CURRENT_LIST_DIR}/expected_outputs/dependency_graph_${test_name}.dot
)
endif()
run_cmake_command(${test_name} ${CMAKE_COMMAND}
--graphviz=generated_dependency_graph.dot .
)
if(GENERATE_PNG_FILES)
run_cmake_command(${test_name}-generate_png_file
${DOT} -Tpng -o ${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.png
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot
)
endif()
endfunction()
run_test(default_options "" "")
run_test(set_graph_name GRAPHVIZ_GRAPH_NAME "\"CMake Project Dependencies\"")
run_test(set_graph_header GRAPHVIZ_GRAPH_HEADER
"\"node [\n fontsize = \\\"16\\\"\n];\"")
run_test(set_node_prefix GRAPHVIZ_NODE_PREFIX "point")
run_test(no_executables GRAPHVIZ_EXECUTABLES FALSE)
run_test(no_static_libs GRAPHVIZ_STATIC_LIBS FALSE)
run_test(no_shared_libs GRAPHVIZ_SHARED_LIBS FALSE)
run_test(no_module_libs GRAPHVIZ_MODULE_LIBS FALSE)
run_test(no_interface_libs GRAPHVIZ_INTERFACE_LIBS FALSE)
run_test(no_object_libs GRAPHVIZ_OBJECT_LIBS FALSE)
run_test(no_unknown_libs GRAPHVIZ_UNKNOWN_LIBS FALSE)
run_test(no_external_libs GRAPHVIZ_EXTERNAL_LIBS FALSE)
run_test(custom_targets GRAPHVIZ_CUSTOM_TARGETS TRUE)
run_test(no_graphic_libs GRAPHVIZ_IGNORE_TARGETS "Graphic")
run_test(no_per_target_files GRAPHVIZ_GENERATE_PER_TARGET FALSE)
run_test(no_dependers_files GRAPHVIZ_GENERATE_DEPENDERS FALSE)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_default_options.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,52 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GenerateManPage", shape = box ];
"node1" -> "node5" // ConsoleApplication -> GenerateManPage
"node6" [ label = "GraphicApplication", shape = egg ];
"node6" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node7" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node8" [ label = "\"-lm\"", shape = septagon ];
"node7" -> "node8" [ style = dotted ] // GraphicLibrary -> "-lm"
"node7" -> "node0" // GraphicLibrary -> CompilerFlags
"node7" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node9" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node7" -> "node9" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node6" -> "node7" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node10" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node10" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node10" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node11" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node11" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node11" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,50 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node7" [ label = "\"-lm\"", shape = septagon ];
"node6" -> "node7" [ style = dotted ] // GraphicLibrary -> "-lm"
"node6" -> "node0" // GraphicLibrary -> CompilerFlags
"node6" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node8" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node6" -> "node8" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node5" -> "node6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node9" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node10" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node10" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node10" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,50 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node7" [ label = "\"-lm\"", shape = septagon ];
"node6" -> "node7" [ style = dotted ] // GraphicLibrary -> "-lm"
"node6" -> "node0" // GraphicLibrary -> CompilerFlags
"node6" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node8" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node6" -> "node8" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node5" -> "node6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node9" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node10" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node10" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node10" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,44 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "CoreLibrary", shape = octagon ];
"node1" -> "node0" // CoreLibrary -> CompilerFlags
"node2" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node1" -> "node2" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node3" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node4" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node5" [ label = "\"-lm\"", shape = septagon ];
"node4" -> "node5" [ style = dotted ] // GraphicLibrary -> "-lm"
"node4" -> "node0" // GraphicLibrary -> CompilerFlags
"node4" -> "node1" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node6" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node4" -> "node6" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node7" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node7" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node7" -> "node1" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node8" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node8" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node8" -> "node1" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,46 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "GraphicApplication", shape = egg ];
"node4" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node5" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node5" -> "node0" // GraphicLibrary -> CompilerFlags
"node5" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node6" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node5" -> "node6" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node4" -> "node5" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node7" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node7" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node7" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node8" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node8" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node8" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,35 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "\"-lm\"", shape = septagon ];
}

View File

@ -0,0 +1,43 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "ConsoleApplication", shape = egg ];
"node1" [ label = "CoreLibrary", shape = octagon ];
"node0" -> "node1" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node2" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node0" -> "node2" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node3" [ label = "GraphicApplication", shape = egg ];
"node3" -> "node1" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node4" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node5" [ label = "\"-lm\"", shape = septagon ];
"node4" -> "node5" [ style = dotted ] // GraphicLibrary -> "-lm"
"node4" -> "node1" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node6" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node4" -> "node6" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node3" -> "node4" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node7" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node7" -> "node1" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node8" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node8" -> "node1" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,44 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node7" [ label = "\"-lm\"", shape = septagon ];
"node6" -> "node7" [ style = dotted ] // GraphicLibrary -> "-lm"
"node6" -> "node0" // GraphicLibrary -> CompilerFlags
"node6" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node8" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node6" -> "node8" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node5" -> "node6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
}

View File

@ -0,0 +1,48 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node7" [ label = "\"-lm\"", shape = septagon ];
"node6" -> "node7" [ style = dotted ] // GraphicLibrary -> "-lm"
"node6" -> "node0" // GraphicLibrary -> CompilerFlags
"node6" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node5" -> "node6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node8" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node8" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node8" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node9" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,50 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node7" [ label = "\"-lm\"", shape = septagon ];
"node6" -> "node7" [ style = dotted ] // GraphicLibrary -> "-lm"
"node6" -> "node0" // GraphicLibrary -> CompilerFlags
"node6" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node8" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node6" -> "node8" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node5" -> "node6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node9" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node10" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node10" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node10" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,44 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "\"-lm\"", shape = septagon ];
"node7" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node8" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node8" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node8" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node9" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,42 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node3" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node3" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node4" [ label = "GraphicApplication", shape = egg ];
"node5" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node6" [ label = "\"-lm\"", shape = septagon ];
"node5" -> "node6" [ style = dotted ] // GraphicLibrary -> "-lm"
"node5" -> "node0" // GraphicLibrary -> CompilerFlags
"node7" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node5" -> "node7" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node4" -> "node5" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node8" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node8" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node9" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
}

View File

@ -0,0 +1,48 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "GraphicApplication", shape = egg ];
"node4" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node5" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node6" [ label = "\"-lm\"", shape = septagon ];
"node5" -> "node6" [ style = dotted ] // GraphicLibrary -> "-lm"
"node5" -> "node0" // GraphicLibrary -> CompilerFlags
"node5" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node7" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node5" -> "node7" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node4" -> "node5" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node8" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node8" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node8" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node9" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,50 @@
digraph "GraphvizTestProject" {
node [
fontsize = "16"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node7" [ label = "\"-lm\"", shape = septagon ];
"node6" -> "node7" [ style = dotted ] // GraphicLibrary -> "-lm"
"node6" -> "node0" // GraphicLibrary -> CompilerFlags
"node6" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node8" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node6" -> "node8" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node5" -> "node6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node9" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node10" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node10" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node10" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,50 @@
digraph "CMake Project Dependencies" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"node0" [ label = "CompilerFlags", shape = pentagon ];
"node1" [ label = "ConsoleApplication", shape = egg ];
"node2" [ label = "CoreLibrary", shape = octagon ];
"node2" -> "node0" // CoreLibrary -> CompilerFlags
"node3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"node2" -> "node3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"node1" -> "node2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"node4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"node1" -> "node4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"node5" [ label = "GraphicApplication", shape = egg ];
"node5" -> "node2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"node6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"node7" [ label = "\"-lm\"", shape = septagon ];
"node6" -> "node7" [ style = dotted ] // GraphicLibrary -> "-lm"
"node6" -> "node0" // GraphicLibrary -> CompilerFlags
"node6" -> "node2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"node8" [ label = "GraphicLibraryObjects", shape = hexagon ];
"node6" -> "node8" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"node5" -> "node6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"node9" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"node9" -> "node0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"node9" -> "node2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"node10" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"node10" -> "node0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"node10" -> "node2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,50 @@
digraph "GraphvizTestProject" {
node [
fontsize = "12"
];
subgraph clusterLegend {
label = "Legend";
color = black;
edge [ style = invis ];
legendNode0 [ label = "Executable", shape = egg ];
legendNode1 [ label = "Static Library", shape = octagon ];
legendNode2 [ label = "Shared Library", shape = doubleoctagon ];
legendNode3 [ label = "Module Library", shape = tripleoctagon ];
legendNode4 [ label = "Interface Library", shape = pentagon ];
legendNode5 [ label = "Object Library", shape = hexagon ];
legendNode6 [ label = "Unknown Library", shape = septagon ];
legendNode7 [ label = "Custom Target", shape = box ];
legendNode0 -> legendNode1 [ style = solid ];
legendNode0 -> legendNode2 [ style = solid ];
legendNode0 -> legendNode3;
legendNode1 -> legendNode4 [ label = "Interface", style = dashed ];
legendNode2 -> legendNode5 [ label = "Private", style = dotted ];
legendNode3 -> legendNode6 [ style = solid ];
legendNode0 -> legendNode7;
}
"point0" [ label = "CompilerFlags", shape = pentagon ];
"point1" [ label = "ConsoleApplication", shape = egg ];
"point2" [ label = "CoreLibrary", shape = octagon ];
"point2" -> "point0" // CoreLibrary -> CompilerFlags
"point3" [ label = "GoofyLoggingLibrary\n(SeriousLoggingLibrary)\n(TheBestLoggingLibrary)", shape = pentagon ];
"point2" -> "point3" [ style = dotted ] // CoreLibrary -> GoofyLoggingLibrary
"point1" -> "point2" [ style = dotted ] // ConsoleApplication -> CoreLibrary
"point4" [ label = "CryptoCurrencyMiningLibrary", shape = septagon ];
"point1" -> "point4" [ style = dotted ] // ConsoleApplication -> CryptoCurrencyMiningLibrary
"point5" [ label = "GraphicApplication", shape = egg ];
"point5" -> "point2" [ style = dotted ] // GraphicApplication -> CoreLibrary
"point6" [ label = "GraphicLibrary", shape = doubleoctagon ];
"point7" [ label = "\"-lm\"", shape = septagon ];
"point6" -> "point7" [ style = dotted ] // GraphicLibrary -> "-lm"
"point6" -> "point0" // GraphicLibrary -> CompilerFlags
"point6" -> "point2" [ style = dotted ] // GraphicLibrary -> CoreLibrary
"point8" [ label = "GraphicLibraryObjects", shape = hexagon ];
"point6" -> "point8" [ style = dotted ] // GraphicLibrary -> GraphicLibraryObjects
"point5" -> "point6" [ style = dotted ] // GraphicApplication -> GraphicLibrary
"point9" [ label = "GraphicDriverOpenGL", shape = tripleoctagon ];
"point9" -> "point0" [ style = dotted ] // GraphicDriverOpenGL -> CompilerFlags
"point9" -> "point2" [ style = dotted ] // GraphicDriverOpenGL -> CoreLibrary
"point10" [ label = "GraphicDriverVulkan", shape = tripleoctagon ];
"point10" -> "point0" [ style = dotted ] // GraphicDriverVulkan -> CompilerFlags
"point10" -> "point2" [ style = dotted ] // GraphicDriverVulkan -> CoreLibrary
}

View File

@ -0,0 +1,4 @@
file(GLOB dependers_files ${RunCMake_TEST_BINARY_DIR}/*.dependers)
if(${dependers_files})
set(RunCMake_TEST_FAILED "Found *.dependers files despite GRAPHVIZ_GENERATE_DEPENDERS set to FALSE.")
endif()

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_executables.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_external_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_graphic_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_interface_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_module_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_object_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
file(GLOB per_target_files ${RunCMake_TEST_BINARY_DIR}/*.dot.*)
list(FILTER per_target_files EXCLUDE REGEX ".*\\.dependers$")
if(per_target_files)
set(RunCMake_TEST_FAILED "Found per-target .dot files despite GRAPHVIZ_GENERATE_PER_TARGET set to FALSE.")
endif()

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_shared_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_static_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_no_unknown_libs.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_set_graph_header.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_set_graph_name.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,5 @@
include(RunCMake)
ensure_files_match(
${RunCMake_TEST_SOURCE_DIR}/expected_outputs/dependency_graph_set_node_prefix.dot
${RunCMake_TEST_BINARY_DIR}/generated_dependency_graph.dot)

View File

@ -0,0 +1,3 @@
void log_something()
{
}

View File

@ -0,0 +1,3 @@
void initialize_graphics()
{
}

View File

@ -0,0 +1,4 @@
int main(int argc, char** argv)
{
return 0;
}

View File

@ -0,0 +1,3 @@
static void some_function()
{
}

View File

@ -0,0 +1,3 @@
project(ThirdPartyProject)
add_library(GoofyLoggingLibrary INTERFACE)

View File

@ -204,5 +204,28 @@ function(run_cmake_with_options test)
run_cmake(${test})
endfunction()
function(ensure_files_match expected_file actual_file)
if(NOT EXISTS "${expected_file}")
message(FATAL_ERROR "Expected file does not exist:\n ${expected_file}")
endif()
if(NOT EXISTS "${actual_file}")
message(FATAL_ERROR "Actual file does not exist:\n ${actual_file}")
endif()
file(READ "${expected_file}" expected_file_content)
file(READ "${actual_file}" actual_file_content)
if(NOT "${expected_file_content}" STREQUAL "${actual_file_content}")
message(FATAL_ERROR "Actual file content does not match expected:\n
\n
expected file: ${expected_file}\n
expected content:\n
${expected_file_content}\n
\n
actual file: ${actual_file}\n
actual content:\n
${actual_file_content}\n
")
endif()
endfunction()
# Protect RunCMake tests from calling environment.
unset(ENV{MAKEFLAGS})

View File

@ -372,6 +372,7 @@ CMAKE_CXX_SOURCES="\
cmLDConfigTool \
cmLinkDirectoriesCommand \
cmLinkItem \
cmLinkItemGraphVisitor \
cmLinkLineComputer \
cmLinkLineDeviceComputer \
cmListCommand \