Documenting SpiNNaker C Code using Doxygen

Follow me on GitHub

Documenting SpiNNaker C Code using Doxygen

We use Doxygen to translate special comments in our C code into our online documentation trees.

Doxygen is very complicated, but we only use a subset of it. The configuration file for the Doxygen build is called Doxyfile, and is huge and complex; we try to turn off most of the features.

Documented files

We configure Doxygen to make documentation from all files with the extensions *.c, *.h, *.md (good for auxiliary pages) and *.dox.

The *.dox files should contain a single C-style documentation comment, and are places for putting code information that doesn’t fit anywhere else. For example, they can be used to provide the front page of the documentation tree, or to contain directory level documentation if there isn’t an obvious other file to put them in. Note that *.dox files do not appear in the file tree.

Documentation comment format

All Doxygen documentation (in C) is placed in a comment like this (common):

//! ...

or like this (uncommon):

/*!
 * ...
 */

Or like this (very uncommon in our code):

/**
 * ...
 */

Directives

All directives (within a documentation comment) start with either \ or @ (you can use either; we commonly use backslash). The main ones you need to know are:

  • \fileEvery *.c and *.h file needs one of these. Probably followed by a \brief directive to provide a short description of the file.

  • \brief — A short description of something, usually a function, macro, variable, structure, etc. Lasts until the next block-level directive or a blank doc-comment line. Under normal circumstances, this should be a short sentence.

  • \details — The long description of something. Can be multiple paragraphs. Paragraphs are basically in Markdown format.

  • \param — Describes a parameter to a function or macro. Optionally followed by [in], [out], or [in,out] to say what the parameter is doing ([in] is probably the normal case). That is then followed by the name of the parameter (which must match the declaration) and an optional colon (:); the colon is strongly recommended for clarity.

    For example, this might document an argument int foo:

      \param[in] foo: This is the primary foo thing in consideration.
    
  • \return — Describes the result of the function or macro. Functions that have void as their result type must not have a \return directive!

Bare documentation comments without any directive are treated as \brief if they are a single line, and are treated as \details otherwise.

Inline directives

We don’t use many directives, but here are some useful ones:

  • \a — The next word is an argument. See also \p.

  • \p — The next word is a parameter. See also \a.

  • \c — The next word is to be in typewriter font. Normally it is easier to use markdown-style backticks.

  • \f$ — Marks the start and end of some inline mathematical content. The part inside should be in LaTeX format.

  • \f[\f] — Contains a long-format formula in LaTeX format.

Note that Doxygen is apparently poor at recovering from typos in formulæ.

Rare directives

You won’t see these so much.

  • \mainpage — Used to provide content for the main page of the documentation tree. Obviously, there should only be one of these per repository! By convention, this is usually in a file called mainpage.dox, but that’s just for our convenience.

  • \dir — Used to describe a whole directory. Only appears once at most in that entire directory.

  • \section — Starts a section in the documentation. Not that useful when incorporated into pages that actually document code, but may be useful in auxiliary pages (such as the main page).

  • \name — Introduces a section of declarations within a file. The declarations themselves are then surrounded by \{ and \}. Useful in long files with many declarations and some natural grouping, but where a file has few declarations it is unnecessary.

  • \note — Marks a paragraph as worthy of note to the user.

  • \warning — Marks a paragraph as particularly worthy of note!

  • \todo — Marks something that’s perhaps unfinished. These are also called out to a separate summary file.

  • \bug — Marks something that’s outright wrong. These are also called out to a separate summary file. If putting one of these in, make sure you have also filed an Issue on Github!

  • \cond\endcond — Used to hide things from Doxygen’s output. Try to avoid using these!

  • \internal — Used to hide things from Doxygen’s output.

  • \author, \copyright, \date — Fairly obvious things for the file header material.

Cross references

You link to a function definition by using its name with () immediately afterwards.

You link to a macro by preceding it with #.

You link to a source file by just mentioning it including extension. You do not need to include any part of the path unless the name is ambiguous; don’t create ambiguous filenames!

You link to other global symbols (e.g., enumeration values, types) by preceding them with :: (which sort of makes sense in a C++ way).

Writing style

Every public symbol should have a \brief description. In our general style, symbols are public unless excluded by the Doxyfile’s EXCLUDE_SYMBOLS configuration file. We normally document static symbols and symbols starting with one or more underscores (_).

Functions should have their brief description begin with a present tense immediate active verb. Thus, instead of:

This function frobnicates the mome-raths.

or:

Frobnicates the mome-raths.

use:

Frobnicate the mome-raths.

Macros that are used like functions should follow the same rules.

Things that work like enumerations should be enumerations, and not just big collections of #defines! (Unless it is impossible for type reasons.)

Do not describe the type of arguments or return values in documentation comments. The documentation tool picks up the type just fine from the declaration itself.

Predefined macros

We disable most C macro processing when loading in source files, but we do enable some. In particular, we define the symbol DOXYGEN when building and you can use that to hide things from the processing tool that confuse it (notably including __attribute__(...) annotations).

The configuration of that is controlled by the PREDEFINED configuration value in the Doxyfile. An example setting there is:

PREDEFINED             = DOXYGEN=1 \
                         NO_INLINE= \
                         UNIMPLEMENTED=

This provides default empty definitions for the NO_INLINE and UNIMPLEMENTED macros, which are defined like this in an appropriate C file:

#ifndef DOXYGEN
#define NO_INLINE      __attribute__((noinline))
#define UNIMPLEMENTED  __attribute__((deprecated("Not implemented")))
#endif // DOXYGEN

The standard pattern of #ifndef/#define guards for a whole file do not need to be documented (and should not be).

Documentation Deployment

Our documentation deployments are driven automatically on Github (using Travis to do much of the work) and, for Python only, on ReadTheDocs (which also stores old versions).

The core of our Github/Travis deployment rules are based on this blog comment (!) with some adaptations. Essentially, we run Doxygen as part of the build, and then we assemble the output into the right directory with minimal extra work. The magic token required for publishing from Travis to Github is placed in the GITHUB_TOKEN environment variable by the configuration settings on Travis (no, it’s not in the repository) only for the master branch. If you want to test how your documentation changes look, you should run Doxygen locally; there is no capacity to do test deployments on a per-branch/PR basis.