Skip to content

Transitions can inspect configurable attributes, but don't see a value for them #15157

@illicitonion

Description

@illicitonion

Description of the problem / feature request:

When a rule has a configurable attribute, a transition which operates on the rule gets an attr map which doesn't contain the attr name or value at all.

This can cause buggy behaviour. e.g. this transition in rules_go looks at what the value of getattr(attr, "static", "auto") to decide whether to statically link a binary, but if the static attribute is a configurable attribute, the transition will always see None as the value of the attribute, so will fall back to its default behaviour.

Ideally the transition would get the current configuration's value of the attribute.

If that's not feasible, trying to read one of these attributes in a configuration should be an error (conceptually, we'd be declaring any attribute used in a transition as non-configurable, but by doing this detection on the fly and raising a runtime error; alternatively, we could introduce a "non-configurable" marker on attrs, and require it to be set for any attributes read by transitions).

In the case we decide to forbid this acces, it would be great to work out how rules_go's transition should be determining whether a target should be compiled as static.

Either way, silently hiding the attribute is error-prone.

Bugs: what's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

% touch WORKSPACE
% cat >rules.bzl <<EOF
def _print_transition_impl(settings, attr):
    print("Attr: {}".format(getattr(attr, "explicit_config", "NOT PRESENT")))
    return {}

print_transition = transition(
    implementation = _print_transition_impl,
    inputs = ["//command_line_option:cpu"],
    outputs = ["//command_line_option:compilation_mode"],
)

def _is_transitioned_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.write(
        output = out,
        content = ctx.attr.explicit_config,
    )
    return [DefaultInfo(files=depset([out]))]

is_transitioned = rule(
    implementation = _is_transitioned_impl,
    attrs = {
        "explicit_config": attr.string(default = "one"),
        "_whitelist_function_transition": attr.label(default = "@bazel_tools//tools/whitelists/function_transition_whitelist"),
    },
    cfg = print_transition,
)
EOF
% cat >BUILD.bazel <<EOF
load("//:rules.bzl", "is_transitioned")

is_transitioned(
    name = "transitioned",
    # This version shows up in the transition:
    #explicit_config = "String in BUILD",
    # This version does not:
    explicit_config = select({"//conditions:default": "Default condition in BUILD"}),
)
EOF
% bazel build :transitioned

Expect to see printed "Attr: Default condition in BUILD"
Actually see printed: "Attr: NOT PRESENT"

If you switch over the commented explicit_config values in the BUILD.bazel file and re-run, you'll see "Attr: String in BUILD" as expected.

What operating system are you running Bazel on?

macOS

What's the output of bazel info release?

release 6.0.0-pre.20220324.1

Metadata

Metadata

Assignees

Labels

P1I'll work on this now. (Assignee required)team-Configurabilityplatforms, toolchains, cquery, select(), config transitionstype: feature request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions