Skip to content

Commit bab121a

Browse files
committed
[lldb][docs] Use sphinx instead of epydoc to generate LLDB's Python reference
Currently LLDB uses epydoc to generate the Python API reference for the website. epydoc however is unmaintained since more than a decade and no longer works with Python 3. Also whatever setup we had once for generating the documentation on the website server no longer seems to work, so the current website documentation has been stale since more than a year. This patch replaces epydoc with sphinx and its automodapi plugin that can generate Python API references. LLVM already uses sphinx for the rest of the documentation, so this way we are more consistent with the rest of LLVM. The only new dependency is the automodapi plugin for sphinx. This patch effectively does the following things: * Remove the epydoc code. * Make a new dummy Python API page in our website that just calls the Sphinx command for generated the API documentation. * Add a mock _lldb module that is only used when generating the Python API. This way we don't have to build all of LLDB to generate the API reference. Some notes: * The long list of skips is necessary due to boilerplate functions that SWIG is generating. Sadly automodapi is not really scriptable from what I can see, so we have to blacklist this stuff manually. * The .gitignore change because automodapi wants a subfolder of our documentation directory to place generated documentation files there. The path is also what is used on the website, so we can't really workaround this (without copying the whole `docs` dir somewhere else when we build). * We have to use environment variables to pass our build path to our sphinx configuration. Sphinx doesn't support passing variables onto that script. Reviewed By: JDevlieghere Differential Revision: https://reviews.llvm.org/D94489
1 parent 619eb14 commit bab121a

File tree

7 files changed

+156
-51
lines changed

7 files changed

