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

Use edge transitions not self transitions #3130

Closed

Conversation

illicitonion
Copy link
Contributor

What type of PR is this?

Bug fix

What does this PR do? Why is it needed?

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.

Which issues(s) does this PR fix?

Fixes #3103

Other notes for review

The first commit just extracts variables for attr dicts without modifying them, the second contains the meat of the change.

@illicitonion
Copy link
Contributor Author

/cc @uhthomas @fmeum

@achew22
Copy link
Member

achew22 commented May 4, 2022

@fmeum, how much does this overlap with your other transition PRs?

@fmeum
Copy link
Collaborator

fmeum commented May 4, 2022

@fmeum, how much does this overlap with your other transition PRs?

It does overlap in terms of merge conflicts, but the goals are completely separate. When in doubt, merge this PR first as it is certainly more complicated than mine and would require more effort to rebase.

@achew22 achew22 requested review from achew22 and linzhp May 4, 2022 18:20
@linzhp
Copy link
Contributor

linzhp commented May 4, 2022

@achew22 let me know when you finish your review and want me to test in Uber.

@illicitonion illicitonion force-pushed the edge-transitions-reviewable branch 3 times, most recently from 56bbbb2 to cbc9a5c Compare May 23, 2022 13:30
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
Copy link
Contributor Author

@achew22 I've rebased this past #3143 - would you have some time to take a look? It's gotten smaller as a result of the rebasing :)

Copy link
Collaborator

@fmeum fmeum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only while reviewing did I gain some much-needed clarity on the way to configurable attributes. @illicitonion Sorry for all the back and forth on this!

Currently, no attributes are configurable since we use a self transition. With this PR, all attributes would beome configurable, with potentially confusing or at least unclear semantics in the case where goos/goarch is set and there is also a select on a constraint setting.

At the same time, while technically sound, this PR adds quite a bit of complexity: A new wrapper (around a wrapper), including the need to forward providers and symlink executables.

@uhthomas @linzhp Given the fact that the alternate approach of #3116 would achieve mostly the same by deleting complex code, I would like to hear your thoughts on the following plan:

  1. Merge Transition on edges not self #3116, but with a small addition: Keep a macro wrapper around go_binary that fails the build if goos/goarch are set and any other attribute is a select expression. Since select expressions didn't work before, this does not affect backwards compatibility - it merely restricts a new feature to an (IMO) reasonable subset of its full scope.
  2. Think about ways to replace goos/goarch in a way that doesn't complicate go_binary. For example, we could introduce a go_switch_os_arch rule that handles the platform transition for a single go_binary in its attributes.
  3. Deprecate and eventually remove goos/goarch as Bazel platform support matures further.

go_transition_binary = go_transition_rule(executable = True, **_go_binary_kwargs)
go_non_executable_transition_binary = go_transition_rule(executable = False, **_go_binary_kwargs)
go_binary = rule(executable = True, **go_binary_kwargs)
go_non_executable_transition_binary = rule(executable = False, **go_binary_kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
go_non_executable_transition_binary = rule(executable = False, **go_binary_kwargs)
go_non_executable_binary = rule(executable = False, **go_binary_kwargs)

}),
executable = True,
)
go_non_executable_transition_transition_binary = rule(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
go_non_executable_transition_transition_binary = rule(
go_non_executable_transition_binary = rule(

)
go_non_executable_transition_transition_binary = rule(
implementation = forward_through_transition_impl,
attrs = dicts.add(transition_attrs, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implementation and attrs could be extracted into e.g. _go_transition_binary_kwargs.

@@ -165,11 +167,19 @@ def _go_binary_impl(ctx):
ccinfo = cc_common.merge_cc_infos(
cc_infos = [ccinfo] + [d[CcInfo] for d in source.cdeps],
)
providers.append(ccinfo)
providers_to_forward.append(ccinfo)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe leave this as providers as these providers are still forwarded only in addition to being advertised by the rule itself?

implementation = forward_through_transition_impl,
attrs = dicts.add(transition_attrs, {
"is_windows": attr.bool(),
"transition_dep": attr.label(cfg = go_transition),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this is only an internal attribute, we could help the IntelliJ plugin here by giving it a name recognized by its aspect, see fmeum/rules_meta@8d57be3.

@@ -168,13 +191,8 @@ def _go_test_impl(ctx):
# source file is present, Bazel will set the COVERAGE_OUTPUT_FILE
# environment variable during tests and will save that file to the build
# events + test outputs.
return [
providers_to_forward = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, could stay as providers.

go_binary(
name = "configurable_srcs",
srcs = select({
"@io_bazel_rules_go//go/platform:darwin": ["main_darwin.go"],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example contains exactly what worries me a bit about this PR: The use of goos changes the platform, but then another attribute is set based on a select on the platform. It is not immediately obvious in which "order" these are evaluated. Do you see a concrete use case for this pattern? If not, maybe we should make goos/goarch explicitly non-configurable by failing in the wrapper macro if they are set to a type that equals type(select({"//conditions:default": True})).

@@ -456,3 +497,38 @@ def _set_ternary(settings, attr, name):
label = filter_transition_label("@io_bazel_rules_go//go/config:{}".format(name))
settings[label] = value == "on"
return value

def _symlink_file_to_rule_name(ctx, src):
output_file_name = ctx.label.name + (".exe" if ctx.attr.is_windows else "")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK go_binary has an out attribute that we should probably take into account here.


def _symlink_file_to_rule_name(ctx, src):
output_file_name = ctx.label.name + (".exe" if ctx.attr.is_windows else "")
dst = ctx.actions.declare_file(paths.join(ctx.label.name, output_file_name))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same logic that go_binary uses today? Ideally, the new executable would have the same name is that used go_binary before this PR to retain compatibility with hard-coded paths to outputs.

if file == None:
return default_info

data_runfiles = default_info.data_runfiles.merge(ctx.runfiles([file]))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct that the original executable will still be in the runfiles? On Windows, this may be significant as the symlink may end up being a full copy.

@fmeum
Copy link
Collaborator

fmeum commented Jun 2, 2022

@illicitonion I discussed this with @achew22 and @linzhp and we arrived at the conclusion that merging #3116 with the small addition noted in #3130 (review) is the preferred solution for both rules_go users and maintainers: It prevents users from introducing confusing interactions with platforms and essentially implements a new feature by deleting complicated code, which is nice.

@illicitonion
Copy link
Contributor Author

Closing in favour of #3116

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Can't configure static differently for different platforms
4 participants