Skip to content

Registry and build plumbing (1/6) - #4

Merged
tobiaskaestner merged 5 commits into
mainfrom
tkaestner/1-registry-plumbing
Oct 2, 2026
Merged

tobiaskaestner merged 5 commits into
mainfrom
tkaestner/1-registry-plumbing

Conversation

@tobiaskaestner

@tobiaskaestner tobiaskaestner commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Part 1 of 6 of a stacked series. It is based on main; review the commits below only.

These changes come from moving a large consumer docset (Zephyr safety: about
15 documents, 5 Doxygen projects) onto zdocs.

  • crossref: false in the registry lets a document stay out of all link maps.
    It stays in the navigation. A full upstream API reference needs this: its
    symbols are a superset of the safety-scoped projects, so the two sides of a
    tag-file import defer the shared symbols to each other.
  • Stage 1 of a Doxygen build no longer prints warnings. They repeat in stage 2,
    where the tag files are there.
  • doccheck accepts findings by name from the registry (doc_check_accepted).
    Each accepted finding has a reason.
  • A sphinx-needs document can publish its needs as a Doxygen tag file
    (doxygen_tag: {types: [...]}). Then \verifies / \satisfies in Doxygen
    sources resolve against needs that live in RST.
  • Every Sphinx run gets the ZEPHYR_BASE that CMake found, also when the
    shell does not export it.

Note for the reviewer: the first commit's test now expects the relative link
form of #3 (../full-api/index.html).

Commits

  • 67ea011 feat: registry: let a document opt out of cross-referencing
  • 19158c3 fix: doxygen: silence warnings in stage 1
  • 15103f6 feat: doccheck: accept named findings from the registry
  • 0964911 feat: registry: publish a sphinx-needs document's needs as a Doxygen tag file
  • 24659a7 fix: sphinx: pass the found ZEPHYR_BASE to every Sphinx run

Testing

  • Unit suite (python3 -m pytest sphinx/_extensions/_tests -q) at the head of this PR: 172 passed.
  • The safety docset (about 15 documents, 5 Doxygen projects) builds clean on the top of the stack, with all its gates.
  • The acceptance steps for these changes are in zdocs-tests (one PR after this series). With the engine at the top of the stack, that suite passes 321 of 321.

The stack

  1. Registry and build plumbing (1/6) #4 Registry and build plumbing (this PR)
  2. Requirement links from Doxygen (2/6) #5 Requirement links from Doxygen
  3. Test report correctness (3/6) #6 Test report correctness
  4. Implementation layer and conditions (4/6) #7 Implementation layer and conditions
  5. Incremental rebuilds and see-also (5/6) #8 Incremental rebuilds and see-also
  6. Coverage adequacy (6/6) #9 Coverage adequacy

🤖 Generated with Claude Code

tobiaskaestner and others added 5 commits October 2, 2026 16:57
Every document is a link peer of every other: intersphinx, doxylink,
external needs and Doxygen TAGFILES are derived for all pairs. That is
wrong for a large reference build that documents a superset of its
peers' symbols, such as a project's full API beside a scoped subset.
When two Doxygen projects that both document a symbol import each
other's tag files, each defers the symbol to the other and neither
generates its page, leaving dead links on both sides.

Add a per-document `crossref: false`. Such a document is still built,
deployed and listed in the navigation, but it is removed from the link
graph in both directions: no peer gets an entry for it, and it gets none
for its peers. A quoted "false" is rejected at configure time instead of
being read as a truthy string.

First unit tests for docrefs: 6 tests over load(), tagfiles() and
validation. Against the unchanged code 5 fail; the sixth pins that the
navigation entry is kept, which was already true. Suite 147 -> 153.

Found on the Zephyr safety docset, where the full Zephyr API and the
safety-scoped API both document k_queue: 23 dead cross-project links,
all gone with the full API isolated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
Stage 1 runs Doxygen over the same inputs as stage 2, only to produce the
tag file and XML, so every warning was printed twice. Its copy is also
partly false: TAGFILES is cleared in stage 1, so every reference into a
peer document warns as unresolved. That makes it both noise and a hazard
for any consumer who sets WARN_AS_ERROR, because the warnings that count
are stage 2's.

Turn warnings off in the stage-1 overlay. WARNINGS = NO alone is not
enough: the WARN_IF_* switches warn independently of it and left 40 of
341 warnings on one document. With the full set, 0 remain and the tag
file is unchanged (same compounds). WARN_AS_ERROR = NO keeps stage 1
from ever failing the build on a consumer's setting.

On the Zephyr safety docset a full build goes from 3439 to 975 warning
lines (together with the consumer fixing its own stale aliases); the
test specification is unchanged. The acceptance suite (zdocs-tests) was
not available in that workspace and has not been run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
doc-check fails on any finding, including defects the consumer cannot
fix, such as a dead link inside a generated upstream API. The only way to
a passing build was then to stop running the check, which hides every
other finding along with it.

Add an optional registry list, doc_check_accepted, of findings to let
through, each with a required reason. Matching is on the exact printed
finding, never a pattern, so an acceptance cannot swallow a new, different
finding. Accepted findings no longer fail the check but are still printed
under "accepted (N)" with their reason. An entry that matches nothing is
reported as "accepted but no longer found" without failing, so fixing the
defect never breaks the build and the list does not go stale unnoticed.
A malformed entry exits 2.

First tests for doccheck: 5, run as real invocations against a small
deploy tree. Against the unchanged code 4 fail; the fifth pins that an
unaccepted dead link still fails. Suite 153 -> 158. The acceptance suite
(zdocs-tests) was not available and has not been run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
…tag file

A `kind: sphinx` document with `needs: {source: json}` can now declare
`doxygen_tag: true` or `doxygen_tag: {types: [...]}`. The new
`<id>-needstag` target (`docrefs.py needs-tag`) turns the document's
stage-1 needs.json into deploy/html/<id>/needs.tag, one
`<compound kind="requirement">` per need, and every doxygen peer lists it
in TAGFILES. `\verifies` and `\satisfies` then resolve against requirements
authored in reStructuredText and link to their Sphinx pages. An unknown
UID still trips the stage-2 "Reference to unknown requirement" gate.

The target joins doc-index but not doc-tags. Every Sphinx index waits on
doc-tags, so a Doxygen input derived from a Sphinx index could not be
built there without a cycle. Nothing needs the tag earlier, because
stage-1 Doxygen blanks its TAGFILES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
zdocs_conf loads Zephyr's doc extensions, including external_content,
which stages the sources, only when ZEPHYR_BASE is in the environment. Its
comment assumed find_package(Zephyr) exports it, but find_package sets only
the CMake variable. So a build worked only when the user's shell exported
ZEPHYR_BASE. In a fresh west workspace, a plain `cmake -S doc -B build`
staged nothing, and Sphinx failed with "unable to load the master
document" (found in the safety-toolbox standalone workspace).

Add ZEPHYR_BASE=${ZEPHYR_BASE} to SPHINX_ENV. Being explicit also
overrides a stale shell value that points at a different Zephyr.
Acceptance suite without ZEPHYR_BASE in the shell (Zephyr found via
CMAKE_PREFIX_PATH): 276/276. Before this change: test_05 errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
@tobiaskaestner
tobiaskaestner force-pushed the tkaestner/1-registry-plumbing branch from 24659a7 to 3ded634 Compare October 2, 2026 14:57
@tobiaskaestner
tobiaskaestner merged commit 1387c73 into main Oct 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant