Skip to content

Implement transition.and_then - #29542

Closed
fmeum wants to merge 1 commit into
bazelbuild:masterfrom
fmeum:compose-transitions
Closed

Implement transition.and_then#29542
fmeum wants to merge 1 commit into
bazelbuild:masterfrom
fmeum:compose-transitions

Conversation

@fmeum

@fmeum fmeum commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Description

Implements the proposal https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

Motivation

This change in particular allows non-toolchain dependencies of rules to inherit both the execution platform (via config.exec()) as well as the current target platform (via a custom transition that records //command_line_option:platforms), which is important for certain cross-compilation scenarios.

Build API Changes

Yes, as discussed and approved in https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

Checklist

  • I have added tests for the new use cases (if any).
  • I have updated the documentation (if applicable).

Release Notes

RELNOTES[NEW]: The and_then method on transitions can be used to compose transitions. Both Starlark transitions and native transitions (e.g. config.exec()) are supported.

@fmeum
fmeum force-pushed the compose-transitions branch 3 times, most recently from 5091465 to e33d3b9 Compare May 17, 2026 06:15
@fmeum
fmeum marked this pull request as ready for review May 17, 2026 06:30
@fmeum
fmeum requested review from a team, fweikert and gregestren as code owners May 17, 2026 06:30
@fmeum
fmeum requested a review from katre May 17, 2026 06:30
@github-actions github-actions Bot added team-Configurability platforms, toolchains, cquery, select(), config transitions team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file. team-Documentation Documentation improvements that cannot be directly linked to other team labels awaiting-review PR is awaiting review from an assigned reviewer team-Rules-API API for writing rules/aspects: providers, runfiles, actions, artifacts team-Loading-API BUILD file and macro processing: labels, package(), visibility, glob labels May 17, 2026
@fmeum

fmeum commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

FYI @UebelAndre

@fmeum fmeum removed team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file. team-Loading-API BUILD file and macro processing: labels, package(), visibility, glob labels May 17, 2026
@fmeum
fmeum removed request for a team and fweikert May 17, 2026 06:31

@katre katre left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The Starlark transition composition implementation is great: it's a very nice approach that I hadn't considered.

I'm confused about the WASM compilation and CHANGELOG changes, are they from a different PR?

// defined in Starlark via, cfg = transition
return new StarlarkRuleTransitionProvider(starlarkDefinedConfigTransition);
}
if (cfg instanceof ComposedConfigurationTransitionApi composition) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can this be combined with the equivalent code from StarlarkAttrModule?

* subsequent transition reads the build settings produced by the previous one. Nested compositions
* are flattened, so {@link #getElements} is always a flat chain of non-composed transitions.
*/
public final class ComposedConfigurationTransitionApi implements ConfigurationTransitionApi {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Given that this doesn't define a specific Starlark API:
a) Should this be in a different package (which makes it difficult to use from ConfigurationTransitionApi)?
b) Should this have a different name (just remove the Api)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Renamed to ComposedConfigurationTransition (dropping Api) — agree it isn't a Starlark API surface in its own right, it's just the deferred holder backing transition.and_then. Kept it in starlarkbuildapi.config so ConfigurationTransitionApi's default and_then can still reference it; moving it under analysis.config would force every ConfigurationTransitionApi implementation to override and_then to escape the package layering.

