No way to use toolchains correctly in executable rule output
- Dominant language
- Java
- Stars
- 25.8k
- Forks
- 4.6k
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 72
Description
### Description of the feature request:
A common pattern[^1] I see in rules is toolchain declarations that effectively capture a tool _path_, and templated executable/test rules that resolve the toolchain and substitute the tool path into the executable file output.
Unfortunately this is incorrect! When the exec and target platforms differ, the toolchain will be resolved for the _exec_ platform, but the executable/test output needs to be for the _target_ platform.
As far as I can see, there is no way to make toolchain resolution / platform transitions do the correct thing here. Since executable rules are how bazel tests work, this is a pretty major gap in the toolchains/platforms story. I can't file bugs against rules, because there is no reasonable solution to suggest. This is a feature request for a solution that is (a) correct, and also ideally (b) easy/obvious.
One onerous current workaround is to declare _two_ toolchains for all such cases. One that uses exec_compatible_with/target_compatible_with as usual, and is used when the toolchain is invoked by an action directly. And a second toolchain that uses target_compatible_with _only_, which can be used in executable rule output. This is moderately terrible, since this duplicates work for everybody, confuses the meaning of exec_compatible_with vs target_compatible_with, and toolchain authors can't anticipate when their toolchain needs to be used in this way - undoing the loose coupling between author and consumer that is kind of the point of toolchains.
An incorrect (afaics) version of the two-toolchains approach is to _only_ declare target_compatible_with toolchain, then create a "current_foo_toolchain" rule that resolves the toolchain for a particular target, and returns the toolchain provider through a rule attribute - see eg [python toolchain](https://github.com/bazelbuild/rules_python/blob/main/docs/python.md#current_py_toolchain). This is incorrect in general because another toolchain that depends on this toolchain cannot be resolved at the same time for the same exec+target combination (ie: `toolchains=[foo]` with a dependency on another rule with `toolchains=[bar]`, is not the same as `toolchains=[foo, bar]`).
As a possible implementation, I'd _love_ to be able to do `exec_group(cfg='target')`, since I think `ctx.exec_groups["target"].toolchains` is an intuitive and 'obvious' way to address the above.
Another option is to _not_ use toolchains for what are effectively artifacts. Rule authors obviously like the loose coupling between toolchain declaration and use however, so this potential path would require widespread advocacy/documentation, and perhaps a canonical meta-library for an "artifact toolchain".
See also slack thread: https://bazelbuild.slack.com/archives/CA31HN1T3/p1690176577746239
[^1]: Examples of (incorrect) toolchains used for executable rules in [rules_js](https://github.com/aspect-build/rules_js/issues/1168), [rules_docker](https://github.com/bazelbuild/rules_docker/blob/8e70c6bcb584a15a8fd061ea489b933c0ff344ca/contrib/push-all.bzl#L56), [rules_oci](https://github.com/bazel-contrib/rules_oci/blob/54d04c20c6bfa0aa1089385e5abe35af462f0bd7/oci/private/push.bzl#L102-L103), [gazelle](https://github.com/bazelbuild/bazel-gazelle/blob/c11d7ce0133088d69e5a18541f219490cc3c1996/def.bzl#L93)
### Which category does this issue belong to?
Core
### What underlying problem are you trying to solve with this feature?
It should be possible to resolve and refer to the correct toolchain for an executable rule output.
### Which operating system are you running Bazel on?
macos and Linux
### What is the output of `bazel info release`?
6.3.2
### If `bazel info release` returns `development version` or `(@non-git)`, tell us how you built Bazel.
_No response_
### What's the output of `git remote get-url origin; git rev-parse master; git rev-parse HEAD` ?
_No response_
### Have you found anything relevant by searching the web?
_No response_
### Any other information, logs, or outputs that you want to share?
_No response_
Contributor guide
Research direction
Start with executable rules, toolchain resolution, exec groups, and the proposed ctx.exec_groups["target"].toolchains entry point. Compare the linked rules_js, rules_docker, rules_oci, and Gazelle examples with the current two-toolchain workaround. Done means executable outputs resolve the toolchain for the target platform without duplicated declarations, while preserving correct exec-platform resolution.
Written by the indexing model from the issue text.
Assessment
- Domain
- build-system
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100