mirror of
https://github.com/reactos/CMake.git
synced 2024-11-23 11:39:48 +00:00
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:
parent
4c29297495
commit
553658393c
@ -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
|
||||
#]=======================================================================]
|
||||
|
@ -289,6 +289,8 @@ set(SRCS
|
||||
cmGeneratorExpression.h
|
||||
cmGeneratorTarget.cxx
|
||||
cmGeneratorTarget.h
|
||||
cmLinkItemGraphVisitor.cxx
|
||||
cmLinkItemGraphVisitor.h
|
||||
cmGetPipes.cxx
|
||||
cmGetPipes.h
|
||||
cmGlobalCommonGenerator.cxx
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
142
Source/cmLinkItemGraphVisitor.cxx
Normal file
142
Source/cmLinkItemGraphVisitor.cxx
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
75
Source/cmLinkItemGraphVisitor.h
Normal file
75
Source/cmLinkItemGraphVisitor.h
Normal 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
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
1
Tests/RunCMake/Graphviz/CMakeGraphVizOptions.cmake.in
Normal file
1
Tests/RunCMake/Graphviz/CMakeGraphVizOptions.cmake.in
Normal file
@ -0,0 +1 @@
|
||||
set(${graphviz_option_name} ${graphviz_option_value})
|
3
Tests/RunCMake/Graphviz/CMakeLists.txt
Normal file
3
Tests/RunCMake/Graphviz/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(${RunCMake_TEST} C)
|
||||
include(${RunCMake_TEST}.cmake)
|
58
Tests/RunCMake/Graphviz/GraphvizTestProject.cmake
Normal file
58
Tests/RunCMake/Graphviz/GraphvizTestProject.cmake
Normal 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)
|
82
Tests/RunCMake/Graphviz/RunCMakeTest.cmake
Normal file
82
Tests/RunCMake/Graphviz/RunCMakeTest.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/default_options-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/default_options-check.cmake
Normal 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)
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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 ];
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
4
Tests/RunCMake/Graphviz/no_dependers_files-check.cmake
Normal file
4
Tests/RunCMake/Graphviz/no_dependers_files-check.cmake
Normal 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()
|
5
Tests/RunCMake/Graphviz/no_executables-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_executables-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_external_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_external_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_graphic_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_graphic_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_interface_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_interface_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_module_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_module_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_object_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_object_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_per_target_files-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_per_target_files-check.cmake
Normal 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()
|
5
Tests/RunCMake/Graphviz/no_shared_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_shared_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_static_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_static_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/no_unknown_libs-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/no_unknown_libs-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/set_graph_header-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/set_graph_header-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/set_graph_name-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/set_graph_name-check.cmake
Normal 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)
|
5
Tests/RunCMake/Graphviz/set_node_prefix-check.cmake
Normal file
5
Tests/RunCMake/Graphviz/set_node_prefix-check.cmake
Normal 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)
|
3
Tests/RunCMake/Graphviz/test_project/core_library.c
Normal file
3
Tests/RunCMake/Graphviz/test_project/core_library.c
Normal file
@ -0,0 +1,3 @@
|
||||
void log_something()
|
||||
{
|
||||
}
|
3
Tests/RunCMake/Graphviz/test_project/graphic_library.c
Normal file
3
Tests/RunCMake/Graphviz/test_project/graphic_library.c
Normal file
@ -0,0 +1,3 @@
|
||||
void initialize_graphics()
|
||||
{
|
||||
}
|
4
Tests/RunCMake/Graphviz/test_project/main.c
Normal file
4
Tests/RunCMake/Graphviz/test_project/main.c
Normal file
@ -0,0 +1,4 @@
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
return 0;
|
||||
}
|
3
Tests/RunCMake/Graphviz/test_project/module.c
Normal file
3
Tests/RunCMake/Graphviz/test_project/module.c
Normal file
@ -0,0 +1,3 @@
|
||||
static void some_function()
|
||||
{
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
project(ThirdPartyProject)
|
||||
|
||||
add_library(GoofyLoggingLibrary INTERFACE)
|
@ -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})
|
||||
|
Loading…
Reference in New Issue
Block a user