Documentation Authoring Guide¶
How to add, edit, and organize documentation pages in DNDSR. The project uses a dual doc system: content lives in shared Markdown files consumed by both Doxygen and Sphinx.
Architecture overview¶
docs/
├── index.md # Sphinx root page (NOT seen by Doxygen)
├── doxygen/
│ ├── Doxyfile # Doxygen config (configure_file'd by CMake)
│ └── main.dox # Doxygen sidebar tree (@mainpage + @subpage)
├── sphinx/
│ ├── conf.py # Sphinx config (Breathe, MyST, theme)
│ ├── _ext/doxygen_compat.py # Translates @ref/@tableofcontents for MyST
│ ├── guides.md # Sphinx toctree stub for Guides section
│ ├── architecture.md # Sphinx toctree stub for Architecture
│ ├── theory.md # ...
│ ├── tests.md
│ ├── dev.md
│ ├── api_cpp.md # C++ API via Breathe
│ └── api_python.md # Python API via autodoc
├── guides/ # Shared content (both systems)
│ ├── building.md
│ ├── style_guide.md
│ └── ...
├── architecture/ # Shared content
├── theory/ # Shared content
├── tests/ # Shared content
├── dev/ # Shared content
├── reports/ # Legacy reports (excluded from both)
├── clean_doxygen_xml.py # Fixes malformed Doxygen XML
├── getAllAttachForDox.py # Copies image attachments for Doxygen
└── serve_docs.sh # Standalone doc server script
Every .md file under guides/, architecture/, theory/, tests/,
dev/ is consumed by both Doxygen and Sphinx. Doxygen reads them
natively; Sphinx reads them via MyST-Parser, and the doxygen_compat.py
extension translates Doxygen commands (@ref, @tableofcontents, etc.)
into MyST equivalents.
How content flows through both systems¶
Markdown file (docs/guides/my_page.md)
│
├──► Doxygen reads it directly
│ - {#anchor} becomes a page ID
│ - @ref, [create](#create) hyperlinks
│ - Output: build/docs/html/ (embedded at /doxygen/)
│
└──► Sphinx reads it via MyST-Parser
- doxygen_compat.py translates @ref → [anchor](#anchor)
- {#anchor} → (anchor)= label (clean sidebar titles)
- Output: build/docs/sphinx/
Adding a new documentation page¶
Step 1: Create the Markdown file¶
Place it in the appropriate directory under docs/:
Section |
Directory |
Example |
|---|---|---|
Guides |
|
|
Architecture |
|
|
Theory |
|
|
Tests |
|
|
Dev notes |
|
|
The file must start with a heading that includes a {#page_id} anchor:
(my_guide)=
# My New Guide
Content goes here...
The {#my_guide} anchor serves both systems:
Doxygen uses it as the page identifier for
@subpage/@ref.Sphinx uses it as a cross-reference label (translated to
(my_guide)=bydoxygen_compat.py).
Step 3: Register in the Sphinx toctree¶
Edit the corresponding Sphinx toctree stub in docs/sphinx/. For a
guide, edit docs/sphinx/guides.md:
\`\`\`{toctree}
:maxdepth: 2
/guides/building
/guides/style_guide
/guides/my_guide
\`\`\`
Paths are absolute from the Sphinx source root (docs/), so
/guides/my_guide resolves to docs/guides/my_guide.md.
Step 4: Build and verify¶
# Incremental build (only rebuilds changed files)
cmake --build build -t docs
# Serve and inspect
cmake --build build -t serve-docs
# Then open http://localhost:8000/ (Sphinx) and
# http://localhost:8000/doxygen/ (Doxygen) to verify both sidebars.
Sphinx toctree stubs (docs/sphinx/)¶
Each section has a stub file that defines the Sphinx sidebar tree using
MyST toctree directives:
Stub file |
Section |
|---|---|
|
Guides |
|
Architecture |
|
Theory |
|
Unit Tests |
|
Development Notes |
The stubs are referenced from docs/index.md (the Sphinx root):
\`\`\`{toctree}
:maxdepth: 2
:caption: Contents
/sphinx/guides
/sphinx/architecture
/sphinx/theory
/sphinx/tests
/sphinx/dev
/sphinx/api_cpp
/sphinx/api_python
\`\`\`
Rules:
Toctree paths are absolute from the source root (
docs/).The path
/guides/buildingresolves todocs/guides/building.md.Files not listed in any toctree generate an “orphan” warning.
The file must exist or sphinx-build will fail.
Cross-referencing between pages¶
In C++ docstrings¶
See the C++ Docstrings section of the
style_guide for @ref usage in source comments.
The doxygen_compat.py extension¶
This Sphinx extension (docs/sphinx/_ext/doxygen_compat.py) runs as a
source-read hook, rewriting Doxygen commands before MyST parses the
Markdown. Translations:
Doxygen command |
Sphinx/MyST equivalent |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See also: target |
|
|
|
|
|
fenced code block |
Build system targets¶
Target |
Behavior |
What it builds |
|---|---|---|
|
Incremental (stamp-based) |
Doxygen XML → Sphinx HTML + embedded Doxygen HTML |
|
Always runs |
Sphinx HTML (forces rebuild) |
|
Always runs |
Doxygen HTML + XML (forces rebuild) |
|
Incremental + prints serve command |
Full site |
|
Forces doxygen + prints serve command |
Doxygen standalone |
The docs target uses ninja dependency tracking:
C++ source changes → re-run doxygen → re-run sphinx
Markdown changes → re-run sphinx only (doxygen skipped)
No changes →
ninja: no work to do(instant)
Checklist for adding a page¶
Create
docs/<section>/my_page.mdwith# Title {#page_id}headingAdd
[page_id](#page_id)to the correct@pageblock indocs/doxygen/main.doxAdd
/section/my_pageto the correct toctree indocs/sphinx/<section>.mdRun
cmake --build build -t docsand verify both sidebarsCheck for orphan warnings in the build output