Purpose
-------
Workspace-mode orchestrator for ``oct docs``. When the docs command is
invoked from a workspace root (a directory containing
``.option_c_workspace.json``), this module fans out the build across
each subproject, builds the workspace-tier "Option C" Sphinx tree from
the spec markdown, and aggregates everything under
``<workspace>/_site/`` with a Jinja-rendered front page.
Single-project invocations (from inside ``oct/`` or ``oc_diagnostics/``,
or from any non-workspace directory) bypass this module entirely - the
existing single-project pipelines in :mod:`oct.docs.oct_docs` handle
those unchanged.
Responsibilities
----------------
- Detect workspace mode at the dispatcher boundary and refuse to run
outside a real workspace root.
- Walk ``workspace.subprojects`` from the manifest and invoke the
single-project Sphinx / Doxygen pipelines for each, suppressing
recursion via ``no_workspace=True``. A subproject whose manifest entry
sets ``html_docs: false`` is skipped and its aggregated output
withdrawn; ``--clean`` still sweeps it, because opting out of building
is not opting out of tidying up.
- Keep a subproject's Doxygen off the site when its manifest entry sets
``html_docs: {"doxygen": false}`` (2026-09-28) or it has no Doxyfile:
the fan-out neither builds nor aggregates it, and any
``_site/<sp>/doxygen/`` an earlier run left is withdrawn in every build
mode. A direct ``oct docs --doxygen`` inside the subproject is not
routed through here and still builds.
- Build the Option C tier Sphinx tree from
``<workspace>/docs/option_c_specs/*.md`` plus the workspace
``README.md`` as the toctree root (Phase D).
- Aggregate every per-subproject ``docs/_build/html/`` into
``_site/<sp>/sphinx/html/``; same for Doxygen output.
- Render the front page via :mod:`oct.docs.front_page` (Phase B).
- Implement ``--clean`` semantics that strip ``_site/`` and every
subproject's ``docs/_build/`` (Phase F).
Diagnostics
-----------
Domain: OCT-DOCS
Levels:
L2 - orchestrator entry / exit, mode dispatch
L3 - per-subproject build dispatch, aggregation steps
L4 - per-file copy decisions
Contracts
---------
- ``run_workspace_docs`` is the single entry point. It must never be
called from inside a subproject root - the dispatcher in
:mod:`oct.docs.oct_docs` is responsible for that gate.
- The function returns a non-zero exit code on any sub-build failure;
failures in one subproject do not abort the others, so the user sees
every problem in one run.
- Aggregation uses :func:`shutil.copytree` not symlinks (Windows-safe).
Dependencies
------------
- oct.core.workspace (manifest loader, find_workspace_root)
- oct.core.diagnostics (_dbg structured logger)
- oct.tools.fs_remove (remove_tree_or_warn - clears aggregated and generated output, warning on a tree that will not go)
- oct.docs.front_page (lazy; render_front_page, FrontPageError - the hub front page)
- oct.docs.oct_docs (lazy; run_docs_sphinx, run_docs_doxygen, _find_doxyfile - the single-project pipelines)
- oct.docs.option_c_tier (lazy; build_option_c_tier - the workspace-tier Option C Sphinx tree)
- oct.docs.typedoc (lazy; has_typedoc, run_docs_typedoc, typedoc_output_dir - the TypeDoc tier)
| str | None oct.docs.workspace_docs._doxygen_withheld |
( |
Subproject | sp | ) |
|
|
protected |
Return why *sp* has no Doxygen on the site, or ``None`` when it has.
Two causes share one consequence. The manifest switch
``html_docs: {"doxygen": false}`` keeps a declared Doxygen tier off the
site, and a subproject with no Doxyfile has no Doxygen tier to publish.
Either way the fan-out builds nothing and aggregates nothing, and any
tree an earlier run left under ``_site/<sp>/doxygen/`` is withdrawn, so
one answer serves the build step and the aggregation step alike. The
reason is worded to follow ``has`` in the printed line.
Definition at line 120 of file workspace_docs.py.