Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
illicitonion opened this issue Apr 1, 2022 · 14 comments
Assignees
Labels
P1 I'll work on this now. (Assignee required) team-Configurability platforms, toolchains, cquery, select(), config transitions type: feature request

Comments

@illicitonion
Copy link
Contributor

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

@illicitonion
Copy link
Contributor Author

/cc @gregestren

@gregestren
Copy link
Contributor

Just skimming this (apologies!), but I think this is an intentional design restriction described at https://bazel.build/rules/config#defining. Specifically:

When attached as an incoming edge transition, attr does not include any attributes that use a selector to resolve their value. If an incoming edge transition on --foo reads attribute bar and then also selects on --foo to set attribute bar, then there's a chance for the incoming edge transition to read the wrong value of bar in the transition.

https://docs.google.com/document/d/1VIRx06cZB4wLU-ASq1XKFHmx67yfHtNOCbCempaPeaA/ gets into the ugly details:

In order to make the second change work, we need to introduce restrictions for starlark rules that use parameterized rule class transitions. Parameterized rule class transitions cannot depend on attribute values that proceed to depend on configuration (i.e. through a select) since this would introduce a configuration->attribute->configuration dependency cycle.

To deal with this, we’ll throw runtime errors for transition implementation functions that create these dependency cycles. The specific case we’re interested in is when (1) a build setting* is declared written by a transition at any point** on the incoming edge to a rule-transitioned target, (2) an incoming rule transition transition reads an attribute of the target, and (3) that target configures the same attribute using the same build setting. If this happens, we’ll throw an error when we try to access that attribute in the transition implementation function. This all gets a bit hairy, see the examples below for a more concrete example.

I agree that silent failure is no good. It looks like the original intention was for Bazel to communicate the problem?

Also, I think the main conclusion from then was that supporting your use case is technically hard. That doesn't necessarily mean it's impossible - there may be some innovative thinking we didn't think of. The current state is largely a desire to get the functionality working and aiming for just not supporting certain use cases vs. blocking the functionality on a more complete solution. I'm open to creative ideas beside better error messaging if you can think of any.

@sgowroji sgowroji added team-Configurability platforms, toolchains, cquery, select(), config transitions untriaged labels Apr 7, 2022
illicitonion added a commit to illicitonion/rules_go that referenced this issue Apr 11, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

Instead, insert a dummy rule which exists only to transition its
dependencies using an edge transition, which means the appropriate
attributes are resolved before the transition is called.

To achieve this, we symlink the actual built binaries into our
transitioning wrapper rules, and forward any providers needed.

Fixes bazelbuild#3103.
illicitonion added a commit to illicitonion/rules_go that referenced this issue Apr 12, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

We only actually consult configuration data in `_go_context_data`, so we
only actually need to transition on the edges which (transitively) reach
a `_go_context_data`, which is `_go_context_data` itself and `deps`.
@gregestren gregestren added P3 We're not considering working on this, but happy to review a PR. (No assignee) type: feature request and removed untriaged labels Apr 13, 2022
illicitonion added a commit to illicitonion/rules_go that referenced this issue Apr 19, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

We only actually consult configuration data in `_go_context_data`, so we
only actually need to transition on the edges which (transitively) reach
a `_go_context_data`, which is `_go_context_data` itself and `deps`.
illicitonion added a commit to illicitonion/rules_go that referenced this issue Apr 25, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

Instead, insert a dummy rule which exists only to transition its
dependencies using an edge transition, which means the appropriate
attributes are resolved before the transition is called.

To achieve this, we symlink the actual built binaries into our
transitioning wrapper rules, and forward any providers needed.

Fixes bazelbuild#3103.
@fmeum
Copy link
Collaborator

fmeum commented May 4, 2022

@gregestren I'm not sure how difficult this would be to implement, but how about the following: Populate the configured value for all attributes for which the select keys are disjoint from the set of output settings declared by the transition. This should get around all the difficult situations, but still allow for the common, non-pathological use cases.

@gregestren
Copy link
Contributor

I think that's possible. I think the logic would be:

  1. In the part of the parent's ConfiguredTargetFunction that computes child configurations:
  2. if //:child has both a self-transition and a select()
  3. then get its select() keys as labels and Skyframe-evaluate those labels to targets.
  4. Then parse those targets to fine-tune the set of unsafe flags.

It sounds tricky but doable. Mostly tricky in not spreading the logic across too many classes or overly-complicating method signatures. I'm also not sure about manually parsing the config_setting's Target vs. calling its ConfiguredTarget and leveraging whatever code already determines what flags are in there.

@gregestren
Copy link
Contributor

Re: rules_go - are there reports of breakages? How'd you find this error?

@illicitonion
Copy link
Contributor Author

Re: rules_go - are there reports of breakages? How'd you find this error?

Yes, I ran into this issue when I started trying to conditionally make static binaries depending on the target platform. bazelbuild/rules_go#3103 was my issue report there, and bazelbuild/rules_go#3130 a proposed (but somewhat hacky) fix.

@gregestren
Copy link
Contributor

Got it, thanks.

Principally I support narrowing down which attributes we can't access as much as we reasonably can. I don't have time at the moment to tackle this but I'm open to contributions. With the caveat that there is a balance of utility vs. code complexity, so any code I'd want to be very careful doesn't overly complicate Bazel's code in one of the more nuance parts of its code base. That's where I'd focus review attention.

illicitonion added a commit to illicitonion/rules_go that referenced this issue May 23, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

Instead, insert a dummy rule which exists only to transition its
dependencies using an edge transition, which means the appropriate
attributes are resolved before the transition is called.

To achieve this, we symlink the actual built binaries into our
transitioning wrapper rules, and forward any providers needed.

Fixes bazelbuild#3103.
illicitonion added a commit to illicitonion/rules_go that referenced this issue May 23, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

Instead, insert a dummy rule which exists only to transition its
dependencies using an edge transition, which means the appropriate
attributes are resolved before the transition is called.

To achieve this, we symlink the actual built binaries into our
transitioning wrapper rules, and forward any providers needed.

Fixes bazelbuild#3103.
illicitonion added a commit to illicitonion/rules_go that referenced this issue May 23, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

Instead, insert a dummy rule which exists only to transition its
dependencies using an edge transition, which means the appropriate
attributes are resolved before the transition is called.

To achieve this, we symlink the actual built binaries into our
transitioning wrapper rules, and forward any providers needed.

Fixes bazelbuild#3103.
illicitonion added a commit to illicitonion/rules_go that referenced this issue May 23, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

Instead, insert a dummy rule which exists only to transition its
dependencies using an edge transition, which means the appropriate
attributes are resolved before the transition is called.

To achieve this, we symlink the actual built binaries into our
transitioning wrapper rules, and forward any providers needed.

Fixes bazelbuild#3103.
illicitonion added a commit to illicitonion/rules_go that referenced this issue Jun 14, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

We only actually consult configuration data in `_go_context_data`, so we
only actually need to transition on the edges which (transitively) reach
a `_go_context_data`, which is `_go_context_data` itself and `deps`.
illicitonion added a commit to illicitonion/rules_go that referenced this issue Jun 20, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

We only actually consult configuration data in `_go_context_data`, so we
only actually need to transition on the edges which (transitively) reach
a `_go_context_data`, which is `_go_context_data` itself and `deps`.
linzhp pushed a commit to bazelbuild/rules_go that referenced this issue Jun 23, 2022
As per bazelbuild/bazel#15157 currently we
can't configure any transition-relevant attributes using select. This is
because we use self-transitions on targets, and when this happens,
configurable attributes aren't passed to the transition, so we make
incorrect transition decisions.

We only actually consult configuration data in `_go_context_data`, so we
only actually need to transition on the edges which (transitively) reach
a `_go_context_data`, which is `_go_context_data` itself and `deps`.
@gregestren gregestren added the next_month Issues that we will review next month. This is currently mainly used by Configurability team label Nov 4, 2022
@gregestren
Copy link
Contributor

I've heard another request from https://github.com/lizkammer.

@aiuto aiuto self-assigned this Jan 12, 2023
@aiuto
Copy link
Contributor

aiuto commented Jan 12, 2023

Not really assigning myself, but github does not have a CC capability

@aranguyen aranguyen removed the next_month Issues that we will review next month. This is currently mainly used by Configurability team label Jan 13, 2023
@aiuto
Copy link
Contributor

aiuto commented Jan 13, 2023

We do intend to prioritize this shortly. Stay tuned.

@aranguyen aranguyen assigned aranguyen and unassigned aiuto and gregestren Jan 27, 2023
@aranguyen aranguyen added P2 We'll consider working on this in future. (Assignee optional) and removed P3 We're not considering working on this, but happy to review a PR. (No assignee) labels Jan 27, 2023
@gregestren
Copy link
Contributor

Update:

@aranguyen has been working on this with good success. Main remaining technical complications:

We intend to either have this done by end-of-June or a have a clear update of the final work remaining by then.

@gregestren gregestren added P1 I'll work on this now. (Assignee required) and removed P2 We'll consider working on this in future. (Assignee optional) labels Jun 12, 2023
@gregestren
Copy link
Contributor

Reviewing at https://bazel-review.googlesource.com/c/bazel/+/224533.

@aiuto
Copy link
Contributor

aiuto commented Aug 16, 2023

Any updates?

@gregestren
Copy link
Contributor

You can see the latest patch set from https://bazel-review.googlesource.com/c/bazel/+/224533 is from Aug 15. We're doing an internal review on this, which looks close to ready to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P1 I'll work on this now. (Assignee required) team-Configurability platforms, toolchains, cquery, select(), config transitions type: feature request
Projects
None yet
Development

No branches or pull requests

6 participants