rdoxygen is an R package for the creation of doxygen documentation for source code (C/C++/Fortran/Java/etc.) in R packages (typically found in src
or inst/include
). It allows to trigger the rendering process automatically and effortlessly. Optionally, the doxygen output can be accessed by users of the package via R vignettes.
It's initially based on Corentin M. Barbu's answer to this stackoverflow question, was packaged by Clemens Schmid and improved by Martin Lysy.
This post introduces rdoxygen v2.0.
To use rdoxygen you need a working installation of the system program doxygen. You can either install it from source or rely on the package management system of your OS to get it.
The R package rdoxygen can be installed from CRAN or -- the latest development version -- from Github with devtools via
devtools::install_github("nevrome/rdoxygen")
To use roxygen to setup and afterwards update your doxygen documentation, you can simply run
roxygen::doxy()
in your package directory. doxy()
calls doxy_init()
if there's no Doxyfile (doxygen configuration file) yet. Otherwise it just updates the documentation.
The package provides a RStudio Addin named rdoxygenize that binds to the function doxy()
. doxy()
can therefore be called with a keyboard shortcut (e.g. CTRL+SHIFT+-). This makes the user experience comparable to roxygen2 documentation via devtools::document()
(usually CTRL+SHIFT+D).
The rdoxygen package serves by itself as an example for its application. You can browse the code on Github. For the application and preparation of rdoxygen we're going to look at the package's DESCRIPTION
file and the directories src
, inst
and vignettes
.
In our example we have a single C++ function docu_test_function()
that's made accessible to R with Rcpp. In the src
directory of our package we find a header file foo.h
, the respective implementation in bar.cpp
and the automatically created RcppExports.cpp
.
|-- src | |-- bar.cpp | |-- foo.h | |-- RcppExports.cpp
In foo.h
the docu_test_function()
gets declared:
#ifndef FOOREGISTER_FOO_H #define FOOREGISTER_FOO_H #include <Rcpp.h> double docu_test_function(double a, double b); #endif //FOOREGISTER_FOO_H
The implementation belongs in bar.cpp
:
#include "foo.h" // [[Rcpp::export]] double docu_test_function(double a, double b) { double c = a + b; return c; }
It's possible to document C++ functions that are exposed to R with roxygen2. To do so, we could modify the .cpp
file and add the relevant roxygen2 tags as described in Section 2.6 of the Rcpp Attributes vignette.
#include "foo.h" //' Example function: roxygen2 documentation //' //' This is the roxygen2 documentation of an example function. //' //' @param a A double //' @param b A double //' //' @return A double a + b //' //' @export //' // [[Rcpp::export]] double docu_test_function(double a, double b) { double c = a + b; return c; }
Now for the idea behind rdoxygen: If we want to add doxygen documentation for this function, we can modify the .h
file and add doxygen comment blocks:
#ifndef FOOREGISTER_FOO_H #define FOOREGISTER_FOO_H #include <Rcpp.h> /** * \file * * Example function: doxygen documentation * * This is the doxygen documentation of an example function. */ double docu_test_function(double a, double b); #endif //FOOREGISTER_FOO_H
docu_test_function()
behaves documentation wise as a global function. Therefore we have to add the \file
tag in the documentation. This is more straight forward for classes and class methods. That's where doxygen truly shines.
If you want to add some general information at the doxygen startpage you can add it with the \mainpage
tag anywhere in your header files:
/*! \mainpage rdoxygen example index page * * rdoxygen test page * * A test function is documented here: \link foo.h \endlink */
Now that we have some doxygen documentation we can set up doxygen for our R package. To do this we can rely on the the main function of rdoxygen: doxy()
. It requires doxygen to be installed (see Quickstart) and to be included in the system path variable. doxy()
will first create a Doxyfile at inst/doc/doxygen/Doxyfile
with doxy_init()
if it doesn't yet exist. Next, it runs doxygen on the Doxyfile and creates a directory inst/doc/html
where you can watch the result by opening the index.hml file with a browser. If vignette = TRUE, it creates a vignette allowing the Doxygen documentation to be viewed from within R with a call to vignette()
. More about this in the next section.
Whenever you edit the documentation or the Doxyfile you can and should rerun doxy()
to update the documentation. To simplify this, rdoxygen provides the RStudio Addin rdoxygenize. doxy()
can therefore be called with a keyboard shortcut.
TODO
rdoxygen was
# create default Doxyfile, process it with Doxygen, optionally wrap in R vignette doxy(vignette = TRUE) # separate steps above doxy_init() # create default Doxyfile doxy(vignette = FALSE) # process doxy_vignette() # wrap in R vignette # can also edit an existing Doxyfile doxy_edit(options = c(AUTOLINK_SUPPORT = "NO"))
Stability. The output of rdoxygen should function as expected with minimal additional intervention from the user. If using option vignette = TRUE
, the package should get automatically configured to process rmarkdown vignettes.
Dependencies. Since rdoxygen is used for package development, it tries as much as possible to avoid adding unnecessary dependencies to the user's package (i.e., if I want to use rdoxygen to add Doxygen documentation to my package, ideally that shouldn't force my package users to install rdoxygen itself). Nevertheless helpful packages for achieving the above are:
# do all steps below and trigger doxygen rendering doxy <- function( pkg = ".", # same as devtools::{document/load_all/install} argument, i.e., any subfolder of package root doxyfile = "inst/doc/doxygen/Doxyfile", # path to doxyfile relative to package root options, # passed to doxy_edit vignette = FALSE # add vignette: if TRUE then doxy_vignette is triggered name = "DoxygenVignette.Rmd", # passed to doxy_vignette index # passed to doxy_vignette ) # add Doxyfile in package if it does not exist doxy_init <- function( pkg = ".", doxyfile = "inst/doc/doxygen/Doxyfile" ) # edit Doxyfile tags if doxyfile exists doxy_edit <- function( pkg = ".", doxyfile = "inst/doc/doxygen/Doxyfile", options # named vector of key-value pairs to edit Doxyfile tags ) # wrap Doxygen documentation in R vignette doxy_vignette <- function( pkg = ".", name = "DoxygenVignette.Rmd", # name of Doxygen vignette index # name of vignette Index Entry. defaults to "C++ library documentation for package PackageName" overwrite = FALSE # should an existing vignette file be overwritten )
Doxygen documentation. Installed R vignettes can only display HTML files stored in a subfolder of inst/doc
(as documented here). Therefore, the suggested location for Doxygen documentation is inst/doc/doxygen
.
Doxyfile. As a package developer, I feel like whatever is required to create the package exactly as it should be installed on disk should be part of the package itself. In this sense, the Doxyfile
needed to format the Doxygen documentation exactly as I want it should be part of the package as well. An obvious location for this file is inst/doc/doxygen
. However, inst/doc
is typically .gitignore
d (e.g., by devtools::use_vignette()
and usethis::use_vignettes()
). So we'd have to manually exclude inst/doc/doxygen/Doxyfile
, via appending an existing .gitignore
with something like this:
# paste the following to the bottom of existing .gitignore !inst/doc # unignores inst/doc inst/doc/* # ignore everything inside inst/doc but not inst/doc itself !inst/doc/doxygen/Doxyfile # unignore Doxyfile
First, it should be noted that the INPUT
tag cannot handle relative directories outside of where it's run, i.e.:
# in Doxyfile INPUT = src/ # works fine INPUT = ../src/ # does not work
Thus, rdoxygen runs Doxygen from the package root folder. That being said, here are the default tags rdoxygen sets in the Doxyfile:
INPUT = src/ inst/include/ # the two locations in which you expect to find C++ code OUTPUT_DIRECTORY = inst/doc/doxygen PROJECT_NAME = "C++ Library Documentation for Package PackageName"
Also note that another useful option might be USE_MATHJAX = YES
, which makes formulas look much nicer than when this option is set to NO
.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.