Bazel Rules: Attributes

2021-12-27 • edited 2021-12-30

We can now try to improve our previous example. An obvious code smell that we did not address yet is the magic strings. In this part, we will replace this horrible abomination

...
        content = "#!/bin/sh\necho \"Hello, World!\"\n",
...

with something more practical and introduce a way to pass user-defined parameters.

Script Body #

StarLark top-level variables are immutable and by convention use CAPITAL case. We can define a private multi-line string variable with a Python heredoc:

_TEMPLATE = """\
#!/bin/sh
echo "{}"
"""
...

The {} placeholder is for the format function in the implementation function:

...
        content = _TEMPLATE.format("Hello, World!"),
...

Attributes #

This looks better, but it would be nice to make the message configurable. Attributes are used to define custom rule parameters that we can set in the BUILD file:

...
demo_binary(
    name = "attributes",
    message = "Hello, World!",
)

To support this, we need a couple of rules.bzl changes. First, we define the attributes as the rule function parameter:

...
demo_binary = rule(
    ...
    attrs = {
        "message": attr.string(mandatory = True),
    },
)
...

Our actions can now use the attribute through the context object:

def _demo_binary_impl(ctx):
    ...
    ctx.actions.write(
        output = out,
        content = _TEMPLATE.format(ctx.attr.message),
    )
    ...

Results #

We can build and run the target:

$ bazel run //attributes
...
Target //attributes:attributes up-to-date:
  bazel-bin/attributes/hello
...
Hello, World!

And verify the script content:

$ cat bazel-bin/attributes/hello
#!/bin/sh
echo "Hello, World!"

The complete example is here.

bazel

Bazel Rules: Create an Executable Bazel Rules: Output