diff options
Diffstat (limited to 'doc/sphinx')
| -rw-r--r-- | doc/sphinx/binman_docs.py | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/doc/sphinx/binman_docs.py b/doc/sphinx/binman_docs.py new file mode 100644 index 00000000000..34d12f1b7d5 --- /dev/null +++ b/doc/sphinx/binman_docs.py @@ -0,0 +1,207 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2026 Simon Glass <[email protected]> +# +"""Sphinx extension to auto-generate binman entry and bintool documentation. + +This parses etype and btool source files using the ast module to extract +class docstrings, avoiding the need to import binman modules (which have +dependencies like libfdt that may not be available in the doc-build +environment). + +The generated files are written to doc/develop/package/ (alongside +binman.rst) and included via toctree directives. They are .gitignore'd +since they are always regenerated during the build. + +To use, add 'binman_docs' to the extensions list in conf.py. +""" + +import ast +import os + + +def get_entry_docstring(source_file): + """Extract the Entry_ class docstring from an etype source file. + + Some files contain helper classes before the Entry_ class, so we look + specifically for a class whose name starts with 'Entry_'. + + Args: + source_file: Path to the source file + + Returns: + The docstring of the Entry_ class, or None + """ + with open(source_file) as inf: + tree = ast.parse(inf.read()) + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.ClassDef) and node.name.startswith('Entry_'): + return ast.get_docstring(node, clean=False) + return None + + +def get_bintool_docstring(source_file): + """Extract the Bintool class docstring from a btool source file. + + Args: + source_file: Path to the source file + + Returns: + The docstring of the Bintool class, or None + """ + with open(source_file) as inf: + tree = ast.parse(inf.read()) + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.ClassDef) and node.name.startswith('Bintool'): + return ast.get_docstring(node, clean=False) + return None + + +def generate_entry_docs(srcdir): + """Generate entries.rst content from etype source files. + + Args: + srcdir: Root of the U-Boot source tree + + Returns: + String containing RST content + """ + etype_dir = os.path.join(srcdir, 'tools', 'binman', 'etype') + modules = sorted([ + os.path.splitext(f)[0] for f in os.listdir(etype_dir) + if f.endswith('.py') and not f.startswith('_') and f != '__init__.py' + ]) + + parts = ['''\ +Binman Entry Documentation +========================== + +This file describes the entry types supported by binman. These entry types can +be placed in an image one by one to build up a final firmware image. It is +fairly easy to create new entry types. Just add a new file to the 'etype' +directory. You can use the existing entries as examples. + +Note that some entries are subclasses of others, using and extending their +features to produce new behaviours. + + +'''] + + missing = [] + for name in modules: + source = os.path.join(etype_dir, name + '.py') + docs = get_entry_docstring(source) + if docs: + lines = docs.splitlines() + first_line = lines[0] + rest = [line[4:] for line in lines[1:]] + hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line) + + ref_name = 'etype_%s' % name + parts.append('.. _%s:' % ref_name) + parts.append('') + parts.append(hdr) + parts.append('-' * len(hdr)) + parts.append('\n'.join(rest)) + parts.append('') + parts.append('') + else: + missing.append(name) + + if missing: + raise ValueError('Documentation is missing for modules: %s' % + ', '.join(missing)) + + return '\n'.join(parts) + + +def generate_bintool_docs(srcdir): + """Generate bintools.rst content from btool source files. + + Args: + srcdir: Root of the U-Boot source tree + + Returns: + String containing RST content + """ + btool_dir = os.path.join(srcdir, 'tools', 'binman', 'btool') + fnames = [ + f for f in os.listdir(btool_dir) + if f.endswith('.py') and not f.startswith('_') and f != '__init__.py' + ] + + def tool_sort_name(fname): + name = os.path.splitext(fname)[0] + if name.startswith('btool_'): + name = name[6:] + return name + + fnames.sort(key=tool_sort_name) + + parts = ['''\ +.. SPDX-License-Identifier: GPL-2.0+ + +Binman bintool Documentation +============================ + +This file describes the bintools (binary tools) supported by binman. Bintools +are binman's name for external executables that it runs to generate or process +binaries. It is fairly easy to create new bintools. Just add a new file to the +'btool' directory. You can use existing bintools as examples. + + +'''] + + missing = [] + for fname in fnames: + name = os.path.splitext(fname)[0] + # Strip btool_ prefix used for modules that conflict with Python libs + if name.startswith('btool_'): + name = name[6:] + source = os.path.join(btool_dir, fname) + docs = get_bintool_docstring(source) + if docs: + lines = docs.splitlines() + first_line = lines[0] + rest = [line[4:] for line in lines[1:]] + hdr = 'Bintool: %s: %s' % (name, first_line) + parts.append(hdr) + parts.append('-' * len(hdr)) + parts.append('\n'.join(rest)) + parts.append('') + parts.append('') + else: + missing.append(name) + + if missing: + raise ValueError('Documentation is missing for modules: %s' % + ', '.join(missing)) + + return '\n'.join(parts) + + +def generate_docs(app): + """Generate binman documentation RST files. + + Called by Sphinx during the builder-inited event, before any RST files + are read. + + Args: + app: The Sphinx application object + """ + srcdir = os.path.abspath(os.path.join(app.srcdir, '..')) + outdir = os.path.join(app.srcdir, 'develop', 'package') + + entries_rst = os.path.join(outdir, 'entries.rst') + content = generate_entry_docs(srcdir) + with open(entries_rst, 'w') as outf: + outf.write(content) + + bintools_rst = os.path.join(outdir, 'bintools.rst') + content = generate_bintool_docs(srcdir) + with open(bintools_rst, 'w') as outf: + outf.write(content) + + +def setup(app): + app.connect('builder-inited', generate_docs) + return {'version': '1.0', 'parallel_read_safe': True} |
