From c1cdc010026f3bc7313cfee526847f232162457c Mon Sep 17 00:00:00 2001 From: Dana Robinson Date: Sun, 18 Oct 2026 17:33:28 -0700 Subject: [PATCH] Make the documentation build without CMake docs/conf.py and the Doxygen configuration were templates that CMake filled in with configure_file(): the version, the Doxygen output directory, and Breathe's XML path. Run on their own, as Read the Docs runs them for rocm-systems projects, they didn't work. Both now work as committed, and the CMake build only sends their output to the build tree. - docs/Doxyfile.in is now docs/doxygen/Doxyfile, with paths relative to docs/doxygen. Running doxygen there writes the XML to docs/doxygen/xml/, which .gitignore ignores. STRIP_FROM_PATH = ../.. keeps the XML's file names relative to the repository root (include/hipobj.h). PROJECT_NUMBER is gone, since nothing that Breathe renders uses it. - docs/conf.py reads the version from the top-level CMakeLists.txt, and looks for the XML in doxygen/xml by default. - HIPOBJDocumentation.cmake writes a Doxyfile in the build tree that @INCLUDEs the committed one and changes only OUTPUT_DIRECTORY, and runs doxygen in docs/doxygen. It runs Sphinx on docs/ directly, and passes the XML's location in the build tree with -D breathe_projects.hipobj=..., so it no longer copies conf.py into the build tree. - The documentation's Python requirements moved from the top-level requirements.txt to docs/sphinx/requirements.txt, where ROCm projects keep them. The requirements.txt entries in the workflows' path filters are gone, since docs/** covers the new location, and the documentation check and deploy workflows also run when the top-level CMakeLists.txt changes, since conf.py reads the version from it. docs/building.rst describes the virtual environment and the build without CMake, AGENTS.md says to keep CMake substitutions out of both files, and CHANGELOG.md has an entry for the moves. Testing: built the documentation before and after the change with a docs-only CMake configuration. The Doxygen XML is the same apart from its record of the configuration, and the HTML from clean builds is identical apart from Sphinx's .buildinfo file. Running doxygen in docs/doxygen and then sphinx-build -W on docs, without CMake, produces the same HTML. black, pylint, vermin, cmakelint, actionlint, doc8, and codespell are clean. Co-authored-by: Claude --- .github/workflows/codeql.yml | 1 - .github/workflows/hipobject-build.yml | 1 - .github/workflows/hipobject-clang-tidy.yml | 1 - .../hipobject-documentation-check.yml | 3 +- .../hipobject-documentation-deploy.yml | 3 +- .github/workflows/hipobject-iwyu.yml | 1 - .gitignore | 1 + AGENTS.md | 12 ++++- CHANGELOG.md | 9 +++- cmake/HIPOBJDocumentation.cmake | 44 +++++++++---------- docs/building.rst | 15 +++++++ docs/conf.py | 34 +++++++++++--- docs/{Doxyfile.in => doxygen/Doxyfile} | 18 +++++--- .../sphinx/requirements.txt | 0 14 files changed, 102 insertions(+), 41 deletions(-) rename docs/{Doxyfile.in => doxygen/Doxyfile} (71%) rename requirements.txt => docs/sphinx/requirements.txt (100%) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index bf982d18..3cc28654 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,7 +21,6 @@ on: - '**/*.rst' - 'cmake/HIPOBJDocumentation.cmake' - 'docs/**' - - 'requirements.txt' push: branches: [ "develop" ] paths-ignore: *paths-ignore diff --git a/.github/workflows/hipobject-build.yml b/.github/workflows/hipobject-build.yml index be0b267f..647471d7 100644 --- a/.github/workflows/hipobject-build.yml +++ b/.github/workflows/hipobject-build.yml @@ -24,7 +24,6 @@ on: - '!ci/**' - '!cmake/HIPOBJDocumentation.cmake' - '!docs/**' - - '!requirements.txt' - '!.github/**' - '.github/workflows/hipobject-build.yml' - '.github/actions/install-rocm/**' diff --git a/.github/workflows/hipobject-clang-tidy.yml b/.github/workflows/hipobject-clang-tidy.yml index 490094cf..33456c3f 100644 --- a/.github/workflows/hipobject-clang-tidy.yml +++ b/.github/workflows/hipobject-clang-tidy.yml @@ -31,7 +31,6 @@ on: - '!ci/**' - '!cmake/HIPOBJDocumentation.cmake' - '!docs/**' - - '!requirements.txt' - '!.github/**' - '.github/workflows/hipobject-clang-tidy.yml' - '.github/actions/install-rocm/**' diff --git a/.github/workflows/hipobject-documentation-check.yml b/.github/workflows/hipobject-documentation-check.yml index f691a98b..722616b6 100644 --- a/.github/workflows/hipobject-documentation-check.yml +++ b/.github/workflows/hipobject-documentation-check.yml @@ -3,11 +3,12 @@ name: hipObject - Documentation Check on: pull_request: branches: [develop] + # docs/conf.py reads the version from the top-level CMakeLists.txt. paths: &paths - 'docs/**' - 'include/**/*.h' - 'README.md' - - 'requirements.txt' + - 'CMakeLists.txt' - 'cmake/HIPOBJDocumentation.cmake' - '.github/workflows/hipobject-documentation-check.yml' push: diff --git a/.github/workflows/hipobject-documentation-deploy.yml b/.github/workflows/hipobject-documentation-deploy.yml index 77bf1f10..b88f0720 100644 --- a/.github/workflows/hipobject-documentation-deploy.yml +++ b/.github/workflows/hipobject-documentation-deploy.yml @@ -10,10 +10,11 @@ name: hipObject - Documentation Deploy on: push: branches: [develop] + # docs/conf.py reads the version from the top-level CMakeLists.txt. paths: - 'docs/**' - 'include/**/*.h' - - 'requirements.txt' + - 'CMakeLists.txt' - 'cmake/HIPOBJDocumentation.cmake' - '.github/workflows/hipobject-documentation-deploy.yml' workflow_dispatch: diff --git a/.github/workflows/hipobject-iwyu.yml b/.github/workflows/hipobject-iwyu.yml index d15a905e..cadb97ce 100644 --- a/.github/workflows/hipobject-iwyu.yml +++ b/.github/workflows/hipobject-iwyu.yml @@ -26,7 +26,6 @@ on: - '!ci/**' - '!cmake/HIPOBJDocumentation.cmake' - '!docs/**' - - '!requirements.txt' - '!.github/**' - 'ci/iwyu/**' - '.github/workflows/hipobject-iwyu.yml' diff --git a/.gitignore b/.gitignore index 64db02d9..6f4c2bfd 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ tmp/ # Documentation build artifacts docs/_build/ +docs/doxygen/xml/ # Test artifacts Testing/ diff --git a/AGENTS.md b/AGENTS.md index 2492ecf2..c8d404c9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -757,8 +757,16 @@ Python code must run on **Python 3.10**. Don't use anything newer, such as CI: [hipobject-documentation-check.yml](.github/workflows/hipobject-documentation-check.yml), - when the documentation, the public header, `README.md`, `requirements.txt`, - the documentation's build files, or the workflow file change. + when the documentation (including `docs/sphinx/requirements.txt`), the + public header, `README.md`, the top-level `CMakeLists.txt` (which + `docs/conf.py` reads the version from), the documentation's build files, + or the workflow file change. +- **The documentation has to build without CMake too.** After the move to + rocm-systems, Read the Docs will run doxygen in `docs/doxygen` and Sphinx + on `docs/conf.py` directly, so keep CMake's `@VARIABLE@` substitutions + out of both files. The CMake build only redirects their output to the + build directory (see + [`HIPOBJDocumentation.cmake`](cmake/HIPOBJDocumentation.cmake)). ### Ansible diff --git a/CHANGELOG.md b/CHANGELOG.md index d3480036..a52079dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -226,7 +226,14 @@ dependency on a shared library they don't call, such as `libm`. - Building the documentation (`HIPOBJ_BUILD_DOCS`) requires Breathe 4.36 or later, up from 4.35. The documentation build installs it from - `requirements.txt` into its own virtual environment. + `docs/sphinx/requirements.txt` into its own virtual environment. +- The documentation's Python requirements moved from the top-level + `requirements.txt` to `docs/sphinx/requirements.txt`, and the Doxygen + configuration from `docs/Doxyfile.in` to `docs/doxygen/Doxyfile`. Both + `docs/doxygen/Doxyfile` and `docs/conf.py` now work without CMake, so + the documentation can be built by running Doxygen in `docs/doxygen` and + then Sphinx on `docs` (see "Building Documentation" in + `docs/building.rst`). The CMake build works as before. - The documentation build's targets are now `hipobj-sphinx-html`, `hipobj-doxygen`, and `hipobj-docs-venv`, rather than `sphinx-html`, `doxygen`, and `docs-venv`, so they can't collide with another diff --git a/cmake/HIPOBJDocumentation.cmake b/cmake/HIPOBJDocumentation.cmake index b8e87f7a..73501096 100644 --- a/cmake/HIPOBJDocumentation.cmake +++ b/cmake/HIPOBJDocumentation.cmake @@ -6,7 +6,12 @@ # (following ROCm best practices) # # A Python venv is created automatically in the build -# tree and populated from requirements.txt. +# tree and populated from docs/sphinx/requirements.txt. +# +# docs/doxygen/Doxyfile and docs/conf.py work without +# CMake too, as Read the Docs uses them, so nothing here +# configures them. The build only sends the output to the +# build tree. option(HIPOBJ_BUILD_DOCS "Build documentation (Sphinx + Breathe + Doxygen)" @@ -38,7 +43,7 @@ if(HIPOBJ_BUILD_DOCS) set(HIPOBJ_DOCS_VENV_STAMP "${HIPOBJ_DOCS_VENV}/stamp") set(HIPOBJ_DOCS_REQUIREMENTS - "${PROJECT_SOURCE_DIR}/requirements.txt") + "${PROJECT_SOURCE_DIR}/docs/sphinx/requirements.txt") if(WIN32) set(HIPOBJ_VENV_BIN @@ -73,33 +78,27 @@ if(HIPOBJ_BUILD_DOCS) # ── Paths ──────────────────────────────────────────── set(HIPOBJ_DOC_PATH "${PROJECT_BINARY_DIR}/docs") - set(BREATHE_DOC_XML_DIR - "${HIPOBJ_DOC_PATH}/xml") - - set(HIPOBJ_DOXYFILE_INPUT - "${PROJECT_SOURCE_DIR}/include") - - # Configure Doxyfile (substitutes @VARIABLES@) - configure_file( - ${PROJECT_SOURCE_DIR}/docs/Doxyfile.in - ${PROJECT_BINARY_DIR}/Doxyfile - @ONLY - ESCAPE_QUOTES - ) + set(HIPOBJ_DOXYGEN_DIR + "${PROJECT_SOURCE_DIR}/docs/doxygen") - # Configure conf.py (substitutes the version and Breathe XML path) - configure_file( - ${PROJECT_SOURCE_DIR}/docs/conf.py - ${PROJECT_BINARY_DIR}/docs-sphinx/conf.py + # A Doxyfile that reads the committed one and changes + # only where the output goes. Doxygen still has to run + # in docs/doxygen, which the committed file's relative + # paths are from. + file(CONFIGURE + OUTPUT ${PROJECT_BINARY_DIR}/Doxyfile + CONTENT [[ +@INCLUDE = "@HIPOBJ_DOXYGEN_DIR@/Doxyfile" +OUTPUT_DIRECTORY = "@HIPOBJ_DOC_PATH@" +]] @ONLY - ESCAPE_QUOTES ) # ── Doxygen target: source headers -> XML ──────────── add_custom_target(hipobj-doxygen COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + WORKING_DIRECTORY ${HIPOBJ_DOXYGEN_DIR} COMMENT "Generating Doxygen XML" VERBATIM ) @@ -107,11 +106,12 @@ if(HIPOBJ_BUILD_DOCS) # ── Sphinx target: RST + Doxygen XML -> HTML ───────── # -d keeps Sphinx's doctree cache out of the HTML # directory, which is published to GitHub Pages as is. + # -D points Breathe at the XML in the build tree. add_custom_target(hipobj-sphinx-html COMMAND ${SPHINX_BUILD} -b html -d ${HIPOBJ_DOC_PATH}/doctrees - -c ${PROJECT_BINARY_DIR}/docs-sphinx + -D breathe_projects.hipobj=${HIPOBJ_DOC_PATH}/xml ${PROJECT_SOURCE_DIR}/docs ${HIPOBJ_DOC_PATH}/html DEPENDS hipobj-doxygen hipobj-docs-venv diff --git a/docs/building.rst b/docs/building.rst index e971a6cd..6b384fb0 100644 --- a/docs/building.rst +++ b/docs/building.rst @@ -517,6 +517,21 @@ toolchain: cmake --build build --target hipobj-sphinx-html +The build installs Sphinx and the other Python packages +that ``docs/sphinx/requirements.txt`` lists into a virtual +environment in the build directory, which needs network +access the first time. + +The documentation also builds without CMake, with Doxygen +and the packages in ``docs/sphinx/requirements.txt`` +installed. Run Doxygen in ``docs/doxygen``, which writes its +XML to ``docs/doxygen/xml/``, and then Sphinx: + +.. code-block:: bash + + (cd docs/doxygen && doxygen Doxyfile) + sphinx-build -b html docs build/docs/html + Environment Variables --------------------- diff --git a/docs/conf.py b/docs/conf.py index 0cd2168e..e9052915 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,7 +5,14 @@ # Sphinx reads its settings from lowercase module-level names # pylint: disable=invalid-name -"""Sphinx configuration for hipObject documentation.""" +"""Sphinx configuration for hipObject documentation. + +Sphinx reads this file as it is, with or without CMake, so it has +to find everything it needs from its own directory. +""" + +import re +from pathlib import Path project = "hipObject" author = "Advanced Micro Devices, Inc." @@ -13,9 +20,23 @@ copyright = "2025-2026 Advanced Micro Devices, Inc. All rights reserved." # pylint: enable=redefined-builtin -# The project's version, from CMakeLists.txt, which configure_file() fills -# in (see cmake/HIPOBJDocumentation.cmake) -version = "@PROJECT_VERSION@" + +def _cmake_version(): + """Return the version that the top-level CMakeLists.txt sets.""" + cmake = (Path(__file__).resolve().parent.parent / "CMakeLists.txt").read_text( + encoding="utf-8" + ) + parts = [] + for part in ("MAJOR", "MINOR", "PATCH"): + match = re.search(rf"^set\(HIPOBJ_VERSION_{part} (\d+)\)$", cmake, re.MULTILINE) + if not match: + raise ValueError(f"HIPOBJ_VERSION_{part} not found in CMakeLists.txt") + parts.append(match.group(1)) + return ".".join(parts) + + +# The project's version, from CMakeLists.txt +version = _cmake_version() release = version # -- Extensions ---------------------------------------------- @@ -31,8 +52,11 @@ # -- Breathe (Doxygen XML import) ---------------------------- +# Where running doxygen in doxygen/ puts the XML. The CMake build +# writes it to the build directory, and passes that path with +# -D breathe_projects.hipobj=... breathe_projects = { - "hipobj": "@BREATHE_DOC_XML_DIR@", + "hipobj": "doxygen/xml", } breathe_default_project = "hipobj" breathe_default_members = ("members",) diff --git a/docs/Doxyfile.in b/docs/doxygen/Doxyfile similarity index 71% rename from docs/Doxyfile.in rename to docs/doxygen/Doxyfile index 3d7798f9..51af98e6 100644 --- a/docs/Doxyfile.in +++ b/docs/doxygen/Doxyfile @@ -1,18 +1,26 @@ # Doxyfile for hipObject -# Generated from docs/Doxyfile.in by CMake # # Doxygen generates XML consumed by Breathe; Sphinx # produces the final HTML output. +# +# Run doxygen in this directory, which the relative +# paths below are from. That writes the XML into +# xml/, where docs/conf.py looks for it. The CMake +# build includes this file from one of its own that +# sends the output to the build directory instead +# (see cmake/HIPOBJDocumentation.cmake). -PROJECT_NAME = "@PROJECT_NAME@" -PROJECT_NUMBER = "@PROJECT_VERSION@" +PROJECT_NAME = "hipobj" PROJECT_BRIEF = \ "RDMA-accelerated S3 object storage for GPUs" -OUTPUT_DIRECTORY = "@HIPOBJ_DOC_PATH@" -INPUT = "@HIPOBJ_DOXYFILE_INPUT@" +OUTPUT_DIRECTORY = . +INPUT = ../../include RECURSIVE = YES FILE_PATTERNS = *.h +# Name the files from the repository's root +# (include/hipobj.h), not from here +STRIP_FROM_PATH = ../.. EXTRACT_ALL = YES EXTRACT_PRIVATE = NO diff --git a/requirements.txt b/docs/sphinx/requirements.txt similarity index 100% rename from requirements.txt rename to docs/sphinx/requirements.txt