BUILD:load(":defs.bzl", "installer", "flag", "config_hints", "genfoo")
cc_binary(
name = "bin_a",
srcs = ["main_a.c", ":foo"],
)
cc_binary(
name = "bin_b",
srcs = ["main_b.c", ":foo"],
)
genfoo(
name = "foo",
)
installer(
name = "installer",
deps = [
":bin_a",
":bin_b",
]
)
flag(
name = "flag",
build_setting_default = "0",
)
config_hints(name = "config_hints")
defs.bzl:# Custom Starlark flag
FlagProvider = provider(fields = ['value'])
def _impl(ctx):
return FlagProvider(value = ctx.build_setting_value)
flag = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
# Split transition
def _platform_expander_transition_impl(settings, attr):
return [
{"//command_line_option:compilation_mode": "opt", "//:flag": "1"},
{"//command_line_option:compilation_mode": "opt", "//:flag": "2"},
{"//command_line_option:compilation_mode": "dbg", "//:flag": "1"},
{"//command_line_option:compilation_mode": "dbg", "//:flag": "2"},
]
platform_expander_transition = transition(
implementation = _platform_expander_transition_impl,
inputs = [],
outputs = [
"//command_line_option:compilation_mode",
"//:flag",
],
)
# Puts configuration data into a provider so that each configuration split
# can be identified in _installer_impl
ConfigHintsProvider = provider(fields = ["compilation_mode", "flag"])
def _config_hints_impl(ctx):
return ConfigHintsProvider(
compilation_mode = ctx.var["COMPILATION_MODE"],
flag = ctx.attr.flag[FlagProvider].value,
)
config_hints = rule(
implementation = _config_hints_impl,
attrs = {
"flag": attr.label(default="//:flag"),
},
)
# Generate a file based on //:flag
def _genfoo_impl(ctx):
foo = ctx.actions.declare_file(ctx.attr.name + ".c")
ctx.actions.write(
output = foo,
content = """
int foo() {
return 42 * %d;
}
""" % int(ctx.attr.flag[FlagProvider].value))
return DefaultInfo(files = depset(direct = [foo]))
genfoo = rule(
implementation = _genfoo_impl,
attrs = {
"flag": attr.label(default="//:flag"),
},
)
# Installer rule
def _installer_impl(ctx):
installer = ctx.actions.declare_file(ctx.attr.name + ".zip")
zipper_args = ctx.actions.args()
zipper_args.add("c", installer.path)
zipper_inputs = []
for config_key in ctx.split_attr.deps:
config_hints = ctx.split_attr._config_hints[config_key][ConfigHintsProvider]
for dep in ctx.split_attr.deps[config_key]:
binary = dep.files.to_list()[0] # not usually a good idea to collapse depsets
zipper_inputs.append(binary)
path_in_zip = "%s-flag-%s/%s" % (
config_hints.compilation_mode,
config_hints.flag,
binary.basename)
zipper_args.add(path_in_zip + "=" + binary.path)
ctx.actions.run(
inputs = zipper_inputs,
outputs = [installer],
executable = ctx.executable._zipper,
arguments = [zipper_args],
progress_message = "Creating installer",
mnemonic = "zipper",
)
return DefaultInfo(files = depset(direct = [installer]))
installer = rule(
implementation = _installer_impl,
attrs = {
"deps": attr.label_list(cfg = platform_expander_transition),
"_config_hints": attr.label(default = ":config_hints", cfg = platform_expander_transition),
"_zipper": attr.label(default = "@bazel_tools//tools/zip:zipper", cfg = "exec", executable = True),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
},
)
main_a.c:#include "stdio.h"
int foo();
int main() {
#ifdef NDEBUG
printf("main a opt: foo is %d\n", foo());
#else
printf("main a dbg: foo is %d\n", foo());
#endif
return 0;
}
main_b.c:#include "stdio.h"
int foo();
int main() {
#ifdef NDEBUG
printf("main b opt: foo is %d\n", foo());
#else
printf("main b dbg: foo is %d\n", foo());
#endif
return 0;
}--
You received this message because you are subscribed to the Google Groups "bazel-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-discuss/32541363-f795-47ae-b65c-0c101abbf5f3n%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-discuss/CAAvaYBkgDsJ8JJdG0MtGOZY-%3Dhhkb8xstemAqKDQ4ykzbRm1QA%40mail.gmail.com.
Thank you Alex and Brian! It took me some time to wrap my head around those concepts, but by now I feel pretty much enlightened 😊
Interestingly I recently started moving in a very similar direction (although not that far) because of the different problem – we have aspect which needs to know some configuration. Naïve attempt to give aspect an attribute and populate it with select() failed with this error: “Unfortunately aspect attributes don't currently support select()”. Bummer. So after some deliberation I proudly came with the idea of another rule which can get necessary configuration with select(), pack it into a custom provider and then make our aspect have an attribute of type attr.label and point it to the instance of that “configuration” rule.
So Alex, your example was already somewhat familiar to me, but some fragments were revelations. One is this:
"deps": attr.label_list(cfg = platform_expander_transition),It is pretty bright idea to apply the same transform to two attributes and then check the provider in one to figure configuration of another. That I did not think about.
Another fragment is this:
for config_key in ctx.split_attr.deps:which actually shows how to get the keys to the split attribute dictionary and then extract split attribute values using those keys. I remember seeing it documented somewhere, but I could not make sense of that documentation. This example helped tremendously.
Brian, thank you for the hint about ctx.target_platform_has_constraint I did not know about it. At first it did not look very useful, because it is just another way of doing what we already can do with the configuration conveying rule. Also it requires implicit attribute for each configuration parameter we want to check. But then looking at this:
attrs = {
...
'_windows_constraint': attr.label(default = '@bazel_platforms//os:windows'),
},
windows_constraint = ctx.attr._windows_constraint[platform_common.ConstraintValueInfo]
if ctx.target_platform_has_constraint(windows_constraint):
I grasped the point about the correctness guarantees and it made sense after all.
I have not implemented it all on my side yet, but I feel pretty confident that I have all the pieces of the puzzle to put it together.
Thank you again for being so helpful!
Konstantin