[libc-commits] [libc] [libc][docs] introduce docgen (PR #87682)

Nick Desaulniers via libc-commits libc-commits at lists.llvm.org
Thu Apr 4 14:20:12 PDT 2024


https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/87682

>From 4cfea46a345f53a4d5d86d6e3ca38496f9849064 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 4 Apr 2024 10:51:37 -0700
Subject: [PATCH 1/4] [libc][docs] introduce docgen

This script+config should help us generate more consistent documentation wrt.
what we currently support or not.

As an example usage:

    $ ./libc/utils/docgen/docgen.py fenv.h

Will spit out an RST formatted table that can be copy+pasted into our docs.

The config is not filled out entirely, but doing so and then updating our docs
would be great beginner bugs for new contributors.

Having python+yaml generate things like docs, or headers (as imagined in
https://github.com/nickdesaulniers/llvm-project/tree/hdr-gen2) is perhaps
easier to work with than tablegen, and doesn't introduce a dependency on a host
tool that needs to be compiled from llvm sources before building the rest of
the libc. This can probably be merged with whatever we end up doing to replace
libc-hdrgen.

Please use
https://llvm.org/docs/CodingStandards.html#python-version-and-source-code-formatting
for keeping this file formatted.
---
 libc/docs/fenv.rst          |  64 +++++++++++++++++++++
 libc/docs/index.rst         |   1 +
 libc/utils/docgen/api.yml   | 107 ++++++++++++++++++++++++++++++++++++
 libc/utils/docgen/docgen.py |  77 ++++++++++++++++++++++++++
 4 files changed, 249 insertions(+)
 create mode 100644 libc/docs/fenv.rst
 create mode 100644 libc/utils/docgen/api.yml
 create mode 100755 libc/utils/docgen/docgen.py

diff --git a/libc/docs/fenv.rst b/libc/docs/fenv.rst
new file mode 100644
index 00000000000000..b0518f7a0b257e
--- /dev/null
+++ b/libc/docs/fenv.rst
@@ -0,0 +1,64 @@
+.. include:: check.rst
+
+fenv.h Functions
+================
+
+.. list-table::
+  :widths: auto
+  :align: center
+  :header-rows: 1
+
+  * - Function
+    - Implemented
+    - Standard
+  * - fe_dec_getround
+    - |check|
+    - 7.6.5.3
+  * - fe_dec_setround
+    - |check|
+    - 7.6.5.6
+  * - feclearexcept
+    - |check|
+    - 7.6.4.1
+  * - fegetenv
+    - |check|
+    - 7.6.6.1
+  * - fegetexceptflag
+    - |check|
+    - 7.6.4.2
+  * - fegetmode
+    - |check|
+    - 7.6.5.1
+  * - fegetround
+    - |check|
+    - 7.6.5.2
+  * - feholdexcept
+    - |check|
+    - 7.6.6.2
+  * - feraiseexcept
+    - |check|
+    - 7.6.4.3
+  * - fesetenv
+    - |check|
+    - 7.6.6.3
+  * - fesetexcept
+    - |check|
+    - 7.6.4.4
+  * - fesetexceptflag
+    - |check|
+    - 7.6.4.5
+  * - fesetmode
+    - |check|
+    - 7.6.5.4
+  * - fesetround
+    - |check|
+    - 7.6.5.5
+  * - fetestexcept
+    - |check|
+    - 7.6.4.7
+  * - fetestexceptflag
+    - |check|
+    - 7.6.4.6
+  * - feupdateenv
+    - |check|
+    - 7.6.6.4
diff --git a/libc/docs/index.rst b/libc/docs/index.rst
index 370fcd843974e8..65ccb91e92ffe1 100644
--- a/libc/docs/index.rst
+++ b/libc/docs/index.rst
@@ -66,6 +66,7 @@ stages there is no ABI stability in any form.
    strings
    stdio
    stdbit
+   fenv
    libc_search
    c23
 
diff --git a/libc/utils/docgen/api.yml b/libc/utils/docgen/api.yml
new file mode 100644
index 00000000000000..3e5429f68cdf8c
--- /dev/null
+++ b/libc/utils/docgen/api.yml
@@ -0,0 +1,107 @@
+ctype.h:
+  functions:
+    isalnum:
+      returns: int
+      params:
+        - int
+    isalpha:
+      returns: int
+      params:
+        - int
+    isblank:
+      returns: int
+      params:
+        - int
+  # TODO: fill me out!
+fenv.h:
+  macros:
+    - __STDC_VERSION_FENV_H__
+  functions:
+    feclearexcept:
+      returns: int
+      params:
+        - int
+      defined: '7.6.4.1'
+    fegetexceptflag:
+      returns: int
+      params:
+        - fexcept_t*
+        - int
+      defined: '7.6.4.2'
+    feraiseexcept:
+      returns: int
+      params:
+        - int
+      defined: '7.6.4.3'
+    fesetexcept:
+      returns: int
+      params:
+        - int
+      defined: '7.6.4.4'
+    fesetexceptflag:
+      returns: int
+      params:
+        - const fexcept_t*
+        - int
+      defined: '7.6.4.5'
+    fetestexceptflag:
+      returns: int
+      params:
+        - const fexcept_t*
+        - int
+      defined: '7.6.4.6'
+    fetestexcept:
+      returns: int
+      params:
+        - int
+      defined: '7.6.4.7'
+    fegetmode:
+      returns: int
+      params:
+        - femode_t*
+      defined: '7.6.5.1'
+    fegetround:
+      returns: int
+      params: []
+      defined: '7.6.5.2'
+    fe_dec_getround:
+      returns: int
+      params: []
+      defined: '7.6.5.3'
+    fesetmode:
+      returns: int
+      params:
+        - const femode_t*
+      defined: '7.6.5.4'
+    fesetround:
+      returns: int
+      params:
+        - int
+      defined: '7.6.5.5'
+    fe_dec_setround:
+      returns: int
+      params:
+        - int
+      defined: '7.6.5.6'
+    fegetenv:
+      returns: int
+      params:
+        - fenv_t*
+      defined: '7.6.6.1'
+    feholdexcept:
+      returns: int
+      params:
+        - fenv_t*
+      defined: '7.6.6.2'
+    fesetenv:
+      returns: int
+      params:
+        - const fentv_t*
+      defined: '7.6.6.3'
+    feupdateenv:
+      returns: int
+      params:
+        - const fenv_t*
+      defined: '7.6.6.4'
+
+# TODO: yay fill out moar stuff!!!1
diff --git a/libc/utils/docgen/docgen.py b/libc/utils/docgen/docgen.py
new file mode 100755
index 00000000000000..db5cdf1d327b82
--- /dev/null
+++ b/libc/utils/docgen/docgen.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# ====- Generate documentation for libc functions  ------------*- python -*--==#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ==-------------------------------------------------------------------------==#
+from argparse import ArgumentParser
+from pathlib import Path
+import sys
+import yaml
+
+
+def load_api():
+    p = Path(Path(__file__).resolve().parent, "api.yml")
+    api = p.read_text(encoding="utf-8")
+    return yaml.load(api, Loader=yaml.FullLoader)
+
+
+# TODO: we may need to get more sophisticated for less generic implementations.
+# Does libc/src/{hname minus .h suffix}/{fname}.cpp exist?
+def is_implemented(hname, fname):
+    return Path(
+        Path(__file__).resolve().parent.parent.parent,
+        "src",
+        hname.rstrip(".h"),
+        fname + ".cpp",
+    ).exists()
+
+
+def print_functions(header, functions):
+    for key in sorted(functions.keys()):
+        print(f"  * - {key}")
+
+        if is_implemented(header, key):
+            print("    - |check|")
+        else:
+            print("    -")
+
+        if "defined" in functions[key]:
+            print(f'    - {functions[key]["defined"]}')
+        else:
+            print("    -")
+
+
+def print_header(header, api):
+    fns = f"{header} Functions"
+    print(fns)
+    print("=" * (len(fns)))
+    print(
+        f"""
+.. list-table::
+  :widths: auto
+  :align: center
+  :header-rows: 1
+
+  * - Function
+    - Implemented
+    - Standard"""
+    )
+    # TODO: how do we want to signal implementation of macros?
+    print_functions(header, api["functions"])
+
+
+def parse_args(header_choices):
+    parser = ArgumentParser()
+    parser.add_argument("header_name", choices=header_choices)
+    return parser.parse_args()
+
+
+if __name__ == "__main__":
+    api = load_api()
+    args = parse_args(api.keys())
+
+    print_header(args.header_name, api[args.header_name])

>From 7dc46ac82f8bc0a054f594b4301696292e2ee8dc Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 4 Apr 2024 12:09:04 -0700
Subject: [PATCH 2/4] actually use the generated output rather than test output
 from earlier

---
 libc/docs/fenv.rst | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libc/docs/fenv.rst b/libc/docs/fenv.rst
index b0518f7a0b257e..6574fb7246ddd2 100644
--- a/libc/docs/fenv.rst
+++ b/libc/docs/fenv.rst
@@ -12,10 +12,10 @@ fenv.h Functions
     - Implemented
     - Standard
   * - fe_dec_getround
-    - |check|
+    -
     - 7.6.5.3
   * - fe_dec_setround
-    - |check|
+    -
     - 7.6.5.6
   * - feclearexcept
     - |check|
@@ -27,7 +27,7 @@ fenv.h Functions
     - |check|
     - 7.6.4.2
   * - fegetmode
-    - |check|
+    -
     - 7.6.5.1
   * - fegetround
     - |check|
@@ -42,13 +42,13 @@ fenv.h Functions
     - |check|
     - 7.6.6.3
   * - fesetexcept
-    - |check|
+    -
     - 7.6.4.4
   * - fesetexceptflag
     - |check|
     - 7.6.4.5
   * - fesetmode
-    - |check|
+    -
     - 7.6.5.4
   * - fesetround
     - |check|
@@ -57,7 +57,7 @@ fenv.h Functions
     - |check|
     - 7.6.4.7
   * - fetestexceptflag
-    - |check|
+    -
     - 7.6.4.6
   * - feupdateenv
     - |check|

>From 2c30d1984683defeae021e6f5f58c5edae9fff2d Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 4 Apr 2024 14:10:58 -0700
Subject: [PATCH 3/4] split into individual headers

---
 libc/utils/docgen/api.yml   | 107 ------------------------------------
 libc/utils/docgen/ctype.yml |  14 +++++
 libc/utils/docgen/docgen.py |  18 +++---
 libc/utils/docgen/fenv.yml  |  91 ++++++++++++++++++++++++++++++
 4 files changed, 116 insertions(+), 114 deletions(-)
 delete mode 100644 libc/utils/docgen/api.yml
 create mode 100644 libc/utils/docgen/ctype.yml
 create mode 100644 libc/utils/docgen/fenv.yml

diff --git a/libc/utils/docgen/api.yml b/libc/utils/docgen/api.yml
deleted file mode 100644
index 3e5429f68cdf8c..00000000000000
--- a/libc/utils/docgen/api.yml
+++ /dev/null
@@ -1,107 +0,0 @@
-ctype.h:
-  functions:
-    isalnum:
-      returns: int
-      params:
-        - int
-    isalpha:
-      returns: int
-      params:
-        - int
-    isblank:
-      returns: int
-      params:
-        - int
-  # TODO: fill me out!
-fenv.h:
-  macros:
-    - __STDC_VERSION_FENV_H__
-  functions:
-    feclearexcept:
-      returns: int
-      params:
-        - int
-      defined: '7.6.4.1'
-    fegetexceptflag:
-      returns: int
-      params:
-        - fexcept_t*
-        - int
-      defined: '7.6.4.2'
-    feraiseexcept:
-      returns: int
-      params:
-        - int
-      defined: '7.6.4.3'
-    fesetexcept:
-      returns: int
-      params:
-        - int
-      defined: '7.6.4.4'
-    fesetexceptflag:
-      returns: int
-      params:
-        - const fexcept_t*
-        - int
-      defined: '7.6.4.5'
-    fetestexceptflag:
-      returns: int
-      params:
-        - const fexcept_t*
-        - int
-      defined: '7.6.4.6'
-    fetestexcept:
-      returns: int
-      params:
-        - int
-      defined: '7.6.4.7'
-    fegetmode:
-      returns: int
-      params:
-        - femode_t*
-      defined: '7.6.5.1'
-    fegetround:
-      returns: int
-      params: []
-      defined: '7.6.5.2'
-    fe_dec_getround:
-      returns: int
-      params: []
-      defined: '7.6.5.3'
-    fesetmode:
-      returns: int
-      params:
-        - const femode_t*
-      defined: '7.6.5.4'
-    fesetround:
-      returns: int
-      params:
-        - int
-      defined: '7.6.5.5'
-    fe_dec_setround:
-      returns: int
-      params:
-        - int
-      defined: '7.6.5.6'
-    fegetenv:
-      returns: int
-      params:
-        - fenv_t*
-      defined: '7.6.6.1'
-    feholdexcept:
-      returns: int
-      params:
-        - fenv_t*
-      defined: '7.6.6.2'
-    fesetenv:
-      returns: int
-      params:
-        - const fentv_t*
-      defined: '7.6.6.3'
-    feupdateenv:
-      returns: int
-      params:
-        - const fenv_t*
-      defined: '7.6.6.4'
-
-# TODO: yay fill out moar stuff!!!1
diff --git a/libc/utils/docgen/ctype.yml b/libc/utils/docgen/ctype.yml
new file mode 100644
index 00000000000000..c67084463963ef
--- /dev/null
+++ b/libc/utils/docgen/ctype.yml
@@ -0,0 +1,14 @@
+functions:
+  isalnum:
+    returns: int
+    params:
+      - int
+  isalpha:
+    returns: int
+    params:
+      - int
+  isblank:
+    returns: int
+    params:
+      - int
+# TODO: fill me out!
diff --git a/libc/utils/docgen/docgen.py b/libc/utils/docgen/docgen.py
index db5cdf1d327b82..a275d434e03907 100755
--- a/libc/utils/docgen/docgen.py
+++ b/libc/utils/docgen/docgen.py
@@ -13,8 +13,8 @@
 import yaml
 
 
-def load_api():
-    p = Path(Path(__file__).resolve().parent, "api.yml")
+def load_api(hname):
+    p = Path(Path(__file__).parent, Path(hname).with_suffix(".yml"))
     api = p.read_text(encoding="utf-8")
     return yaml.load(api, Loader=yaml.FullLoader)
 
@@ -64,14 +64,18 @@ def print_header(header, api):
     print_functions(header, api["functions"])
 
 
-def parse_args(header_choices):
+def get_possible_choices():
+    return [p.with_suffix(".h").name for p in Path(__file__).parent.glob("*.yml")]
+
+
+def parse_args():
     parser = ArgumentParser()
-    parser.add_argument("header_name", choices=header_choices)
+    parser.add_argument("header_name", choices=get_possible_choices())
     return parser.parse_args()
 
 
 if __name__ == "__main__":
-    api = load_api()
-    args = parse_args(api.keys())
+    args = parse_args()
+    api = load_api(args.header_name)
 
-    print_header(args.header_name, api[args.header_name])
+    print_header(args.header_name, api)
diff --git a/libc/utils/docgen/fenv.yml b/libc/utils/docgen/fenv.yml
new file mode 100644
index 00000000000000..83a55fae386d1d
--- /dev/null
+++ b/libc/utils/docgen/fenv.yml
@@ -0,0 +1,91 @@
+macros:
+  - __STDC_VERSION_FENV_H__
+functions:
+  feclearexcept:
+    returns: int
+    params:
+      - int
+    defined: '7.6.4.1'
+  fegetexceptflag:
+    returns: int
+    params:
+      - fexcept_t*
+      - int
+    defined: '7.6.4.2'
+  feraiseexcept:
+    returns: int
+    params:
+      - int
+    defined: '7.6.4.3'
+  fesetexcept:
+    returns: int
+    params:
+      - int
+    defined: '7.6.4.4'
+  fesetexceptflag:
+    returns: int
+    params:
+      - const fexcept_t*
+      - int
+    defined: '7.6.4.5'
+  fetestexceptflag:
+    returns: int
+    params:
+      - const fexcept_t*
+      - int
+    defined: '7.6.4.6'
+  fetestexcept:
+    returns: int
+    params:
+      - int
+    defined: '7.6.4.7'
+  fegetmode:
+    returns: int
+    params:
+      - femode_t*
+    defined: '7.6.5.1'
+  fegetround:
+    returns: int
+    params: []
+    defined: '7.6.5.2'
+  fe_dec_getround:
+    returns: int
+    params: []
+    defined: '7.6.5.3'
+  fesetmode:
+    returns: int
+    params:
+      - const femode_t*
+    defined: '7.6.5.4'
+  fesetround:
+    returns: int
+    params:
+      - int
+    defined: '7.6.5.5'
+  fe_dec_setround:
+    returns: int
+    params:
+      - int
+    defined: '7.6.5.6'
+  fegetenv:
+    returns: int
+    params:
+      - fenv_t*
+    defined: '7.6.6.1'
+  feholdexcept:
+    returns: int
+    params:
+      - fenv_t*
+    defined: '7.6.6.2'
+  fesetenv:
+    returns: int
+    params:
+      - const fentv_t*
+    defined: '7.6.6.3'
+  feupdateenv:
+    returns: int
+    params:
+      - const fenv_t*
+    defined: '7.6.6.4'
+
+# TODO: yay fill out moar stuff!!!1

>From 29213d3706085000192a4b2259c346e50c5a0ffc Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 4 Apr 2024 14:19:58 -0700
Subject: [PATCH 4/4] remove fn api for now

---
 libc/utils/docgen/ctype.yml | 16 +++--------
 libc/utils/docgen/docgen.py |  3 ++-
 libc/utils/docgen/fenv.yml  | 54 -------------------------------------
 3 files changed, 6 insertions(+), 67 deletions(-)

diff --git a/libc/utils/docgen/ctype.yml b/libc/utils/docgen/ctype.yml
index c67084463963ef..f8056fca35d4da 100644
--- a/libc/utils/docgen/ctype.yml
+++ b/libc/utils/docgen/ctype.yml
@@ -1,14 +1,6 @@
 functions:
-  isalnum:
-    returns: int
-    params:
-      - int
-  isalpha:
-    returns: int
-    params:
-      - int
-  isblank:
-    returns: int
-    params:
-      - int
+  isalnum: ~
+  isalpha: ~
+  isblank: ~
+
 # TODO: fill me out!
diff --git a/libc/utils/docgen/docgen.py b/libc/utils/docgen/docgen.py
index a275d434e03907..4e5fb977ff03fa 100755
--- a/libc/utils/docgen/docgen.py
+++ b/libc/utils/docgen/docgen.py
@@ -39,7 +39,8 @@ def print_functions(header, functions):
         else:
             print("    -")
 
-        if "defined" in functions[key]:
+        # defined is optional. Having any content is optional.
+        if functions[key] is not None and "defined" in functions[key]:
             print(f'    - {functions[key]["defined"]}')
         else:
             print("    -")
diff --git a/libc/utils/docgen/fenv.yml b/libc/utils/docgen/fenv.yml
index 83a55fae386d1d..00c7f3f766c000 100644
--- a/libc/utils/docgen/fenv.yml
+++ b/libc/utils/docgen/fenv.yml
@@ -2,90 +2,36 @@ macros:
   - __STDC_VERSION_FENV_H__
 functions:
   feclearexcept:
-    returns: int
-    params:
-      - int
     defined: '7.6.4.1'
   fegetexceptflag:
-    returns: int
-    params:
-      - fexcept_t*
-      - int
     defined: '7.6.4.2'
   feraiseexcept:
-    returns: int
-    params:
-      - int
     defined: '7.6.4.3'
   fesetexcept:
-    returns: int
-    params:
-      - int
     defined: '7.6.4.4'
   fesetexceptflag:
-    returns: int
-    params:
-      - const fexcept_t*
-      - int
     defined: '7.6.4.5'
   fetestexceptflag:
-    returns: int
-    params:
-      - const fexcept_t*
-      - int
     defined: '7.6.4.6'
   fetestexcept:
-    returns: int
-    params:
-      - int
     defined: '7.6.4.7'
   fegetmode:
-    returns: int
-    params:
-      - femode_t*
     defined: '7.6.5.1'
   fegetround:
-    returns: int
-    params: []
     defined: '7.6.5.2'
   fe_dec_getround:
-    returns: int
-    params: []
     defined: '7.6.5.3'
   fesetmode:
-    returns: int
-    params:
-      - const femode_t*
     defined: '7.6.5.4'
   fesetround:
-    returns: int
-    params:
-      - int
     defined: '7.6.5.5'
   fe_dec_setround:
-    returns: int
-    params:
-      - int
     defined: '7.6.5.6'
   fegetenv:
-    returns: int
-    params:
-      - fenv_t*
     defined: '7.6.6.1'
   feholdexcept:
-    returns: int
-    params:
-      - fenv_t*
     defined: '7.6.6.2'
   fesetenv:
-    returns: int
-    params:
-      - const fentv_t*
     defined: '7.6.6.3'
   feupdateenv:
-    returns: int
-    params:
-      - const fenv_t*
     defined: '7.6.6.4'
-
-# TODO: yay fill out moar stuff!!!1



More information about the libc-commits mailing list