How to Document Code

The documentation for xAODAnaHelpers uses a slightly non-trivial workflow:

  1. Doxygen parses the header and source files to generate an XML tree of the code

  2. breathe is a sphinx wrapper that enables us to parse the XML tree from doxygen

  3. sphinx is what produces the various output formats such as html, latex, e-pub from source code comments

  4. ReadTheDocs.org uses doxygen, breathe, and sphinx to automatically produce our documentation everytime main changes.

Our documentation is automatically generated for us so we will always guarantee that our documentation is up-to-date for all users.

The aim of this document is to help you get started with producing your own documentation locally to help resolve errors, typos, and make sure you’re formatting it the way that you want before pushing it to our github repo.

Setting it up Locally

Locally, we are going to need doxygen to do the initial parsing. Note that out of the box without doxygen, we can parse python scripts, such as xAH_run.py API Reference, which are included as part of xAODAnaHelpers. However, if we wish to have all of our C++ code’s documentation included, we will need doxygen to do parse it.

Doxygen

Get doxygen however you want. For Macs, we can use:

brew install doxygen

to install it. At this point, one should be able to generate the XML tree by navigating to the docs folder and running doxygen with no arguments:

cd docs
doxygen

since we provide a Doxyfile in the docs directory with the correct configurations.

Python Virtual Environment

Next, I suggest setting up a python virtual environment. Luckily, this solution is the hardest part. Most (rational) people use virtualenvwrapper to manage my python dependencies and workspace. It is assumed you already have pip.

To get the entire functionality of venvwrapper, we just need to grab the package and update our environment when we want to use it:

pip install virtualenvwrapper
echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bash_profile

Note

Don’t forget to source your profile if you’re going to use the same shell:

source ~/.bash_profile

From now on, we will have commands like mkvirtualenv, workon, and rmvirtualenv in our shell. As a first-time user, you haven’t made a virtual environment yet, so the first thing we do is make one:

mkvirtualenv xAH

This will also automatically call workon xAH. This is something we will always run in the future to enter the virtual environment.

Note

If you ever forget the name of the virtual environment you made, just run workon without any arguments. There is also tab completion.

Python Packages

Note

If you choose to use a virtual environment, enter it workon xAH

This is super easy. We provide a requirements.txt file:

cd docs
pip install -r requirements.txt

which will install all the required packages for you. As of the time of this document, this contains the following packages:

alabaster==0.7.12
Babel==2.9.1
beautifulsoup4==4.8.1
breathe==4.35.0
bs4==0.0.1
certifi==2023.7.22
chardet==3.0.4
docutils==0.15.2
exhale==0.2.4
idna==2.8
imagesize==1.1.0
Jinja2==3.1.3
lxml==4.9.1
MarkupSafe==2.1.3
packaging==19.2
Pygments==2.15.0
pyparsing==2.4.5
pytz==2019.3
PyYAML==6.0
requests==2.31.0
six==1.13.0
snowballstemmer==2.0.0
soupsieve==1.9.5
Sphinx==4.5.0
sphinx-argparse==0.2.5
sphinx-rtd-theme==0.4.3
sphinxcontrib-applehelp==1.0.1
sphinxcontrib-devhelp==1.0.1
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.2
sphinxcontrib-serializinghtml==1.1.5
urllib3==1.26.18

Generate Docs Locally

Now that we have doxygen and all of the required python packages installed, all you need to do now is process everything:

cd docs
make clean
doxygen
make html
open _build/html/index.html

and we’re good to go. Sphinx provides a Makefile in docs/ to make the html generation much easier to work with.

You may not always run all of these pieces each time you generate documentation. For example, if you need to make a change to the header/source files of any kind, you will need to re-run doxygen. In the rare case that the html generation isn’t working right, you might want to run make clean so you start over again. If you’re only changing the reStructuredText (rst) files in docs/ you might only ever need to run make html. All in all, it doesn’t take more than 10-15 seconds to generate the necessary documentation.

Documenting Code

In most cases, we will want to follow the reStructuredText directives and formatting for doing the code documentation. We just want to use doxygen + breathe to expose those comments to sphinx to parse and display correctly. In what follows, we provide a set of guidelines (really, examples) to make it easier to document our code specifically.

Note

All comments for a given class, function, variable should be prior to the given item you’re adding documentation for.

If you have a question about how to do something, google it in the context of reStructuredText or ask on the mailing list. Also have a look through most of our source code and compare it to the docs to figure out how we do something.

One-Line Comments

One-line comments are very useful in cases where we do not have much to say about something, perhaps because it is a rather trivial item:

/** @brief generically the main name assigned to all histograms */
std::string m_name;

which will render as

std::string HistogramManager::m_name

generically the main name assigned to all histograms

Block Comments

Block comments are very useful in all other cases. When in doubt, you can always make a block comment with just a single line, even for a variable. The flexibility allows us to include a lot more detail and formatting such as tables and latex:

/**
    @brief Destructor, allows the user to delete histograms that are not being recorded.
*/
virtual ~HistogramManager();

which will render as

virtual HistogramManager::~HistogramManager()

Destructor, allows the user to delete histograms that are not being recorded.

Doxygen rst directive

To tell doxygen and breathe that a given block of text should be considered as reStructuredText, we simply need to wrap it:

@rst
    This is now inside a doxygen directive that tells doxygen not to parse it, so that breathe can parse it for Sphinx.

@endrst

which will render as expected if we were writing it inside a standard .rst file. As usual, we have an example:

/**
    @brief This is used by any class extending to pre-define a set of histograms to book by default.
    @rst
        .. note:: The expectation is that the user does not directly use this class but rather inherits from it.

        We expect the user to create a new group of histograms, such as for jets::

            class JetHists : public HistogramManager
            {
              public:
                JetHists(std::string name, std::string detailStr);
                virtual ~JetHists() ;

                StatusCode initialize();
                StatusCode execute( const xAOD::JetContainer* jets, float eventWeight, int pvLoc = -1);
                StatusCode execute( const xAOD::Jet* jet, float eventWeight, int pvLoc = -1 );
                using HistogramManager::book; // make other overloaded version of book() to show up in subclass
                using HistogramManager::execute; // overload
            };

        The above example is taken from our implementation in :cpp:class:`JetHists`.

    @endrst
 */
class HistogramManager {};

which will render as

class HistogramManager

This is used by any class extending to pre-define a set of histograms to book by default.

We expect the user to create a new group of histograms, such as for jets:

class JetHists : public HistogramManager
{
  public:
    JetHists(std::string name, std::string detailStr);
    virtual ~JetHists() ;

    bool m_debug;
    StatusCode initialize();
    StatusCode execute( const xAOD::JetContainer  jets, float eventWeight, int pvLoc = -1);
    StatusCode execute( const xAOD::Jet  jet, float eventWeight, int pvLoc = -1 );
    using HistogramManager::book; // make other overloaded version of book() to show up in subclass
    using HistogramManager::execute; // overload
};

The above example is taken from our implementation in JetHists.

Note

The expectation is that the user does not directly use this class but rather inherits from it.

Subclassed by MetHists

For everything else…

These cover the general basics of how to document code for xAODAnaHelpers. Everything else is specific to how doxygen and Sphinx and breathe work. Most of these are well-supported with a large community, so googling is always very helpful here. Otherwise, feel free to ask on the mailing list.