With renewed activity around rosdoc2 (my PR got merged after 498 days!), and ongoing discussion about the state of documentation in ROS 2, I decided to look at ROS 2 package docs once again. This is also a followup to my older post on releasing a ROS package.

rosdoc2

The tool responsible for generating ROS 2 package docs is rosdoc2. It is what runs on the ROS build farm whenever you update your package and have enabled the appropriate options in rosdistro/<ROS DISTRO>/distribution.yaml:

  rviz_2d_overlay_plugins:
    doc:
      type: git
      url: https://github.com/teamspatzenhirn/rviz_2d_overlay_plugins.git
      version: main
    release:
    [...]

While somewhat intimidating at first, rosdoc2 really is just a very convenient wrapper around the commonly used Sphinx documentation framework including its C++ integration with doxygen via breathe + exhale. It creates a somewhat sensible default configuration for packages with no documentation or configuration at all, applies some options for a uniform theme and integration with other packages.

Custom Sphinx project

But where do we go once the default is not sufficient anymore (which is immediately once you start writing docs which go beyond API-docs)? Luckily, it is possible to setup a completely standard sphinx project, which rosdoc2 will pick up on. Using the sphinx-quickstart tool in the <package-root>/doc directory creates an empty sphinx project that rosdoc2 will find. This does not require any custom rosdoc2.yaml! Now, you can write any free-form documentation, tutorials and more just as you would with sphinx, and build and preview them using:

rosdoc2 build --package-path ./<package-path>

Including the generated (default) pages in the custom project

I wanted to still include the generated API docs in a subdirectory, and link to it from my new, custom index page. Looking at the default index page, all it does is link to generated/index. I added a page cpp_api_docs.rst besides index.rst, with the following content:

C++ API Docs
============

These are the autogenerated docs for the internal implementation.

.. toctree::
   :maxdepth: 3
   :caption: Contents:

   rviz_2d_overlay_plugins <generated/index>

Which is included from index.rst:

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   self
   cpp_api_docs

This results in the generated docs to appear in a sub-category “C++ API Docs” without cluttering the index page or table of contents!

Including an existing README.md

I already had a README.md with a few screenshots, which I wanted to use as the index page for the documentation, without copying it. After some fiddling around with sphinx, I found a satisfying solution which doesn’t require changing the readme to rst and doesn’t break relative paths to images included in the markdown:

First, create a proxy-file readme_include.md next to index.rst. This is a markdown file which just includes the original README.md, but preserves the relative image paths, which would otherwise break in the next step:

```{include} ../README.md
:relative-images:
```

Then, include the contents of this file from index.rst using myst to include markdown from rst:

.. include:: readme_include.md
   :parser: myst_parser.sphinx_

This also requires adding myst_parser to the extensions in conf.py:

extensions = ["myst_parser"]

What now?

I will now try to include this in the README of rosdoc2, i guess…

Up next: finding out how to automate more of the process, such as eliminating options in config.py that could be generated from package.xml and generating docs from ROS message definitions!