This documentation is for rule writers who are planning to make their rules available to others.
New rules should go into their own GitHub repository under your organization. Contact the bazel-dev mailing list if you feel like your rules belong in the bazelbuild organization.
Repository names for Bazel rules are standardized on the following format: $ORGANIZATION/rules_$NAME
. See examples on GitHub. For consistency, you must follow this same format when publishing your Bazel rules.
Make sure to use a descriptive GitHub repository description and README.md
title, example:
bazelbuild/rules_go
golang
, bazel
README.md
header: 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 (e.g., Scala) or some notion of platform (e.g., Android).
Every rule repository should have a certain layout so that users can quickly understand new rules.
For example, suppose we are writing new rules for the (make-believe) mockascript
language. We 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>
(e.g. rules_mockascript
). Otherwise, you should name your repository <org>_rules_<lang>
(e.g. build_stack_rules_proto
). Please contact bazel-dev mailing list if you feel like your rules should follow the convention for rules in the bazelbuild organization.
In the following sections, we will 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 rules, this looks like:
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 Skydoc rules, which depend on the Sass rules), and provide a WORKSPACE macro that will download all dependencies (see rules_go
above).
Often times there will be multiple rules provided by your repository. Create a directory named by the language and provide an entry point - defs.bzl
file exporting all rules (also include a BUILD
file so the directory is a package). For 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 toolchain rules, it‘s possible that you’ll need to define custom constraint_setting
s and/or constraint_value
s. Put these into a //<LANG>/constraints
package. Your directory structure will look like this:
/ mockascript/ constraints/ BUILD BUILD defs.bzl
Please read github.com/bazelbuild/platforms 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, using selects). With custom constraints, you define a language that the whole Bazel ecosystem will speak.
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 of //<LANG>/runfiles:runfiles
). User targets that need to access their data dependencies will typically add this target to their deps
attribute.
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 WORKSPACE file.
Create a file named <LANG>/repositories.bzl
and provide a single entry point macro named 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 WORKSPACE macro that registers these toolchains. This way users can decide to omit the previous macro and control dependencies manually, while still being allowed to register toolchains.
Therefore add a WORKSPACE macro named rules_<LANG>_toolchains
into <LANG>/repositories.bzl
file.
Note that in order to resolve toolchains in the analysis phase Bazel needs to analyze all 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 repository with <LANG>_toolchain
targets. Former will be always fetched, and the latter will only be fetched when user actually needs to build <LANG>
code.
In your release announcement provide a snippet that your users can copy-paste into their 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.
Set up Travis as described in their getting started docs. Then add a .travis.yml
file to your repository with the following content:
dist: xenial # Ubuntu 16.04 # On trusty (or later) images, the Bazel apt repository can be used. addons: apt: sources: - sourceline: 'deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8' key_url: 'https://bazel.build/bazel-release.pub.gpg' packages: - bazel script: - bazel build //... - bazel test //...
If your repository is under the bazelbuild organization, you can ask to add it to ci.bazel.build.
See the Skydoc documentation for instructions on how to comment your rules so that documentation can be generated automatically.
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_rules
or //tools/build_defs
). We still have a couple rules there, but we are working on moving the remaining rules out.