blob: 8376f60679a125e32719afa35b73930fa5747d50 [file] [log] [blame] [view] [edit]
# Bazel plugin for Sphinx
The `sphinx_bzl` Python package is a Sphinx plugin that defines a custom domain
("bzl") in the Sphinx system. This provides first-class integration with Sphinx
and allows code comments to provide rich information and allows manually writing
docs for objects that aren't directly representable in bzl source code. For
example, the fields of a provider can use `:type:` to indicate the type of a
field, or manually written docs can use the `{bzl:target}` directive to document
a well known target.
## Configuring Sphinx
To enable the plugin in Sphinx, depend on
`@rules_python//sphinxdocs/src/sphinx_bzl` and enable it in `conf.py`:
```
extensions = [
"sphinx_bzl.bzl",
]
```
## Brief introduction to Sphinx terminology
To aid understanding how to write docs, lets define a few common terms:
* **Role**: A role is the "bzl:obj" part when writing ``{bzl:obj}`ref` ``.
Roles mark inline text as needing special processing. There's generally
two types of processing: creating cross references, or role-specific custom
rendering. For example `{bzl:obj}` will create a cross references, while
`{bzl:default-value}` indicates the default value of an argument.
* **Directive**: A directive is indicated with `:::` and allows defining an
entire object and its parts. For example, to describe a function and its
arguments, the `:::{bzl:function}` directive is used.
* **Directive Option**: A directive option is the "type" part when writing
`:type:` within a directive. Directive options are how directives are told
the meaning of certain values, such as the type of a provider field. Depending
on the object being documented, a directive option may be used instead of
special role to indicate semantic values.
Most often, you'll be using roles to refer other objects or indicate special
values in doc strings. For directives, you're likely to only use them when
manually writing docs to document flags, targets, or other objects that
`sphinx_stardoc` generates for you.
## MyST vs RST
By default, Sphinx uses ReStructured Text (RST) syntax for its documents.
Unfortunately, RST syntax is very different than the popular Markdown syntax. To
bridge the gap, MyST translates Markdown-style syntax into the RST equivalents.
This allows easily using Markdown in bzl files.
While MyST isn't required for the core `sphinx_bzl` plugin to work, this
document uses MyST syntax because `sphinx_stardoc` bzl doc gen rule requires
MyST.
The main difference in syntax is:
* MyST directives use `:::{name}` with closing `:::` instead of `.. name::` with
indented content.
* MyST roles use `{role:name}` instead of `:role:name:`
## Type expressions
Several roles or fields accept type expressions. Type expressions use
Python-style annotation syntax to describe data types. For example `None | list[str]`
describes a type of "None or a list of strings". Each component of the
expression is parsed and cross reference to its associated type definition.
## Cross references
In brief, to reference bzl objects, use the `bzl:obj` role and use the
Bazel label string you would use to refer to the object in Bazel (using `%` to
denote names within a file). For example, to unambiguously refer to `py_binary`:
```
{bzl:obj}`@rules_python//python:py_binary.bzl%py_binary`
```
The above is pretty long, so shorter names are also supported, and `sphinx_bzl`
will try to find something that matches. Additionally, in `.bzl` code, the
`bzl:` prefix is set as the default. The above can then be shortened to:
```
{obj}`py_binary`
```
The text that is displayed can be customized by putting the reference string in
chevrons (`<>`):
```
{obj}`the binary rule <py_binary>`
```
Specific types of objects (rules, functions, providers, etc) can be
specified to help disambiguate short names:
```
{function}`py_binary` # Refers to the wrapping macro
{rule}`py_binary` # Refers to the underlying rule
```
Finally, objects built into Bazel can be explicitly referenced by forcing
a lookup outside the local project using `{external}`. For example, the symbol
`toolchain` is a builtin Bazel function, but it could also be the name of a tag
class in the local project. To force looking up the builtin Bazel `toolchain` rule,
`{external:bzl:rule}` can be used, e.g.:
```
{external:bzl:obj}`toolchain`
```
Those are the basics of cross referencing. Sphinx has several additional
syntaxes for finding and referencing objects; see
[the MyST docs for supported
syntaxes](https://myst-parser.readthedocs.io/en/latest/syntax/cross-referencing.html#reference-roles)
### Cross reference roles
A cross reference role is the `obj` portion of `{bzl:obj}`. It affects what is
searched and matched.
:::{note}
The documentation renders using RST notation (`:foo:role:`), not
MyST notation (`{foo:role}`.
:::
:::{rst:role} bzl:arg
Refer to a function argument.
:::
:::{rst:role} bzl:attr
Refer to a rule attribute.
:::
:::{rst:role} bzl:flag
Refer to a flag.
:::
:::{rst:role} bzl:obj
Refer to any type of Bazel object
:::
:::{rst:role} bzl:rule
Refer to a rule.
:::
:::{rst:role} bzl:target
Refer to a target.
:::
:::{rst:role} bzl:type
Refer to a type or type expression; can also be used in argument documentation.
```
def func(arg):
"""Do stuff
Args:
arg: {type}`int | str` the arg
"""
print(arg + 1)
```
:::
## Special roles
There are several special roles that can be used to annotate parts of objects,
such as the type of arguments or their default values.
:::{note}
The documentation renders using RST notation (`:foo:role:`), not
MyST notation (`{foo:role}`.
:::
:::{rst:role} bzl:default-value
Indicate the default value for a function argument or rule attribute. Use it in
the Args doc of a function or the doc text of an attribute.
```
def func(arg=1):
"""Do stuff
Args:
foo: {default-value}`1` the arg
my_rule = rule(attrs = {
"foo": attr.string(doc="{default-value}`bar`)
})
```
:::
:::{rst:role} bzl:return-type
Indicates the return type for a function. Use it in the Returns doc of a
function.
```
def func():
"""Do stuff
Returns:
{return-type}`int`
"""
return 1
```
:::
## Directives
Most directives are automatically generated by `sphinx_stardoc`. Here, we only
document ones that must be manually written.
To write a directive, a line starts with 3 to 6 colons (`:`), followed by the
directive name in braces (`{}`), and eventually ended by the same number of
colons on their own line. For example:
```
:::{bzl:target} //my:target
Doc about target
:::
```
:::{note}
The documentation renders using RST notation (`.. directive::`), not
MyST notation.
:::
Directives can be nested, but [the inner directives must have **fewer** colons
than outer
directives](https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html#nesting-directives).
:::{rst:directive} .. bzl:currentfile:: file
This directive indicates the Bazel file that objects defined in the current
documentation file are in. This is required for any page that defines Bazel
objects. The format of `file` is Bazel label syntax, e.g. `//foo:bar.bzl` for bzl
files, and `//foo:BUILD.bazel` for things in BUILD files.
:::
:::::{rst:directive} .. bzl:target:: target
Documents a target. It takes no directive options. The format of `target`
can either be a fully qualified label (`//foo:bar`), or the base target name
relative to `{bzl:currentfile}`.
````
:::{bzl:target} //foo:target
My docs
:::
````
:::::
:::{rst:directive} .. bzl:flag:: target
Documents a flag. It has the same format as `{bzl:target}`
:::
::::::{rst:directive} .. bzl:typedef:: typename
Documents a user-defined structural "type". These are typically generated by
the {obj}`sphinx_stardoc` rule after following [User-defined types] to create a
struct with a `TYPEDEF` field, but can also be manually defined if there's
no natural place for it in code, e.g. some ad-hoc structural type.
`````
::::{bzl:typedef} Square
Doc about Square
:::{bzl:field} width
:type: int
:::
:::{bzl:function} new(size)
...
:::
:::{bzl:function} area()
...
:::
::::
`````
Note that MyST requires the number of colons for the outer typedef directive
to be greater than the inner directives. Otherwise, only the first nested
directive is parsed as part of the typedef, but subsequent ones are not.
::::::
:::::{rst:directive} .. bzl:field:: fieldname
Documents a field of an object. These are nested within some other directive,
typically `{bzl:typedef}`
Directive options:
* `:type:` specifies the type of the field
````
:::{bzl:field} fieldname
:type: int | None | str
Doc about field
:::
````
:::::
:::::{rst:directive} .. bzl:provider-field:: fieldname
Documents a field of a provider. The directive itself is autogenerated by
`sphinx_stardoc`, but the content is simply the documentation string specified
in the provider's field.
Directive options:
* `:type:` specifies the type of the field
````
:::{bzl:provider-field} fieldname
:type: depset[File] | None
Doc about the provider field
:::
````
:::::