created | last updated | status | reviewers | title | authors | discussion thread | ||
---|---|---|---|---|---|---|---|---|
2023-06-08 |
2023-08-08 |
Approved |
|
Standard Platform Transitions |
|
Many rule authors and users want to create targets that can easily change the target platform: rule authors may want to add convenient rule attributes for this, whereas users frequently need to bundle together executables for multiple platforms in a single high-level artifact.
A previous proposal was submitted, but the functionality was too narrowly scoped to fit most use cases, and the changes to Bazel were too high to be worth it. This proposal instead suggests several transitions and rules which can be added to the existing bazelbuild/platforms repository, and which rule authors or users can then import and use. Because these are pure Starlark implementations, with no Bazel changes, rule authors can feel free to extend or ignore these as needed.
Rules that want to change the target platform can use the change_platform
transition, which can be parameterized in a few different ways. These work with
incoming (rule) transitions and outgoing (attribute) transitions.
Some rules want to change to a single, statically determined platform, either
for the entire rule or for a specific attribute. They can use the platform
parameter to change_platform
, passing a Label to use for the new value of the
--platforms
flag.
my_rule = rule(
cfg = change_platform(platform = Label("//new/target:platform")),
)
Some rules want to change to a single platform based on a standard attribute
named “platform”, either for the entire rule or for a specific attribute. The
rule is responsible for defining the platform
attribute.
Open Question: Should we define a function to help define the attribute?
my_rule = rule(
cfg = change_platform(),
attrs = {
"platform": attr.label(providers = [platform_common.PlatformInfo]),
},
)
Some rules want to change to a single platform based on a custom attribute, either for the entire rule or for a specific attribute. The rule is responsible for defining the attribute.
my_rule = rule(
cfg = change_platform(attribute = "my_platform"),
attrs = {
"my_platform": attr.label(providers = [platform_common.PlatformInfo]),
},
)
Rules that want to perform split transitions can use the split_platforms
transition, with similar parameters. These only work with outgoing (attribute)
transitions.
Some rules want to change to a set of statically determined platforms for a
specific attribute. They can use the platforms
parameter to
split_platforms
, passing a list of Labels to use for the new value of the
--platforms
flag.
my_rule = rule(
attrs = {
"deps": attr.label_list(
cfg = split_platforms(platforms = [
Label("//new/target:platform1"),
Label("//new/target:platform1"),
]),
),
}
)
Some rules want to change to a set of platforms based on a standard attribute
named “platforms” for a specific attribute. The rule is responsible for
defining the platforms
attribute.
my_rule = rule(
attrs = {
"deps": attr.label_list(
cfg = split_platforms(),
),
"platforms": attr.label_list(providers = [platform_common.PlatformInfo]),
}
)
Some rules want to change to a set of platforms based on a custom attribute for a specific attribute. The rule is responsible for defining the attribute.
my_rule = rule(
attrs = {
"deps": attr.label_list(
cfg = split_platforms(attribute = "my_platforms"),
),
"my_platforms": attr.label_list(providers = [platform_common.PlatformInfo]),
}
)
The different variants of these transitions are technically functions that
create transitions (in order to parameterize the new flag values correctly).
Each will be given a unique name based on the current package and be used only
for the rule it is attached to. Specifically, using change_platform(platform = foo)
multiple times from different attributes or rules will generate multiple
transitions.
An alternate approach would be to name each based on a hash of the parameters, and thus avoid duplication in the same package. This can be explored if the overhead of multiple identical transitions seems to be too high.
The platform_data
rule can be used to change the target platform of a target,
and then depend on that elsewhere in the build tree.
cc_binary(name = "foo")
platform_data(
name = "foo_embedded",
target = ":foo",
platform = "//my/new:platform",
)
py_binary(
name = "flasher",
srcs = ...,
data = [
":foo_embedded",
],
)
Regardless of what platform the top-level :flasher
binary is built for, the
:foo_embedded
target will be built for //my/new:platform
.
In theory, a similar multiplatform_data
rule could be written which does a
split transition on a platforms
attribute, but it is currently unclear what
the best way to pass that data back to the parent target would be, given that
we do not want to rewrite rules to understand a custom provider. This will not
be implemented until there is a clearer use case.