buck2 icon indicating copy to clipboard operation
buck2 copied to clipboard

Footgun: `dynamic_actions` in bxl top-level scope is `bxl.dynamic_actions` which is different than the normal `dynamic_actions` in a bzl file

Open lf- opened this issue 5 months ago • 0 comments

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!
    """
)

lf- avatar Jul 11 '25 23:07 lf-