scratch.file(
"test/rules.bzl",
"""
bad_transition = config.exec().and_then(config.exec())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Which line is reported as the error, line 262 or line 266? I want to be sure this is debugabble in more complex situations where transitions are defined in different Starlark files.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good question. With the original code the error fired at the use site (the attr.label(cfg = bad_transition) line), which for cross-file definitions left the user to grep for bad_transition to find the actual .and_then call.

To make this debuggable I now capture thread.getCallerLocation() at and_then time and store it on the ComposedConfigurationTransition. The materializer includes that location in the error message, so users see both the cfg= use site (in the standard Starlark traceback) and the original composition site. For this test the error reads:

File ".../test/rules.bzl", line 5, column 31, in <toplevel>
    attrs = {"dep": attr.label(cfg = bad_transition)},
Error in label: invalid composed transition for `cfg`: can't compose two exec transitions (composed at /workspace/test/rules.bzl:1:40)

Test now asserts both substrings (attrs = {"dep": attr.label(cfg = bad_transition)}, and composed at /workspace/test/rules.bzl:1:).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, this looks much easier to debug.

Comment thread CHANGELOG.md
@fmeum
fmeum force-pushed the compose-transitions branch from e33d3b9 to bf62502 Compare May 21, 2026 20:34
Adds an `and_then(other)` method to the `transition` Starlark builtin
that returns a new transition applying this one followed by `other`.
The result is itself a transition and can be composed further.

A composition may be used as a rule or attribute transition wherever
its component transitions could be used. At most one of the composed
factories may target the exec configuration (enforced by
`ComposingTransitionFactory.of`, which now throws the new checked
`IncompatibleTransitionsException` for invalid compositions; an
`ofUnchecked` wrapper is provided for internal call sites where the
inputs are structurally guaranteed compatible).

When two of the composed transitions are 1:2+, the result has the
cross product of their splits; the key for each combined split is the
comma-separated concatenation of the component keys. Documents the
new feature in `site/en/extending/config.md`.

Also fixes `ComposingTransitionFactory.transitionType` to return the
more specific of its children's types (previously always the first
child's, which was incorrect for `ANY + RULE/ATTRIBUTE` compositions),
and updates `DependencyResolutionHelpers.getExecutionPlatformLabel` to
find the exec transition factory anywhere inside a composition via
`visit`.

Adds `StarlarkTransitionCompositionTest` covering all combinations of
1-2 Starlark transitions and 0-1 exec transitions (including S+E+S
and split+split), plus the error cases E+E and exec-in-rule-cfg.
@fmeum
fmeum force-pushed the compose-transitions branch from bf62502 to 29ee03d Compare May 21, 2026 20:49
scratch.file(
"test/rules.bzl",
"""
bad_transition = config.exec().and_then(config.exec())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, this looks much easier to debug.

@fmeum

fmeum commented May 21, 2026

Copy link
Copy Markdown
Collaborator Author

@bazel-io fork 9.2.0

@fmeum

fmeum commented May 21, 2026

Copy link
Copy Markdown
Collaborator Author

@bazel-io fork 8.8.0

@github-actions github-actions Bot added the community-reviewed Reviewed by a trusted community contributor label May 21, 2026
@fmeum
fmeum requested a review from aranguyen May 22, 2026 18:54
@fmeum

fmeum commented May 22, 2026

Copy link
Copy Markdown
Collaborator Author

@aranguyen Could you review this for approval?

@katre
katre requested a review from dabanki May 22, 2026 18:56

@gregestren gregestren left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice to see this, @fmeum !

FYI @ajsinclair who's also interested in this.

Comment thread site/en/extending/config.md

A composed transition can be attached to a rule or attribute wherever its
component transitions could be used (subject to the usual restriction that an
[incoming edge transition](#incoming-edge-transitions) must be 1:1). At most one

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I can't remember how the code enforces this restriction.

A Starlark transition can dynamically produce 1 or >1 outputs in its implementation function, so this property can't be checked statically. I don't think this PR changes that, but do you remember how we protect against an accidental 1:2 incoming transition? Is it only checked on evaluation?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

There is

checkState(transitionResult.size() == 1, "Expected exactly one result: %s", transitionResult);
and
if (result.size() != 1) {
eventHandler.handle(
Event.error(
starlarkDefinedConfigTransition.getLocation(),
"Rule transition only allowed to return a single transitioned configuration."));
return buildOptions.clone();

@gregestren gregestren removed the team-Rules-API API for writing rules/aspects: providers, runfiles, actions, artifacts label Jun 2, 2026
@fmeum
fmeum requested a review from gregestren June 8, 2026 20:27
@gregestren gregestren added awaiting-PR-merge PR has been approved by a reviewer and is ready to be merge internally and removed awaiting-review PR is awaiting review from an assigned reviewer labels Jun 12, 2026
@fmeum

fmeum commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

@gregestren This would be great to get into 9.2.0 so that folks can start playing with it, do you think that's possible?

@ajsinclair

Copy link
Copy Markdown
Contributor

@gregestren This would be great to get into 9.2.0 so that folks can start playing with it, do you think that's possible?

For what it's worth I'll do some testing with it internal to Google once we have this submitted and released internally. Those changes might make their way out to rules_android depending on how we handle Bazel versioning requirements with that ruleset.

@gregestren

Copy link
Copy Markdown
Contributor

@gregestren This would be great to get into 9.2.0 so that folks can start playing with it, do you think that's possible?

I pinged the import merge.

@github-actions github-actions Bot removed the awaiting-PR-merge PR has been approved by a reviewer and is ready to be merge internally label Jun 18, 2026
fmeum added a commit to fmeum/bazel that referenced this pull request Jun 18, 2026
Implements the proposal https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

This change in particular allows non-toolchain dependencies of rules to inherit both the execution platform (via `config.exec()`) as well as the current target platform (via a custom transition that records `//command_line_option:platforms`), which is important for certain cross-compilation scenarios.

Yes, as discussed and approved in https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

- [x] I have added tests for the new use cases (if any).
- [x] I have updated the documentation (if applicable).

RELNOTES[NEW]: The `and_then` method on `transition`s can be used to compose transitions. Both Starlark transitions and native transitions (e.g. `config.exec()`) are supported.

Closes bazelbuild#29542.

PiperOrigin-RevId: 934095515
Change-Id: I3f36ec0907c4af5bdf43e38323d166422b47051c
(cherry picked from commit d089ae2)
bazel-io pushed a commit to bazel-io/bazel that referenced this pull request Jun 22, 2026
Implements the proposal
https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

This change in particular allows non-toolchain dependencies of rules to
inherit both the execution platform (via `config.exec()`) as well as the
current target platform (via a custom transition that records
`//command_line_option:platforms`), which is important for certain
cross-compilation scenarios.

Yes, as discussed and approved in
https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

- [x] I have added tests for the new use cases (if any).
- [x] I have updated the documentation (if applicable).

RELNOTES[NEW]: The `and_then` method on `transition`s can be used to
compose transitions. Both Starlark transitions and native transitions
(e.g. `config.exec()`) are supported.

Closes bazelbuild#29542.

PiperOrigin-RevId: 934095515
Change-Id: I3f36ec0907c4af5bdf43e38323d166422b47051c 
(cherry picked from commit d089ae2)

<!--
Thank you for contributing to Bazel!
Please read the contribution guidelines:
https://bazel.build/contribute.html
-->

### Description
<!--
Please provide a brief summary of the changes in this PR.
-->

### Motivation
<!--
Why is this change important? Does it fix a specific bug or add a new
feature?
If this PR fixes an existing issue, please link it here (e.g. "Fixes
bazelbuild#1234").
-->

### Build API Changes
<!--
Does this PR affect the Build API? (e.g. Starlark API, providers,
command-line flags, native rules)
If yes, please answer the following:
1. Has this been discussed in a design doc or issue? (Please link it)
2. Is the change backward compatible?
3. If it's a breaking change, what is the migration plan?
-->

No

### Checklist

- [ ] I have added tests for the new use cases (if any).
- [ ] I have updated the documentation (if applicable).

### Release Notes

<!--
If this is a new feature, please add 'RELNOTES[NEW]: <description>'
here.
If this is a breaking change, please add 'RELNOTES[INC]: <reason>' here.
If this change should be mentioned in release notes, please add
'RELNOTES: <reason>' here.
-->

RELNOTES: None
fmeum added a commit to fmeum/bazel that referenced this pull request Jul 17, 2026
### Description

Implements the proposal https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

### Motivation

This change in particular allows non-toolchain dependencies of rules to inherit both the execution platform (via `config.exec()`) as well as the current target platform (via a custom transition that records `//command_line_option:platforms`), which is important for certain cross-compilation scenarios.

### Build API Changes

Yes, as discussed and approved in https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

### Checklist

- [x] I have added tests for the new use cases (if any).
- [x] I have updated the documentation (if applicable).

### Release Notes

RELNOTES[NEW]: The `and_then` method on `transition`s can be used to compose transitions. Both Starlark transitions and native transitions (e.g. `config.exec()`) are supported.

Closes bazelbuild#29542.

PiperOrigin-RevId: 934095515
Change-Id: I3f36ec0907c4af5bdf43e38323d166422b47051c
(cherry picked from commit d089ae2)
bazel-io pushed a commit to bazel-io/bazel that referenced this pull request Jul 21, 2026
### Description

Implements the proposal
https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

### Motivation

This change in particular allows non-toolchain dependencies of rules to
inherit both the execution platform (via `config.exec()`) as well as the
current target platform (via a custom transition that records
`//command_line_option:platforms`), which is important for certain
cross-compilation scenarios.

### Build API Changes

Yes, as discussed and approved in
https://github.com/bazelbuild/proposals/blob/main/designs/2024-04-16-transition-composition.md.

### Checklist

- [x] I have added tests for the new use cases (if any).
- [x] I have updated the documentation (if applicable).

### Release Notes

RELNOTES[NEW]: The `and_then` method on `transition`s can be used to
compose transitions. Both Starlark transitions and native transitions
(e.g. `config.exec()`) are supported.

Closes bazelbuild#29542.

PiperOrigin-RevId: 934095515
Change-Id: I3f36ec0907c4af5bdf43e38323d166422b47051c
(cherry picked from commit d089ae2)

Closes bazelbuild#29620
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-reviewed Reviewed by a trusted community contributor team-Configurability platforms, toolchains, cquery, select(), config transitions team-Documentation Documentation improvements that cannot be directly linked to other team labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants