From: Teemu Murtola Date: Sat, 13 Sep 2014 18:11:13 +0000 (+0300) Subject: Embed module dependency graph in Doxygen docs X-Git-Url: http://biod.pnpi.spb.ru/gitweb/?p=alexxy%2Fgromacs.git;a=commitdiff_plain;h=5139f5a42feb555194c9101b3f419401e0235b49 Embed module dependency graph in Doxygen docs The module dependency graph generated by the dep-graphs target is now embedded into the Doxygen documentation as a separate page (in the lib and full variants). Building these flavors of documentation now requires the depgraphs to be built first, which in turn depends on the doxygen XML output. Added various -fast targets that skip these dependencies, with the cost of potentially producing some warnings about missing stuff. Change-Id: Id3314c598d79588c429fe57108349edf42de3b6b --- diff --git a/docs/doxygen/CMakeLists.txt b/docs/doxygen/CMakeLists.txt index 7ffe276de5..9d439695b3 100644 --- a/docs/doxygen/CMakeLists.txt +++ b/docs/doxygen/CMakeLists.txt @@ -89,6 +89,7 @@ if (DOXYGEN_FOUND) SET(NB_KERNEL_DIRS_TO_IGNORE_IN_DOXYGEN "${NB_KERNEL_DIRS_TO_IGNORE_IN_DOXYGEN} \\\n ${NB_KERNEL_DIR}") endforeach() + set(DEPGRAPH_DIR ${CMAKE_CURRENT_BINARY_DIR}/depgraphs) set(DOXYGEN_SECTIONS "") set(DOXYGEN_EXTRA_SETTINGS "") if (GMX_COMPACT_DOXYGEN) @@ -106,17 +107,27 @@ if (DOXYGEN_FOUND) function (add_doxygen_target TARGET TYPE COMMENT) add_custom_target(${TARGET} - ${CMAKE_COMMAND} -DDOCTYPE=${TYPE} -P RunDoxygen.cmake + # Ensure the directory exists to avoid spurious warnings + ${CMAKE_COMMAND} -E make_directory ${DEPGRAPH_DIR} + COMMAND ${CMAKE_COMMAND} -DDOCTYPE=${TYPE} -P RunDoxygen.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "${COMMENT}" VERBATIM) add_dependencies(${TARGET} doxygen-version) endfunction() - add_doxygen_target(doc-full full "Generating full documentation with Doxygen") - add_doxygen_target(doc-lib lib "Generating library documentation with Doxygen") - add_doxygen_target(doc-user user "Generating public API documentation with Doxygen") - add_doxygen_target(doc-xml xml "Extracting Doxygen documentation to XML") + # The targets ending with -fast do the same thing as the target without the + # suffix, but assume that time-consuming dependencies have already been + # built, making it faster and more convenient to test a single part of the + # system. + add_doxygen_target(doc-full full "Generating full documentation with Doxygen") + add_doxygen_target(doc-full-fast full "Generating full documentation with Doxygen") + add_doxygen_target(doc-lib lib "Generating library documentation with Doxygen") + add_doxygen_target(doc-lib-fast lib "Generating library documentation with Doxygen") + add_doxygen_target(doc-user user "Generating public API documentation with Doxygen") + add_doxygen_target(doc-xml xml "Extracting Doxygen documentation to XML") add_custom_target(doc-all) + add_custom_target(doc-all-fast) add_dependencies(doc-all doc-full doc-lib doc-user) + add_dependencies(doc-all-fast doc-full-fast doc-lib-fast doc-user) if (USE_PYTHON_SCRIPTS) # TODO: Consider whether this is the best name and location for this @@ -132,34 +143,39 @@ if (DOXYGEN_FOUND) add_custom_target(doc-check COMMAND ${doc_check_command} COMMENT "Checking Doxygen documentation" VERBATIM) add_dependencies(doc-check doc-xml find-installed-headers) + add_custom_target(doc-check-fast COMMAND ${doc_check_command} + COMMENT "Checking Doxygen documentation" VERBATIM) + add_dependencies(doc-check-fast find-installed-headers) - set(graphdir ${CMAKE_CURRENT_BINARY_DIR}/depgraphs) set(dep_graphs_command_python ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/graphbuilder.py -S ${CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR} --ignore-cycles ${CMAKE_CURRENT_SOURCE_DIR}/cycle-suppressions.txt - -o ${CMAKE_CURRENT_BINARY_DIR}/depgraphs) + -o ${DEPGRAPH_DIR}) set(dep_graphs_command_dot - ${CMAKE_COMMAND} -DGRAPHDIR=${graphdir} + ${CMAKE_COMMAND} -DGRAPHDIR=${DEPGRAPH_DIR} -DDOT_EXECUTABLE=${DOXYGEN_DOT_EXECUTABLE} -P ${CMAKE_CURRENT_SOURCE_DIR}/generateGraphs.cmake) - add_custom_target(dep-graphs + add_custom_target(dep-graphs-dot COMMAND ${dep_graphs_command_python} + COMMENT "Generating include dependency graphs for dot" VERBATIM) + add_custom_target(dep-graphs COMMAND ${dep_graphs_command_dot} - COMMENT "Generating include dependency graphs" VERBATIM) - add_dependencies(dep-graphs doc-xml find-installed-headers) + COMMENT "Generating PNG include dependency graphs" VERBATIM) + add_dependencies(dep-graphs-dot doc-xml find-installed-headers) + add_dependencies(dep-graphs dep-graphs-dot) + add_dependencies(doc-full dep-graphs-dot) + add_dependencies(doc-lib dep-graphs-dot) - # These targets are the same as above, but they don't rerun Doxygen - # each time, making it faster and more convenient for testing. - add_custom_target(doc-check-fast COMMAND ${doc_check_command} - COMMENT "Checking Doxygen documentation" VERBATIM) - add_custom_target(dep-graphs-fast + add_custom_target(dep-graphs-dot-fast COMMAND ${dep_graphs_command_python} + COMMENT "Generating include dependency graphs for dot" VERBATIM) + add_custom_target(dep-graphs-fast COMMAND ${dep_graphs_command_dot} - COMMENT "Generating include dependency graphs" VERBATIM) + COMMENT "Generating PNG include dependency graphs" VERBATIM) # Finding the installed headers doesn't actually run again if nothing # has changed, so that can be safely added as a dependency. - add_dependencies(doc-check-fast find-installed-headers) - add_dependencies(dep-graphs-fast find-installed-headers) + add_dependencies(dep-graphs-dot-fast find-installed-headers) + add_dependencies(dep-graphs-fast dep-graphs-dot-fast) endif() endif() diff --git a/docs/doxygen/Doxyfile-common.cmakein b/docs/doxygen/Doxyfile-common.cmakein index c6555f16ea..6aa3663209 100644 --- a/docs/doxygen/Doxyfile-common.cmakein +++ b/docs/doxygen/Doxyfile-common.cmakein @@ -27,6 +27,7 @@ INCLUDE_PATH = @CMAKE_SOURCE_DIR@/src HAVE_DOT = @DOXYGEN_DOT_FOUND@ DOT_PATH = @DOXYGEN_DOT_PATH@ MSCGEN_PATH = @DOXYGEN_MSCGEN_PATH@ +DOTFILE_DIRS = @DEPGRAPH_DIR@ @DOXYGEN_EXTRA_SETTINGS@ ENABLED_SECTIONS = @DOXYGEN_SECTIONS@ diff --git a/docs/doxygen/Doxyfile-xml.cmakein b/docs/doxygen/Doxyfile-xml.cmakein index babca03873..7c100e12a1 100644 --- a/docs/doxygen/Doxyfile-xml.cmakein +++ b/docs/doxygen/Doxyfile-xml.cmakein @@ -2,7 +2,7 @@ PREDEFINED += F77_FUNC(name,NAME)=name -ENABLED_SECTIONS += libapi internal +ENABLED_SECTIONS += libapi internal xml INTERNAL_DOCS = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_ANON_NSPACES = YES diff --git a/docs/doxygen/generateGraphs.cmake b/docs/doxygen/generateGraphs.cmake index 3006e4a830..b451c97281 100644 --- a/docs/doxygen/generateGraphs.cmake +++ b/docs/doxygen/generateGraphs.cmake @@ -33,7 +33,6 @@ # the research papers on the package. Check out http://www.gromacs.org. if (DOT_EXECUTABLE) - message("Running dot...") file(GLOB DOT_INPUT_FILES ${GRAPHDIR}/*.dot) execute_process(COMMAND ${DOT_EXECUTABLE} -Tpng -O ${DOT_INPUT_FILES}) endif() diff --git a/docs/doxygen/gmxtree.py b/docs/doxygen/gmxtree.py index 67240f391f..6359b6391e 100644 --- a/docs/doxygen/gmxtree.py +++ b/docs/doxygen/gmxtree.py @@ -325,6 +325,10 @@ class GeneratedFile(File): File.__init__(self, abspath, relpath, directory) self._generator_source_file = None + def scan_contents(self, sourcetree, keep_contents): + if os.path.exists(self.get_abspath()): + File.scan_contents(self, sourcetree, keep_contents) + def set_generator_source(self, sourcefile): self._generator_source_file = sourcefile diff --git a/docs/doxygen/graphbuilder.py b/docs/doxygen/graphbuilder.py index f666dbeaa7..0026e60c09 100755 --- a/docs/doxygen/graphbuilder.py +++ b/docs/doxygen/graphbuilder.py @@ -124,6 +124,7 @@ class Edge(object): def format(self): """Format this edge for 'dot'.""" + # If you change these styles, update also the legend in modulegraph.md if self._fromnode.is_file_node() and self._tonode.is_file_node(): properties = '' elif self._edgetype == EdgeType.intramodule: @@ -409,6 +410,7 @@ class GraphBuilder(object): return edges def _get_module_color(self, modulegroup): + # If you change these styles, update also the legend in modulegraph.md if modulegroup == 'legacy': return 'fillcolor=grey75' elif modulegroup == 'analysismodules': @@ -424,7 +426,8 @@ class GraphBuilder(object): style = [] properties = [] properties.append('shape=ellipse') - properties.append('URL="\\ref module_{0}"'.format(module.get_name())) + if module.is_documented(): + properties.append('URL="\\ref {0}"'.format(module.get_name())) if not module.is_documented(): fillcolor = self._get_module_color('legacy') else: @@ -463,21 +466,6 @@ class GraphBuilder(object): edges.append(edge) return edges - - def _create_legend_node(self, label, modulegroup): - if modulegroup: - nodename = 'legend_' + modulegroup - fillcolor = self._get_module_color(modulegroup) - else: - nodename = 'legend_' + label - fillcolor = None - style = [] - properties = [] - if fillcolor: - style.append('filled') - properties.append(fillcolor) - return Node(nodename, label, style, properties) - def create_modules_graph(self): """Create module dependency graph.""" nodes = [] @@ -492,15 +480,6 @@ class GraphBuilder(object): nodes.append(node) modulenodes[moduleobj] = node edges = self._create_module_edges(modulenodes) - # TODO: Consider adding invisible edges to order the nodes better. - # TODO: Consider adding legend for the edge types as well. - legendnode = Node('legend', 'legend') - legendnode.add_child(self._create_legend_node('legacy', 'legacy')) - legendnode.add_child(self._create_legend_node('analysis', 'analysismodules')) - legendnode.add_child(self._create_legend_node('utility', 'utilitymodules')) - legendnode.add_child(self._create_legend_node('mdrun', 'mdrun')) - legendnode.add_child(Node('legend_installed', 'installed', properties=['color=".66 .5 1"', 'penwidth=3'])) - nodes.append(legendnode) graph = Graph(nodes, edges) graph.set_options(concentrate=False) return graph diff --git a/docs/doxygen/lib/doxygen.md b/docs/doxygen/lib/doxygen.md index f9583280ff..9670069da0 100644 --- a/docs/doxygen/lib/doxygen.md +++ b/docs/doxygen/lib/doxygen.md @@ -375,7 +375,7 @@ their include dependencies. Additionally, a file-level graph is produced for each module, showing the include dependencies within that module. Currently, these are mostly for eye candy, but they can also be used for analyzing problematic dependencies to clean up the architecture. -The output is put in `doxygen/depgraphs/` in the build tree. +The output is put in `docs/doxygen/depgraphs/` in the build tree. As with `doc-check`, Python 2.7 is required (other versions may work, but have not been tested). To get `.png` versions of the graphs, `graphviz` is @@ -383,41 +383,9 @@ additionally required. ### Module graph ### -The graph is written into `module-deps.dot.png`. - -Node colors: -
-
gray background
-
undocumented module
-
orange background
-
documented utility modules
-
red background
-
documented analysis modules
-
violet background
-
documented MD execution modules
-
light blue border
-
module contains public API (installed) headers
-
- -Edge colors (an edge with a certain color indicates that types above it in the -list are not present): -
-
red
-
invalid dependency
-
gray
-
legacy dependency -(dependency on undocumented file, or to undocumented directories)
-
solid black
-
public header depends on the other module
-
solid blue
-
library header depends on the other module
-
dashed blue
-
source file depends on library header in the other module
-
dashed black
-
source file depends on public header in the other module
-
dashed green
-
test file depends on the other module
-
+The graph is written into `module-deps.dot.png`, and embedded into the Doxygen +documentation: \ref page_modulegraph. The embedded version contains a legend +explaining the graph. ### File graph ### diff --git a/docs/doxygen/lib/modulegraph.md b/docs/doxygen/lib/modulegraph.md new file mode 100644 index 0000000000..5467bedd3e --- /dev/null +++ b/docs/doxygen/lib/modulegraph.md @@ -0,0 +1,89 @@ +Module dependency graph {#page_modulegraph} +======================= + +The graph below shows the dependencies between the source code modules, +computed from include statements in the code. +For documented modules (those that do not have a gray background), clicking on +the module takes you to the module documentation. +Legend for the graph can be found below the graph. + +\ifnot xml +\dotfile module-deps.dot +\endif + +Legend +====== + +The graph below annotates the colors and line styles used in the module +dependency graph above. More detailed textual annotation is below the graph. + +\dot +digraph legend { + node [fontname="FreeSans",fontsize=10,height=.2,shape=box] + edge [fontname="FreeSans",fontsize=10] + rankdir = "LR" + subgraph cluster_nodes { + label = "Nodes" + legacy [label="undocumented", fillcolor=grey75, style="filled"] + analysis [label="analysis", fillcolor="0 .2 1", style="filled"] + utility [label="utility", fillcolor=".08 .2 1", style="filled"] + mdrun [label="mdrun", fillcolor=".75 .2 1", style="filled"] + installed [label="installed", color=".66 .5 1", penwidth=3] + } + subgraph cluster_edges { + label = "Edges" + node [label=""] + invalid1 -> invalid2 [label="invalid", color=red] + legacy1 -> legacy2 [label="legacy", color=grey75] + legacy2 [label="undoc"] + public1 -> public2 [label="public", color=black] + public1 [label="public"] + public2 [label="public"] + library1 -> library2 [label="library", color=".66 .8 .8"] + library1 [label="library"] + pubimpl1 -> pubimpl2 [color=black, style=dashed] + pubimpl1 [label="internal"] + pubimpl2 [label="public"] + libimpl1 -> libimpl2 [color=".66 .8 .8", style=dashed] + libimpl1 [label="internal"] + libimpl2 [label="library"] + test1 -> test2 [label="test", color=".33 .8 .8", style=dashed] + test1 [label="test"] + } + legacy -> invalid1 [style="invis"] +} +\enddot + +Node colors: +
+
gray background
+
undocumented module
+
orange background
+
documented utility modules
+
red background
+
documented analysis modules
+
violet background
+
documented MD execution modules
+
light blue border
+
module contains public API (installed) headers
+
+ +Edge colors (an edge with a certain color indicates that types above it in the +list are not present): +
+
red
+
invalid dependency
+
gray
+
legacy dependency +(dependency on undocumented file, or to undocumented directories)
+
solid black
+
public header depends on the other module
+
solid blue
+
library header depends on the other module
+
dashed blue
+
source file depends on library header in the other module
+
dashed black
+
source file depends on public header in the other module
+
dashed green
+
test file depends on the other module
+
diff --git a/docs/doxygen/user/codelayout.md b/docs/doxygen/user/codelayout.md index 727f978c43..efe5a64ff5 100644 --- a/docs/doxygen/user/codelayout.md +++ b/docs/doxygen/user/codelayout.md @@ -38,6 +38,9 @@ not compile. It is not included in the build. \endif +\if libapi +\subpage page_modulegraph +\endif Organization under `src/gromacs/` ---------------------------------