Design Document: Label-keyed String Dictionary Type for Build Attributes
Design documents are not descriptions of the current functionality of Bazel. Always go to the documentation for current information.
Status: Implemented in 5e9e194
Author: Michael Staib
Design document published: 03 March 2017
Background/Motivation
For future work in the realm of allowing Bazel users to define configuration
flags, the config_setting
rule will need to be able to test configuration
values defined by labels rather than strings. The current solution uses a
dictionary from string (the flag to check the value of) to string (the value
to check against). It makes sense, then, to use a dictionary from label (the
flag to check the value of) to string (the value to check against) for
user-defined configuration which is defined by a label.
Additionally, for work which relates to setting such user-defined configuration, rules should be able to declare similar dictionaries for the purposes of setting those same flags.
An example incorporating both testing and setting such flags:
flag_rule(
name = "beep",
values = ["boop", "bop", "bump"],
default = "bump"
)
config_setting(
name = "beep#boop",
flag_values = {
":beep": "boop"
}
)
transition_rule(
name = "configuration",
deps = [
":lib"
],
sets_flags = {
":beep": "boop"
}
)
library_rule(
name = "lib"
deps = select({
":beep#boop": [":boop_dep"],
"//conditions:default": [":other_dep"]
})
)
New attribute type: LABEL_KEYED_STRING_DICT
In order to handle these flag values, the BUILD language will need the ability
to express a mapping from a label (a flag’s label, to be precise) to a string
(the flag’s value). This will be added as BuildType.LABEL_KEYED_STRING_DICT
in
native rules, and as attr.label_keyed_string_dict()
in Skylark (taking the
same parameters as attr.label_list()
). This will have to be serializable
to query --output=proto
format.
Native rule representation
Native rules will be able to take the attribute’s value using an
AttributeMapper
, as normal. In this case, the type returned will be
Map<Label, String>
. In conjunction with RuleContext.getPrerequisites
, this
can be used to get both the target and the string value associated with it by
iterating over the return value from getPrerequisites
and looking up the
labels of the TransitiveInfoCollection
s in the map.
Skylark representation
Skylark rules must render some representation of this structure in
ctx.attr.<attrname>
. The only restriction on Skylark dictionary keys is that
they must be immutable, which the various ConfiguredTarget
s are (although they
must be annotated as such). Accordingly, the value of ctx.attr.<attrname>
is a
dictionary mapping Target to string. This will have to be changed to be another
special case in the SkylarkRuleContext
.
Because each target in an attribute will undergo the same transition - if any - and the transition of the target itself will always be the same, the keys of this dict will be unique - i.e., there will be no collisions - as long as the labels used to construct it were unique.
Handling collisions when converting attribute values from Skylark
Labels are special in that there are multiple ways (and possibly multiple
encodings!) to represent them in a BUILD or Skylark file which are not the same
from Skylark’s point of view. In the package //label
, the strings "label"
,
":label"
, "//label"
, and "//label:label"
all evaluate to the same Label
when they are picked up by Bazel, but they will be different keys in the dict
created by Skylark, where they are merely strings. Skylark does have a label
type (constructed with Label("//label")
, yet another way of representing the
same label), and Bazel does accept it for LABEL
attributes, but most uses of
label-type attributes take advantage of Bazel’s automatic conversion of strings
in label-type attributes. That conversion does not happen until the Skylark
value enters the build system at a rule attribute, at which point the value may
have been mutated, read, and passed around in Skylark several times.
In Skylark, it is an error for a dictionary literal to contain multiple items
with the same key. For consistency and simplicity, LABEL_KEYED_STRING_DICT
will throw a ConversionException
in its convert method if two of the Skylark
dict’s keys evaluate to the same label, even if they also have the same value.
This only covers the case where the two keys are distinct strings; if two
identical keys are used in a dictionary literal, there will be an error in
Skylark before this logic ever sees it. Mutations of a key (i.e.,
dictionary[key] = value
for a key
which is already in the dictionary) will
continue to be allowed as normal.
Testing Plan
- Conversion exception for non-dict values
- Conversion exception for dicts other than string-to-string
- Conversion exception for dicts with multiple keys evaluating to the same label
- Conversion exception for dicts with invalid labels as keys
- Successfully converts to
Map<Label, String>
- Successfully converts to query proto
- Successfully converts to query XML
- Successfully outputs in build format from query
- visitLabels visits the labels in the keys
- Skylark can define
label_keyed_string_dict
attributes and receive them as a dict of Target to string - Skylark can define
label_keyed_string_dict
attributes with provider requirements and have them be respected - Skylark can define
label_keyed_string_dict
attributes with filetype requirements and have them be respected - Skylark can define
label_keyed_string_dict
attributes and require they not be empty - Skylark can define
label_keyed_string_dict
attributes and make them mandatory - Skylark can define
label_keyed_string_dict
attributes and set the default value - Skylark can define
label_keyed_string_dict
attributes and have Aspects follow them