+156
-51
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,5 @@ pythonenv*
6363
/clang/utils/analyzer/projects/*/PatchedSource
6464
/clang/utils/analyzer/projects/*/ScanBuildResults
6565
/clang/utils/analyzer/projects/*/RefScanBuildResults
66+
# automodapi puts generated documentation files here.
67+
/lldb/docs/python_api/

lldb/docs/CMakeLists.txt

Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,39 @@ if(DOXYGEN_FOUND)
1515
)
1616
endif()
1717

18-
if (LLDB_ENABLE_PYTHON)
19-
find_program(EPYDOC_EXECUTABLE NAMES epydoc epydoc.py)
20-
if(EPYDOC_EXECUTABLE)
21-
message(STATUS "Found epydoc - ${EPYDOC_EXECUTABLE}")
22-
23-
find_program(DOT_EXECUTABLE dot)
24-
if(DOT_EXECUTABLE)
25-
set(EPYDOC_OPTIONS ${EPYDOC_OPTIONS} --graph all --dotpath ${DOT_EXECUTABLE})
26-
message(STATUS "Found dot - ${DOT_EXECUTABLE}")
27-
endif()
18+
if (LLVM_ENABLE_SPHINX)
19+
include(AddSphinxTarget)
20+
endif()
2821

29-
# Pretend to make a python package so that we can generate the reference.
30-
# Because we don't build liblldb, epydoc will complain that the import of
31-
# _lldb.so failed, but that doesn't prevent it from generating the docs.
22+
if (LLDB_ENABLE_PYTHON AND SPHINX_FOUND)
23+
if (${SPHINX_OUTPUT_HTML})
24+
# Pretend that the SWIG generated API is a Python package.
3225
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lldb)
3326
get_target_property(lldb_bindings_dir swig_wrapper_python BINARY_DIR)
3427
add_custom_target(lldb-python-doc-package
3528
COMMAND "${CMAKE_COMMAND}" -E copy "${lldb_bindings_dir}/lldb.py" "${CMAKE_CURRENT_BINARY_DIR}/lldb/__init__.py"
36-
COMMENT "Copying lldb.py to pretend package.")
29+
COMMENT "Copying lldb.py to pretend its a Python package.")
3730
add_dependencies(lldb-python-doc-package swig_wrapper_python)
3831

39-
set(DOC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/doc")
40-
file(MAKE_DIRECTORY "${DOC_DIR}")
41-
add_custom_target(lldb-python-doc
42-
${EPYDOC_EXECUTABLE}
43-
--html
44-
lldb
45-
-o ${CMAKE_CURRENT_BINARY_DIR}/python_reference
46-
--name "LLDB python API"
47-
--url "http://lldb.llvm.org"
48-
${EPYDOC_OPTIONS}
49-
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
50-
COMMENT "Generating LLDB Python API reference with epydoc" VERBATIM
51-
)
52-
add_dependencies(lldb-python-doc swig_wrapper_python lldb-python-doc-package)
53-
else()
54-
message(STATUS "Could NOT find epydoc")
32+
# FIXME: Don't treat Sphinx warnings as errors. The files generated by
33+
# automodapi are full of warnings (partly caused by SWIG, our documentation
34+
# and probably also automodapi itself), so those warnings need to be fixed
35+
# first before we can turn this on.
36+
set(SPHINX_WARNINGS_AS_ERRORS Off)
37+
38+
# The sphinx config needs to know where the generated LLDB Python module is.
39+
# There is no way to pass a variable into our sphinx config, so just pass
40+
# the path to the module via the LLDB_SWIG_MODULE environment variable.
41+
add_sphinx_target(html lldb ENV_VARS "LLDB_SWIG_MODULE=${CMAKE_CURRENT_BINARY_DIR}")
42+
# Sphinx does not reliably update the custom CSS files, so force
43+
# a clean rebuild of the documentation every time.
44+
add_custom_target(clean-lldb-html COMMAND "${CMAKE_COMMAND}" -E
45+
remove_directory ${CMAKE_CURRENT_BINARY_DIR}/html)
46+
add_dependencies(docs-lldb-html swig_wrapper_python
47+
lldb-python-doc-package clean-lldb-html)
5548
endif()
56-
endif ()
57-
58-
if (LLVM_ENABLE_SPHINX)
59-
include(AddSphinxTarget)
60-
if (SPHINX_FOUND)
61-
if (${SPHINX_OUTPUT_HTML})
62-
add_sphinx_target(html lldb)
63-
# Sphinx does not reliably update the custom CSS files, so force
64-
# a clean rebuild of the documentation every time.
65-
add_custom_target(clean-lldb-html COMMAND "${CMAKE_COMMAND}" -E
66-
remove_directory ${CMAKE_CURRENT_BINARY_DIR}/html)
67-
add_dependencies(docs-lldb-html clean-lldb-html)
68-
endif()
6949

70-
if (${SPHINX_OUTPUT_MAN})
71-
add_sphinx_target(man lldb)
72-
endif()
50+
if (${SPHINX_OUTPUT_MAN})
51+
add_sphinx_target(man lldb)
7352
endif()
7453
endif()

lldb/docs/_lldb/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from unittest.mock import Mock
2+
import sys
3+
import types
4+
5+
# This package acts as a mock implementation of the native _lldb module so
6+
# that generating the LLDB documentation doesn't actually require building all
7+
# of LLDB.
8+
module_name = '_lldb'
9+
sys.modules[module_name] = Mock()

lldb/docs/conf.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,17 @@
1818
# If extensions (or modules to document with autodoc) are in another directory,
1919
# add these directories to sys.path here. If the directory is relative to the
2020
# documentation root, use os.path.abspath to make it absolute, like shown here.
21-
#sys.path.insert(0, os.path.abspath('.'))
21+
22+
# Add the current directory that contains the mock _lldb native module which
23+
# is imported by the `lldb` module.
24+
sys.path.insert(0, os.path.abspath("."))
25+
# Add the build directory that contains the `lldb` module. LLDB_SWIG_MODULE is
26+
# set by CMake.
27+
sys.path.insert(0, os.getenv("LLDB_SWIG_MODULE"))
28+
29+
# Put the generated Python API documentation in the 'python_api' folder. This
30+
# also defines the URL these files will have in the generated website.
31+
automodapi_toctreedirnm = 'python_api'
2232

2333
# -- General configuration -----------------------------------------------------
2434

@@ -27,7 +37,8 @@
2737

2838
# Add any Sphinx extension module names here, as strings. They can be extensions
2939
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
30-
extensions = ['sphinx.ext.todo', 'sphinx.ext.mathjax', 'sphinx.ext.intersphinx']
40+
extensions = ['sphinx.ext.todo', 'sphinx.ext.mathjax', 'sphinx.ext.intersphinx',
41+
'sphinx_automodapi.automodapi']
3142

3243
# Add any paths that contain templates here, relative to this directory.
3344
templates_path = ['_templates']

lldb/docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ interesting areas to contribute to lldb.
164164
:maxdepth: 1
165165
:caption: Reference
166166

167-
Public Python API <https://lldb.llvm.org/python_reference/index.html>
167+
Public Python API <python_api>
168168
Public C++ API <https://lldb.llvm.org/cpp_reference/namespacelldb.html>
169169
Private C++ API <https://lldb.llvm.org/cpp_reference/index.html>
170170
Man Page <man/lldb>

lldb/docs/python_api.rst

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
LLDB Python API
2+
================================
3+
4+
..
5+
The long list of "skip" filters out several global functions that are
6+
generated by SWIG (but which are not useful as they are only the
7+
backend for their respective static functions in the classes).
8+
Without this list
9+
.. automodapi:: lldb
10+
:no-inheritance-diagram:
11+
:skip: SBBreakpoint_EventIsBreakpointEvent
12+
:skip: SBBreakpoint_GetBreakpointEventTypeFromEvent
13+
:skip: SBBreakpoint_GetBreakpointFromEvent
14+
:skip: SBBreakpoint_GetBreakpointLocationAtIndexFromEvent
15+
:skip: SBBreakpoint_GetNumBreakpointLocationsFromEvent
16+
:skip: SBCommandInterpreter_EventIsCommandInterpreterEvent
17+
:skip: SBCommandInterpreter_GetArgumentDescriptionAsCString
18+
:skip: SBCommandInterpreter_GetArgumentTypeAsCString
19+
:skip: SBCommandInterpreter_GetBroadcasterClass
20+
:skip: SBCommunication_GetBroadcasterClass
21+
:skip: SBData_CreateDataFromCString
22+
:skip: SBData_CreateDataFromDoubleArray
23+
:skip: SBData_CreateDataFromSInt32Array
24+
:skip: SBData_CreateDataFromSInt64Array
25+
:skip: SBData_CreateDataFromUInt32Array
26+
:skip: SBData_CreateDataFromUInt64Array
27+
:skip: SBDebugger_Create
28+
:skip: SBDebugger_Create
29+
:skip: SBDebugger_Destroy
30+
:skip: SBDebugger_FindDebuggerWithID
31+
:skip: SBDebugger_GetBuildConfiguration
32+
:skip: SBDebugger_GetDefaultArchitecture
33+
:skip: SBDebugger_GetInternalVariableValue
34+
:skip: SBDebugger_GetVersionString
35+
:skip: SBDebugger_Initialize
36+
:skip: SBDebugger_InitializeWithErrorHandling
37+
:skip: SBDebugger_MemoryPressureDetected
38+
:skip: SBDebugger_SetDefaultArchitecture
39+
:skip: SBDebugger_SetInternalVariable
40+
:skip: SBDebugger_StateAsCString
41+
:skip: SBDebugger_StateIsRunningState
42+
:skip: SBDebugger_StateIsStoppedState
43+
:skip: SBDebugger_Terminate
44+
:skip: SBEvent_GetCStringFromEvent
45+
:skip: SBFileSpec_ResolvePath
46+
:skip: SBFile_MakeBorrowed
47+
:skip: SBFile_MakeBorrowedForcingIOMethods
48+
:skip: SBFile_MakeForcingIOMethods
49+
:skip: SBHostOS_GetLLDBPath
50+
:skip: SBHostOS_GetLLDBPythonPath
51+
:skip: SBHostOS_GetProgramFileSpec
52+
:skip: SBHostOS_GetUserHomeDirectory
53+
:skip: SBHostOS_ThreadCancel
54+
:skip: SBHostOS_ThreadCreate
55+
:skip: SBHostOS_ThreadCreated
56+
:skip: SBHostOS_ThreadDetach
57+
:skip: SBHostOS_ThreadJoin
58+
:skip: SBLanguageRuntime_GetLanguageTypeFromString
59+
:skip: SBLanguageRuntime_GetNameForLanguageType
60+
:skip: SBModuleSpecList_GetModuleSpecifications
61+
:skip: SBModule_GarbageCollectAllocatedModules
62+
:skip: SBModule_GetNumberAllocatedModules
63+
:skip: SBPlatform_GetHostPlatform
64+
:skip: SBProcess_EventIsProcessEvent
65+
:skip: SBProcess_EventIsStructuredDataEvent
66+
:skip: SBProcess_GetBroadcasterClassName
67+
:skip: SBProcess_GetInterruptedFromEvent
68+
:skip: SBProcess_GetNumRestartedReasonsFromEvent
69+
:skip: SBProcess_GetProcessFromEvent
70+
:skip: SBProcess_GetRestartedFromEvent
71+
:skip: SBProcess_GetRestartedReasonAtIndexFromEvent
72+
:skip: SBProcess_GetStateFromEvent
73+
:skip: SBProcess_GetStructuredDataFromEvent
74+
:skip: SBReproducer_Capture
75+
:skip: SBReproducer_PassiveReplay
76+
:skip: SBReproducer_SetAutoGenerate
77+
:skip: SBReproducer_SetWorkingDirectory
78+
:skip: SBTarget_EventIsTargetEvent
79+
:skip: SBTarget_GetBroadcasterClassName
80+
:skip: SBTarget_GetModuleAtIndexFromEvent
81+
:skip: SBTarget_GetNumModulesFromEvent
82+
:skip: SBTarget_GetTargetFromEvent
83+
:skip: SBThread_EventIsThreadEvent
84+
:skip: SBThread_GetBroadcasterClassName
85+
:skip: SBThread_GetStackFrameFromEvent
86+
:skip: SBThread_GetThreadFromEvent
87+
:skip: SBTypeSummary_CreateWithFunctionName
88+
:skip: SBTypeSummary_CreateWithScriptCode
89+
:skip: SBTypeSummary_CreateWithSummaryString
90+
:skip: SBTypeSynthetic_CreateWithClassName
91+
:skip: SBTypeSynthetic_CreateWithScriptCode
92+
:skip: SBWatchpoint_EventIsWatchpointEvent
93+
:skip: SBWatchpoint_GetWatchpointEventTypeFromEvent
94+
:skip: SBWatchpoint_GetWatchpointFromEvent
95+
:skip: command
96+
:skip: in_range
97+
:skip: is_numeric_type
98+
:skip: lldb_iter

llvm/cmake/modules/AddSphinxTarget.cmake

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@ endif()
1717
# the sphinx-build command.
1818
#
1919
# ``project`` should be the project name
20+
#
21+
# Named arguments:
22+
# ``ENV_VARS`` should be a list of environment variables that should be set when
23+
# running Sphinx. Each environment variable should be a string with
24+
# the form KEY=VALUE.
2025
function (add_sphinx_target builder project)
21-
cmake_parse_arguments(ARG "" "SOURCE_DIR" "" ${ARGN})
26+
cmake_parse_arguments(ARG "" "SOURCE_DIR" "ENV_VARS" ${ARGN})
2227
set(SPHINX_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${builder}")
2328
set(SPHINX_DOC_TREE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees-${project}-${builder}")
2429
set(SPHINX_TARGET_NAME docs-${project}-${builder})
@@ -34,7 +39,8 @@ function (add_sphinx_target builder project)
3439
endif()
3540

3641
add_custom_target(${SPHINX_TARGET_NAME}
37-
COMMAND ${SPHINX_EXECUTABLE}
42+
COMMAND ${CMAKE_COMMAND} -E env ${ARG_ENV_VARS}
43+
${SPHINX_EXECUTABLE}
3844
-b ${builder}
3945
-d "${SPHINX_DOC_TREE_DIR}"
4046
-q # Quiet: no output other than errors and warnings.

0 commit comments

Comments
 (0)