-
-
Notifications
You must be signed in to change notification settings - Fork 673
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
Use edge transitions not self transitions #3130
Conversation
79da723
to
95331e7
Compare
95331e7
to
8ba8b61
Compare
@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 let me know when you finish your review and want me to test in Uber. |
56bbbb2
to
cbc9a5c
Compare
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 bazel-contrib#3103.
cbc9a5c
to
4077a66
Compare
There was a problem hiding this 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:
- Merge Transition on edges not self #3116, but with a small addition: Keep a macro wrapper around
go_binary
thatfail
s the build ifgoos
/goarch
are set and any other attribute is aselect
expression. Sinceselect
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. - Think about ways to replace
goos
/goarch
in a way that doesn't complicatego_binary
. For example, we could introduce ago_switch_os_arch
rule that handles the platform transition for a singlego_binary
in its attributes. - 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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 = [ |
There was a problem hiding this comment.
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"], |
There was a problem hiding this comment.
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 fail
ing 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 "") |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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])) |
There was a problem hiding this comment.
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.
@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. |
Closing in favour of #3116 |
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.