Skip to content

[25592] Reduce Read the Docs build time and fix version branch resolution (backport #1306) - #1310

Open
mergify[bot] wants to merge 1 commit into
3.2.xfrom
mergify/bp/3.2.x/pr-1306
Open

[25592] Reduce Read the Docs build time and fix version branch resolution (backport #1306)#1310
mergify[bot] wants to merge 1 commit into
3.2.xfrom
mergify/bp/3.2.x/pr-1306

Conversation

@mergify

@mergify mergify Bot commented Sep 2, 2026

Copy link
Copy Markdown

Description

Read the Docs builds of latest / 3.x were taking around 37 minutes (2248 s, 2199 s, 2244 s on recent builds). The suspicion was that the breathe-generated C++ API Reference was to blame. It is a large cost, but not the main one: the build was paying for the same work four times over.

Read the Docs runs a separate sphinx-build per output format, and readthedocs.yaml requested formats: all. The build log of a latest build shows where the 37 minutes go:

Step Time
python -m sphinx -b html 735.9 s
python -m sphinx -b singlehtml (htmlzip) 491.2 s
python -m sphinx -b latex (pdf) 463.9 s
python -m sphinx -b epub 392.9 s
latexmk 72.1 s
apt + pip + git setup ~70 s

93% of the wall time is those four Sphinx invocations. Since each one imports docs/conf.py again, the preparation done there also ran four times: the log contains Read the Docs environment detected!, Cloning Fast DDS, Cloning Fast DDS Python Bindings and Configuring Doxyfile 4x each, plus Removing existing repository in ... 6 times — runs 2 to 4 deleted and re-cloned Fast DDS (149 MB of history, no --depth) and Fast DDS Python, and re-ran doxygen and SWIG.

PR builds only took ~13 min because Read the Docs skips additional formats for external versions.

This PR contains four changes:

1. Build only HTML, and generate the downloadable documentation from it (readthedocs.yaml)

formats: all -> formats: [htmlzip]. The latex and epub runs and latexmk are removed: -929 s (~15.5 min). The PDF and epub downloads disappear from the Read the Docs flyout menu. htmlzip is kept, as it is the format that provides the downloadable documentation for offline reading.

By default, htmlzip is not a copy of the HTML output: Read the Docs generates it with an extra sphinx-build -b singlehtml run, that is, by building the whole documentation a second time (491.2 s in the log above). Since html is built first and its output is already the complete documentation, build.jobs.build.htmlzip is overridden to compress that output instead:

build:
  jobs:
    build:
      htmlzip:
        - mkdir -p $READTHEDOCS_OUTPUT/htmlzip
        - python -m zipfile -c $READTHEDOCS_OUTPUT/htmlzip/fast-dds-docs.zip $READTHEDOCS_OUTPUT/html

491.2 s -> ~1 s, and the downloaded file becomes the whole documentation site, navigation and search included, instead of a single page. Only the htmlzip job is overridden; every other format keeps its default command.

2. Stop generating doxygen output that nothing consumes (code/doxygen-config.in)

Tag Before After Reason
XML_PROGRAMLISTING YES NO 11.5 MB of the 32.3 MB XML that breathe parses on every build were <programlisting> elements. None of the 492 directives used in the API Reference renders program listings. XML: 33 MB -> 22 MB.
GENERATE_HTML YES NO 51 MB of standalone doxygen HTML was written to @PROJECT_BINARY_DIR@/html/doxygen and never published: html_extra_path is commented out, CMakeLists.txt installs only doxygen/xml, and no .rst file links into doxygen/.
SEARCHENGINE YES NO Only applies to the HTML output disabled above.

The API Reference is unaffected: breathe consumes the XML output, which is still generated.

3. Clone Fast DDS cheaply, and prepare it once per build (docs/conf.py)

resolve_remote_ref() resolves the branch or tag with git ls-remote, without downloading anything, so falling back to master when the remote does not have the wanted ref costs nothing. clone_repo_at_ref() then clones it with --depth 1 --single-branch, which is all doxygen and SWIG need — 4.5 MB and 1.6 s instead of 149 MB of history.

The shutil.rmtree + re-clone on every import is replaced by a reuse check: the clone, doxygen and SWIG steps are skipped when the repositories are already checked out at the expected commit (repo_is_at_commit(), comparing HEAD against the commit resolved above) and doxygen/xml/index.xml and the SWIG wrapper are already in place. So the htmlzip run reuses everything the html run prepared instead of repeating it.

4. Build the API Reference from the right branch (docs/conf.py)

Unrelated bug found in the same logs: on the latest (3.6.x) build, get_git_branch() returns None for the latest pseudo-name, so the log reads Current documentation branch could not be determined followed by Checking out Fast DDS branch "origin/master". The 3.6.x API Reference was therefore generated from Fast DDS master headers, and the GitHub links pointed at master.

get_git_branch() now prefers Read the Docs' READTHEDOCS_GIT_IDENTIFIER (the ref actually checked out, e.g. 3.6.x) for the latest and stable pseudo-names. The value is validated against the remote by change 3, so an unexpected value still falls back to master exactly as before.

Result

Before After
Sphinx run (local, same command, fresh doctrees) 405.2 s 338.7 s (-16.4%)
Doxygen 4.6 s 2.7 s
Doxygen XML parsed by breathe 33 MB 22 MB
Fast DDS clone 149 MB of history 4.5 MB, 1.6 s
Repository clone + doxygen + SWIG per build 4 times once
Downloadable documentation (htmlzip) 491.2 s ~1 s
latest / 3.x build on Read the Docs ~37 min ~11 min
PR build on Read the Docs ~13 min ~11 min

The estimate for latest is the html run with 16.4% off (~615 s), about a second of compression, and the ~70 s of environment setup.

Verification

  • colcon build --packages-select fastdds-docs succeeds, build succeeded with no warnings.
  • The API Reference output is unchanged: all 350 fastdds/api_reference HTML pages are byte-identical before and after the doxygen changes (51,136,889 bytes either way), compared with the same command and a fresh doctrees directory.
  • resolve_remote_ref() was exercised against the real remotes for an existing branch (3.6.x), an existing tag (v3.6.1), a ref that only tail-matches an unrelated branch, and an absent ref: the first two are used with their commit, the last two fall back to master without failing.
  • repo_is_at_commit() returns True only for a clone at the expected commit, and False, with a printed reason, for a clone at a different commit, a missing directory, a directory that is not a repository, and an unresolved commit.
  • The htmlzip commands were run against a real HTML output: 1.1 s to compress 98 MB into a 15 MB archive that unzips into a browsable site with the 559 pages, the 350 API Reference pages, the static assets, searchindex.js and the images. The build log confirms Read the Docs builds the html format first, so its output is in place when the htmlzip job runs.
  • The Read the Docs block was executed with the clone, doxygen and SWIG steps stubbed out, covering its four paths: nothing cloned yet, everything present at the expected commit (reused), present at a different commit (re-cloned and regenerated), and present at the right commit but without the doxygen XML (regenerated).
  • get_git_branch() was exercised for a latest build tracking 3.6.x (now resolves to 3.6.x), a latest build with no READTHEDOCS_GIT_IDENTIFIER (still None, unchanged), and an external PR build (still None, so the PR number is never used as a ref).

Not included

Moving fastdds/api_reference into its own Read the Docs subproject. It is the biggest remaining lever — the API Reference is 65% of each Sphinx run (262 s of 405 s locally; excluding it drops the build to 143 s) — but it requires intersphinx to keep the ~1000 :cpp: aliases in docs/03-exports/aliases-api.include resolving from the prose, so it is left for a separate discussion.

Contributor Checklist

  • Commit messages follow the project guidelines.
  • N/A Code snippets related to the added documentation have been provided.
  • Documentation tests pass locally.
  • N/A The Pro version badge has been added if the documented feature is exclusive to Fast DDS Pro.
  • Applicable backports have been included in the description.

Reviewer Checklist

  • The PR has a milestone assigned.
  • The title and description correctly express the PR's purpose.
  • Check contributor checklist is correct.
  • CI passes without warnings or errors.

This is an automatic backport of pull request #1306 done by [Mergify](https://mergify.com).

)

* Reduce Read the Docs build time and fix version branch resolution

Signed-off-by: Raul Sanchez-Mateos <raul@eprosima.com>

* Keep htmlzip format and verify the reused repositories are at the expected commit

Signed-off-by: Raul Sanchez-Mateos <raul@eprosima.com>

* Build the htmlzip download from the already built HTML

Signed-off-by: Raul Sanchez-Mateos <raul@eprosima.com>

---------

Signed-off-by: Raul Sanchez-Mateos <raul@eprosima.com>
(cherry picked from commit 3fee709)

# Conflicts:
#	docs/conf.py
@mergify mergify Bot added the conflicts label Sep 2, 2026
@mergify

mergify Bot commented Sep 2, 2026

Copy link
Copy Markdown
Author

Cherry-pick of 3fee709 has failed:

On branch mergify/bp/3.2.x/pr-1306
Your branch is up to date with 'origin/3.2.x'.

You are currently cherry-picking commit 3fee709.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	modified:   code/doxygen-config.in
	modified:   readthedocs.yaml

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   docs/conf.py

To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants