Generating Doxygen Documentation for C++ using CMake

Tutorials Jun 06, 2020

Introduction

In early October 2019, whilst I was searching for a Visual Novel game engine, I happened to stumble upon a project called NovelRT.
NovelRT is a newer game engine designed with Visual Novels (VNs) in mind. Seeing as how no documentation was present when I planned to use it I decided to PR the documentation and documentation generation myself.
This blog post intends to show and explain how I achieved that goal.

Resources

Since some of you might wonder what the results are I decided to provide a link to the repository.

Note

The official Doxygen site contains plenty of information on how to use its syntax to generate *.html documentation files. For this reason, we are not going to be covering that.
At the same time we will presume you already have some basic knowledge of CMake.

Project file structure

./
|-- doxygen/
|   |-- CMakeLists.txt
|   |-- Doxyfile.in
|   `-- logo.png
|-- include/
|   `-- (All header files)
|-- src/
|   |-- CMakeLists.txt
|   `-- (All source files)
`-- CMakeLists.txt
Example directory structure

With this setup the Doxygen output will be in the CMake binaries directory.

CMake setup

The first CMake file that we shall configure will be the root one. Its main function is to load the other files so they get properly recognized. We will achieve that by using the add_subdirectory() function, the result should look something like this:

# Specifies the minimum required CMake version.
# More information found at https://cmake.org/cmake/help/v3.17/command/cmake_minimum_required.html?highlight=cmake_minimum_required
cmake_minimum_required(VERSION 3.17)

# This just sets the CMake project name.
# More information found at https://cmake.org/cmake/help/v3.17/command/project.html?highlight=project
project(PROJECT_NAME VERSION 0.0.1)

# Adds the given directories to those the compiler uses to search for include files.
# More information found at https://cmake.org/cmake/help/v3.17/command/include_directories.html
include_directories(include)

# "add_subdirectory" has a rather fitting name, it adds a subdirectory to the build.
# More information found at https://cmake.org/cmake/help/v3.17/command/add_subdirectory.html?highlight=add_subdirectory

# This adds the src folder a new directory so that it can be properly handled by CMake
add_subdirectory(src)
# and this adds the doxygen folder as a CMake subdirectory.
add_subdirectory(doxygen)

The 2nd CMakeLists file that we need is located in the doxygen directory and is responsible for handling the documentation generation.

# Finds and loads settings from an external project.
# "QUIET" makes it so that CMake won't complain in case Doxygen isn't present.
# More information at https://cmake.org/cmake/help/latest/command/find_package.html
find_package(Doxygen QUIET)

# Here we set a list of images that can/will be used in the documentation.
# For now we only have a logo to use.
set(DOCS_IMAGES
 logo.png
)

# While "DOCS_RESOURCES" contains all the other lists so that the file moving can be done with only one call in CMake.
set(DOCS_RESOURCES
  ${DOCS_IMAGES}
)

if (DOXYGEN_FOUND)
  # Input directory of header files from which the documentation will be formed.
  set(DOXYGEN_INPUT_DIR ${CMAKE_SOURCE_DIR}/include/)
  # Output directory for built documentation.
  # "CMAKE_BINARY_DIR" is usually a directory named "out" located in the root of the project.
  set(DOXYGEN_OUTPUT_DIR ${CMAKE_BINARY_DIR}/docs)
  # "DOXYGEN_INDEX_FILE" is the path to the documentation homepage html file.
  set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/html/index.html)
  # The following is used to always force Doxygen to try a run regardless if one was already performed.
  set(DOXYGEN_FAKE_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/html/__index.html)
  
  # The 2 following lines just specify the input path for the template Doxygen options.
  # Whilst the 2nd specifies the output for when the place holder data has already been filled in.
  set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
  set(DOXYFILE_OUT ${DOXYGEN_OUTPUT_DIR}/Doxyfile)

  set(DOXYGEN_PROJECT_LOGO ${DOXYGEN_OUTPUT_DIR}/logo.png)

  #Replace variables inside @@ with the current values
  configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
  # Here we use CMake to manually create the Doxygen output directory since it doesn't do it itself.
  file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR})
  # Copy the needed resources like the logo for instance into the output directory.
  file(COPY ${DOCS_RESOURCES} DESTINATION ${DOXYGEN_OUTPUT_DIR})

  # More information at https://cmake.org/cmake/help/latest/command/add_custom_command.html
  add_custom_command(
                     OUTPUT
                       ${DOXYGEN_INDEX_FILE}
                       ${DOXYGEN_FAKE_INDEX_FILE}
                     COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
                     MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN}
                     COMMENT "Generating docs")

  # More information at https://cmake.org/cmake/help/latest/command/add_custom_target.html
  add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE})
else ()
	message("Doxygen need to be installed to generate the doxygen documentation")
endif (DOXYGEN_FOUND)

Doxyfile setup

The Doxyfile.in contains the Doxygen recipe for building the docs. At first, it is recommended to generate a default Doxyfile and then edit the necessary settings within the file.
For our compatibility with the CMakeLists file, we have to set the pre-configured variables inside the Doxyfile.in. We can do so by enclosing the set variable with @@; an example of that would be @[email protected].
The full Doxyfile being referenced can be found here.

Example class documentation page

If you've stuck around to read the whole post I have a small reward for you, a rather nice song.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.