Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,10 @@
"label": "Build documentation for a variant",
"detail": "Sphinx docs for one variant, without configuring or building the software",
"type": "shell",
"command": "${command:python.interpreterPath} -m sphinx -b html . build/docs/${input:variant}",
"command": "${command:python.interpreterPath} -m sphinx -b html -D needs_variant_data_file=build/variants/${input:variant}/${input:buildKit}/${input:docsTarget}.json . build/docs/${input:variant}",
"options": {
"env": {
"VARIANT": "${input:variant}",
"VARIANT_DATA_FILE": "build/variants/${input:variant}/${input:buildKit}/${input:docsTarget}.json"
"VARIANT": "${input:variant}"
}
},
"problemMatcher": []
Expand Down
81 changes: 48 additions & 33 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,21 @@ CMake side at a local checkout:

```bash
.venv/bin/python -m pip install -e ../spl-core --no-deps # point at the checkout
./build.sh --install # ...and back to the pinned release
./build.sh --install # ...and back to the pinned version
```

The path is deliberately **not** committed to `pyproject.toml`: CI must keep
resolving the released spl-core, so a local override never silently becomes the
build everyone gets. Land the spl-core change upstream, release it, and bump the
pin in `pyproject.toml` — the override is for the span of one change, not a
working mode.
A local path is never committed to `pyproject.toml`: every machine and CI must
resolve the same spl-core, so a local override never silently becomes the
build everyone gets. The override is for the span of one change, not a working
mode.

`pyproject.toml` currently pins a **commit of the useblocks fork** of spl-core
(branch `feat/configurable-docs-pipeline`, based on spl-core 8.9.0). It carries
the documentation changes this project relies on: `SPL_SOURCE_DOCS_JINJA_RAW_TAGS`,
`SPL_VARIANT_DATA_FILE_DOCS` / `_REPORTS`, `SPL_SPHINX_BINARY_DIR` and
`KConfig.declared_boolean_symbols()`. Pinning a commit keeps every build on the
same code. Switch back to a PyPI release once upstream spl-core has released
them.

### VS Code CMake Extension Configuration

Expand Down Expand Up @@ -165,15 +172,16 @@ Check feature values in source code via generated `autoconf.h` header.

## Variant-Dependent Documentation

[VARIANTS.md](VARIANTS.md) is the step-by-step guide to this, by use case, for people new to the
project. This section is the design behind it.

Documents never use Jinja. The global `source-read` pass that rendered every
document is gone, and bringing it back is a regression, not a shortcut.

One narrowly scoped `source-read` handler does exist, and it is not that.
spl-core passes `--jinja-raw-tags` to clanguru, so generated source listings
under `__source_docs` wrap their code in `{% raw %}` markers that nothing else
removes. `conf.py` blanks those two lines, for those docnames only: a line
filter, not a template render. It goes away when `pyproject.toml` can pin an
spl-core that lets the flag be turned off -- no released version does yet.
The generated source listings need no exception either. spl-core can have
clanguru wrap their code in Jinja `{% raw %}` markers for projects that do
render through Jinja; `CMakeLists.txt` turns that off
(`SPL_SOURCE_DOCS_JINJA_RAW_TAGS`), so no `source-read` handler exists at all.

Everything variant-dependent is decided from **one file**: the variant data
that `tools/variant_data.py` writes, exposed as `var.*`. The governing rule:
Expand All @@ -183,8 +191,9 @@ that `tools/variant_data.py` writes, exposed as `var.*`. The governing rule:
A key that only `conf.py` knows is invisible to ubCode, `ubc` and a reviewer's
editor, so their view of the project silently disagrees with the build —
silently, because a condition a tool cannot evaluate gates content **off**
rather than failing. That is why `conf.py` reads the file and adds nothing to
it.
rather than failing. That is why `conf.py` does not touch the file at all:
sphinx-needs reads it, and which cell a build reads is a command-line override
(`-D needs_variant_data_file=...`), exactly as for `ubc`.

### The three mechanisms

Expand Down Expand Up @@ -291,23 +300,24 @@ Nothing is generated, no loop is edited, and nothing under `build/` is touched.
assistant.** The editor is configured to refuse it (`files.readonlyInclude`) and
`build/variants/GENERATED` says so on disk.

`generated/` is the configured variant's build directory, and today **nothing
reads it**. The report toctrees still glob `/build/**`, and `conf.py` narrows
the source set to the configured build so each glob resolves to one page.

That is a deferral, not the end state. A fixed path under `generated/` would be
better, and the configuration for it is already in place -- the rst parser
include and the `generated/...` entries in every variant rule. It cannot be
switched on from this repository: spl-core writes the gcovr tree at
`reports/html/<build-relative page path>/coverage/index.html` and looks its
report artifacts up in the same place, so moving the page that links to it
without moving the tree breaks every coverage link.

When spl-core does write the tree relative to the page, the `build/` forwarding
in `conf.py` and the `generated` entry in its `exclude_patterns` have to go in
the *same* change, or Sphinx discovers every report page under both names.
`test_generated_and_build_discovery_are_never_both_live` fails if only half of
that is done.
`generated/` is the configured variant's build directory: a symlink (a junction
on Windows without Developer Mode) that `tools/variant_data.py` points at the
build CMake configures. `CMakeLists.txt` hands it to spl-core as
`SPL_SPHINX_BINARY_DIR`, so every page spl-core generates is named through it:
`generated/components/<c>/reports/coverage`, whatever the variant, kit or build
type. The report sections therefore name their pages directly, spl-core writes
each coverage report next to its page, and `SplBuild` finds the report
artifacts where the pages landed.

