Doxygen contributor’s guide#
This guide shows Upstream Pigweed maintainers how to contribute content to Pigweed’s C/C++ API reference, which is powered by Doxygen.
For Doxygen style rules, see Doxygen style guide.
Overview#
Most of pigweed.dev is built with Sphinx. The C/C++ API reference however is
built with Doxygen. rules_doxygen
orchestrates the Doxygen build (//docs/doxygen:build) hermetically within
Bazel.
The C/C++ API reference is organized along the lines of Pigweed modules. This is
achieved through Doxygen groups. The groups are defined in
//docs/doxygen/modules.h. Header files are annotated with @module and
@submodule (aliases defined in docs/doxygen/Doxyfile) to group APIs
into their respective Pigweed modules.
Quickstart#
Check if your module is defined in docs/doxygen/modules.h. If not, define it now.
/// @defgroup pw_bytes pw_bytes /// @brief Utilities for manipulating binary data. /// @maindocs /// [Home](../../pw_bytes/docs.html) /// @endmaindocs
The pattern is
@defgroup <id> <title>. If<title>is omitted, Doxygen mistakenly capitalizes the module asPw_bytes.@briefshould contain the module’s tagline as defined in docs/sphinx/module_metadata.json. If your module doesn’t have a tagline, now is a good time to create one!The content between
@maindocsand@endmaindocsshould be a complete list of links to the module’s Sphinx docs.(Optional) If your module has a large, complex API, define “submodule” groups (such as the
pw_bytes_ptrsubgroup shown below) in docs/doxygen/modules.h to help further organize your module’s API reference:/// @defgroup pw_bytes_ptr Packed pointers /// @ingroup pw_bytes /// @brief Store data in unused pointer bits
See pw_bytes to view the rendered example. The
pw_bytesgroup controls the module’s API reference landing page. The main page is organized by “submodules”. Thepw_bytes_ptrgroup is one of the submodules. On that page, all APIs related to packed pointers are presented.In your module’s main
BUILD.bazelfile (e.g.//pw_bytes/BUILD.bazel) create afilegroupnameddoxygen. Forsrcs, list all Doxygen inputs:filegroup( name = "doxygen", srcs = [ "public/pw_bytes/alignment.h", "public/pw_bytes/array.h", "public/pw_bytes/bit.h", "public/pw_bytes/byte_builder.h", "public/pw_bytes/endian.h", "public/pw_bytes/packed_ptr.h", "public/pw_bytes/span.h", "public/pw_bytes/suffix.h", "public/pw_bytes/units.h", ], )
Important
The Doxygen build is hermetic and all inputs are explicitly defined. If you forget to include a header at this stage, Doxygen won’t know that the header exists.
Add your target to
//docs/doxygen:srcs:filegroup( name = "srcs", srcs = [ # … "//pw_bytes:doxygen", # … ] )
In each of the headers that you’ve listed in your module’s
doxygentarget, add@moduleor@submoduleannotations. Always explicitly close with@endmoduleor@endsubmodule.For simple modules where you want the entire API listed on a single page, use
@module:namespace pw { /// @module{pw_foo} // … /// @} } // namespace pw
For more complex modules with large APIs, use
@submodule:namespace pw { /// @submodule{pw_bytes,ptr} // … /// @} } // namespace pw
@submodule{pw_bytes,ptr}will organize the API under thepw_bytes_ptrgroup that’s defined in docs/doxygen/modules.h.The lack of whitespace between
pw_bytesandptris important!Remember to use
@}to close off your annotation!Follow the style rules in Doxygen style guide.
Link from Sphinx to Doxygen#
To link from the Sphinx docs to a Doxygen page, use the :cc: role. See
Sphinx-to-Doxygen links for usage and style guidance.
Link from Doxygen to Sphinx#
Use normal Markdown links with relative paths. There is not yet any utility
like :cc: for managing Doxygen-to-Sphinx links.
/// [Home](../../pw_bytes/docs.html)
Tip
Doxygen outputs all files into a single directory, so the Sphinx docs are
always available two directories below (../..).
Troubleshooting#
Each section below is a verbatim error message thrown by Doxygen.
… represents variable text. The list at the start of a section
explains how to resolve the error. The first item is the most probable
fix, the second item the second most probable, etc.
General tips:
The tagfile generated at
//bazel-bin/docs/sphinx/_docs/_sources/doxygen/api/cc/index.tagis essentially an authoritative index of the entire public API, as Doxygen understands it.Tweak the Doxygen config (
//docs/doxygen/Doxyfile) as needed and then re-run the build.
… has @param documentation sections but no arguments#
Remove all
@paramdocumentation because the function has no args.
@copybrief or @copydoc target … not found#
Make sure that the target exists. It may have been removed recently.
Disambiguate the namespace of the target:
- /// @copydoc internal::GenericIntrusiveList<ItemBase>::clear + /// @copydoc containers::internal::GenericIntrusiveList<ItemBase>::clear
Remove backticks from the target name.
- /// @copydoc `BucketBase::Remove` + /// @copydoc BucketBase::Remove
Specify the exact signature of the target method.
- /// @copydoc Base::Method + /// @copydoc Base::Method(int) void Method(int x);
Check if the target has a custom annotation and then look for that annotation in the
PREDEFINEDlist in//docs/doxygen/Doxyfile. For example, the following@copydocwas technically correct:/// @copydoc MultiBufChunks::push_front void PushFrontChunk(OwnedChunk&& chunk) { MultiBufChunks::push_front(std::move(chunk)); }
The
MultiBufChunksclass did indeed have apush_frontmethod. The issue was thatMultiBufChunkswas annotated withPW_MULTIBUF_DEPRECATEDand Doxygen was instructed to ignore this definition. See Preprocessing.PREDEFINED = … \ PW_MULTIBUF_DEPRECATED=
Argument … from the argument list of … has multiple @param documentation sections#
Document each
@paramname only once.There must be a space between
@paramand the parameter name. The text in brackets (e.g.@param[*]) is only for specifying direction. The only 3 valid direction values arein,out, andin,out.- /// @param[dest] The memory area to copy to. + /// @param dest The memory area to copy to.
Argument … of command @param is not found in the argument list#
Match the
@paramname to the function signature name exactly.- /// @param foo The integer... + /// @param value The integer... constexpr size_t EncodedSize(T value)
If the function signature does not name the parameter, provide a name and use that for
@param. Avoid unused parameter warnings by keeping it unnamed in the implementation./// @param Status A status... - void FinalizeRead(Status) override; + void FinalizeRead(Status status) override;
Delete the
@paramdocumentation if the parameter has been recently removed from the code.Use
@tparamfor template parameters, not@param.- /// @param kMaxSize Size of the largest allocation... + /// @tparam kMaxSize Size of the largest allocation... template <size_t kMaxRequests, size_t kMaxSize> auto ArbitraryRequests()
When using
@copydoc, the function signatures need to match.
Detected potential recursive class relation#
Hide the inheritance from Doxygen using the
@condand@endconddirectives. Also leave a comment pointing to b/513051956.template <typename T, size_t kCapacity> class MyClass // TODO: b/513051956 - Fix `recursive class relation` error /// @cond : public MyClass<T, kGenericSized> /// @endcond {
Note
This warning occurs when a class template inherits from another instantiation of itself (for example, a capacity-specific version inheriting from a generic-capacity version). Doxygen’s parser struggles to resolve template specializations and suspects circular inheritance.
Documented symbol … was not declared or defined#
Instruct Doxygen to expand or ignore the symbol by adding it to the
PREDEFINEDlist in docs/doxygen/Doxyfile.To ignore the symbol (expand to nothing):
PREDEFINED = ... \ MY_SYMBOL(...)= \To define it to a specific value:
PREDEFINED = ... \ MY_SYMBOL(...)=value \
Note
Doxygen’s parser can get confused by macros that look like function calls
or are placed in locations where they might resemble C++ syntax (e.g.
trailing return types). This often happens when macro arguments contain
complex C++ syntax (e.g., -> inside arguments).
End of file while inside a group#
Close all
@moduleand@submodulegroups with@endmoduleor@endsubmodule.Close all explicit group start markers (
@{) with group end markers (@}).If a file contains
@defgroupand@{, check if the group should be removed from the header and instead defined centrally in//docs/doxygen/modules.hvia@moduleor@submodule.Avoid nesting
@condblocks inside@submoduleor@modulegroups. Close the group with@endsubmoduleor@endmodulebefore starting a@condblock, and reopen the group after@endcondif necessary.Avoid C-style inline comments inside of
@codeblocks.No:
/// @code{.cpp} /// Layout MyGetLayoutFromPointer(const void* ptr) { /* ... */ } /// @endcode
Yes:
/// @code{.cpp} /// Layout MyGetLayoutFromPointer(const void* ptr) { /// … /// } /// @endcode
Note
Pigweed does a lot of customization to force Doxygen to organize everything
along the lines of Pigweed modules. The module-based organization is mainly
achieved through the @module and @submodule groups defined in
docs/doxygen/modules.h. These are custom aliases; see
docs/doxygen/Doxyfile to understand what the aliases expand to. Most
grouping errors are fixed by using @module and @submodule properly.
Headers occasionally use explicit start and end markers (@{ and @}) to
create custom groups, which can also lead to grouping errors.
Explicit link request to … could not be resolved#
Use
@codeand@endcodeblocks instead of inline backticks to demonstrate the code.- /// To use, add ``using ::pw::operator""_b;`` + /// To use, add: + /// @code + /// using ::pw::operator""_b; + /// @endcode
Note
Doxygen’s autolink feature might try to resolve links for operator names
(like operator""_b) even when they are in inline code backticks, and
fail if it cannot resolve them.
Found ')' without opening '(' for trailing return type#
Found documented return type for … that does not return anything#
Do not use
@returnor@returnsfor constructors, destructors, or functions returningvoid.
Found recursive @copybrief or @copydoc relation for argument …#
Break the cycle by documenting one of the entities directly instead of using
@copydocor@copybrief, and have the other entity copy from the now-directly documented one.
Found unknown command#
Check Special Commands to verify that the command is valid.
Use
@p <name>to refer to a parameter, or wrap<name>in single backticks. Do not use@<name>because Doxygen will interpret it as a command.- /// Creates a combined view of a @code_point and its encoded @size. + /// Creates a combined view of a @p code_point and its encoded `size`.
Do not use
@important. Use@attentionor@warning`instead.@importantwas introduced in a later version of Doxygen than what Pigweed uses.- /// @important This is... + /// @attention This is...
Include file … not found#
Make sure that all inputs that Doxygen depends on are explicitly declared in the
doxygentarget of a relevantBUILD.bazelfile.
Missing title after \defgroup#
Usually,
defgroupshould not be used at all. Instead, define a@submodulein//docs/doxygen/modules.h.If the
defgroupmust exist in the header for some reason, adding a title after the group ID should fix the missing title warning.- /// @defgroup foo + /// @defgroup foo Foo
No uniquely matching class member found#
If the error is related to a
frienddeclaration, wrap thefriendline in@condand@endconddirectives and add a comment explaining the suppression.+ // Suppress `no uniquely matching class member found` Doxygen error + /// @cond friend typename Base::Base; + /// @endcond
Note
This error occurs when Doxygen’s parser fails to resolve a type or member
name, often in friend declarations involving nested template aliases (e.g.
friend typename Base::Base;) or common names (e.g.
friend internal::Channel<T>;). Because Doxygen doesn’t require friend
declarations to generate public API documentation, you can safely hide them
from Doxygen.
Refusing to add group … to itself#
Look for an extra
@moduleor@submodule. E.g. 2@moduleannotations but only 1@endmodule.
Unbalanced grouping commands#
Check if another file in the same
@moduleor@submodulegroup is not explicitly closing with@endmoduleor@endsubmodule. The error may not actually be in the file that Doxygen is reporting.
Unsupported xml/html tag#
Wrap any text that uses angle brackets in single backticks to format it as inline code. Otherwise Doxygen will interpret the text as inline HTML or XML.
- /// Container-based version of the <algorithm> functions + /// Container-based version of the `<algorithm>` functions