Integrating Doxygen with Autotools

For my thesis source code, I desired to have Doxygen documentation automatically generated by make. However, after searching the Internet, I was unable to find a decent tutorial. Luckily after reading the great book that I cannot recommend enough, A Practitioner's Guide to GNU Autoconf, Automake, and Libtool by John Calcote, I learned a great solution. I intend to outline that solution based off his book.

The first step is to decide how the process will work. In my case, I wrote documentation for a library that I plan for other developers to use. I want the documentation to be able to be viewed using the man utility. The documentation should be generated when the user runs 'make' and should be installed when the user runs 'make install'. If doxygen is not installed, output a warning message during configuration and do not generate any documentation.

The first step is to determine if the user has Doxygen installed. We can do this by searching for the Doxygen program using the Autoconf macro AC_CHECK_PROGS. If Doxygen is not found, output a warning message. We can put all of this logic in an m4 script or in-line in the configure.ac file. For this example, I will just put this in configure.ac:
AC_CHECK_PROGS([DOXYGEN], [doxygen])
if test -z "$DOXYGEN";
   then AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])
fi


Now in configure.ac, we can check to see if we have Doxygen and if so, add some files to later be processed.
AM_CONDITIONAL([HAVE_DOXYGEN],
[test -n "$DOXYGEN"])AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([docs/Doxyfile])])


We see a mention to 'docs/Doxyfile' above. To any Autotools users, this should outline the next step, to write the 'Doxyfile.in' file. This is a file that will be transformed into 'Doxyfile', which will control how Doxygen generates the documentation. The reason we describe the configuration file in a way that configure modifies it is in case that we want certain properties of the documentation to be configurable. For example, we could add a configure option to optionally generate html documentation, --enable-html-doc for example.

In the subdirectory docs of the project, we can start by running:
doxygen -g Doxyfile.in
This generates a template configuration file for Doxygen that we can modify to get our final configuration file. We need to supply the project name, project version, and any other options determined by configure. Change the following properties as below:
PROJECT_NAME = @PACKAGE_NAME@
PROJECT_NUMBER = @PACKAGE_VERSION@
INPUT = @top_srcdir@
Be sure to change other options that you desire, such as HTML output, man output, etc.


Now that we have a configuration file ready to be parsed by configuration script, we need to tell Automake to generate makefiles that have rules to generate the actual documentation. In the docs subdirectory, we can create the 'Makefile.am' file that will be transformed by automake into 'Makefile'. Here is a what it looks like:
if HAVE_DOXYGEN
directory = $(top_srcdir)/docs/man/man3/

dist_man_MANS = $(directory)/man_page_1.3 $(directory)/man_page_2.3
$(directory)/man_page_1.3: doxyfile.stamp
$(directory)/man_page_2.3: doxyfile.stamp

doxyfile.stamp:
   $(DOXYGEN) Doxyfile
   echo Timestamp > doxyfile.stamp

CLEANFILES = doxyfile.stamp

all-local: doxyfile.stamp
clean-local:
   rm -rf $(top_srcdir)/docs/man
endif

Now let's look at this carefully. The first thing that you should notice is that all the rules are only generated if configure set HAVE_DOXYGEN. We can see in the m4 code above that this is set is Doxygen is found.

Next we set up a convenience variable directory to save typing later on. Then we set the man pages that we wish to install when 'make install' is called. We set up a "Product List Variable" dist_man_MANS. This is a normal product list variable with a dist modifier. This means that we want to install these files when we run 'make install'. The man_ portion is the prefix, meaning we want to install these files in the $(mandir) directory. This is set by configure. The MANS portion of this product list is one of the primaries telling Autoconf which types of files are in this list.

We need make rules to build these man pages during the make stage. To do this, we make a rule for each man page to be installed, each one depending on a file called doxyfile.stamp. The reason we use one file is because of the one to many relationship between the one doxygen command and the many files generated. We need to be able to determine when we need to generate documentation, but which file do we check for. It doesn't make sense to pick one documentation file at random and check for its existence, so we use this solution.

The doxyfile.stamp target will generate the documentation by using the $DOXYGEN file determined by configure and the 'Doxyfile' generated from 'Doxyfile.in'. This target must also generate the doxyfile.stamp file also so future documentation file dependency checks are satisfied.

Now we need to tell Automake we want these built not at 'make install' time, but at 'make' time. To do this, we use a hook to the normal all target. By making the target all-local, we are making a target that will get executed before the normal all target. Now we are set. Make will install the man pages with the 'make install' command as we use the product list man_MANS with the dist prefix. We just need to handle cleaning up files on make clean. To do this, we add the 'doxyfile.stamp' file to the CLEANFILES list. This list is automatically cleaned when 'make clean' is run. To remove the directory of man pages is slightly different though. We can't easily remove a directory using the CLEANFILES variable. We simply hook into clean as we did with all and manually remove the directory.

Now we are set and ready to go. Please comment on improvements or errors in my guide.

5 comments:

Unknown said...

Nice instructions, thanks!
I had one problem. When copy-pasting Makefile.am contents, one should change the space indentation to tabs.

Unknown said...

@justme
That is a very good point! It is very difficult to get the files display correctly on the web browser. You are exactly correct. Tabs are used in Makefiles, and thus Makefile.in and Makefile.am files, not spaces. Some programs such as vim will alert you to this. Thanks justme.

Unknown said...

You saved me a lot of time - THANKS

duncan_roe said...

This article was a great starting point - thanks!
I did extend the methodology a bit. You can see the result in this release of libnetfilter_queue.
The Makefile runs doxygen, although ./configure turns this off by default. doxyfile.stamp depends on all C source files, so the documentation is re-generated any time a source is changed, since there is no way to tell whether that change updated the Doxygen comments therein.
make also generates a full set on man pages, one for each function in the library. Modern Doxygen likes functions to belong to modules and creates a man page for each module. Script fixmanpages.sh, which has to be maintained by hand, renames each of these to its first contained funtion, and symlinks to it for the rest.

duncan_roe said...

The presence of fixmanpages.sh would cause make distcheck to fail, solved at the time by making doxygen be off by default.
make distcheck now passes with doxygen on. The resolution was to fold fixmanpages.sh into doxygen/Makefile.am. The script becomes a single line (i.e. multiple lines ending ;\) so the functions still work.
The updated system can be viewed now by git clone https://git.netfilter.org/libnetfilter_queue and should be in the next release.