| # 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 | |
| `//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 {bzl: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 | |
| ::: | |
| ```` | |
| ::::: |