Footgun: `dynamic_actions` in bxl top-level scope is `bxl.dynamic_actions` which is different than the normal `dynamic_actions` in a bzl file
Problem:
$ buck bxl //snowydeer/snowydeer.bxl:main -- --target toolchains//:hspec-discover
BXL FAILED
Traceback (most recent call last):
File <builtin>, in <module>
error: Missing parameter `actions` for call to `snowydeer/snowydeer.bxl._dump_nix_paths_dynamic_impl`
This happens if you copy paste dynamic_actions out of a .bzl file into a .bxl file, in which case it has a slightly different type signature: instead of taking actions as the first argument, it takes bxl.Context.
Additionally the error message doesn't provide any traceback as to why it happened; this is also a bug.
What I would like is an error that catches doing the wrong thing and tells you you're doing the wrong thing, ideally with a link to the docs. Maybe dynamic_actions should just not exist at top level scope in bxl files (I suspect this is related to #1021 where it appears that bxl used to use global scope but now doesn't for many things), so one possible solution could be a deprecation warning of the old one that says the type signature is different?
The following is an example which I wish I had more time to minimize, but I am including it verbatim as illustration; it's not runnable in its included state. NixDynamicInfo is a provider that exists on a target and is returned from a rule implementation:
nix_dynamic_info = NixDynamicInfo(
dynamic = _read_out_link(ctx, out_link),
)
# ...
return [ DefaultInfo(...), nix_dynamic_info ]
Definition of NixDynamicInfo:
NixDynamicInfo = provider(
doc = """Provides nix-side dynamic information.""",
fields = {
"dynamic": DynamicValue,
},
)
NixPathInfo = provider(
doc = """Provides the absolute /nix/store path.""",
fields = {
"path": str,
},
)
Code for //snowydeer/snowydeer.bxl (not minimized, sorry):
load("@toolchains//nix.bzl", "NixDynamicInfo", "NixPathInfo")
def _dump_nix_paths_dynamic_impl(
actions,
list_of_packages: OutputArtifact,
dyn_nix_paths: list[ResolvedDynamicValue]) -> list[Provider]:
nix_paths = [dyn_nix_path.providers[NixPathInfo].path for dyn_nix_path in dyn_nix_paths]
str = "".join(nix_paths).strip()
actions.write(list_of_packages, "{}".format(str))
return []
_dump_nix_paths_dynamic = dynamic_actions(
impl = _dump_nix_paths_dynamic_impl,
attrs = {
"list_of_packages": dynattrs.output(),
"dyn_nix_paths": dynattrs.list(dynattrs.dynamic_value()),
},
)
def _collect_nix_deps(ctx: bxl.Context):
target = ctx.cli_args.target
target = ctx.analysis(ctx.configured_targets(target))
artifact = target.providers()[NixDynamicInfo].dynamic
actions = ctx.bxl_actions()
list_of_paths = actions.actions.declare_output("all_nix_paths.txt")
actions.actions.dynamic_output_new(_dump_nix_paths_dynamic(
dyn_nix_paths = [artifact],
list_of_packages = list_of_paths.as_output(),
))
ctx.output.ensure(list_of_paths)
ctx.output.print("meow")
main = bxl.main(
impl = _collect_nix_deps,
cli_args = {
"target": bxl.cli_args.target_label("Target to inspect"),
},
doc = """
Boops!
"""
)