bazelrules

Bazel make variables substitution


I read the following bazel manual about make variables (or custom variables): https://bazel.build/reference/be/make-variables#custom_variables

"Attributes marked as "Subject to 'Make variable' substitution" can reference the "Make" variable FOO as follows:"

my_attr = "prefix $(FOO) suffix"

My question is: how to mark attribute as "Subject to 'Make variable' substitution" ?

There is nothing about this in the documentation about rule attributes: https://bazel.build/rules/lib/toplevel/attr

Please help

In my example

### BUILD.bazel

load("version.bzl", "version_rule")
load("file.bzl", "file")

version_rule(
    name = "version_provider",
    varname = "VERSION",
)

file(
    name = "hello",
    version = ":version_provider",
    output_name = "hello",
    content = "Hello world $(VERSION)\n",
)

### version.bzl

def _version_rule_impl(ctx):
    print("\n------------------- _version_rule_impl\n")
    return [
        platform_common.TemplateVariableInfo({
            'VERSION': '1-2-3-4',
        }),
    ]

version_rule = rule(
    implementation = _version_rule_impl,
    attrs = {
        "varname": attr.string(mandatory = True),
    },
)

### file.bzl

def file(**kwargs):
    _file(out = "{output_name}.txt".format(**kwargs), **kwargs)

def _impl(ctx):
    output = ctx.outputs.out
    ctx.actions.write(output = output, content = ctx.attr.content)

_file = rule(
    implementation = _impl,
    attrs = {
        "version": attr.label(providers=[platform_common.TemplateVariableInfo]),
        "output_name": attr.string(mandatory=True),
        "content": attr.string(),
        "out": attr.output()
    },
)

I expect that generated file contents will be "Hello world 1-2-3-4\n" but it is "Hello world $(VERSION)\n"


Solution

  • Unfortunately this isn't super clear, but "Attributes marked as "Subject to 'Make variable' substitution" is just talking about the documentation of rule attributes. There's no configuring of an attribute to make it automatically substitute variables. Variable substitution is done in the rule implementation code:

    def _impl(ctx):
        output = ctx.outputs.out
        ctx.actions.write(
            output = output,
            content = ctx.expand_make_variables(
                "content",
                ctx.attr.content,
                ctx.attr.version[platform_common.TemplateVariableInfo].variables),
        )
    

    There's nothing special about platform_common.TemplateVariableInfo in this usage, I believe it was originally used as a bridge between some Starlark code and some native (i.e. implemented inside Bazel in Java) platform configuration code. It's special in that ctx.expand_make_variables can automatically search for variables from platform_common.TemplateVariableInfo providers, but only through specific attributes which can't really be used from Starlark (https://github.com/bazelbuild/bazel/blob/3f93d377d036d773fd505a18e084425a00fb94ea/src/main/java/com/google/devtools/build/lib/analysis/ConfigurationMakeVariableContext.java#L68-L72)