Skip to content

Instantly share code, notes, and snippets.

@rrbutani
Created February 14, 2026 05:26
Show Gist options
  • Select an option

  • Save rrbutani/4bdb2e99cb776832a806203e00659aec to your computer and use it in GitHub Desktop.

Select an option

Save rrbutani/4bdb2e99cb776832a806203e00659aec to your computer and use it in GitHub Desktop.

default on attr.label (and attr.label_list, attr.string_keyed_label_dict) can be a function!

  • but: only for private attributes

this includes things like "late bound defaults"

but also your own functions can ask for other (public) attributes of the rule as keyword args and use them to compute a default!

a neat alternative to initializers on rules

this gist contains an example use case and implementations using computed defaults as well as rule initializers

see defs.bzl and BUILD.bazel

Note

The neat capability that these (computed defaults, initializers) have as compared with regular macros is that they run after the selects have been resolved!

use nix -p bazel_8
/.direnv
/bazel-*
/MODULE.bazel.lock
load(":defs.bzl", "project_component", "consumer1", "consumer2")
################################################################################
# defines `a.foo`, `a.bar`, `a.baz` as well
project_component(name = "a")
project_component(name = "b") # likewise
# by default this uses `a.foo` for `foo`, `a.bar` for `bar`, etc.
consumer1(name = "c1", component = ":a")
# same thing but with initializers:
# consumer2(name = "c2", component = ":a", foo = ":b.foo")
################################################################################
# interactions with `select`:
config_setting(
name = "opt",
values = {"compilation_mode": "opt"},
)
consumer1(name = "c1_s", component = select({
":opt": ":b",
"//conditions:default": ":a",
}))
# consumer2(name = "c2_s", component = select({"//conditions:default": ":a"}))
ComponentInfo = provider(fields = dict(foo = "", bar = "", baz = ""))
foo = rule(implementation = lambda _: [])
bar = rule(implementation = lambda _: [])
baz = rule(implementation = lambda _: [])
component = rule(
implementation = lambda c: ComponentInfo(
foo = c.attr.foo, bar = c.attr.bar, baz = c.attr.baz,
),
provides = [ComponentInfo],
attrs = dict(foo = attr.label(), bar = attr.label(), baz = attr.label()),
)
project_component = macro(
implementation = lambda name, visibility, **kw: [
foo(name = name + ".foo", visibility = visibility, **kw),
bar(name = name + ".bar", visibility = visibility, **kw),
baz(name = name + ".baz", visibility = visibility, **kw),
component(
name = name, visibility = visibility,
foo = name + ".foo",
bar = name + ".bar",
baz = name + ".baz",
),
None
][-1],
)
consumer1 = rule(
implementation = lambda _: [],
attrs = dict(
component = attr.label(providers = [ComponentInfo]),
_foo = attr.label(default = lambda component: Label(str(component) + [print(component, type(component)), ".foo"][1])),
_bar = attr.label(default = lambda component: Label(str(component) + ".bar")),
_baz = attr.label(default = lambda component: Label(str(component) + ".baz")),
)
)
consumer2 = rule(
implementation = lambda _: [],
attrs = dict(
component = attr.label(providers = [ComponentInfo]),
_foo = attr.label(),
_bar = attr.label(),
_baz = attr.label(),
),
initializer = lambda component, **kw: dict(
_foo = str(component) + [".foo", print(component, type(component))][0],
_bar = str(component) + ".bar",
_baz = str(component) + ".baz",
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment