CMake modules

Extracted from #[==[.rst: bracket comments in each .cmake module (D3) via .. cmake-module::, in the order they are include()d by cmake/zdocs.cmake. Nothing below is hand-written prose about the CMake surface — it is the bracket comments themselves, rendered.

common.cmake

Shared helpers used by every other zdocs CMake module: add_doc_target (the internal <name>/<name>-nodeps target-pair helper), zdocs_resolve_docdir() (the DOCDIR resolution rule add_sphinx_target/add_doxygen_target share), the build-stage aggregate targets (doc-tags, doc-index, all-docs, clean-docs) every document factory contributes to, and add_doc_check(), the cross-reference integrity gate.

zdocs_resolve_docdir
zdocs_resolve_docdir(<out_var> <caller_dir> <docdir> <default_subdir>)

Shared DOCDIR resolution for add_sphinx_target() and add_doxygen_target(). Sets <out_var> in the caller’s scope (PARENT_SCOPE) to:

  • <caller_dir>/<default_subdir> when <docdir> is the empty string — the pre-DOCDIR default, so a caller that never passes one sees no behaviour change.

  • <docdir> itself, unchanged, when it is already absolute (IS_ABSOLUTE).

  • <caller_dir>/<docdir> otherwise.

Tested with STREQUAL "" rather than if(docdir), deliberately: CMake reads "0", "OFF", "NO", "FALSE" and anything ending in -NOTFOUND as false, so the short form would silently fall back to the default for a folder literally named off, or for a docdir that came back from a failed find_path as ..-NOTFOUND.

add_doc_check
add_doc_check(REGISTRY <documents.yaml>)

Wires scripts/doccheck.py — the cross-reference integrity gate — as a standalone doc-check target AND as a POST_BUILD step of all-docs, so an ordinary build already runs it. REGISTRY is the only argument, and is required; the checks read the built deploy/ tree, which the engine locates itself.

Catches the failure mode this engine is prone to: a cross-reference that breaks WITHOUT breaking the build — a removed document leaving a reference that quietly degrades to plain text, or a parse-time role whose peer inventory was not ready emitting nothing at all.

doxygen.cmake

Drives Doxygen for one document via add_doxygen_target(): the generated-doxyfile overlay pattern (a consumer Doxyfile.in is expanded, then the engine-owned keys — HTML_OUTPUT, GENERATE_TAGFILE, GENERATE_XML, the theme, cross-document TAGFILES, path stripping — are appended so Doxygen’s “last value of a repeated key wins” rule makes them authoritative), the two-stage tag-file build, and the builder-first deploy layout (deploy/html/<id>/, deploy/xml/<id>/) — both keyed on the document’s registry id, verbatim.

add_doxygen_target
add_doxygen_target(<name>
                    [REGISTRY <documents.yaml>]
                    [DOXYFILE_IN <path>]
                    [DOCDIR <dir>])

Declares one Doxygen document named <name>, taken verbatim — <name> IS the document’s registry id; a consumer that wants it to carry a dox- prefix (or any other convention) writes that into the id itself and gets it back unchanged in the source folder, the deploy paths and every target name. Requires find_package(Doxygen REQUIRED), which this module runs itself.

REGISTRY

Path to documents.yaml. When given, this document’s inter-doxygen TAGFILES and its project-scoped doxygen_xml: opt-in are derived from it; omitted, no cross-document tag links and XML stays off.

DOXYFILE_IN

Template to configure (@ONLY). Defaults to Doxyfile.in inside the (possibly DOCDIR-relocated) source folder; pass an explicit path to document this project from another project’s own template (e.g. Zephyr’s zephyr.doxyfile.in).

DOCDIR

Relocates only where the sources (Doxyfile.in, mainpage.md, …) are read from; the deploy folder (deploy/html/<name>/) and every target name stay keyed on <name>. See zdocs_resolve_docdir().

sphinx.cmake

Drives Sphinx for one document via add_sphinx_target(): a two-stage build (a xref-builder cross-reference index pass, then one pass per requested builder), the sphinx-git .git pointer for the copied source tree, and the builder-first deploy layout (deploy/<builder>/<doc>/).

add_sphinx_target
add_sphinx_target(<doc_name>
                   BUILDERS <b1> [<b2> ...]
                   [REGISTRY <documents.yaml>]
                   [TAGS <t1> [<t2> ...]]
                   [DEPENDS <doc1> [<doc2> ...]]
                   [DOCDIR <dir>])

Declares one Sphinx document. <doc_name> is both the target-name stem (<doc_name>-<builder>, <doc_name>-<builder>-nodeps, <doc_name>-index) and the registry key; its sources live in <caller dir>/<doc_name>/ unless DOCDIR relocates only where they are read from.

BUILDERS (required, non-empty)

Sphinx builders to run, e.g. html or html latex. Only the html builder’s target joins all-docs.

REGISTRY

Path to documents.yaml. When given, this document’s intersphinx mapping, external needs and version scope are derived from it; omitted, the document builds standalone with no cross-document links.

TAGS

Extra Sphinx tags (-t), appended after ZDOCS_DOC_TAG.

DEPENDS

Other document ids this one actually cross-references, ordering the stage-1 index builds to reduce transient warnings on a from-scratch parallel build.

DOCDIR

Relocates where the sources are read from (resolved against the caller’s directory, or used as-is if absolute); everything else — target names, deploy path, registry key — stays keyed on <doc_name>. See zdocs_resolve_docdir().

registry.cmake

add_docs_from_registry() reads documents.yaml (via docrefs.py manifest) and dispatches one add_sphinx_target() or add_doxygen_target() call per document, so a consumer’s CMakeLists.txt never hand-writes one factory call per document. It also creates the per-(group, builder) aggregate targets (<group>-<builder>/<group>-<builder>-nodeps) documented on add_docs_from_registry() itself.

add_docs_from_registry
add_docs_from_registry(REGISTRY <documents.yaml>)

Declares every document in REGISTRY as a CMake target, dispatched by kind:

  • sphinx (or omitted) — add_sphinx_target() (<id> BUILDERS <builders...> REGISTRY <REGISTRY> [DOCDIR ...]). A configure-time FATAL_ERROR naming the document if builders: is empty or missing. A document with doxygen_tag: also gets <id>-needstag, which writes its needs as a Doxygen tag file after its stage-1 index (in doc-index, not doc-tags).

  • doxygen — add_doxygen_target() (<id> REGISTRY <REGISTRY> [DOCDIR ...]).

  • external / sphinx-external — no CMake target of any kind.

  • doxygen-external — exactly one target, <id>-tag, that downloads remote-tagfile: at build time.

Also creates, per distinct (group, builder) pair with at least one contributing document, two aggregate targets: <group>-<builder> (depends on every contributor’s own <id>-<builder>) and <group>-<builder>-nodeps (the same, against each -nodeps twin). A Doxygen document counts as builder html for this aggregation only; its own target is named after its registry id, exactly.

doc_dir: in the registry, when relative, is resolved against the directory containing REGISTRY itself — not against whichever CMakeLists.txt called this command.

zdocs.cmake

Single entry point. A consumer includes this after adding ${ZEPHYR_ZDOCS_MODULE_DIR}/cmake to CMAKE_MODULE_PATH; it in turn include()``s ``common, doxygen, sphinx and registry, which is what brings add_sphinx_target(), add_doxygen_target(), add_docs_from_registry() and add_doc_check() into scope. Requires ZDOCS_PROJECT_BASE to already be set; see the consumer configuration block below for every other ZDOCS_* variable it reads.

download_external_tag.cmake

Internal cmake -P wrapper, invoked as the COMMAND of an <id>-tag custom target created by add_docs_from_registry() for a kind: doxygen-external document. Downloads -DURL=<remote-tagfile> to -DDEST=<local path> (file(DOWNLOAD ...)) at BUILD time, so the fetch is refreshed on every run rather than baked in as a configure-time side effect. No public commands — the two -D arguments above are its whole interface, exercised only through the registry dispatcher.

run_doxygen.cmake

Internal cmake -P wrapper invoked by add_doxygen_target()’s two doxygen custom targets (stage 1’s tag build and stage 2’s full build), so that the git-derived PROJECT_NUMBER is recomputed at BUILD time on every run rather than baked in at configure time. Computes the version via docrefs.py version, writes a thin @INCLUDE overlay next to the given doxyfile that overrides PROJECT_NUMBER, then runs Doxygen on the overlay. No public commands — its whole interface is the -D arguments above, exercised only through add_doxygen_target().

check_doxygen_warnings.cmake

Internal cmake -P gate run after add_doxygen_target()’s stage-2 Doxygen build. Stage 2 writes its warnings to a log file instead of the console; this script echoes that log, so the console shows what it always did, then fails the target if any line matches one of ZDOCS_DOXYGEN_WARN_FAIL_PATTERNS.

Stage 2 only, deliberately. Stage 1 runs with TAGFILES cleared, so every reference into a peer document — including every \verifies and \satisfies against another project’s \requirement — warns there falsely; stage 1 is therefore silenced and never gated.

The gate exists because the Doxygen XML cannot tell a real requirement link from a typo: Doxygen synthesizes <requirement refid="requirement_<UID>"> from the UID string whether or not any \requirement defines it. Its warning is the only signal.