Simple Documentation Generation

Upon starting a new C++ project recently I wanted to integrate the documentation process into it as early as possible. Having been frustrated by doxygen and bewildered by sphinx in the past I decided to look for something different and found cldoc. Clang is my preferred development compiler so seeing libclang as a dependency peaked my interest. Another feature that excited me (I don’t get out much) is that there is no special syntax for generating the documentation, instead cldoc simply uses the standard C/C++ comment style which means that existing comments on your code will automatically be used. In combination with Markdown support, this example documentation, and ability to launch the generated documentation locally built-in, I decided to take the plunge.

Being written in Python and available through the Python Package Index cldoc was easy to get hold of, alternately installing from source is documented here. Let’s have a look at generating documentation for Foo.h.

// Foo needs Bar.
struct Foo { ... };

// Bar feeds Foo.
struct Bar { ... };

// make_foo creates a Foo.
// @bar what is foo without bar.
// 
// make_foo is the FooBar factory.
//
// @return the new Foo object.
Foo make_foo(Bar bar) { ... }

To generate the documentation from this file cldoc needs to be fed the output directory and the file name.

cldoc generate -- --output ./doc ./foo.h

To view the generated documentation we need to use the serve utility and provide the output directory, this will launch your default browser with the generated documentation.

cldoc serve -- ./doc

It is also easy to add documentation out of source in markdown files using the merge option. A simple index.md file like this can be added as follows.

#<cldoc:index>

Title bar text

# Documentation Title

Documentation blurb

[link](http:/git.server/repo)
cldoc generate -- --output ./doc --merge ./index.md ./foo.h

Great, that was painless but projects are not often (if at all?) contained in a single file. Handling multiple files is simply a matter of appending them to the generate command, however lets going to go a step further and integrate cldoc into the build system which in my case is cmake. To do this you we need some infromation: the path to cldoc’s install directory, compiler flags such as header include directories, the list of source files, markdown files to be merged, and the output directory. With these we will generate a custom cmake target as follows.

if(NOT ${CLDOC_DIR} STREQUAL "")
  add_custom_target(docs COMMAND
    ${CLDOC_DIR}/cldoc generate
    -std=c++11 -I${INCLUDE_DIR}
    --
    --merge ${DOCS_DIR}
    --output ${OUTPUT_DIR}
    ${HEADERS} ${SOURCES}
  )
  set_target_properties(docs PROPERTIES
    EXCLUDE_FROM_ALL 1
    EXCLUDE_FROM_DEFAULT_BUILD 1
  )

  add_custom_target(serve_docs COMMAND
    ${CLDOC_DIR}/cldoc serve -- .
    WORKING_DIR ${OUTPUT_DIR}
  )
  set_target_properties(docs PROPERTIES
    EXCLUDE_FROM_ALL 1
    EXCLUDE_FROM_DEFAULT_BUILD 1
  )
  add_dependencies(serve_docs docs) 
else()
  message(STATUS
    "Documentation will not be generated"
  )
endif()

We now have two cmake targets, docs and serve_docs. Both have been excluded from the default build so will not hamper build times and serve_docs depends on docs so that documentation will be generated and displayed in a single command.

And we are done, life is good.

Native Development