//python/private:rule_builders.bzl
Builders for creating rules, aspects et al.
When defining rules, Bazel only allows creating immutable objects that can’t be introspected. This makes it difficult to perform arbitrary customizations of how a rule is defined, which makes extending a rule implementation prone to copy/paste issues and version skew.
These builders are, essentially, mutable and inspectable wrappers for those Bazel objects. This allows defining a rule where the values are mutable and callers can customize them to derive their own variant of the rule while still inheriting everything else about the rule.
To that end, the builders are not strict in how they handle values. They
generally assume that the values provided are valid and provide ways to
override their logic and force particular values to be used when they are
eventually converted to the args for calling e.g. rule().
Important
When using builders, most lists, dicts, et al passed into them must be
locally created values, otherwise they won’t be mutable. This is due to Bazel’s
implicit immutability rules: after evaluating a .bzl file, its global
variables are frozen.
Tip
To aid defining reusable pieces, many APIs accept no-arg callable functions
that create a builder. For example, common attributes can be stored
in a dict[str, lambda], e.g. ATTRS = {"srcs": lambda: LabelList(...)}.
Example usage:
load(":rule_builders.bzl", "ruleb")
load(":attr_builders.bzl", "attrb")
# File: foo_binary.bzl
_COMMON_ATTRS = {
"srcs": lambda: attrb.LabelList(...),
}
def create_foo_binary_builder():
foo = ruleb.Rule(
executable = True,
)
foo.implementation.set(_foo_binary_impl)
foo.attrs.update(COMMON_ATTRS)
return foo
def create_foo_test_builder():
foo = create_foo_binary_build()
binary_impl = foo.implementation.get()
def foo_test_impl(ctx):
binary_impl(ctx)
...
foo.implementation.set(foo_test_impl)
foo.executable.set(False)
foo.test.test(True)
foo.attrs.update(
_coverage = attrb.Label(default="//:coverage")
)
return foo
foo_binary = create_foo_binary_builder().build()
foo_test = create_foo_test_builder().build()
# File: custom_foo_binary.bzl
load(":foo_binary.bzl", "create_foo_binary_builder")
def create_custom_foo_binary():
r = create_foo_binary_builder()
r.attrs["srcs"].default.append("whatever.txt")
return r.build()
custom_foo_binary = create_custom_foo_binary()
Added in version 1.3.0.
- typedef AttributeBuilder
An abstract base typedef for builder for a Bazel
AttributeInstances of this are a builder for a particular
Attributetype, e.g.attr.label,attr.string, etc.
- typedef AttrsDict
Builder for the dictionary of rule attributes.
- AttrsDict.map: dict[str, AttributeBuilder]
The underlying dict of attributes. Directly accessible so that regular dict operations (e.g.
x in y) can be performed, if necessary.
- AttrsDict.get(key, default=None)
Get an entry from the dict. Convenience wrapper for
.map.get(...)
- AttrsDict.items() list[tuple[str, object]]
Returns a list of key-value tuples. Convenience wrapper for
.map.items()
- AttrsDict.pop(key, default) object
Removes a key from the attr dict
- AttrsDict.build()
Build an attribute dict for passing to
rule().
- AttrsDict.new(initial)
Creates a builder for the
rule.attrsdict.
- AttrsDict.update(other)
Merge
otherinto this object.- Args:
other– (dict[str,callable|AttributeBuilder])the values to merge into this object. If the value a function, it is called with no args and expected to return an attribute builder. This allows defining dicts of common attributes (where the values are functions that create a builder) and merge them into the rule.
- typedef ExecGroup
Builder for
exec_group- ExecGroup.toolchains() list[ToolchainType]
- ExecGroup.exec_compatible_with() list[str | Label]
- ExecGroup.kwargs: dict[str, Any]
Additional kwargs to use when building. This is to allow manipulations that aren’t directly supported by the builder’s API. The state of this dict may or may not reflect prior API calls, and subsequent API calls may modify this dict. The general contract is that modifications to this will be respected when
build()is called, assuming there were no API calls in between.
- ExecGroup.build()
- ExecGroup.new(**kwargs)
Creates a builder for
exec_group.- Args:
kwargs– Same asexec_group
- Returns:
- typedef Rule
A builder to accumulate state for constructing a
ruleobject.- Rule.doc() str
- Rule.exec_groups() dict[str, ExecGroup]
- Rule.executable() bool
- Rule.kwargs: dict[str, Any]
Additional kwargs to use when building. This is to allow manipulations that aren’t directly supported by the builder’s API. The state of this dict may or may not reflect prior API calls, and subsequent API calls may modify this dict. The general contract is that modifications to this will be respected when
build()is called, assuming there were no API calls in between.
- Rule.fragments() list[str]
- Rule.implementation() callable | None
- Rule.provides() list[provider | list[provider]]
- Rule.set_doc(v)
- Rule.set_executable(v)
- Rule.set_implementation(v)
- Rule.set_test(v)
- Rule.test() bool
- Rule.toolchains() list[ToolchainType]
- Rule.build(debug='')
Builds a
ruleobject
- ruleb.ExecGroup(**kwargs)
Creates a builder for
exec_group.- Args:
kwargs– Same asexec_group
- Returns:
- ruleb.Rule(**kwargs)
Builder for creating rules.
- Args:
kwargs– The same as therule()function, but using builders or dicts to specify sub-objects instead of the immutable Bazel objects.
- ruleb.ToolchainType(name=None, **kwargs)
Creates a builder for
config_common.toolchain_type.- Args:
name– (str|Label|None) (default None)the toolchain type target.
kwargs– Same asconfig_common.toolchain_type
- Returns:
- typedef RuleCfg
Wrapper for
rule.cfgarg.- RuleCfg.implementation() str | callable | None | config.target | config.none
- RuleCfg.inputs() list[Label]
See also
The
add_inputs()andupdate_inputsmethods for adding unique values.
- RuleCfg.outputs() list[Label]
See also
The
add_outputs()andupdate_outputsmethods for adding unique values.
- RuleCfg.set_implementation(v)
The string values “target” and “none” are supported.
- RuleCfg.add_inputs(*inputs)
Adds an input to the list of inputs, if not present already.
See also
The
update_inputs()method for adding a collection of values.
- RuleCfg.add_outputs(*outputs)
Adds an output to the list of outputs, if not present already.
See also
The
update_outputs()method for adding a collection of values.
- RuleCfg.build()
Builds the rule cfg into the value rule.cfg arg value.
- Returns:
transitionthe transition object to apply to the rule.
- RuleCfg.new(rule_cfg_arg)
Creates a builder for the
rule.cfgarg.- Args:
rule_cfg_arg– (str|dict|None)The
cfgarg passed to Rule().
- Returns:
- RuleCfg.update_inputs(*others)
Add a collection of values to inputs.
- typedef ToolchainType
Builder for
config_common.toolchain_type- ToolchainType.kwargs: dict[str, Any]
Additional kwargs to use when building. This is to allow manipulations that aren’t directly supported by the builder’s API. The state of this dict may or may not reflect prior API calls, and subsequent API calls may modify this dict. The general contract is that modifications to this will be respected when
build()is called, assuming there were no API calls in between.
- ToolchainType.mandatory() bool
- ToolchainType.name() str | Label | None
- ToolchainType.set_name(v)
- ToolchainType.set_mandatory(v)
- ToolchainType.build()
Builds a
config_common.toolchain_type- Returns: