Repository Rules
This page covers how to create repository rules and provides examples for more details.
An external repository is a rule that can be used only
in the WORKSPACE
file and enables non-hermetic operation at the loading phase
of Bazel. Each external repository rule creates its own workspace, with its
own BUILD files and artifacts. They can be used to depend on third-party
libraries (such as Maven packaged libraries) but also to generate BUILD files
specific to the host Bazel is running on.
Repository rule creation
In a .bzl
file, use the
repository_rule function to create a new
repository rule and store it in a global variable.
A custom repository rule can be used just like a native repository rule. It
has a mandatory name
attribute and every target present in its build files
can be referred as @<name>//package:target
where <name>
is the value of the
name
attribute.
The rule is loaded when you explicitly build it, or if it is a dependency of
the build. In this case, Bazel will execute its implementation
function. This
function describe how to create the repository, its content and BUILD files.
Attributes
An attribute is a rule argument, such as url
or sha256
. You must list
the attributes and their types when you define a repository rule.
local_repository = repository_rule(
implementation=_impl,
local=True,
attrs={"path": attr.string(mandatory=True)})
To access an attribute, use repository_ctx.attr.<attribute_name>
.
All repository_rule
s have implicitly defined attributes (just like build
rules). The two implicit attributes are name
(just like for build rules) and
repo_mapping
. The name of a repository rule is accessible with
repository_ctx.name
. The meaning of repo_mapping
is the same as for the
native repository rules
local_repository
and
new_local_repository
.
If an attribute name starts with _
it is private and users cannot set it.
Implementation function
Every repository rule requires an implementation
function. It contains the
actual logic of the rule and is executed strictly in the Loading Phase.
The function has exactly one input parameter, repository_ctx
. The function
returns either None
to signify that the rule is reproducible given the
specified parameters, or a dict with a set of parameters for that rule that
would turn that rule into a reproducible one generating the same repository. For
example, for a rule tracking a git repository that would mean returning a
specific commit identifier instead of a floating branch that was originally
specified.
The input parameter repository_ctx
can be used to
access attribute values, and non-hermetic functions (finding a binary,
executing a binary, creating a file in the repository or downloading a file
from the Internet). See the library for more
context. Example:
def _impl(repository_ctx):
repository_ctx.symlink(repository_ctx.attr.path, "")
local_repository = repository_rule(
implementation=_impl,
...)
When is the implementation function executed?
If the repository is declared as local
then change in a dependency
in the dependency graph (including the WORKSPACE file itself) will
cause an execution of the implementation function.
The implementation function can be restarted if a dependency it requests is missing. The beginning of the implementation function will be re-executed after the dependency has been resolved. To avoid unnecessary restarts (which are expensive, as network access might have to be repeated), label arguments are prefetched, provided all label arguments can be resolved to an existing file. Note that resolving a path from a string or a label that was constructed only during execution of the function might still cause a restart.
Finally, for non-local
repositories, only a change in the following
dependencies might cause a restart:
.bzl
files needed to define the repository rule.- Declaration of the repository rule in the
WORKSPACE
file. - Value of any environment variable declared with the
environ
attribute of therepository_rule
function. The value of those environment variable can be enforced from the command line with the--action_env
flag (but this flag will invalidate every action of the build). - Content of any file used and referred to by a label (e.g.,
//mypkg:label.txt
notmypkg/label.txt
).
Forcing refetch of external repositories
Sometimes, an external repository can become outdated without any change to its
definition or dependencies. For example, a repository fetching sources might
follow a particular branch of a third-party repository, and new commits are
available on that branch. In this case, you can ask bazel to refetch all
external repositories unconditionally by calling bazel sync
.
Moreover, some rules inspect the local machine and might become
outdated if the local machine was upgraded. Here you can ask bazel to
only refetch those external repositories where the
repository_rule
definition has the configure
attribute set, use bazel sync --configure
.
Examples
-
C++ auto-configured toolchain: it uses a repository rule to automatically create the C++ configuration files for Bazel by looking for the local C++ compiler, the environment and the flags the C++ compiler supports.
-
Go repositories uses several
repository_rule
to defines the list of dependencies needed to use the Go rules. -
rules_jvm_external creates an external repository called
@maven
by default that generates build targets for every Maven artifact in the transitive dependency tree.