Using Sphinx and kerneldoc for API documentation

Sphinx is a great tool for writing project documentation and I use it whenever I can. It is pretty easy to bootstrap using the sphinx-quickstart utility.

I start by creating a docs directory and invoking sphinx-quickstart. After answering some questions I'm left with a initialized documentation directory and the all important configuration file. I usually tweak it a bit. I prefer the Read The Docs theme, so I make sure that is installed and change html_theme to sphinx_rtd_theme. I also prefer inline code to not be syntax highlighted by default, so I set highlight_language = 'none' as well. Inside the _static directory I drop a custom css file that improves the theme in some opinionated ways. This is based on the overrides that the Linux Kernel use for their documentation.

A final trick is to add default_role = 'any'. This is a trick I picked up from QEMU and it causes single backticks to always be a cross reference to some object. It's easy to use single backticks by mistake when you actually wanted to use double backticks for monospace emphasis. Setting any causes Sphinx to complain if the reference is unresolvable or ambiguous.

meson'ifying it

The next step is to get rid of that pesky Makefile. This is a meson shop, so let's meson-it-up, but you can of course skip this if you want to stick with the Makefile. Since this is probably in a subdirectory of an existing project, we use a variable to control if or not the docs should be built. We could also use meson_options.txt to set it and leave it out here.

builds_docs = false

Like everything else meson, we are pretty explicit about the files in our build, so we define those.

docs_sources = files(


docs_deps += docs_sources

The reason we add this to the docs_deps variable is that I'm also using Sphinx for API documentation. We will get back to this in a bit.

Next up, let's locate the sphinx-build binary and only consider building docs if we found it.

sphinx_build = find_program('sphinx-build', required: get_option('docs'))

if sphinx_build.found()
        build_docs = true

        sphinx_args = [sphinx_build]

Finally, we create a custom target for this.

        docs = custom_target('manual',
                build_by_default: builds_docs,
                output: 'docs',
                input: files(''),
                depend_files: docs_deps,
                command: [sphinx_args, '-q', '-b', 'html',
                        '-d', meson.current_build_dir() / 'manual.p',
                        meson.current_source_dir(), meson.current_build_dir() / 'manual',

Finally, make sure that subdir('docs') is somewhere at the end of your top level and do the meson setup and ninja dance to builds your documentation.

Adding API documentation with kerneldoc

Sphinx has pretty decent built in support for working with Doxygen. I find Doxygen a little clunky and I'd rather not have the dependency if I can get away with it. Fortunately, the nice kernel hackers built kerneldoc. kerneldoc is pretty similar to Doxygen and also generates Sphinx C Domain function and type descriptions. Please see the kerneldoc documentation for more information on how to actually the API documentation.

The first step is to grab scripts/kernel-doc from a kernel source tree. Do not be alarmed about the 2500 lines of Perl. Either you are like me and you get a warm fuzzy feeling in your stomach. Or you wanna puke, but then just don't look at it. I typically drop it into the scripts directory of my own project.

You might want to apply this patch to make it actually output Sphinx C Domain tags for functions. Next, we need a Sphinx extension. Grab this and its accompaning from your favorite kernel mirror and drop them into docs/sphinx. Finally, edit to pick up the extension and configure it:

import os
import sys
sys.path.insert(0, os.path.abspath("sphinx"))

kerneldoc_bin = '../scripts/kernel-doc'
kerneldoc_srctree = '..'

Assuming you added some kernel-doc documentation to your C files, add a new documentation file, say api.rst and add:

.. kernel-doc:: include/api.h

That's it! Enjoy your crisp autogenerated API documentation!