Intro #
Packaging Bazel rules for re-use is not very straightforward, and I hope the addition of Bzlmod in Bazel 5 will help to improve the situation. For now, we typically package rules as a Git repo or an HTTP archive and then use workspace rules to load them into our current workspace. We will create a couple of repositories demonstrating this scenario in this example.
Packaging a repository #
The demo_repo
folder provides an example of a packaged Bazel
repository. It contains a simple rule that produces a text
file:
demo_repo/
├── BUILD.bazel
├── rules.bzl
└── WORKSPACE.bazel
When dealing with multiple repositories, naming becomes essential, so
the WORKSPACE.bazel
file contains the name of the repo:
workspace(name = "demo_repo")
The rules.bzl
file provides the demo_rule
implementation:
def _demo_rule_impl(ctx):
ctx.actions.write(
output = ctx.outputs.out,
content = "Hello, World!\n",
)
demo_rule = rule(
implementation = _demo_rule_impl,
attrs = {
"out": attr.output(mandatory = True),
},
)
The BUILD.bazel
file is empty as I don’t intend to build any targets
within this repository. Still, Bazel requires this file to be present
to indicate that the folder constitutes a package.
Using the packaged repository #
We have a second repository called user_repo
which we will use to call
the demo_rule
:
user_repo/
├── BUILD.bazel
└── WORKSPACE.bazel
Let’s have a look at the WORKSPACE.bazel
:
workspace(name = "user_repo")
local_repository(
name = "demo_repo",
path = "../demo_repo",
)
The important part here is the call to local_repository
function that
allows us to import an external repository from the local filesystem into
our workspace. While local_repository
is very convenient for testing and
debugging rules, the actual users would expect to be able to use either
the http_archive
or the git_repository
rule:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
http_archive(
name = "foo_rules",
urls = ["http://example.com/foo_rules.zip"],
sha256 = "...",
)
git_repository(
name = "bar_rules",
remote = "http://example.com/git/bar_rules.git",
tag = "...",
)
We can use non-Bazel sources with new_local_repository
,
new_http_archive
, or new_git_repository
rules, and we can also
implement a custom [repository rule], but that is out of scope for now.
The only interesting part about the BUILD.bazel
file is that we refer
to the repository we imported:
load("@demo_repo//:rules.bzl", "demo_rule")
demo_rule(
name = "file",
out = "hello.txt",
)
Building the example within the user_repo
should now produce the file:
$ cd user_repo
$ build //:file
...
Target //:file up-to-date:
bazel-bin/hello.txt
...
$ cat bazel-bin/hello.txt
Hello, World!
The complete code for this example is here.