Using the sphinx_stardoc
rule, API documentation can be generated from bzl source code. This rule requires both MyST-based markdown and the sphinx_bzl
Sphinx extension are enabled. This allows source code to use Markdown and Sphinx syntax to create rich documentation with cross references, types, and more.
While the sphinx_stardoc
rule doesn't require Sphinx itself, the source it generates requires some additional Sphinx plugins and config settings.
When defining the sphinx_build_binary
target, also depend on:
@rules_python//sphinxdocs/src/sphinx_bzl:sphinx_bzl
myst_parser
(e.g. @pypi//myst_parser
)typing_extensions
(e.g. @pypi//myst_parser
)sphinx_build_binary( name = "sphinx-build", deps = [ "@rules_python//sphinxdocs/src/sphinx_bzl", "@pypi//myst_parser", "@pypi//typing_extensions", ... ] )
In conf.py
, enable the sphinx_bzl
extension, myst_parser
extension, and the colon_fence
MyST extension.
extensions = [ "myst_parser", "sphinx_bzl.bzl", ] myst_enable_extensions = [ "colon_fence", ]
To convert the bzl code to Sphinx doc sources, sphinx_stardocs
is the primary rule to do so. It takes a list of bzl_library
targets or files and generates docs for each. When a bzl_library
target is passed, the bzl_library.srcs
value can only have a single file.
Example:
sphinx_stardocs( name = "my_docs", srcs = [ ":binary_bzl", ":library_bzl", ] ) bzl_library( name = "binary_bzl", srcs = ["binary.bzl"], deps = ... ) bzl_library( name = "library_bzl", srcs = ["library.bzl"], deps = ... )
While Starlark doesn‘t have user-defined types as a first-class concept, it’s still possible to create such objects using struct
and lambdas. For the purposes of documentation, they can be documented by creating a module-level struct
with matching fields and also a field named TYPEDEF
. When the sphinx_stardoc
rule sees a struct with a TYPEDEF
field, it generates doc using the {rst:directive}bzl:typedef
directive and puts all the struct's fields within the typedef. The net result is the rendered docs look similar to how a class would be documented in other programming languages.
For example, a the Starlark implemenation of a Square
object with a area()
method would look like:
def _Square_typedef(): """A square with fixed size. :::{field} width :type: int ::: """ def _Square_new(width): """Creates a Square. Args: width: {type}`int` width of square Returns: {type}`Square` """ self = struct( area = lambda *a, **k: _Square_area(self, *a, **k), width = width ) return self def _Square_area(self, ): """Tells the area of the square.""" return self.width * self.width Square = struct( TYPEDEF = _Square_typedef, new = _Square_new, area = _Square_area, )
This will then genereate markdown that looks like:
::::{bzl:typedef} Square A square with fixed size :::{bzl:field} width :type: int ::: :::{bzl:function} new() ...args etc from _Square_new... ::: :::{bzl:function} area() ...args etc from _Square_area... ::: ::::
Which renders as:
:::{bzl:currentfile} //example:square.bzl :::
::::{bzl:typedef} Square A square with fixed size
:::{bzl:field} width :type: int ::: :::{bzl:function} new() ... ::: :::{bzl:function} area() ... ::: ::::