Graphviz feature request: visualize cycles of static library dependencies
I have big project that has (for now) cyclic dependencies in static libraries. I know it is bad architecture because efectively all modules in cycle are big single module. To fix it, CMake graphviz helps somewhat, unless there are hundreds modules - then the graph looks like a big pile of spaghetti.
I created python script that finds the cycles and separates them on the side. It uses networkx and pydot modules. Here it is: FindCyclesInDot.py
Example. Given:
project(FooProject CXX)
add_library (duoA STATIC empty.cpp)
add_library (duoB STATIC empty.cpp)
target_link_libraries(duoA PUBLIC duoB)
target_link_libraries(duoB PUBLIC duoA)
add_library (trioA STATIC empty.cpp)
add_library (trioB STATIC empty.cpp)
add_library (trioC STATIC empty.cpp)
target_link_libraries(trioA PUBLIC trioB)
target_link_libraries(trioB PUBLIC trioC)
target_link_libraries(trioC PUBLIC trioA)
add_library (motherA STATIC empty.cpp)
add_library (motherB STATIC empty.cpp)
target_link_libraries(motherA PUBLIC trioA)
target_link_libraries(motherB PUBLIC trioB)
target_link_libraries(motherA PUBLIC duoA)
target_link_libraries(motherB PUBLIC duoB)
add_library (child STATIC empty.cpp)
target_link_libraries(duoA PUBLIC child)
target_link_libraries(duoB PUBLIC child)
CMake gives graph (without legend):
digraph "FooProject" {
node [
fontsize = "12"
];
"node0" [ label = "child", shape = octagon ];
"node1" [ label = "duoA", shape = octagon ];
"node1" -> "node0" // duoA -> child
"node2" [ label = "duoB", shape = octagon ];
"node2" -> "node0" // duoB -> child
"node2" -> "node1" // duoB -> duoA
"node1" -> "node2" // duoA -> duoB
"node3" [ label = "motherA", shape = octagon ];
"node3" -> "node1" // motherA -> duoA
"node4" [ label = "trioA", shape = octagon ];
"node5" [ label = "trioB", shape = octagon ];
"node6" [ label = "trioC", shape = octagon ];
"node6" -> "node4" // trioC -> trioA
"node4" -> "node5" // trioA -> trioB
"node5" -> "node6" // trioB -> trioC
"node3" -> "node4" // motherA -> trioA
"node7" [ label = "motherB", shape = octagon ];
"node7" -> "node2" // motherB -> duoB
"node7" -> "node5" // motherB -> trioB
}
and my script ./FindCyclesInDot.py deps.dot cycles.dot
gives:
strict digraph "GG" {
node [
fontsize = "12"
];
compound=true
subgraph cluster_0 {label="Cycle 0 (3 members)";node [style=filled];color=blue"node5""node4""node6"}subgraph cluster_1 {label="Cycle 1 (2 members)";node [style=filled];color=blue"node2""node1"}
"node0" [ label = "child", shape = octagon ];
"node1" [ label = "duoA", shape = octagon ];
"Cycle 1" -> "node0" // duoA -> child
"node2" [ label = "duoB", shape = octagon ];
"Cycle 1" -> "node0" // duoB -> child
"node2" -> "node1" // duoB -> duoA
"node1" -> "node2" // duoA -> duoB
"node3" [ label = "motherA", shape = octagon ];
"node3" -> "Cycle 1" // motherA -> duoA
"node4" [ label = "trioA", shape = octagon ];
"node5" [ label = "trioB", shape = octagon ];
"node6" [ label = "trioC", shape = octagon ];
"node6" -> "node4" // trioC -> trioA
"node4" -> "node5" // trioA -> trioB
"node5" -> "node6" // trioB -> trioC
"node3" -> "Cycle 0" // motherA -> trioA
"node7" [ label = "motherB", shape = octagon ];
"node7" -> "Cycle 1" // motherB -> duoB
"node7" -> "Cycle 0" // motherB -> trioB
}
- All cycles are separated from main graph (subgraphs)
- If cycles have common points, they are single cycle (I may misuse graph theory nomenclature here; not shown in example)
- Everything that is left, is DAG.
- If node in DAG depends on multiple nodes in cycle, only 1 dependency arrow is shown (strict digraph)
- I realize this script is not implemented optimally - I wanted effects, not optimal performance.
It would be great if graphs like this would come out of cmake directly (probably enabled/disabled by some option), because it helps make sense of big projects. Only negative side is that graph doesn't show connections between specific nodes of cycle and specific nodes of DAG, so this presentation is not good as ONLY presentation. It is great as addition. Personally I don't use graphs without decycling.