Skip to content

Add "triggers" to quickly decide when to not run builders. #4084

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

Merged
merged 3 commits into from
Aug 12, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion build_config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 1.1.3-wip
## 1.2.0-wip

- Add top level key `triggers`. See
[the docs](https://github.com/dart-lang/build/blob/master/build_config/README.md#triggers)
for more information.
- Bump the min sdk to 3.7.0.
- Remove unused dep: `yaml`.

Expand Down
75 changes: 75 additions & 0 deletions build_config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,81 @@ these options should be used rarely.
and `applies_builder` to configure both ordering and ensure that steps are not
skipped.

## Triggers

Triggers are a performance heuristic that allow builders to quickly decide
_not_ to run.

A builder runs only if triggered if the option `run_only_if_triggered` is
`true`. This can be enabled for the builder:

```yaml
builders:
my_builder:
import: "package:my_package/builder.dart"
builder_factories: ["myBuilder"]
build_extensions: {".dart": [".my_package.dart"]}
defaults:
options:
run_only_if_triggered: true
```

Or, enabled/disabled in the `build.yaml` of the package applying the builder:

```yaml
targets:
$default:
builders:
my_package:my_builder:
options:
run_only_if_triggered: true # or `false`
```

Triggers are defined in a new top-level section called `triggers`:

```yaml
triggers:
my_package:my_builder:
- annotation MyAnnotation
- import my_package/my_builder_annotation.dart
```

An `annotation` trigger causes the builder to run if an annotation is used.
So, `- annotation MyAnnotation` is a check for `@MyAnnotation` being used.
A part file included from a library is also checked for the annotation.

An `import` trigger says that the builder runs if there is a direct import
of the specified library. This might be useful if a builder can run on code
without annotations, for example on all classes that `implement` a particular
type. Then, the import of the type used to trigger generation can be the
trigger.

Only one trigger has to match for the builder to run; adding more triggers
can never prevent a builder from running. So, a builder usually only needs
either an `import` trigger or an `annotation` trigger, not both.

Triggers are collected from all packages in the codebase, not just packages
defining or applying builders. This allows a package to provide new ways to
trigger a builder from an unrelated package. For example, if
`third_party_package` re-exports the annotation in
`package:my_package/my_builder_annotation.dart` then it should also add a
trigger:

```yaml
triggers:
my_package:my_builder:
- import third_party_package/annotations.dart
```

Or, if `third_party_package` defines a new constant `NewAnnotation` that can be
used as an annotation for `my_builder`, it should add a trigger:

```yaml
triggers:
my_package:my_builder:
- annotation NewAnnotation
```

# Publishing `build.yaml` files

`build.yaml` configuration should be published to pub with the package and
Expand Down
8 changes: 8 additions & 0 deletions build_config/lib/src/build_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ class BuildConfig {

final List<String> additionalPublicAssets;

/// Triggers for builders with the option `run_only_if_triggered`.
///
/// Keys are builder names, values are not defined here: validity and meaning
/// is up to `build_runner`.
@JsonKey(name: 'triggers')
final Map<String, Object> triggersByBuilder;

/// The default config if you have no `build.yaml` file.
factory BuildConfig.useDefault(
String packageName,
Expand Down Expand Up @@ -148,6 +155,7 @@ class BuildConfig {
Map<String, PostProcessBuilderDefinition>? postProcessBuilderDefinitions =
const {},
this.additionalPublicAssets = const [],
this.triggersByBuilder = const {},
}) : buildTargets =
identical(buildTargets, BuildConfig._placeholderBuildTarget)
? {
Expand Down
8 changes: 8 additions & 0 deletions build_config/lib/src/build_config.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build_config/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: build_config
version: 1.1.3-wip
version: 1.2.0-wip
description: >-
Format definition and support for parsing `build.yaml` configuration.
repository: https://github.com/dart-lang/build/tree/master/build_config
Expand Down
1 change: 1 addition & 0 deletions build_resolvers/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 3.0.2-wip

- Use `build` 3.0.2.
- Use `build_runner` 2.7.0.

## 3.0.1

Expand Down
5 changes: 5 additions & 0 deletions build_runner/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## 2.7.0-wip

- Performance: builders can choose to run only when "triggered". A builder runs
only if triggered if the option `run_only_if_triggered` is `true`. Triggers
are configured in new a top-level section of `build.yaml` called `triggers`.
See [the `build_config` docs](https://pub.dev/packages/build_config#triggers)
for more information.
- Remove interactive prompts for whether to delete files.
- Ignore `-d` flag: always delete files as if `-d` was passed.

Expand Down
2 changes: 1 addition & 1 deletion build_runner/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
args: ^2.0.0
async: ^2.5.0
build: '3.0.2-wip'
build_config: ">=1.1.0 <1.2.0"
build_config: ">=1.2.0-wip <1.3.0"
build_daemon: ^4.0.0
build_runner_core: '9.3.0-wip'
code_builder: ^4.2.0
Expand Down
1 change: 1 addition & 0 deletions build_runner_core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 9.3.0-wip

- Add support for build.yaml `triggers`. See `build_runner` 2.7.0 for usage.
- Remove interactive prompts for whether to delete files.
- Ignore `-d` flag: always delete files as if `-d` was passed.

Expand Down
4 changes: 4 additions & 0 deletions build_runner_core/lib/src/asset_graph/graph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class AssetGraph implements GeneratedAssetHider {
final Map<String, Map<PostProcessBuildStepId, Set<AssetId>>>
_postProcessBuildStepOutputs = {};

/// Digest of the previous build's `BuildTriggers`, or `null` if this is a
/// clean build.
Digest? previousBuildTriggersDigest;

/// Digests from the previous build's [BuildPhases], or `null` if this is a
/// clean build.
BuiltList<Digest>? previousInBuildPhasesOptionsDigests;
Expand Down
7 changes: 6 additions & 1 deletion build_runner_core/lib/src/asset_graph/serialization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ part of 'graph.dart';
///
/// This should be incremented any time the serialize/deserialize formats
/// change.
const _version = 30;
const _version = 31;

/// Deserializes an [AssetGraph] from a [Map].
AssetGraph deserializeAssetGraph(List<int> bytes) {
Expand Down Expand Up @@ -55,6 +55,10 @@ AssetGraph deserializeAssetGraph(List<int> bytes) {
BuiltList<String>.from(serializedGraph['enabledExperiments'] as List),
);

graph.previousBuildTriggersDigest = _deserializeDigest(
serializedGraph['buildTriggersDigest'] as String?,
);

for (var serializedItem in serializedGraph['nodes'] as Iterable) {
graph._add(_deserializeAssetNode(serializedItem as List));
}
Expand Down Expand Up @@ -102,6 +106,7 @@ List<int> serializeAssetGraph(AssetGraph graph) {
'ids': identityAssetIdSerializer.serializedObjects,
'dart_version': graph.dartVersion,
'nodes': nodes,
'buildTriggersDigest': _serializeDigest(graph.previousBuildTriggersDigest),
'buildActionsDigest': _serializeDigest(graph.buildPhasesDigest),
'packageLanguageVersions':
graph.packageLanguageVersions
Expand Down
Loading
Loading