Skip to content

Instantly share code, notes, and snippets.

@rrbutani
Last active May 23, 2024 21:30
Show Gist options
  • Save rrbutani/f93bba7a353f0e8b65fd6842108fe387 to your computer and use it in GitHub Desktop.
Save rrbutani/f93bba7a353f0e8b65fd6842108fe387 to your computer and use it in GitHub Desktop.
bazel double exec transition test
common --toolchain_resolution_debug=.*
use nix -p bazel_7
/bazel-*
.direnv
/MODULE.bazel.lock
load(":defs.bzl", "emit_plats", "exec_plat_indicator", "emit_plats_behind_exec_transition", "string", "string_set")
constraint_setting(name = "setting", default_constraint_value = ":value0")
constraint_value(name = "value0", constraint_setting = ":setting")
constraint_value(name = "value1", constraint_setting = ":setting")
constraint_value(name = "value2", constraint_setting = ":setting")
constraint_value(name = "value3", constraint_setting = ":setting")
constraint_value(name = "value4", constraint_setting = ":setting")
config_setting(name = "is_0", constraint_values = [":value0"])
config_setting(name = "is_1", constraint_values = [":value1"])
config_setting(name = "is_2", constraint_values = [":value2"])
config_setting(name = "is_3", constraint_values = [":value3"])
config_setting(name = "is_target", constraint_values = [":value4"])
platform(
name = "exec_plat_1",
parents = ["@local_config_platform//:host"],
constraint_values = [":value1"],
)
platform(
name = "exec_plat_2",
parents = ["@local_config_platform//:host"],
constraint_values = [":value2"],
)
platform(
name = "exec_plat_3",
parents = ["@local_config_platform//:host"],
constraint_values = [":value3"],
)
exec_plat_indicator(
name = "exec_plat_indicator",
num = select({
":is_0": 0,
":is_1": 1,
":is_2": 2,
":is_3": 3,
})
)
################################################################################
emit_plats(
name = "plats1",
exec_compatible_with = [
":value1", # if not specified, will use the exec plat of the parent; i.e. when building `plats2`, exec will be 2 (not 0)
],
)
emit_plats_behind_exec_transition(
name = "plats2",
of = ":plats1",
exec_compatible_with = [
":value2",
],
)
################################################################################
# with `--platforms=//:target_plat`
platform(
name = "target_plat",
parents = ["@local_config_platform//:host"],
constraint_values = [":value4"],
)
string(
name = "libs",
string = select({
":is_target": "target",
"//conditions:default": "some_exec_platform",
}),
target_compatible_with = [
":value4", # can only be built *for* the target platform!
],
)
string(
name = "tools",
string = select({
":value0": "0",
":value1": "1",
":value2": "2",
":value3": "3",
}),
target_compatible_with = [
":value2",
],
)
string_set(
name = "toolchain",
exec = [":tools"],
target = [":libs"],
# Note: if these aren't specified, `exec_plat_2` won't be picked and
# `tools` (which is behind an exec transition) won't have its
# `target_compatible_with` constraints met:
exec_compatible_with = [
":value2",
],
)
# this is okay:
string_set(
name = "reexport_toolchain",
target = [":toolchain"],
)
# this is *not* okay:
string_set(
name = "reexport_toolchain_via_exec",
exec = [":toolchain"],
# - this rule selects: `target_plat`, `exec_plat_1` (target fixed, exec unconstrainted)
# - `:toolchain` gets: `exec_plat_1`, `exec_plat_2` (target comes from exec in ^, exec is constrained by `:value2`)
# - `:tools` then has: `exec_plat_2`, `exec_plat_1` (target comes from exec in ^`, exec unconstrained)
# * this fails because we're now building `tools` for the exec platform
# which is not what we want
)
ExecPlatIndicatorInfo = provider(fields = dict(num = "int"))
exec_plat_indicator = rule(
implementation = lambda ctx: [ExecPlatIndicatorInfo(num = ctx.attr.num)],
attrs = dict(num = attr.int()),
provides = [ExecPlatIndicatorInfo],
)
PlatsInfo = provider(fields = dict(plats_file = "file"))
def _emit_plats_impl(ctx):
file = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
file,
content = (
"build: " + str(ctx.attr._exec[ExecPlatIndicatorInfo].num) + "\n" +
# "host: " + str(ctx.fragments.platform.host_platform) + "\n" +
"target: " + str(ctx.fragments.platform.platform) + "\n"
),
)
return [DefaultInfo(files = depset([file])), PlatsInfo(plats_file = file)]
emit_plats = rule(
implementation = _emit_plats_impl,
attrs = {
"_exec": attr.label(
default = Label("//:exec_plat_indicator"),
providers = [ExecPlatIndicatorInfo],
cfg = "exec",
),
},
fragments = ["platform"],
provides = [DefaultInfo, PlatsInfo],
)
emit_plats_behind_exec_transition = rule(
implementation = lambda ctx: [
ctx.attr.of[DefaultInfo], ctx.attr.of[PlatsInfo],
],
attrs = {
"of": attr.label(providers = [DefaultInfo, PlatsInfo], cfg = "exec"),
},
provides = [DefaultInfo, PlatsInfo],
)
################################################################################
def _ssi_to_providers(ctx, strings):
file = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
file, content = "\n".join([
"{}: {}".format(k, v) for k, v in strings.items()
]),
)
return [
DefaultInfo(files = depset([file])),
SomeStringsInfo(strs = strings),
]
SomeStringsInfo = provider(fields = dict(strs = "dict[name, string]"))
string = rule(
implementation = lambda ctx: _ssi_to_providers(ctx, {
ctx.label.name: ctx.attr.string,
}),
attrs = dict(string = attr.string()),
provides = [SomeStringsInfo],
)
string_set = rule(
implementation = lambda ctx: _ssi_to_providers(ctx, {
k: v
for tgt in (ctx.attr.exec + ctx.attr.target)
for k, v in tgt[SomeStringsInfo].strs.items()
}),
attrs = dict(
exec = attr.label_list(providers = [SomeStringsInfo], cfg = "exec"),
target = attr.label_list(providers = [SomeStringsInfo], cfg = "target"),
),
provides = [SomeStringsInfo],
)
module(name = "double_exec_transition")
bazel_dep(name = "platforms", version = "0.0.7")
register_execution_platforms(
"//:exec_plat_1",
"//:exec_plat_2",
"//:exec_plat_3",
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment