This page is for rule writers who are planning to make their rules available to others.
We recommend you start a new ruleset from the template repository: https://github.com/bazel-contrib/rules-template That template follows the recommendations below, and includes API documentation generation and sets up a CI/CD pipeline to make it trivial to distribute your ruleset.
Hosting and naming rules
Repository names for Bazel rules are standardized on the following format:
See examples on GitHub.
For consistency, you should follow this same format when publishing your Bazel rules.
Make sure to use a descriptive GitHub repository description and
- Repository name:
- Repository description: Go rules for Bazel
- Repository tags:
README.mdheader: Go rules for Bazel (note the link to https://bazel.build which will guide users who are unfamiliar with Bazel to the right place)
Rules can be grouped either by language (such as Scala), runtime platform (such as Android), or framework (such as Spring).
Every rule repository should have a certain layout so that users can quickly understand new rules.
For example, when writing new rules for the (make-believe)
mockascript language, the rule repository would have the following structure:
/ LICENSE README WORKSPACE mockascript/ constraints/ BUILD runfiles/ BUILD runfiles.mocs BUILD defs.bzl tests/ BUILD some_test.sh another_test.py examples/ BUILD bin.mocs lib.mocs test.mocs
In the project's
WORKSPACE, you should define the name that users will use
to reference your rules. If your rules belong to the
bazelbuild organization, you must use
rules_<lang> (such as
rules_mockascript). Otherwise, you should name your
<org>_rules_<lang> (such as
start a thread on GitHub
if you feel like your rules should follow the convention for rules in the
In the following sections, assume the repository belongs to the bazelbuild organization.
workspace(name = "rules_mockascript")
At the top level, there should be a
README that contains (at least) what
users will need to copy-paste into their
WORKSPACE file to use your rule.
In general, this will be a
http_archive pointing to your GitHub release and
a macro call that downloads/configures any tools your rule needs. For example,
for the Go
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_go", urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.18.5/rules_go-0.18.5.tar.gz"], sha256 = "a82a352bffae6bee4e95f68a8d80a70e87f42c4741e6a448bec11998fcc82329", ) load("@rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains") go_rules_dependencies() go_register_toolchains()
If your rules depend on another repository's rules, specify that in the
rules documentation (for example, see the
which depend on the Sass rules), and provide a
macro that will download all dependencies (see
Often times there will be multiple rules provided by your repository. Create a
directory named by the language and provide an entry point -
exporting all rules (also include a
BUILD file so the directory is a package).
rules_mockascript that means there will be a directory named
mockascript, and a
BUILD file and a
defs.bzl file inside:
/ mockascript/ BUILD defs.bzl
If your rule defines
it's possible that you'll need to define custom
constraint_values. Put these into a
//<LANG>/constraints package. Your
directory structure will look like this:
/ mockascript/ constraints/ BUILD BUILD defs.bzl
for best practices, and to see what constraints are already present, and
consider contributing your constraints there if they are language independent.
Be mindful of introducing custom constraints, all users of your rules will
use them to perform platform specific logic in their
BUILD files (for example,
With custom constraints, you define a language that the whole Bazel ecosystem
If your rule provides a standard library for accessing runfiles, it should be
in the form of a library target located at
//<LANG>/runfiles (an abbreviation
//<LANG>/runfiles:runfiles). User targets that need to access their data
dependencies will typically add this target to their
Your rules might have external dependencies. To make depending on your rules
simpler, please provide a
WORKSPACE macro that will declare dependencies on
those external dependencies. Do not declare dependencies of tests there, only
dependencies that rules require to work. Put development dependencies into the
Create a file named
<LANG>/repositories.bzl and provide a single entry point
rules_<LANG>_dependencies. Our directory will look as follows:
/ mockascript/ constraints/ BUILD BUILD defs.bzl repositories.bzl
Your rules might also register toolchains. Please provide a separate
macro that registers these toolchains. This way users can decide to omit the
previous macro and control dependencies manually, while still being allowed to
Therefore add a
WORKSPACE macro named
Note that in order to resolve toolchains in the analysis phase Bazel needs to
toolchain targets that are registered. Bazel will not need to
analyze all targets referenced by
toolchain.toolchain attribute. If in order
to register toolchains you need to perform complex computation in the
repository, consider splitting the repository with
toolchain targets from the
<LANG>_toolchain targets. Former will be always fetched, and
the latter will only be fetched when user actually needs to build
In your release announcement provide a snippet that your users can copy-paste
WORKSPACE file. This snippet in general will look as follows:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_<LANG>", urls = ["<url_to_the_release.zip"], sha256 = "4242424242", ) load("@rules_<LANG>//<LANG>:repositories.bzl", "rules_<LANG>_dependencies", "rules_<LANG>_toolchains") rules_<LANG>_dependencies() rules_<LANG>_toolchains()
There should be tests that verify that the rules are working as expected. This
can either be in the standard location for the language the rules are for or a
tests/ directory at the top level.
It is useful to users to have an
examples/ directory that shows users a couple
of basic ways that the rules can be used.
Many rulesets use GitHub Actions. See the configuration used in the rules-template repo, which are simplified using a "reusable workflow" hosted in the bazel-contrib
ci.yaml runs tests on each PR and
main comit, and
release.yaml runs anytime you push a tag to the repository.
See comments in the rules-template repo for more information.
See the Stardoc documentation for instructions on how to comment your rules so that documentation can be generated automatically.
The rules-template docs/ folder
shows a simple way to ensure the Markdown content in the
docs/ folder is always up-to-date
as Starlark files are updated.
Why can't we add our rule to the main Bazel GitHub repository?
We want to decouple rules from Bazel releases as much as possible. It's clearer who owns individual rules, reducing the load on Bazel developers. For our users, decoupling makes it easier to modify, upgrade, downgrade, and replace rules. Contributing to rules can be lighter weight than contributing to Bazel - depending on the rules -, including full submit access to the corresponding GitHub repository. Getting submit access to Bazel itself is a much more involved process.
The downside is a more complicated one-time installation process for our users:
they have to copy-paste a rule into their
WORKSPACE file, as shown in the
README.md section above.
We used to have all of the rules in the Bazel repository (under
//tools/build_defs). We still have a couple rules
there, but we are working on moving the remaining rules out.