The Sphinx build reads generated pages **only** through `generated`: `conf.py`
prunes `build` from its walk, so each page has exactly one name.
`test_generated_is_the_only_route_to_the_generated_pages` guards that. Because
the link is re-pointed on every configure, spl-core stops a documentation build
whose link leads to another build directory. Configure that build again first.

ubCode does not descend the link, so the IDE shows no generated page. They are
output of the reports target, which stays Sphinx-only while its pages use the
sphinx-test-reports directive.

Regenerate without a compiler — KConfig is pure Python, and CMake's top-level
`project()` call demands a C toolchain before it will configure at all:
Expand All @@ -318,12 +328,17 @@ python tools/variant_data.py --variant Sleep --kit test # ...and point at one ce
python tools/variant_data.py --all --check # CI: regenerate and diff
```

Preview a variant by pointing the build at its cell:
Preview a variant by pointing the build at its cell. It is a command-line
override of the same key `ubc check -c` overrides, and the way spl-core selects
the cell for the docs and reports targets; `conf.py` has no say in it:

```bash
VARIANT_DATA_FILE=build/variants/Sleep/test/docs.json sphinx-build -b html . out
sphinx-build -b html -D needs_variant_data_file=build/variants/Sleep/test/docs.json . out
```

Without the override, a build reads the pointer `build/autoconf.json`, which
exists once CMake or `tools/variant_data.py --variant ... --kit ...` has written it.

## Project-Specific Conventions

1. **No direct CMake invocation**: Always use `build.ps1` wrapper (handles variant selection, environment, Poetry, etc.)
Expand Down
43 changes: 22 additions & 21 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,29 +90,30 @@ if(NOT _spled_variant_data_result EQUAL 0)
message(FATAL_ERROR "tools/variant_data.py failed:\n${_spled_variant_data_output}")
endif()

# Publish the configured variant's two cells under fixed names, so a Sphinx
# build can find the one matching its build shape without being told the variant
# or the kit. conf.py reads these; `build/autoconf.json` (the docs cell) stays
# the pointer ubCode reads, so the IDE and the docs build see identical data and
# the report fences resolve to a clean false in the IDE rather than being
# undecidable.
# What spl-core's documentation targets need to know about this project. All of
# it has to be set before parts.cmake adds the components, which is where
# spl-core computes the paths it writes for Sphinx.
#
# Fixed names rather than an environment variable because spl-core only passes
# one in from 8.9 onwards, and this project builds against the version pinned in
# pyproject.toml. Where that variable IS passed, conf.py prefers it.
configure_file(
${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/docs.json
${CMAKE_SOURCE_DIR}/build/variant-data-docs.json COPYONLY)
configure_file(
${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/reports.json
${CMAKE_SOURCE_DIR}/build/variant-data-reports.json COPYONLY)
# The variant data file for each build shape. spl-core hands it to sphinx-build
# as `-D needs_variant_data_file=`, so a reports build evaluates its fences
# against the reports cell. `build/autoconf.json`, the pointer ubCode and a bare
# sphinx-build read, always holds the docs cell, so the IDE sees the report
# fences as a clean false.
set(SPL_VARIANT_DATA_FILE_DOCS ${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/docs.json)
set(SPL_VARIANT_DATA_FILE_REPORTS ${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/reports.json)

# Honoured by spl-core 8.9+; harmlessly unused before that, which is why the
# fixed-name files above exist.
set(SPL_VARIANT_DATA_FILE_DOCS
${CMAKE_SOURCE_DIR}/build/variant-data-docs.json CACHE FILEPATH "" FORCE)
set(SPL_VARIANT_DATA_FILE_REPORTS
${CMAKE_SOURCE_DIR}/build/variant-data-reports.json CACHE FILEPATH "" FORCE)
# Sphinx reaches this build directory through `generated`, the link
# tools/variant_data.py has just pointed at it. The generated report pages are
# therefore named `generated/...` whatever the variant, kit or build type, and
# the report sections name them directly instead of globbing `/build/**`.
# spl-core writes each coverage report next to its page under that name, and
# stops a documentation build whose link has since been re-pointed at another
# build directory.
set(SPL_SPHINX_BINARY_DIR ${CMAKE_SOURCE_DIR}/generated)

# No document is rendered through Jinja, so the source listings clanguru
# generates need no `{% raw %}` armour.
set(SPL_SOURCE_DOCS_JINJA_RAW_TAGS OFF)

# The object_deps_report extension is currently Windows-only: its index.cmake
# hardcodes the runner as "object_deps_report.exe", which does not exist on
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ For the full testing strategy, marker definitions, and the gate assignment matri
./build.sh --selftests --filter Disco --marker gate_develop_push
```

## Variants and their documentation

What a variant contains, and what its documentation shows, is decided by data, not by templates.
[VARIANTS.md](VARIANTS.md) walks through it by use case: switching the variant ubCode shows,
previewing any variant, comparing two variants, trying a change without touching the product, and
writing documentation that depends on the variant.

## Developer Guide

For more information about the architecture, workflows, and conventions, see [AGENTS.md](AGENTS.md). This guide covers:
Expand Down
Loading