1# Starlark docgen 2 3Using the `sphinx_stardoc` rule, API documentation can be generated from bzl 4source code. This rule requires both MyST-based markdown and the `sphinx_bzl` 5Sphinx extension are enabled. This allows source code to use Markdown and 6Sphinx syntax to create rich documentation with cross references, types, and 7more. 8 9 10## Configuring Sphinx 11 12While the `sphinx_stardoc` rule doesn't require Sphinx itself, the source 13it generates requires some additional Sphinx plugins and config settings. 14 15When defining the `sphinx_build_binary` target, also depend on: 16* `@rules_python//sphinxdocs/src/sphinx_bzl:sphinx_bzl` 17* `myst_parser` (e.g. `@pypi//myst_parser`) 18* `typing_extensions` (e.g. `@pypi//myst_parser`) 19 20``` 21sphinx_build_binary( 22 name = "sphinx-build", 23 deps = [ 24 "@rules_python//sphinxdocs/src/sphinx_bzl", 25 "@pypi//myst_parser", 26 "@pypi//typing_extensions", 27 ... 28 ] 29) 30``` 31 32In `conf.py`, enable the `sphinx_bzl` extension, `myst_parser` extension, 33and the `colon_fence` MyST extension. 34 35``` 36extensions = [ 37 "myst_parser", 38 "sphinx_bzl.bzl", 39] 40 41myst_enable_extensions = [ 42 "colon_fence", 43] 44``` 45 46## Generating docs from bzl files 47 48To convert the bzl code to Sphinx doc sources, `sphinx_stardocs` is the primary 49rule to do so. It takes a list of `bzl_library` targets or files and generates docs for 50each. When a `bzl_library` target is passed, the `bzl_library.srcs` value can only 51have a single file. 52 53Example: 54 55``` 56sphinx_stardocs( 57 name = "my_docs", 58 srcs = [ 59 ":binary_bzl", 60 ":library_bzl", 61 ] 62) 63 64bzl_library( 65 name = "binary_bzl", 66 srcs = ["binary.bzl"], 67 deps = ... 68) 69 70bzl_library( 71 name = "library_bzl", 72 srcs = ["library.bzl"], 73 deps = ... 74) 75``` 76 77## User-defined types 78 79While Starlark doesn't have user-defined types as a first-class concept, it's 80still possible to create such objects using `struct` and lambdas. For the 81purposes of documentation, they can be documented by creating a module-level 82`struct` with matching fields *and* also a field named `TYPEDEF`. When the 83`sphinx_stardoc` rule sees a struct with a `TYPEDEF` field, it generates doc 84using the {rst:directive}`bzl:typedef` directive and puts all the struct's fields 85within the typedef. The net result is the rendered docs look similar to how 86a class would be documented in other programming languages. 87 88For example, a the Starlark implemenation of a `Square` object with a `area()` 89method would look like: 90 91``` 92 93def _Square_typedef(): 94 """A square with fixed size. 95 96 :::{field} width 97 :type: int 98 ::: 99 """ 100 101def _Square_new(width): 102 """Creates a Square. 103 104 Args: 105 width: {type}`int` width of square 106 107 Returns: 108 {type}`Square` 109 """ 110 self = struct( 111 area = lambda *a, **k: _Square_area(self, *a, **k), 112 width = width 113 ) 114 return self 115 116def _Square_area(self, ): 117 """Tells the area of the square.""" 118 return self.width * self.width 119 120Square = struct( 121 TYPEDEF = _Square_typedef, 122 new = _Square_new, 123 area = _Square_area, 124) 125``` 126 127This will then genereate markdown that looks like: 128 129``` 130::::{bzl:typedef} Square 131A square with fixed size 132 133:::{bzl:field} width 134:type: int 135::: 136:::{bzl:function} new() 137...args etc from _Square_new... 138::: 139:::{bzl:function} area() 140...args etc from _Square_area... 141::: 142:::: 143``` 144 145Which renders as: 146 147:::{bzl:currentfile} //example:square.bzl 148::: 149 150::::{bzl:typedef} Square 151A square with fixed size 152 153:::{bzl:field} width 154:type: int 155::: 156:::{bzl:function} new() 157... 158::: 159:::{bzl:function} area() 160... 161::: 162:::: 163