Doxygen is a popular tool for generating documentation from annotated C++ sources; it also supports other popular programming languages such as C#, PHP, Java, and Python. Visit the Doxygen website to learn more about the system, and consult the Doxygen Manual for the full information.
This document gives a brief introduction to Doxygen, in particular how it is used in FreeCAD to document its sources. Visit the source documentation page for instructions on building the FreeCAD documentation, which is also hosted online on the FreeCAD API website.
General workflow to produce source code documentation with Doxygen.
The Getting started (Step 3) section of the Doxygen manual mentions the basic ways of documenting the sources.
For members, classes and namespaces there are basically two options:
Note:
Usually you'd want to document the code in the header file, just before the class declaration or function prototype. This keeps the declaration and documentation close to each other, so it's easy to update the latter one if the first one changes.
The special documentation block starts like a C-style comment /*
but has an additional asterisk, so /**
; the block ends with a matching */
. An alternative is using C++-style comments //
with an additional slash, so ///
.
/**
* Returns the name of the workbench object.
*/
std::string name() const;
/**
* Set the name to the workbench object.
*/
void setName(const std::string&);
/// remove the added TaskWatcher
void removeTaskWatcher(void);
Alternatively, the documentation can be placed in another file (or in the same file at the top or bottom, or wherever), away from the class declaration or function prototype. In this case, you will have duplicated information, once in the actual source file, and once in the documentation file.
First file, source.h
:
std::string name() const;
void setName(const std::string&);
Second file, source.h.dox
:
/** \file source.h
* \brief The documentation of source.h
*
* The details of this file go here.
*/
/** \fn std::string name() const;
* \brief Returns the name of the workbench object.
*/
/** \fn void setName(const std::string&);
* \brief Set the name to the workbench object.
*/
In this case the structural command \file
is used to indicate which source file is being documented; a structural command \fn
indicates that the following code is a function, and the command \brief
is used to give a small description of this function.
This way of documenting a source file is useful if you just want to add documentation to your project without adding real code. When you place a comment block in a file with one of the following extensions .dox
, .txt
, or .doc
then Doxygen will parse the comments and build the appropriate documentation, but it will hide this auxiliary file from the file list.
The FreeCAD project adds several files ending in .dox
in many directories in order to provide a description, or examples, of the code there. It is important that such files are correctly categorized in a group or namespace, for which Doxygen provides some auxiliary commands like \defgroup
, \ingroup
, and \namespace
.
Example src/Base/core-base.dox
; this file in FreeCAD's source tree gives a short explanation of the Base
namespace.
/** \defgroup BASE Base
* \ingroup CORE
\brief Basic structures used by other FreeCAD components
The Base module includes most of the basic functions of FreeCAD, such as:
- Console services: printing different kinds of messages to the FreeCAD report view or the terminal
- Python interpreter: handles the execution of Python code in FreeCAD
- Parameter handling: Management, saving and restoring of user preferences settings
- Units: Management and conversion of different units
*/
/*! \namespace Base
\ingroup BASE
\brief Basic structures used by other FreeCAD components
The Base module includes most of the basic functions of FreeCAD, such as:
- Console services: printing different kinds of messages to the FreeCAD report view or the terminal
- Python interpreter: handles the execution of Python code in FreeCAD
- Parameter handling: Management, saving and restoring of user preferences settings
- Units: Management and conversion of different units
*/
Another example is the file src/Gui/Command.cpp
. Before the implementation details of the Gui::Command
methods, there is a documentation block that explains some details of the command framework of FreeCAD. It has various \section
commands to structure the documentation. It even includes example code enclosed in a pair of \code
and \endcode
keywords; when the file is processed by Doxygen this code example will be specially formatted to stand out. The \ref
keyword is used in several places to create links to named sections, subsections, pages or anchors elsewhere in the documentation. Similarly, the \see
or \sa
commands print "See also" and provide a link to other classes, functions, methods, variables, files or URLs.
Example src/Gui/Command.cpp
/** \defgroup commands Command Framework
\ingroup GUI
\brief Structure for registering commands to the FreeCAD system
* \section Overview
* In GUI applications many commands can be invoked via a menu item, a toolbar button or an accelerator key. The answer of Qt to master this
* challenge is the class \a QAction. A QAction object can be added to a popup menu or a toolbar and keep the state of the menu item and
* the toolbar button synchronized.
*
* For example, if the user clicks the menu item of a toggle action then the toolbar button gets also pressed
* and vice versa. For more details refer to your Qt documentation.
*
* \section Drawbacks
* Since QAction inherits QObject and emits the \a triggered() signal or \a toggled() signal for toggle actions it is very convenient to connect
* these signals e.g. with slots of your MainWindow class. But this means that for every action an appropriate slot of MainWindow is necessary
* and leads to an inflated MainWindow class. Furthermore, it's simply impossible to provide plugins that may also need special slots -- without
* changing the MainWindow class.
*
* \section wayout Way out
* To solve these problems we have introduced the command framework to decouple QAction and MainWindow. The base classes of the framework are
* \a Gui::CommandBase and \a Gui::Action that represent the link between Qt's QAction world and the FreeCAD's command world.
*
* The Action class holds a pointer to QAction and CommandBase and acts as a mediator and -- to save memory -- that gets created
* (@ref Gui::CommandBase::createAction()) not before it is added (@ref Gui::Command::addTo()) to a menu or toolbar.
*
* Now, the implementation of the slots of MainWindow can be done in the method \a activated() of subclasses of Command instead.
*
* For example, the implementation of the "Open file" command can be done as follows.
* \code
* class OpenCommand : public Command
* {
* public:
* OpenCommand() : Command("Std_Open")
* {
* // set up menu text, status tip, ...
* sMenuText = "&Open";
* sToolTipText = "Open a file";
* sWhatsThis = "Open a file";
* sStatusTip = "Open a file";
* sPixmap = "Open"; // name of a registered pixmap
* sAccel = "Shift+P"; // or "P" or "P, L" or "Ctrl+X, Ctrl+C" for a sequence
* }
* protected:
* void activated(int)
* {
* QString filter ... // make a filter of all supported file formats
* QStringList FileList = QFileDialog::getOpenFileNames( filter,QString::null, getMainWindow() );
* for ( QStringList::Iterator it = FileList.begin(); it != FileList.end(); ++it ) {
* getGuiApplication()->open((*it).latin1());
* }
* }
* };
* \endcode
* An instance of \a OpenCommand must be created and added to the \ref Gui::CommandManager to make the class known to FreeCAD.
* To see how menus and toolbars can be built go to the @ref workbench.
*
* @see Gui::Command, Gui::CommandManager
*/
This is an example from VTK, a 3D visualization library used to present scientific data, like finite element results, and point cloud information.
A class to store a collection of coordinates is defined in a C++ header file. The top part of the file is commented, and a few keywords are used, like @class
, @brief
, and @sa
to indicate important parts. Inside the class, before the class method prototypes, a block of commented text explains what the function does, and its arguments.
General workflow to produce source code documentation with Doxygen.
To generate the source code documentation there are two basic steps:
doxygen
on that configuration.The process is described in the following.
doxygen
and doxywizard
in your system. It is also recommended to have the dot
program from Graphviz, in order to generate diagrams with the relationships between classes and namespaces. On Linux systems these programs can be installed from your package manager.sudo apt install doxygen doxygen-gui graphviz
cd toplevel-source
doxygen -g DoxyDoc.cfg
to create a configuration file named DoxyDoc.cfg
. If you omit this name, it will default to Doxyfile
without an extension.DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "My Project"
PROJECT_NUMBER =
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY =
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
...
doxywizard
to edit many tags at the same time. With this interface you may define many properties such as project information, type of output (HTML and LaTeX), use of Graphviz to create diagrams, warning messages to display, file patterns (extensions) to document or to exclude, input filters, optional headers and footers for the HTML generated pages, options for LaTeX, RTF, XML, or Docbook outputs, and many other options.doxywizard DoxyDoc.cfg
doxygen DoxyDoc.cfg
toplevel-source/html
. It will consist of many HTML pages, PNG images for graphics, cascading style sheets (.css
), Javascript files (.js
), and potentially many sub-directories with more files depending on the size of your code. The point of entry into the documentation is index.html
, which you can open with a web browser.xdg-open toplevel-source/html/index.html
If you are writing new classes, functions or an entire new workbench, it is recommended that you run doxygen
periodically to see that the documentation blocks, Markdown, and special commands are being read correctly, and that all public functions are fully documented. Please also read the documentation tips located in the source code.
When generating the complete FreeCAD documentation, you don't run doxygen
directly. Instead, the project uses cmake
to configure the build environment, and then make
triggers compilation of the FreeCAD sources and of the Doxygen documentation; this is explained in the source documentation page.
All Doxygen documentation commands start with a backslash \
or an at-symbol @
, at your preference. Normally the backslash \
is used, but occasionally the @
is used to improve readability.
The commands can have one or more arguments. In the Doxygen manual the arguments are described as follows.
<sharp>
braces are used the argument is a single word.(round)
braces are used the argument extends until the end of the line on which the command was found.{curly}
braces are used the argument extends until the next paragraph. Paragraphs are delimited by a blank line or by a section indicator.[square]
brackets are used the argument is optional.Some of the most common keywords used in the FreeCAD documentation are presented here.
\defgroup <name> (group title)
, see \defgroup, and Grouping.\ingroup (<groupname> [<groupname> <groupname>])
, see \ingroup, and Grouping.\addtogroup <name> [(title)]
, see \addtogroup, and Grouping.\author { list of authors }
, see \author; indicates the author of this piece of code.\brief { brief description }
, see \brief; briefly describes the function.\file [<name>]
, see \file; documents a source or header file.\page <name> (title)
, see \page; puts the information in a separate page, not directly related to one specific class, file or member.\package <name>
, see \package; indicates documentation for a Java package (but also used with Python).\fn (function declaration)
, see \fn; documents a function.\var (variable declaration)
, see \var; documents a variable; it is equivalent to \fn
, \property
, and \typedef
.\section <section-name> (section title)
, see \section; starts a section.\subsection <subsection-name> (subsection title)
, see \subsection; starts a subsection.\namespace <name>
, see \namespace; indicates information for a namespace.\cond [(section-label)]
, and \endcond
, see \cond; defines a block to conditionally document or omit.\a <word>
, see \a; displays the argument in italics for emphasis.\param [(dir)] <parameter-name> { parameter description }
, see \param; indicates the parameter of a function.\return { description of the return value }
, see \return; specifies the return value.\sa { references }
, see \sa; prints "See also".\note { text }
, see \note; adds a paragraph to be used as a note.Since Doxygen 1.8, Markdown syntax is recognized in documentation blocks. Markdown is a minimalistic formatting language inspired by plain text email which, similar to wiki syntax, intends to be simple and readable without requiring complicated code like that found in HTML, LaTeX or Doxygen's own commands. Markdown has gained popularity with free software, especially in online platforms like Github, as it allows creating documentation without using complicated code. See the Markdown support section in the Doxygen manual to learn more. Visit the Markdown website to learn more about the origin and philosophy of Markdown.
Doxygen supports a standard set of Markdown instructions, as well as some extensions such as Github Markdown.
Some examples of Markdown formatting are presented next.
The following is standard Markdown.
Here is text for one paragraph.
We continue with more text in another paragraph.
This is a level 1 header
========================
This is a level 2 header
------------------------
# This is a level 1 header
### This is level 3 header #######
> This is a block quote
> spanning multiple lines
- Item 1
More text for this item.
- Item 2
* nested list item.
* another nested item.
- Item 3
1. First item.
2. Second item.
*single asterisks: emphasis*
_single underscores_
**double asterisks: strong emphasis**
__double underscores__
This a normal paragraph
This is a code block
We continue with a normal paragraph again.
Use the `printf()` function. Inline `code`.
[The link text](http://example.net/)
![Caption text](/path/to/img.jpg)
<http://www.example.com>
The following are Markdown extensions.
[TOC] First Header | Second Header ------------- | ------------- Content Cell | Content Cell Content Cell | Content Cell ~~~~~~~~~~~~~{.py} # A class class Dummy: pass ~~~~~~~~~~~~~ ~~~~~~~~~~~~~{.c} int func(int a, int b) { return a*b; } ~~~~~~~~~~~~~ ``` int func(int a, int b) { return a*b; } ```
The text inside a special documentation block is parsed before it is written to the HTML and LaTeX output files. During parsing the following steps take place:
*
) and then optionally more whitespace, then all whitespace and asterisks are removed.%
, then this symbol is removed, and no link is created for the word.Doxygen works best for statically typed languages like C++. However, it can also create documentation for Python files.
There are two ways to write documentation blocks for Python:
'''triple quotes'''
immediately after the class or function definition.##
are used to start the documentation block, and then a single hash character can be used in subsequent lines.Note:
\
or @
won't work.\param
and \var
.In the following example one docstring is at the beginning to explain the general contents of this module (file). Then docstrings appear inside the function, class, and class method definitions. In this way, Doxygen will extract the comments and present them as is, without modification.
'''@package pyexample_a
Documentation for this module.
More details.
'''
def func():
'''Documentation for a function.
More details.
'''
pass
class PyClass:
'''Documentation for a class.
More details.
'''
def __init__(self):
'''The constructor.'''
self._memVar = 0
def PyMethod(self):
'''Documentation for a method.'''
pass
In the following example the documentation blocks start with double hash signs ##
. One appears at the beginning to explain the general content of this module (file). Then there are blocks before the function, class, and class method definitions, and there is one block after a class variable. In this way, Doxygen will extract the documentation, recognize the special commands @package
, @param
, and @var
, and format the text accordingly.
## @package pyexample_b
# Documentation for this module.
#
# More details.
## Documentation for a function.
#
# More details.
def func():
pass
## Documentation for a class.
#
# More details.
class PyClass:
## The constructor.
def __init__(self):
self._memVar = 0
## Documentation for a method.
# @param self The object pointer.
def PyMethod(self):
pass
## A class variable.
classVar = 0
## @var _memVar
# a member variable
Compilation of documentation proceeds the same as for C++ sources. If both Python files, pyexample_a.py
and pyexample_b.py
, with distinct commenting style are in the same directory, both will be processed.
cd toplevel-source/
doxygen -g
doxygen Doxyfile
xdg-open ./html/index.html
The documentation should show similar information to the following, and create appropriate links to the individual modules and classes.
Class List
Here are the classes, structs, unions and interfaces with brief descriptions:
N pyexample_a
C PyClass
N pyexample_b Documentation for this module
C PyClass Documentation for a class
In the previous example, the Python file that is commented in a Doxygen style shows more detailed information and formatting for its classes, functions, and variables. The reason is that this style allows Doxygen to extract the special commands that start with \
or @
, while the Pythonic style does not. Therefore, it would be desirable to convert the Pythonic style to Doxygen style before compiling the documentation. This is possible with an auxiliary Python program called doxypypy. This program is inspired by an older program called doxypy, which would take the Pythonic '''docstrings'''
and convert them to the Doxygen comment blocks that start with a double hash ##
. Doxypypy goes further than this, as it analyzes the docstrings and extracts items of interest like variables and arguments, and even doctests (example code in the docstrings).
Doxypypy can be installed using pip
, the Python package installer.
pip3 install --user doxypypy
If the pip
command is used without the --user
option, it will require superuser (root) privileges to install the package, but this is not needed in most cases; use root permissions only if you are certain the package won't collide with your distribution provided packages.
If the package was installed as a user, it may reside in your home directory, for example, in $HOME/.local/bin
. If this directory is not in your system's PATH
, the program will not be found. Therefore, add the directory to the PATH
variable, either in your $HOME/.bashrc
file, or in your $HOME/.profile
file.
export PATH="$HOME/.local/bin:$PATH"
Alternatively, you can create a symbolic link to the doxypypy
program, placing the link in a directory that is already included in the PATH
.
mkdir -p $HOME/bin
ln -s $HOME/.local/bin/doxypypy $HOME/bin/doxypypy
Once the doxypypy
program is installed, and accessible from the terminal, a Python file with Pythonic docstrings can be reformatted to Doxygen style with the following instructions. The program outputs the reformatted code to standard output, so redirect this output to a new file.
doxypypy -a -c pyexample_pythonic.py > pyexample_doxygen.py
pyexample_pythonic.py
'''@package pyexample_pythonic
Documentation for this module.
More details go here.
'''
def myfunction(arg1, arg2, kwarg='whatever.'):
'''
Does nothing more than demonstrate syntax.
This is an example of how a Pythonic human-readable docstring can
get parsed by doxypypy and marked up with Doxygen commands as a
regular input filter to Doxygen.
Args:
arg1: A positional argument.
arg2: Another positional argument.
Kwargs:
kwarg: A keyword argument.
Returns:
A string holding the result.
Raises:
ZeroDivisionError, AssertionError, & ValueError.
Examples:
>>> myfunction(2, 3)
'5 - 0, whatever.'
>>> myfunction(5, 0, 'oops.')
Traceback (most recent call last):
...
ZeroDivisionError: integer division or modulo by zero
>>> myfunction(4, 1, 'got it.')
'5 - 4, got it.'
>>> myfunction(23.5, 23, 'oh well.')
Traceback (most recent call last):
...
AssertionError
>>> myfunction(5, 50, 'too big.')
Traceback (most recent call last):
...
ValueError
'''
assert isinstance(arg1, int)
if arg2 > 23:
raise ValueError
return '{0} - {1}, {2}'.format(arg1 + arg2, arg1 / arg2, kwarg)
pyexample_doxygen.py
##@package pyexample_pythonic
#Documentation for this module.
#More details go here.
#
## @brief Does nothing more than demonstrate syntax.
#
# This is an example of how a Pythonic human-readable docstring can
# get parsed by doxypypy and marked up with Doxygen commands as a
# regular input filter to Doxygen.
#
#
# @param arg1 A positional argument.
# @param arg2 Another positional argument.
#
#
# @param kwarg A keyword argument.
#
# @return
# A string holding the result.
#
#
# @exception ZeroDivisionError
# @exception AssertionError
# @exception ValueError.
#
# @b Examples
# @code
# >>> myfunction(2, 3)
# '5 - 0, whatever.'
# >>> myfunction(5, 0, 'oops.')
# Traceback (most recent call last):
# ...
# ZeroDivisionError: integer division or modulo by zero
# >>> myfunction(4, 1, 'got it.')
# '5 - 4, got it.'
# >>> myfunction(23.5, 23, 'oh well.')
# Traceback (most recent call last):
# ...
# AssertionError
# >>> myfunction(5, 50, 'too big.')
# Traceback (most recent call last):
# ...
# ValueError
# @endcode
#
def myfunction(arg1, arg2, kwarg='whatever.'):
assert isinstance(arg1, int)
if arg2 > 23:
raise ValueError
return '{0} - {1}, {2}'.format(arg1 + arg2, arg1 / arg2, kwarg)
The original file has a comment at the top '''@package pyexample_pythonic
that indicates the module or namespace that is being described by the file. This @package
keyword is not interpreted when using triple quotes in the comment block.
In the new file the commenting style is changed so the line becomes ##@package pyexample_pythonic
, which now will be interpreted by Doxygen. However, to be interpreted correctly, the argument has to be edited manually to match the new module (file) name; after doing this the line should be ##@package pyexample_doxygen
.
pyexample_doxygen.py
(the top is manually edited, the rest stays the same)
##@package pyexample_doxygen
#Documentation for this module.
#More details go here.
#
To compile, create the configuration, and run doxygen
in the toplevel directory that contains the files.
cd toplevel-source/
doxygen -g
doxygen Doxyfile
xdg-open ./html/index.html
The documentation should show similar information to the following, and create appropriate links to the individual modules.
Namespace List
Here is a list of all documented namespaces with brief descriptions:
N pyexample_doxygen Documentation for this module
N pyexample_pythonic
In the previous example, the conversion of the documentation blocks was done manually with only one source file. Ideally we want this conversion to occur automatically, on the fly, with any number of Python files. To do this, the Doxygen configuration must be edited accordingly.
To start, don't use the doxypypy
program directly; instead, create the configuration file with doxygen -g
, then edit the created Doxyfile
, and modify the following tag.
FILTER_PATTERNS = *.py=doxypypy_filter
What this does is that files that match the pattern, all files with a extension ending in .py
, will go through the doxypypy_filter
program. Every time Doxygen encounters such file in the source tree, the file name will be passed as the first argument to this program.
doxypypy_filter example.py
The doxypypy_filter
program does not exist by default; it should be created as a shell script to run doxypypy
with the appropriate options, and to take a file as its first argument.
#!/bin/sh
doxypypy -a -c "$1"
After saving this shell script, make sure it has execute permissions, and that it is located in a directory contained in your system's PATH
.
chmod a+x doxypypy_filter
mv doxypypy_filter $HOME/bin
On Windows systems, a batch file can be used in a similar way.
doxypypy -a -c %1
With this configuration done, the doxygen Doxyfile
command can be run to generate the documentation as usual. Every Python file using Pythonic '''triple quotes'''
will be reformatted on the fly to use ##Doxygen
style comments, and then will be processed by Doxygen, which now will be able to interpret the special commands and Mardown syntax. The original source code won't be modified, and no temporary file with a different name needs to be created as in the previous section; therefore, if a @package example
instruction is found, it doesn't need to be changed manually.
Note that existing Python files which already use the ##double hash
style for their comment blocks won't be affected by the doxypypy
filter, and will be processed by Doxygen normally.
General workflow to produce source code documentation with Doxygen, when the Python files are filtered to transform the comment blocks.
To use the automatic conversion of documentation blocks it is important that the original Python sources are correctly written, following the Pythonic guidelines in PEP8 and PEP257. Sloppily written code will cause doxypypy
to fail when processing the file, and thus Doxygen will be unable to format the documentation correctly.
The following commenting styles will not allow parsing of the docstrings by doxypypy
, so they should be avoided.
'''@package Bad
'''
def first_f(one, two):
"Bad comment 1"
result = one + two
result_m = one * two
return result, result_m
def second_f(one, two):
"Bad comment 2"
result = one + two
result_m = one * two
return result, result_m
def third_f(one, two):
'Bad comment 3'
result = one + two
result_m = one * two
return result, result_m
def fourth_f(one, two):
#Bad comment 4
result = one + two
result_m = one * two
return result, result_m
Always use triple quotes for the docstrings, and make sure they immediately follow the class or function declaration.
It is also a good idea to verify the quality of your Python code with a tool such as flake8 (Gitlab). Flake8 primarily combines three tools, Pyflakes, Pycodestyle (formerly pep8), and the McCabe complexity checker, in order to enforce proper Pythonic style.
pip install --user flake8
flake8 example.py
To check all files inside a source tree use find
.
find toplevel-source/ -name '*.py' -exec flake8 {} '+'
If the project demands it, some code checks deemed too strict can be ignored. The error codes can be consulted in the Pycodestyle documentation.
find toplevel-source/ -name '*.py' -exec flake8 --ignore=E266,E402,E722,W503 --max-line-length=100 {} '+'
In similar way, a program that primarily checks that comments comply with PEP257 is Pydocstyle. The error codes can be consulted in the Pydocstyle documentation.
pip install --user pydocstyle
pydocstyle example.py
Also use it with find
to perform docstring checks on all source files.
find toplevel-source/ -name '*.py' -exec pydocstyle {} '+'
Sphinx is the most popular system to document Python source code. However, since FreeCAD's core functions and workbenches are written in C++ it was deemed that Doxygen is a better documentation tool for this project.
While Sphinx can natively parse Python docstrings, it requires a bit more work to parse C++ sources. The Breathe (Github) project is an attempt at bridging the gap between Sphinx and Doxygen, in order to integrate both Python and C++ source code documentation in the same system. First, Doxygen needs to be configured to output an XML file; the XML output is then read by Breathe, and converted to suitable input for Sphinx.
See the Quick start guide in the Breathe documentation to know more about this process.
See this answer in Stackoverflow for other alternatives to documenting C++ and Python code together in the same project.