Repository navigation
[libc][docs] Generate implementation status dashboard - #241945
Merged
Merged
Conversation
The top-level headers/index.md page currently renders a plain static list of header names without indicating overall C23 or POSIX.1-2024 coverage or per-header completion across the library. Replace static headers/index.md with docgen.py --index (invoked by the CMake libc-docgen-index target) to generate top-level C23, POSIX.1-2024, and symbol-category completeness cards alongside colour-coded progress rows for every catalogued header, while hiding top-of-page source links on the aggregated index page in conf.py. Assisted-by: Automated tooling, human reviewed.
Member
Author
|
@llvm/pr-subscribers-libc Author: Jeff Bailey (kaladron) ChangesThe top-level headers/index.md page currently renders a plain static list of header names without indicating overall C23 or POSIX.1-2024 coverage or per-header completion across the library. Replace static headers/index.md with docgen.py --index (invoked by the CMake libc-docgen-index target) to generate top-level C23, POSIX.1-2024, and symbol-category completeness cards alongside colour-coded progress rows for every catalogued header, while hiding top-of-page source links on the aggregated index page in conf.py. Assisted-by: Automated tooling, human reviewed. Full diff: https://github.com/llvm/llvm-project/pull/241945.diff 6 Files Affected:
diff --git a/libc/docs/CMakeLists.txt b/libc/docs/CMakeLists.txt
index bd71375f98055..6d67aef96091d 100644
--- a/libc/docs/CMakeLists.txt
+++ b/libc/docs/CMakeLists.txt
@@ -97,7 +97,7 @@ if(LLVM_ENABLE_SPHINX)
foreach(stem IN LISTS docgen_list)
list(APPEND libc_generated_docs "headers/${stem}.md")
endforeach()
- list(APPEND libc_generated_docs configure.rst)
+ list(APPEND libc_generated_docs headers/index.md configure.rst)
add_sphinx_source_sync_target(copy-libc-rst-docs
"${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
PRESERVE_DOCS ${libc_generated_docs})
@@ -141,6 +141,11 @@ if(LLVM_ENABLE_SPHINX)
# depend on the docgen invocation.
add_dependencies(docs-libc-html ${docgen_target_name})
endforeach()
+ add_custom_target(libc-docgen-index
+ COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../utils/docgen/docgen.py --index >
+ ${CMAKE_CURRENT_BINARY_DIR}/headers/index.md)
+ add_dependencies(libc-docgen-index copy-libc-rst-docs)
+ add_dependencies(docs-libc-html libc-docgen-index)
endif()
endif()
endif()
diff --git a/libc/docs/_static/status.css b/libc/docs/_static/status.css
index 632f0df8ab237..66795308a1325 100644
--- a/libc/docs/_static/status.css
+++ b/libc/docs/_static/status.css
@@ -244,3 +244,28 @@ body[data-theme="dark"] {
.hc-mini-val { font-size: 1.1rem; font-weight: 700; margin: 3px 0 6px 0; }
.hc-mini-val span { font-size: 0.78em; font-weight: 600; }
.hc-subcard-title { font-size: 0.88rem; font-weight: 600; margin-bottom: 10px; }
+.hc-grid-standards { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; margin: 16px 0 12px 0; }
+.hc-grid-standards .hc-card { margin: 0; }
+.hc-std-title { font-size: 0.95rem; font-weight: 600; color: var(--color-foreground-muted, #57606a); }
+.hc-std-val { font-size: 1.85rem; font-weight: 700; margin: 4px 0 8px 0; }
+.hc-std-val span { font-size: 0.52em; font-weight: 500; color: var(--color-foreground-muted, #57606a); }
+.hc-headers-list { display: flex; flex-direction: column; gap: 8px; margin: 14px 0 24px 0; }
+.header-card { border: 1px solid var(--color-background-border, #e1e4e8); border-radius: 6px; background: var(--color-background-secondary, #f6f8fa); }
+.header-card > summary { list-style: none; display: flex; align-items: center; justify-content: space-between; padding: 9px 14px; gap: 12px; user-select: none; }
+.header-card > summary::-webkit-details-marker { display: none; }
+.header-card:has(.hc-drawer) > summary { cursor: pointer; }
+.header-card > summary:focus-visible, .hc-page-link:focus-visible { outline: 2px solid var(--hc-blue-text); outline-offset: 2px; }
+.hc-header-title-col { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; width: 240px; flex-shrink: 0; }
+.hc-thermometer-col { flex: 1; min-width: 140px; display: flex; align-items: center; gap: 10px; }
+.hc-thermometer-col .hc-progress-track { flex: 1; height: 10px; }
+.hc-row-pct { font-size: 0.85rem; font-weight: 700; width: 48px; text-align: right; }
+.hc-row-frac { font-size: 0.78rem; color: var(--color-foreground-muted, #57606a); width: 64px; text-align: right; }
+.hc-action-col { display: flex; align-items: center; flex-shrink: 0; }
+.hc-page-link { display: inline-flex; align-items: center; gap: 3px; padding: 3px 8px; border: 1px solid var(--color-background-border, #d0d7de); border-radius: 4px; font-size: 0.76rem; font-weight: 500; text-decoration: none; color: var(--color-foreground-secondary, #57606a); background: var(--color-background-primary, #fff); white-space: nowrap; }
+.hc-page-link:hover { border-color: var(--hc-blue-border); color: var(--hc-blue-text); text-decoration: none; }
+@media (max-width: 640px) {
+ .header-card > summary { flex-wrap: wrap; padding: 9px 12px; gap: 8px; }
+ .hc-header-title-col { width: auto; flex: 1 1 auto; min-width: 0; }
+ .hc-action-col { margin-left: auto; }
+ .hc-thermometer-col { width: 100%; flex: 1 1 100%; order: 3; }
+}
diff --git a/libc/docs/conf.py b/libc/docs/conf.py
index d20346799174b..b02b62bf9a706 100644
--- a/libc/docs/conf.py
+++ b/libc/docs/conf.py
@@ -255,6 +255,9 @@
def _configure_generated_header_links(app, pagename, templatename, context, doctree):
+ if pagename == "headers/index":
+ context["theme_top_of_page_buttons"] = []
+ return
if not pagename.startswith("headers/"):
return
stem = pagename[len("headers/") :]
diff --git a/libc/docs/headers/index.md b/libc/docs/headers/index.md
deleted file mode 100644
index 872a86a3cb049..0000000000000
--- a/libc/docs/headers/index.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# Implementation Status
-
-Purely compiler-provided freestanding C headers (`iso646.h`, `stdalign.h`,
-`stdarg.h`, `stdatomic.h`, `stdbool.h`, `stddef.h`, `stdnoreturn.h`, and
-`tgmath.h`) come from the host compiler (`Clang` or `GCC`) rather than
-LLVM-libc and are omitted below.
-
-```{toctree}
-:maxdepth: 1
-
-aio
-arpa/inet
-assert
-byteswap
-complex
-cpio
-ctype
-dirent
-dlfcn
-endian
-errno
-fcntl
-fenv
-float
-fmtmsg
-fnmatch
-ftw
-glob
-grp
-iconv
-inttypes
-langinfo
-libgen
-limits
-locale
-math
-monetary
-mqueue
-ndbm
-net/if
-netdb
-netinet/in
-netinet/tcp
-nl_types
-poll
-pwd
-regex
-sched
-search
-semaphore
-setjmp
-signal
-spawn
-stdbit
-stdckdint
-stdfix
-stdint
-stdio
-stdlib
-string
-strings
-sys/ipc
-sys/mman
-sys/msg
-sys/resource
-sys/select
-sys/sem
-sys/shm
-sys/socket
-sys/stat
-sys/statvfs
-sys/time
-sys/times
-sys/types
-sys/uio
-sys/un
-sys/utsname
-sys/wait
-syslog
-tar
-termios
-threads
-time
-uchar
-ulimit
-unistd
-utime
-utmpx
-wchar
-wctype
-pthread
-wordexp
-```
-
diff --git a/libc/utils/docgen/docgen.py b/libc/utils/docgen/docgen.py
index ae5b24d2c54ef..94b34b0be1b60 100755
--- a/libc/utils/docgen/docgen.py
+++ b/libc/utils/docgen/docgen.py
@@ -381,6 +381,26 @@ def _precision_grid_html(header_name: str, precisions: List[Dict]) -> str:
return f'<div class="hc-grid-prec">{cards}</div>'
+def _header_pills_html(stats: Dict, short: bool = False) -> str:
+ pills = []
+ if stats["standard"] == "c23":
+ lbl = "C23" if short else "C23 Standard"
+ pills.append(f'<span class="hc-pill hc-pill-c23">{lbl}</span>')
+ elif stats["standard"] == "iso-tr-18037":
+ lbl = "TR 18037" if short else "ISO/IEC TR 18037"
+ pills.append(f'<span class="hc-pill hc-pill-ext">{lbl}</span>')
+ if stats["posix"] == "posix-2024":
+ lbl = "POSIX" if short else "POSIX.1-2024"
+ pills.append(f'<span class="hc-pill hc-pill-posix">{lbl}</span>')
+ if not pills:
+ if stats["removed_posix"]:
+ lbl = "Removed" if short else "Removed in POSIX.1-2024"
+ else:
+ lbl = "Ext" if short else "Extension Header"
+ pills.append(f'<span class="hc-pill hc-pill-ext">{lbl}</span>')
+ return "".join(pills)
+
+
def print_summary_card(header: Header, api: Dict):
stats = header.get_summary_stats(api)
imp, tot = stats["overall"]["implemented"], stats["overall"]["total"]
@@ -388,19 +408,7 @@ def print_summary_card(header: Header, api: Dict):
return
pct = imp / tot * 100.0
tone = _tone(pct)
-
- pills = []
- if stats["standard"] == "c23":
- pills.append('<span class="hc-pill hc-pill-c23">C23 Standard</span>')
- elif stats["standard"] == "iso-tr-18037":
- pills.append('<span class="hc-pill hc-pill-ext">ISO/IEC TR 18037</span>')
- if stats["posix"] == "posix-2024":
- pills.append('<span class="hc-pill hc-pill-posix">POSIX.1-2024</span>')
- if not pills:
- ext_lbl = (
- "Removed in POSIX.1-2024" if stats["removed_posix"] else "Extension Header"
- )
- pills.append(f'<span class="hc-pill hc-pill-ext">{ext_lbl}</span>')
+ pills_html = _header_pills_html(stats)
cards = "".join(
_mini_card_html(
@@ -424,7 +432,7 @@ def print_summary_card(header: Header, api: Dict):
print(
"```{raw} html\n"
f'<div class="hc-card"><div class="hc-summary-top">'
- f'<div class="hc-pill-row">{"".join(pills)}</div>'
+ f'<div class="hc-pill-row">{pills_html}</div>'
f'<div class="hc-overall-stat">{imp:,} / {tot:,} '
f'<span style="color:var(--hc-{tone}-text)">({pct:.1f}%)</span></div></div>'
f'{overall_bar}<div class="hc-grid-cards">{cards}</div></div>'
@@ -503,15 +511,121 @@ def get_choices() -> list:
return sorted(choices)
+def _std_card_html(title: str, imp: int, tot: int, tone: str) -> str:
+ pct = (imp / tot * 100.0) if tot else 0.0
+ bar = _progress_bar_html(pct, title, tone, "hc-progress-lg")
+ return (
+ f'<div class="hc-card"><div class="hc-std-title">{title}</div>'
+ f'<div class="hc-std-val" style="color:var(--hc-{tone}-text)">'
+ f"{pct:.1f}% <span>({imp:,} / {tot:,})</span></div>{bar}</div>"
+ )
+
+
+def generate_index_md():
+ headers_data = []
+ totals = {
+ k: [0, 0]
+ for k in ("c23", "posix_spec", "functions", "macros", "types", "objects")
+ }
+ for hname in get_choices():
+ hdr = Header(hname)
+ check_api(hdr, hdr.api)
+ stats = hdr.get_summary_stats(hdr.api)
+ headers_data.append((hdr, hdr.api, stats))
+ for k, pair in totals.items():
+ pair[0] += stats[k]["implemented"]
+ pair[1] += stats[k]["total"]
+
+ n_c23 = sum(1 for _, _, s in headers_data if s["standard"] == "c23")
+ n_posix = sum(1 for _, _, s in headers_data if s["posix"] == "posix-2024")
+ c23_card = _std_card_html("C23 Standard Completeness", *totals["c23"], "green")
+ pos_card = _std_card_html(
+ "POSIX.1-2024 Completeness", *totals["posix_spec"], "blue"
+ )
+ cat_cards = "".join(
+ _mini_card_html("Overall", title, *totals[k], tone)
+ for k, title, tone in (
+ ("functions", "Functions", "green"),
+ ("macros", "Macros", "blue"),
+ ("types", "Types", "purple"),
+ ("objects", "Global Variables", "amber"),
+ )
+ )
+ print(
+ "(implementation_status)=\n\n(headers_index)=\n\n# Implementation Status\n\n"
+ f"Live symbol coverage across {len(headers_data)} headers ({n_c23} C23 "
+ f"standard headers, {n_posix} POSIX.1-2024 headers, and extensions), verified "
+ "directly against LLVM-libc source files, header generation specifications, "
+ "and exported entrypoints. Purely compiler-provided freestanding C headers "
+ "(`iso646.h`, `stdalign.h`, `stdarg.h`, `stdatomic.h`, `stdbool.h`, "
+ "`stddef.h`, `stdnoreturn.h`, and `tgmath.h`) come from the host compiler "
+ "(`Clang` or `GCC`) rather than LLVM-libc and are omitted below.\n\n"
+ "```{raw} html\n"
+ f'<div class="hc-grid-standards">{c23_card}{pos_card}</div>'
+ f'<div class="hc-grid-cards">{cat_cards}</div>\n```\n\n'
+ "## All Headers\n\n```{raw} html\n"
+ '<div id="headers-list" class="hc-headers-list">'
+ )
+ for hdr, api, stats in headers_data:
+ imp, tot = stats["overall"]["implemented"], stats["overall"]["total"]
+ pct = (imp / tot * 100.0) if tot else 0.0
+ std_tags = []
+ if stats["standard"] == "c23":
+ std_tags.append("c23")
+ if stats["posix"] == "posix-2024":
+ std_tags.append("posix")
+ if not std_tags:
+ std_tags.append("ext")
+ status_attr = (
+ "complete" if tot and imp == tot else ("inprogress" if imp else "planned")
+ )
+ syms = [
+ s
+ for sec in ("families", "functions", "macros", "types", "objects")
+ for s in (api.get(sec) or {})
+ ] + [
+ v
+ for fd in (api.get("families") or {}).values()
+ for v in ((fd or {}).get("variants") or {}).values()
+ if v
+ ]
+ stem = html.escape(hdr.stem, quote=True)
+ hname = html.escape(hdr.name, quote=True)
+ syms_attr = html.escape(" ".join(syms).lower(), quote=True)
+ bar = _progress_bar_html(pct, f"{hdr.name} implementation progress", _tone(pct))
+ print(
+ f'<details class="header-card" data-stem="{stem}" data-name="{hname}" '
+ f'data-standard="{" ".join(std_tags)}" data-status="{status_attr}" '
+ f'data-pct="{pct:.2f}" data-missing="{tot - imp}" '
+ f'data-symbols="{syms_attr}"><summary>'
+ f'<div class="hc-header-title-col"><code class="hc-sym"><{hname}>'
+ f"</code>{_header_pills_html(stats, short=True)}</div>"
+ f'<div class="hc-thermometer-col">{bar}'
+ f'<span class="hc-row-pct">{pct:.1f}%</span>'
+ f'<span class="hc-row-frac">({imp}/{tot})</span></div>'
+ f'<div class="hc-action-col"><a class="hc-page-link" href="{stem}.html" '
+ f'onclick="event.stopPropagation();" '
+ f'aria-label="Open {hname} status page">'
+ 'Page <span aria-hidden="true">↗</span></a></div>'
+ "</summary></details>"
+ )
+ stems = "\n".join(hdr.stem for hdr, _, _ in headers_data)
+ print(f"</div>\n```\n\n```{{toctree}}\n:hidden: true\n:maxdepth: 1\n\n{stems}\n```")
+
+
def parse_args() -> Namespace:
parser = ArgumentParser()
- parser.add_argument("header_name", choices=get_choices())
+ group = parser.add_mutually_exclusive_group(required=True)
+ group.add_argument("header_name", nargs="?", choices=get_choices())
+ group.add_argument("--index", action="store_true")
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
- header = Header(args.header_name)
- check_api(header, header.api)
-
- print_header_status_md(header, header.api)
+ if args.index:
+ generate_index_md()
+ else:
+ header = Header(args.header_name)
+ check_api(header, header.api)
+ print_header_status_md(header, header.api)
diff --git a/libc/utils/docgen/docgen_test.py b/libc/utils/docgen/docgen_test.py
index 33490ca8eddab..a34c63b6cd7c7 100644
--- a/libc/utils/docgen/docgen_test.py
+++ b/libc/utils/docgen/docgen_test.py
@@ -16,7 +16,13 @@
import unittest
sys.path.insert(0, str(Path(__file__).parent))
-from docgen import check_api, cppreference_url, posix_url, print_header_status_md
+from docgen import (
+ check_api,
+ cppreference_url,
+ generate_index_md,
+ posix_url,
+ print_header_status_md,
+)
from header import Header, _is_stub_file
@@ -385,6 +391,22 @@ def test_summary_stats(self):
self.assertGreater(fx_stats["tr18037"]["implemented"], 0)
self.assertEqual(fx_stats["c23"]["total"], 0)
+ def test_index_output_generation(self):
+ buf = io.StringIO()
+ with redirect_stdout(buf), redirect_stderr(io.StringIO()):
+ generate_index_md()
+ out = buf.getvalue()
+ self.assertIn("(implementation_status)=", out)
+ self.assertIn("(headers_index)=", out)
+ self.assertIn("C23 Standard Completeness", out)
+ self.assertIn("POSIX.1-2024 Completeness", out)
+ self.assertIn("Global Variables", out)
+ self.assertIn("`stdarg.h`", out)
+ self.assertIn('data-stem="stdio"', out)
+ self.assertIn('data-stem="arpa/inet"', out)
+ self.assertIn("acosf128", out)
+ self.assertIn("```{toctree}\n:hidden: true", out)
+
if __name__ == "__main__":
unittest.main()
|
lntue
approved these changes
Nov 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The top-level headers/index.md page currently renders a plain static list of header names without indicating overall C23 or POSIX.1-2024 coverage or per-header completion across the library.
Replace static headers/index.md with docgen.py --index (invoked by the CMake libc-docgen-index target) to generate top-level C23, POSIX.1-2024, and symbol-category completeness cards alongside colour-coded progress rows for every catalogued header, while hiding top-of-page source links on the aggregated index page in conf.py.
Assisted-by: Automated tooling, human reviewed.