Repository Rules

Status: Experimental. We may make breaking changes to the API, but we will announce them.

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)})

name attributes are implicitly defined for all repository_rules. To access an attribute, use repository_ctx.attr.<attribute_name>. The name of a repository rule is accessible with repository_ctx.name.

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, 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 request 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 the repository_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 not mypkg/label.txt).

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.

  • maven_jar is a reimplementation of the native maven_jar rule using the maven tool.