From 546fa6d69086c40f6a8aaf7b0ed32d1011ea2a7c Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Mon, 4 May 2026 08:26:27 -0400 Subject: [PATCH] docs: auto-generate Makefile reference page for RTD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a Sphinx extension at docs/_source/_ext/makefile_targets.py that parses the project Makefile's '## description' annotations and writes docs/_source/api/makefile.rst at build time. Same convention 'make help' uses, so the reference stays in sync with the source. Generated page is grouped by target prefix (fetch-, ingest-, distill-, docs-, etc.) and rendered as a list-table. Shows on RTD alongside the autodoc API modules. Generated file is gitignored — RTD regenerates on every build. --- .gitignore | 3 ++ docs/_source/_ext/makefile_targets.py | 74 +++++++++++++++++++++++++++ docs/_source/conf.py | 4 +- docs/_source/index.rst | 1 + 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 docs/_source/_ext/makefile_targets.py diff --git a/.gitignore b/.gitignore index 8a50403..1b38079 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ bench/qa_results/ # Sphinx build artifacts (generated; RTD builds on its servers) docs/_source/_build/ + +# Auto-generated by docs/_source/_ext/makefile_targets.py at Sphinx build time +docs/_source/api/makefile.rst diff --git a/docs/_source/_ext/makefile_targets.py b/docs/_source/_ext/makefile_targets.py new file mode 100644 index 0000000..647da0a --- /dev/null +++ b/docs/_source/_ext/makefile_targets.py @@ -0,0 +1,74 @@ +"""Generate docs/_source/api/makefile.rst from Makefile ## annotations. + +Convention: every documented target has the form + target-name: deps ## one-line description + +This extension parses the project's Makefile and writes an RST page +listing every documented target, grouped by category prefix. +""" + +from pathlib import Path +import re + +TARGET_RE = re.compile(r"^([a-zA-Z0-9_-]+):.*?##\s*(.*)$") + + +def parse_makefile(makefile_path: Path) -> list[tuple[str, str]]: + """Return list of (target, description) tuples from a Makefile.""" + targets = [] + for line in makefile_path.read_text(encoding="utf-8").splitlines(): + m = TARGET_RE.match(line) + if m: + targets.append((m.group(1), m.group(2).strip())) + return targets + + +def categorize(targets: list[tuple[str, str]]) -> dict[str, list[tuple[str, str]]]: + """Group targets by leading prefix (e.g. 'fetch-', 'ingest-', 'docs-').""" + groups: dict[str, list[tuple[str, str]]] = {} + for name, desc in targets: + prefix = name.split("-", 1)[0] if "-" in name else name + groups.setdefault(prefix, []).append((name, desc)) + return groups + + +def generate_rst(targets: list[tuple[str, str]], output_path: Path) -> None: + """Write a single RST page listing every documented target.""" + groups = categorize(targets) + lines = [ + "Makefile reference", + "==================", + "", + "Every aborist workflow lives behind a ``make`` target. This page is", + "auto-generated from the project ``Makefile``'s ``## description``", + "annotations at Sphinx build time, so it stays in sync with the source.", + "", + "Run ``make help`` locally for the same listing.", + "", + ] + for prefix in sorted(groups): + lines.append(prefix) + lines.append("-" * len(prefix)) + lines.append("") + lines.append(".. list-table::") + lines.append(" :widths: 25 75") + lines.append(" :header-rows: 1") + lines.append("") + lines.append(" * - Target") + lines.append(" - Description") + for name, desc in sorted(groups[prefix]): + lines.append(f" * - ``{name}``") + lines.append(f" - {desc}") + lines.append("") + output_path.write_text("\n".join(lines), encoding="utf-8") + + +def setup(app): + """Sphinx hook: regenerate makefile.rst at the start of every build.""" + project_root = Path(app.srcdir).parent.parent + makefile = project_root / "Makefile" + output = Path(app.srcdir) / "api" / "makefile.rst" + if makefile.exists(): + targets = parse_makefile(makefile) + generate_rst(targets, output) + return {"version": "1.0", "parallel_read_safe": True} diff --git a/docs/_source/conf.py b/docs/_source/conf.py index adfd32d..56ba727 100644 --- a/docs/_source/conf.py +++ b/docs/_source/conf.py @@ -3,9 +3,10 @@ import sys from pathlib import Path -# Add aborist package to path +# Add aborist package + local _ext (Sphinx extensions) to path project_root = Path(__file__).parent.parent.parent sys.path.insert(0, str(project_root)) +sys.path.insert(0, str(Path(__file__).parent / "_ext")) # Project info project = "Aborist" @@ -20,6 +21,7 @@ extensions = [ "sphinx.ext.napoleon", "sphinx.ext.intersphinx", "sphinx.ext.viewcode", + "makefile_targets", # local: generates api/makefile.rst from Makefile ## annotations ] # Autodoc settings diff --git a/docs/_source/index.rst b/docs/_source/index.rst index 1d1d5b9..4cd4b95 100644 --- a/docs/_source/index.rst +++ b/docs/_source/index.rst @@ -9,6 +9,7 @@ Contents: :maxdepth: 3 :caption: API Modules + api/makefile api/substrate api/storage api/retrieval