11

I know the common way to generate a CMake project dependencies graph by the CLI:

cmake --graphviz=[file]

But is there a way for it to be autogenerated by just setting a flag or command within a CMakeList? The idea is for the CMakeLists.txt itself to trigger the graph generation, and not the user through command line.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Javier Calzado
  • 111
  • 1
  • 1
  • 4

3 Answers3

14

Not only can you create a CMake custom target for running Graphviz, but you can take it a step further, and have it also generate the image files for you using Dot:

add_custom_target(graphviz ALL
    COMMAND ${CMAKE_COMMAND} "--graphviz=foo.dot" .
    COMMAND dot -Tpng foo.dot -o foo.png
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)

This way, the custom target runs the second command dot -Tpng foo.dot -o foo.png as well. You can output the image files anywhere on your system by pre-pending foo.png with a path of your choosing.

Kevin
  • 16,549
  • 8
  • 60
  • 74
  • 1
    This will run always and trigger a CMake reconfiguration run. Is there a way to only run this when something has really changed? – Axel Heider May 17 '22 at 14:06
  • @AxelHeider You can remove the `ALL` argument, or add `set_target_properties(Graphviz PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1)` afterward. – Kevin May 17 '22 at 14:56
  • I'd like to have automatic recreation when something changes. The core problem seems to be that invoking CMake within a target seems to change something in the build output that triggers it to run on every build. – Axel Heider May 18 '22 at 07:05
  • `EXCLUDE_FROM_DEFAULT_BUILD` is a Visual Studio specific property so not a generic solution. – Zitrax Mar 20 '23 at 11:13
11

You could call CMake inside your script again, e.g. like:

add_custom_target(graphviz ALL
                  "${CMAKE_COMMAND}" "--graphviz=foo" .
                  WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
languitar
  • 6,554
  • 2
  • 37
  • 62
  • This will run always and trigger a CMake reconfiguration run. Is there a way to only run this when something has really changed? – Axel Heider May 17 '22 at 14:06
0

Comparing to other answers I thought it is more convenient if graphviz generation runs only when it is required. Also I check if graphiz/dot itself exists:

find_program(GRAPHVIZ dot)
if(GRAPHVIZ)
    add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/_graphviz
        COMMAND ${CMAKE_COMMAND} "--graphviz=graphviz/deps.txt" .
        COMMAND dot -Tsvg graphviz/deps.txt -o deps.svg
        COMMAND dot -Tpng graphviz/deps.txt -o deps.png
        COMMENT "Plotting dependencies graph to deps.svg"
        DEPENDS <some_target>
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
    add_custom_target(graphviz ALL
        DEPENDS ${CMAKE_BINARY_DIR}/_graphviz)
endif()

When some_target changes it also will try to regenerate OUTPUT _graphviz file, thus plot dependencies.

By default TARGET graphviz target always builds, but it is empty, so plotting itself is not called, if OUTPUT _graphviz is not updated

p.s. found this seeing answer of Alternative to CMake POST_BUILD command when target is in subdirectory

SolomidHero
  • 149
  • 2
  • 10