Files
Mujoco_WASM/doc/generate/generate_functions.py
T
Yuval Tassa 1a33ca4ae5 Release docs auto-generation scripts and test
PiperOrigin-RevId: 950744854
Change-Id: Ibcd9d6bd3e6ec50d5d6753b8c9516f7d31f19e5b
2026-07-20 04:12:10 -07:00

171 lines
5.3 KiB
Python

# Copyright 2026 DeepMind Technologies Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Generates functions API for APIreference.rst."""
import re
import sys
import os
import sys
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
_REPO_ROOT = os.path.dirname(os.path.dirname(_SCRIPT_DIR))
sys.path.insert(0, _SCRIPT_DIR)
import generate_api_header
_FUNCTIONS_OVERRIDE = 'doc/APIreference/functions_override.rst'
_RST_SECTION_REGEX = re.compile(r'^\.\. (?P<section>_[A-Z].+):')
_RST_FUNCTION_REGEX = re.compile(r'^\.\. _(?P<function>mj.+):')
rst_to_section = {
'_Parseandcompile': ['Parse and compile', ''],
'_Mainsimulation': ['Main simulation', ''],
'_Support': ['Support', ''],
'_Components': ['Components', ''],
'_Subcomponents': ['Sub components', ''],
'_Raycollisions': ['Ray casting', ''],
'_Printing': ['Printing', ''],
'_Virtualfilesystem': ['Virtual file system', ''],
'_Assetcache': ['Asset cache', ''],
'_Resources': ['Resources', ''],
'_Initialization': ['Initialization', ''],
'_Errorandmemory': ['Error and memory', ''],
'_Miscellaneous': ['Miscellaneous', ''],
'_Interaction': ['Interaction', ''],
'_Visualization-api': ['Visualization', ''],
'_OpenGLrendering': ['OpenGL rendering', ''],
'_FilamentRenderingApi': ['Filament rendering', ''],
'_UIframework': ['UI framework', ''],
'_Derivatives-api': ['Derivatives', ''],
'_Signeddistancefunction': ['Signed Distance Functions', ''],
'_Plugins-api': ['Plugins', ''],
'_Thread': ['Threads', ''],
'_Standardmath': ['Standard math', ''],
'_Vectormath': ['Vector math', ''],
'_Sparsemath': ['Sparse math', ''],
'_Quaternions': ['Quaternions', ''],
'_Poses': ['Poses', ''],
'_Decompositions': ['Decompositions / Solvers', ''],
'_Attachment': ['Attachment', ''],
'_AddTreeElements': ['Tree elements', ''],
'_AddNonTreeElements': ['Non-tree elements', ''],
'_Setactuatorparameters': ['Set actuator parameters', ''],
'_AddAssets': ['Assets', ''],
'_FindAndGetUtilities': ['Find and get utilities', ''],
'_AttributeSetters': ['Attribute setters', ''],
'_AttributeGetters': ['Attribute getters', ''],
'_SpecUtilities': ['Spec utilities', ''],
'_ElementInitialization': ['Element initialization', ''],
'_ElementCasting': ['Element casting', ''],
}
def generate() -> str:
"""Generates functions.rst."""
rst_str = ''
api = generate_api_header.read_headers()
filepath = os.path.join(_REPO_ROOT, _FUNCTIONS_OVERRIDE)
with open(filepath, 'r', encoding='utf-8') as file:
current_section = None
current_function = None
for line in file:
# Try matching a section.
match = _RST_SECTION_REGEX.search(line)
if match is not None:
current_section = match.group('section')
if current_section in rst_to_section:
current_function = None
else:
current_section = None
continue
# Try matching a function
match = _RST_FUNCTION_REGEX.search(line)
if match is not None:
current_function = match.group('function')
if current_function in api:
api[current_function].doc = ''
current_section = None
else:
current_function = None
continue
# Update section doc.
if current_section is not None:
rst_to_section[current_section][1] += line
# Override doc.
if current_function is not None:
api[current_function].doc += line
# Check that all function sections from the headers are mapped.
mapped_sections = {v[0] for v in rst_to_section.values()}
for token, defn in api.items():
if defn.c_type == 'FUNCTION' and defn.section not in mapped_sections:
raise ValueError(
f'Function {token!r} is in unmapped section {defn.section!r}. '
f'Add an entry to rst_to_section in generate_functions.py.'
)
# Write out RST file.
rst_str += ("""
..
AUTOGENERATED: DO NOT EDIT MANUALLY
""".lstrip())
for section in rst_to_section:
section_title = rst_to_section[section][0]
rst_str += (
f"""
.. {section}:
{section_title}
{'^'*len(section_title)}
{rst_to_section[section][1]}""".lstrip())
for token in api:
if api[token].c_type != 'FUNCTION' or api[token].section != section_title:
continue
doc = api[token].doc.strip()
doc = doc.replace('&nbsp;', '``&nbsp;``')
rst_str += (f"""
.. _{token}:
`{token} <#{token}>`__
{'~'*(2*len(token)+8)}
.. mujoco-include:: {token}
{doc}
""".lstrip())
return rst_str
def main() -> None:
if len(sys.argv) > 1:
sys.exit('Too many command-line arguments.')
sys.stdout.buffer.write(generate().encode('utf-8'))
if __name__ == '__main__':
main()