Basic Usage
The diagram below illustrates the workflow of the Mr.Docs documentation tool. It shows how inputs are transformed step by step into the final documentation output. Each component in the diagram represents a distinct type of entity:
-
Inputs: Provide the initial parameters and settings required to configure and run the tool. These include the configuration file and command-line arguments.
-
Processes: Represent intermediary outputs or data generated during the workflow.
-
Outputs: The final product, in this case, the generated documentation.
In summary:
-
Begin by specifying the configuration file and command-line arguments to set up the options. The configuration options affect all processes.
-
The configuration options define a Compilation Database. All symbols are extracted with Clang to generate a unified corpus of symbols with their corresponding documentation.
-
Finally, This corpus is then fed to a Generator that produces the desired documentation.
For more details on each component, refer to the corresponding sections of this guide.
Mr.Docs configuration file
The mrdocs.yml
configuration file contains information about the project.
If you’re used to Doxygen, this file is similar to the Doxyfile
configuration file.
Here is an example of a mrdocs.yml
file:
source-root: ../include
multipage: false
generator: adoc
In many projects, it is common to have the documentation in a docs
directory, which can also contain this configuration file.
+ <project-directory>
+ docs
+ mrdocs.yml
+ ...
+ include
+ src
+ test
+ ...
The most important information is the source-root
option, which determines the root of the source tree relative to the mrdocs.yml
file.
The list of all available options can be found in the The Configuration File page.
Mr.Docs invocation
For consistency, these instructions assume you have the mrdocs executable in PATH.
|
Basic invocation
You can invoke Mr.Docs from the command line with the following command:
mrdocs path/to/mrdocs.yml
If you are at the path of the mrdocs.yml
file, you can simply run:
mrdocs
You can also specify other commands to Mr.Docs directly from the command line to set or override options from the mrdocs.yml
file (See all options in The Configuration File page).
mrdocs path/to/mrdocs.yml --output=../docs/reference
Except for the path to the mrdocs.yml file, all other relative paths are made absolute relative to the mrdocs.yml file.
|
Compilation databases
One way to simplify the documentation generation process is by using a compile_commands.json
file generated by CMake to determine the source files to process and their compile options.
This file is generated by the CMake configuration step when the CMAKE_EXPORT_COMPILE_COMMANDS
option is set to ON
.
cmake -B build -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
At this step, you can also add any other flags you want to pass to cmake
, such as -DCMAKE_BUILD_TYPE=Release
or -DCMAKE_CXX_COMPILER=clang++
.
If you are using the Visual Studio generator, you might need to switch to the Ninja generator to generate the compile_commands.json file.
|
This will generate a compile_commands.json
file in the build
directory containing all commands needed to compile your project.
This file can be used by MrDocs to determine the source files to process and their compile options.
mrdocs --compilation-database=../build/compile_commands.json --output=../docs/reference
MrDocs will go through all the source files listed in the compile_commands.json
file and generate the documentation for them.
When MrDocs goes through the commands in the compilation database, it will go through the same targets as the compiler. Only symbols present in these targets will be documented. In the case of header-only libraries, the symbols will be documented only if they are included in one of these targets. |
CMake integration
It’s common to have different configurations for the documentation generation than for the project build.
This means CMake is often being run just to generate a custom compile_commands.json
for the documentation.
Also, the compile_commands.json
file is a configuration artifact, which means it often lacks a stable location that can be referenced in the mrdocs.yml
configuration file.
Thus, the pattern of using a compile_commands.json
file generated by CMake is so common that MrDocs provides a CMake module to simplify the process.
You can let MrDocs generate the compile_commands.json
file for you by providing the path to the CMakeLists.txt
file of your project.
mrdocs --compilation-database=../CMakeLists.txt --output=../docs/reference
By providing your CMakeLists.txt
file as the reference for you compilation database, MrDocs will automatically run CMake to generate the compile_commands.json
file in a temporary directory.
Not only this simplifies the usage but also ensures that the stable compilation database file can be used in the mrdocs.yml
configuration file.
MrDocs does not bundle CMake. If you want to use this feature, you need to have CMake installed on your system and available in PATH. |
Parameters for cmake, such as -D BUILDING_TEST=OFF -D MRDOCS_BUILD=ON
can also be specified with the cmake
option in the configuration file.
MrDocs will always append the CMAKE_EXPORT_COMPILE_COMMANDS=ON
flag to the cmake command.
When the
|
MrDocs Builds
In many projects, a common pattern is to define a special build configuration for the documentation generation such that:
-
Tests, examples, and auxiliary targets are excluded
-
All header-only files are included in targets
-
Unnecessary sources files are excluded from targets
This can usually be achieved by defining a custom target in the CMakeLists.txt
with a single source file that includes all the necessary header files.
if (MY_PROJECT_MRDOCS_BUILD)
# Glob all header files
set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
file(GLOB_RECURSE HEADER_FILES_LIST "${INCLUDE_DIR}/*.hpp")
# Create a temporary source file that includes all header files
set(TEMP_CPP_FILE "${CMAKE_CURRENT_BINARY_DIR}/all_headers.cpp")
file(WRITE ${TEMP_CPP_FILE} "// This file is generated automatically by CMake\n\n")
foreach(HEADER_FILE ${HEADER_FILES_LIST})
file(APPEND ${TEMP_CPP_FILE} "#include \"${HEADER_FILE}\"\n")
endforeach()
# Create a custom target for MrDocs
add_library(my_project_mrdocs_target ${TEMP_CPP_FILE})
# Set any other target properties here
target_include_directories(my_project_mrdocs_target PRIVATE ${INCLUDE_DIR})
target_link_libraries(my_project_mrdocs_target PRIVATE an_external_library)
# Don't create any other targets
return()
endif()
To use this target, you can run CMake with the MY_PROJECT_MRDOCS_BUILD
flag set to ON
:
mrdocs --cmake="-D MY_PROJECT_MRDOCS_BUILD=ON" --compilation-database=../CMakeLists.txt --output=..\docs\reference
Because these paths and options are stable, you can specify them in the mrdocs.yml
configuration file.
cmake: "-D MY_PROJECT_MRDOCS_BUILD=ON"
compilation-database: ../CMakeLists.txt
output: ../docs/reference
Extracting Documentation
Unlike other documentation generators, MrDocs only works with valid C++ code. Instead of partially preprocessing the source files and inferring symbols from potentially ill-formed code, MrDocs relies on the compilation database and Clang to preprocess the source files.
Thus, for each common C++ construct, MrDocs provides commands or options to identify and extract the relevant information as intended by the user. For instance, a common ill-formed Doxygen pattern to specify a class as an implementation detail is:
#ifdef DOCS
__implementation_defined__
#else
impl::f_return_t
#endif
f();
In this pattern, the user wants to document the function f
as implementation_defined f();
because the library contract is that the user should not rely on a specific return type here.
However, this ill-formed pattern is problematic:
-
__implementation_defined__
is not a valid symbol so the code is ill-formed -
impl::f_return_t
doesn’t express the intent of the user for the documentation -
the developer has to effectively maintain two versions of the code
-
the original source code becomes more and more unreadable
Instead, when using MrDocs, while the __MRDOCS__
macro is still available for conditional compilation, the same function could be directly documented as:
impl::f_return_t f();
And the user can specify that symbols in the impl
namespace are implementation details in the configuration file:
# ...
implementation-defined: impl::**
# ...
The Commands and The Configuration File pages contain a list of all available commands and options to identify and extract the relevant information as intended by the user.
MrDocs provides multiple mechanisms to specify special C++ patterns, such as the example above. For each common C++ construct that would require macros and two versions of the code, MrDocs provides commands to identify the construct and extract the relevant information as intented by the user.
Dependencies
Another consequence of relying on valid C++ code is that MrDocs needs to know about dependencies of the project for the code to be valid. In particular, it needs access to the header files of the project and its dependencies.
The includes
option in the configuration file specifies the directories to search for header files. Suppose a library depends on an external library, such as Boost:
# A library that depends on an external library
includes:
- /path/to/boost/include
Whatever is specified in the includes
option is passed to Clang as include directories, regardless of the strategy used to generate the compilation database.
If a compile_commands.json
file is used, these include directories are passed directly to Clang as -I
flags.
If a CMakeLists.txt
file is used, the cmake
option in the configuration file can be used to provide the necessary parameters for CMake to find the appropriate header files.
cmake: '-D BOOST_ROOT=/path/to/boost'
Another option supported by CMake is to set the BOOST_ROOT
environment variable as /path/to/boost
before running MrDocs.
Missing includes
Sometimes headers aren’t present in the environment where MrDocs runs (CI, containers, new machines), even with the alternatives presented above. For instance, a library that depends on a large third-party dependency may not be able to vendor that dependency or install it in CI just to generate documentation. Instead of failing immediately, MrDocs can optionally forgive specific missing headers and — even more aggressively — synthesize shims that let Clang parse enough of your public API to extract documentation.
This feature is opt-in. If you don’t configure any forgiveness, MrDocs behaves like plain Clang, and missing headers are hard errors.
-
First pass (normal parse):
-
MrDocs intercepts a narrow set of diagnostics (unknown type/namespace/member) that usually indicate missing symbols.
-
From those diagnostics, it extracts candidate symbols that look like types or namespaces (e.g.,
llvm::StringRef
,foo::bar
). -
It records the candidates.
-
-
Shim synthesis (only if safe):
-
If the recorded names are unambiguous (e.g., clearly a type or clearly a namespace), MrDocs generates a temporary in-memory header with stubs:
-
namespaces become empty namespaces;
-
types become forward declarations or empty class stubs.
-
User-provided shims (see below) override the generated stubs.
-
-
Second pass (reparse with the shim):
-
MrDocs reparses the TU with the synthetic shim injected so Clang can build an AST for your code.
-
If the names were ambiguous or the set is complex/conflicting, MrDocs does not synthesize; it simply re-emits the original errors.
-
This is heuristic by design: it works for simple, surface-level references typically found in public headers; it will not fix deep template logic, macro jungles, or APIs that require real definitions. In these cases, the custom shims (see below) you provide are the only way forward.
-
MrDocs forgives a missing include directive if a shim exists in
missing-include-shims
for that path to serve the shim. The shim should contain stubs for the external types/namespaces you need. -
MrDocs forgives a missing include if the prefix of the include string matches one of the values in
missing-include-prefixes
. These include directives are ignored and replaced with an empty include guard.
When diagnostics still occur and the missing-include-prefixes
option contains at least one value, Mr.Docs will extract missing symbol names from the translation unit to build the temporary in-memory shim with all symbols for the second pass.
The configuration allows users to detemrine where forgiveness is allowed, and optional shims for specific files:
# Provide content for particular forgiven files (keys are include-style paths)
missing-include-shims:
"llvm/ADT/StringRef.h": |
namespace llvm { class StringRef; }
"boost/version.hpp": |
#define BOOST_VERSION 108400
# Allow certain include trees to be missing without failing
missing-include-prefixes:
- "llvm/"
- "boost/"
MrDocs wraps shim contents in an internal include guard, so repeated includes are safe.
MrDocs does not attempt synthesis when a name could be either a namespace or a type and there’s no reliable disambiguation, the set of names implies conflicting declarations, or your diagnostics are unrelated to missing includes (e.g., real semantic errors). In these cases, the first-pass errors are just replayed.