Bazel Rules: Decoupling Rules Interface

2022-02-03

Intro #

The simple structure we used for the packaged repo is convenient for basic scenarios. However, we usually want to separate the externally available rule definitions from their implementation. A conventional way to achieve this is to create a file that will only contain the rule names:

load(
    "//example/packaged:rules.bzl",
    _foo_rule = "foo_rule",
    _bar_rule = "bar_rule",
    ...
)

foo_rule = _foo_rule
bar_rule = _bar_rule
...

Implmentation #

The first step towards this goal is to restructure the demo_repo:

demo_repo/
├── internal/
│  ├── BUILD.bazel
│  └── rules.bzl
├── BUILD.bazel
├── defs.bzl
└── WORKSPACE.bazel

As we can see, the rules are in the internal folder now. The name only communicates our intent as Bazel does not prevent users from loading rules directly. We also need to mark the folder as a package with an empty BUILD.bazel file.

The core of the pattern is the defs.bzl file where we load and expose the rules:

load(
    "//internal:rules.bzl",
    _demo_rule = "demo_rule",
)

demo_rule = _demo_rule

The users can then load them normally:

load("@demo_repo//:defs.bzl", "demo_rule")

demo_rule(
    name = "file",
    out = "hello.txt",
)

The code for the example is in this repo.

bazel

Bazel Rules: Packaging How To Install Docker on Debian Unstable