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.
74 lines
2.7 KiB
Python
74 lines
2.7 KiB
Python
"""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}
|