Skip to content

Commit d88218e

Browse files
committed
Split out post process builder state.
1 parent 7bb331e commit d88218e

15 files changed

+501
-544
lines changed

build_runner/test/generate/watch_test.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,10 @@ void main() {
426426
cachedGraph,
427427
equalsAssetGraph(expectedGraph, checkPreviousInputsDigest: false),
428428
);
429+
expect(
430+
cachedGraph.allPostProcessBuildStepOutputs,
431+
expectedGraph.allPostProcessBuildStepOutputs,
432+
);
429433
});
430434

431435
test('ignores events from nested packages', () async {

build_runner/test/server/asset_handler_test.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:build_runner/src/server/server.dart';
1010
import 'package:build_runner_core/build_runner_core.dart';
1111
import 'package:build_runner_core/src/asset_graph/graph.dart';
1212
import 'package:build_runner_core/src/asset_graph/node.dart';
13+
import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart';
1314
import 'package:build_runner_core/src/generate/build_phases.dart';
1415
import 'package:build_runner_core/src/generate/options.dart';
1516
import 'package:build_runner_core/src/package_graph/target_graph.dart';
@@ -48,9 +49,11 @@ void main() {
4849
void addAsset(String id, String content, {bool deleted = false}) {
4950
var node = makeAssetNode(id, [], computeDigest(AssetId.parse(id), 'a'));
5051
if (deleted) {
51-
node = node.rebuild(
52-
(b) => b..deletedBy.add(node.id.addExtension('.post_anchor.1')),
53-
);
52+
node = node.rebuild((b) {
53+
b.deletedBy.add(
54+
PostProcessBuildStepId(input: node.id, actionNumber: 1),
55+
);
56+
});
5457
}
5558
graph.add(node);
5659
delegate.testing.writeString(node.id, content);

build_runner/test/server/serve_handler_test.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:build_runner/src/server/server.dart';
1515
import 'package:build_runner_core/build_runner_core.dart';
1616
import 'package:build_runner_core/src/asset_graph/graph.dart';
1717
import 'package:build_runner_core/src/asset_graph/node.dart';
18+
import 'package:build_runner_core/src/asset_graph/post_process_build_step_id.dart';
1819
import 'package:build_runner_core/src/generate/build_phases.dart';
1920
import 'package:build_runner_core/src/generate/options.dart';
2021
import 'package:build_runner_core/src/generate/performance_tracker.dart';
@@ -139,9 +140,11 @@ void main() {
139140
void addSource(String id, String content, {bool deleted = false}) {
140141
var node = makeAssetNode(id, [], computeDigest(AssetId.parse(id), content));
141142
if (deleted) {
142-
node = node.rebuild(
143-
(b) => b..deletedBy.add(node.id.addExtension('.post_anchor.1')),
144-
);
143+
node = node.rebuild((b) {
144+
b.deletedBy.add(
145+
PostProcessBuildStepId(input: node.id, actionNumber: 1),
146+
);
147+
});
145148
}
146149
assetGraph.add(node);
147150
readerWriter.testing.writeString(node.id, content);

build_runner_core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
- Add `reportUnusedAssetsForInput` to `BuildOptions`, to listen for when
2828
a builder notifies that an asset is unused.
2929
- Use `LibraryCycleGraphLoader` to load transitive deps for analysis.
30+
- Track post process builder outputs separately from the main graph Instead of
31+
in `postProcessAnchor` nodes.
3032

3133
## 8.0.0
3234

build_runner_core/lib/src/asset_graph/graph.dart

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:build/src/internal.dart';
1414
import 'package:built_collection/built_collection.dart';
1515
import 'package:crypto/crypto.dart';
1616
import 'package:glob/glob.dart';
17+
import 'package:meta/meta.dart';
1718
import 'package:package_config/package_config.dart';
1819
import 'package:watcher/watcher.dart';
1920

@@ -23,6 +24,7 @@ import '../generate/phase.dart';
2324
import '../util/constants.dart';
2425
import 'exceptions.dart';
2526
import 'node.dart';
27+
import 'post_process_build_step_id.dart';
2628

2729
part 'serialization.dart';
2830

@@ -46,6 +48,14 @@ class AssetGraph implements GeneratedAssetHider {
4648

4749
final BuiltMap<String, LanguageVersion?> packageLanguageVersions;
4850

51+
/// All post process build steps outputs, indexed by package then
52+
/// [PostProcessBuildStepId].
53+
///
54+
/// Created with empty outputs at the start of the build if it's a new build
55+
/// step; or deserialized with previous build outputs if it has run before.
56+
final Map<String, Map<PostProcessBuildStepId, Set<AssetId>>>
57+
_postProcessBuildStepOutputs = {};
58+
4959
AssetGraph._(
5060
this.buildPhasesDigest,
5161
this.dartVersion,
@@ -93,6 +103,10 @@ class AssetGraph implements GeneratedAssetHider {
93103

94104
List<int> serialize() => _AssetGraphSerializer(this).serialize();
95105

106+
@visibleForTesting
107+
Map<String, Map<PostProcessBuildStepId, Set<AssetId>>>
108+
get allPostProcessBuildStepOutputs => _postProcessBuildStepOutputs;
109+
96110
/// Checks if [id] exists in the graph.
97111
bool contains(AssetId id) =>
98112
_nodesByPackage[id.package]?.containsKey(id.path) ?? false;
@@ -158,6 +172,7 @@ class AssetGraph implements GeneratedAssetHider {
158172
}
159173
}
160174
_nodesByPackage.putIfAbsent(node.id.package, () => {})[node.id.path] = node;
175+
161176
return node;
162177
}
163178

@@ -238,16 +253,21 @@ class AssetGraph implements GeneratedAssetHider {
238253
/// Also removes all edges between all removed nodes and remaining nodes.
239254
///
240255
/// Returns the IDs of removed asset nodes.
241-
Set<AssetId> _removeRecursive(AssetId id, {Set<AssetId>? removedIds}) {
256+
Set<AssetId> _removeRecursive(
257+
AssetId id, {
258+
Set<AssetId>? removedIds,
259+
Map<AssetId, Set<AssetId>>? anchorOutputs,
260+
}) {
242261
removedIds ??= <AssetId>{};
243262
var node = get(id);
244263
if (node == null) return removedIds;
245264
removedIds.add(id);
246-
for (var anchor in node.anchorOutputs.toList()) {
247-
_removeRecursive(anchor, removedIds: removedIds);
248-
}
249265
for (var output in node.primaryOutputs.toList()) {
250-
_removeRecursive(output, removedIds: removedIds);
266+
_removeRecursive(
267+
output,
268+
removedIds: removedIds,
269+
anchorOutputs: anchorOutputs,
270+
);
251271
}
252272
for (var output in node.outputs) {
253273
updateNodeIfPresent(output, (nodeBuilder) {
@@ -290,6 +310,12 @@ class AssetGraph implements GeneratedAssetHider {
290310
if (node.type != NodeType.missingSource) {
291311
_nodesByPackage[id.package]!.remove(id.path);
292312
}
313+
314+
// Remove post build action applications with removed assets as inputs.
315+
for (final packageOutputs in _postProcessBuildStepOutputs.values) {
316+
packageOutputs.removeWhere((id, _) => removedIds!.contains(id.input));
317+
}
318+
293319
return removedIds;
294320
}
295321

@@ -301,6 +327,25 @@ class AssetGraph implements GeneratedAssetHider {
301327
Iterable<AssetNode> packageNodes(String package) =>
302328
_nodesByPackage[package]?.values ?? [];
303329

330+
Iterable<PostProcessBuildStepId> postProcessBuildStepIds({
331+
required String package,
332+
}) => _postProcessBuildStepOutputs[package]?.keys ?? const [];
333+
334+
void updatePostProcessBuildStep(
335+
PostProcessBuildStepId buildStepId, {
336+
required Set<AssetId> outputs,
337+
}) {
338+
_postProcessBuildStepOutputs.putIfAbsent(
339+
buildStepId.input.package,
340+
() => {},
341+
)[buildStepId] =
342+
outputs;
343+
}
344+
345+
Iterable<AssetId> postProcessBuildStepOutputs(PostProcessBuildStepId action) {
346+
return _postProcessBuildStepOutputs[action.input.package]![action]!;
347+
}
348+
304349
/// All the generated outputs in the graph.
305350
Iterable<AssetId> get outputs =>
306351
allNodes.where((n) => n.type == NodeType.generated).map((n) => n.id);
@@ -547,7 +592,7 @@ class AssetGraph implements GeneratedAssetHider {
547592
),
548593
);
549594
} else if (phase is PostBuildPhase) {
550-
_addPostBuildPhaseAnchors(phase, allInputs);
595+
_addPostBuildActionApplications(phase, allInputs);
551596
} else {
552597
throw StateError('Unrecognized phase type $phase');
553598
}
@@ -600,27 +645,21 @@ class AssetGraph implements GeneratedAssetHider {
600645
return phaseOutputs;
601646
}
602647

603-
/// Adds all [AssetNode.postProcessAnchor]s for [phase] given [allInputs];
604-
///
605-
/// Does not return anything because [AssetNode.postProcessAnchor]s are
606-
/// synthetic and should not be treated as inputs.
607-
void _addPostBuildPhaseAnchors(PostBuildPhase phase, Set<AssetId> allInputs) {
608-
var actionNum = 0;
648+
/// Adds all [PostProcessBuildStepId]s for [phase] given [allInputs];
649+
void _addPostBuildActionApplications(
650+
PostBuildPhase phase,
651+
Set<AssetId> allInputs,
652+
) {
653+
var actionNumber = 0;
609654
for (var action in phase.builderActions) {
610655
var inputs = allInputs.where((input) => _actionMatches(action, input));
611656
for (var input in inputs) {
612-
var buildOptionsNodeId = builderOptionsIdForAction(action, actionNum);
613-
var anchor = AssetNode.postProcessAnchorForInputAndAction(
614-
input,
615-
actionNum,
616-
buildOptionsNodeId,
657+
updatePostProcessBuildStep(
658+
PostProcessBuildStepId(input: input, actionNumber: actionNumber),
659+
outputs: {},
617660
);
618-
add(anchor);
619-
updateNode(input, (nodeBuilder) {
620-
nodeBuilder.anchorOutputs.add(anchor.id);
621-
});
622661
}
623-
actionNum++;
662+
actionNumber++;
624663
}
625664
}
626665

@@ -762,11 +801,14 @@ class AssetGraph implements GeneratedAssetHider {
762801
Digest computeBuilderOptionsDigest(BuilderOptions options) =>
763802
md5.convert(utf8.encode(json.encode(options.config)));
764803

765-
AssetId builderOptionsIdForAction(BuildAction action, int actionNum) {
804+
AssetId builderOptionsIdForAction(BuildAction action, int actionNumber) {
766805
if (action is InBuildPhase) {
767-
return AssetId(action.package, 'Phase$actionNum.builderOptions');
806+
return AssetId(action.package, 'Phase$actionNumber.builderOptions');
768807
} else if (action is PostBuildAction) {
769-
return AssetId(action.package, 'PostPhase$actionNum.builderOptions');
808+
return PostProcessBuildStepId.builderOptionsIdFor(
809+
package: action.package,
810+
actionNumber: actionNumber,
811+
);
770812
} else {
771813
throw StateError('Unsupported action type $action');
772814
}

build_runner_core/lib/src/asset_graph/node.dart

Lines changed: 9 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'package:built_value/built_value.dart';
1010
import 'package:built_value/serializer.dart';
1111
import 'package:crypto/crypto.dart';
1212

13-
import '../generate/phase.dart';
13+
import 'post_process_build_step_id.dart';
1414

1515
part 'node.g.dart';
1616

@@ -23,7 +23,6 @@ class NodeType extends EnumClass {
2323
static const NodeType glob = _$glob;
2424
static const NodeType internal = _$internal;
2525
static const NodeType placeholder = _$placeholder;
26-
static const NodeType postProcessAnchor = _$postProcessAnchor;
2726
static const NodeType source = _$source;
2827
static const NodeType missingSource = _$missingSource;
2928

@@ -54,10 +53,6 @@ abstract class AssetNode implements Built<AssetNode, AssetNodeBuilder> {
5453
/// [AssetNode.glob].
5554
GlobNodeState? get globNodeState;
5655

57-
/// Additional node configuration for an
58-
/// [AssetNode.postProcessAnchorNodeConfiguration].
59-
PostProcessAnchorNodeConfiguration? get postProcessAnchorNodeConfiguration;
60-
6156
/// The assets that any [Builder] in the build graph declares it may output
6257
/// when run on this asset.
6358
BuiltSet<AssetId> get primaryOutputs;
@@ -66,19 +61,14 @@ abstract class AssetNode implements Built<AssetNode, AssetNodeBuilder> {
6661
/// which reads this asset.
6762
BuiltSet<AssetId> get outputs;
6863

69-
/// The [AssetId]s of all [AssetNode.postProcessAnchor] assets for which this
70-
/// node is the primary input.
71-
BuiltSet<AssetId> get anchorOutputs;
72-
7364
/// The [Digest] for this node in its last known state.
7465
///
7566
/// May be `null` if this asset has no outputs, or if it doesn't actually
7667
/// exist.
7768
Digest? get lastKnownDigest;
7869

79-
/// The IDs of the [AssetNode.postProcessAnchor] for post process builder
80-
/// which requested to delete this asset.
81-
BuiltSet<AssetId> get deletedBy;
70+
/// The `PostProcessBuildStep`s which requested to delete this asset.
71+
BuiltSet<PostProcessBuildStepId> get deletedBy;
8272

8373
/// Whether this asset is a normal, readable file.
8474
///
@@ -223,35 +213,6 @@ abstract class AssetNode implements Built<AssetNode, AssetNodeBuilder> {
223213
static AssetId createGlobNodeId(String package, String glob, int phaseNum) =>
224214
AssetId(package, 'glob.$phaseNum.${base64.encode(utf8.encode(glob))}');
225215

226-
/// A [primaryInput] to a [PostBuildAction].
227-
///
228-
/// The [outputs] of this node are the individual outputs created for the
229-
/// [primaryInput] during the [PostBuildAction] at index [actionNumber].
230-
factory AssetNode.postProcessAnchor(
231-
AssetId id, {
232-
required AssetId primaryInput,
233-
required int actionNumber,
234-
required AssetId builderOptionsId,
235-
Digest? previousInputsDigest,
236-
}) => AssetNode((b) {
237-
b.id = id;
238-
b.type = NodeType.postProcessAnchor;
239-
b.postProcessAnchorNodeConfiguration.actionNumber = actionNumber;
240-
b.postProcessAnchorNodeConfiguration.builderOptionsId = builderOptionsId;
241-
b.postProcessAnchorNodeConfiguration.primaryInput = primaryInput;
242-
});
243-
244-
factory AssetNode.postProcessAnchorForInputAndAction(
245-
AssetId primaryInput,
246-
int actionNumber,
247-
AssetId builderOptionsId,
248-
) => AssetNode.postProcessAnchor(
249-
primaryInput.addExtension('.post_anchor.$actionNumber'),
250-
primaryInput: primaryInput,
251-
actionNumber: actionNumber,
252-
builderOptionsId: builderOptionsId,
253-
);
254-
255216
AssetNode._() {
256217
// Check that configuration and state fields are non-null exactly when the
257218
// node is of the corresponding type.
@@ -277,10 +238,6 @@ abstract class AssetNode implements Built<AssetNode, AssetNodeBuilder> {
277238
globNodeConfiguration != null,
278239
globNodeState != null,
279240
);
280-
check(
281-
type == NodeType.postProcessAnchor,
282-
postProcessAnchorNodeConfiguration != null,
283-
);
284241
}
285242
}
286243

@@ -387,27 +344,6 @@ abstract class GlobNodeState
387344
GlobNodeState._();
388345
}
389346

390-
// Additional configuration for an [AssetNode.postProcessAnchor].
391-
abstract class PostProcessAnchorNodeConfiguration
392-
implements
393-
Built<
394-
PostProcessAnchorNodeConfiguration,
395-
PostProcessAnchorNodeConfigurationBuilder
396-
> {
397-
static Serializer<PostProcessAnchorNodeConfiguration> get serializer =>
398-
_$postProcessAnchorNodeConfigurationSerializer;
399-
400-
int get actionNumber;
401-
AssetId get builderOptionsId;
402-
AssetId get primaryInput;
403-
404-
PostProcessAnchorNodeConfiguration._();
405-
406-
factory PostProcessAnchorNodeConfiguration(
407-
void Function(PostProcessAnchorNodeConfigurationBuilder) updates,
408-
) = _$PostProcessAnchorNodeConfiguration;
409-
}
410-
411347
/// Work that needs doing for a node that tracks its inputs.
412348
class PendingBuildAction extends EnumClass {
413349
static Serializer<PendingBuildAction> get serializer =>
@@ -427,8 +363,8 @@ class PendingBuildAction extends EnumClass {
427363
@SerializersFor([AssetNode])
428364
final Serializers serializers =
429365
(_$serializers.toBuilder()
430-
..add(AssetIdSerializer())
431-
..add(DigestSerializer()))
366+
..add(const AssetIdSerializer())
367+
..add(const DigestSerializer()))
432368
.build();
433369

434370
/// Serializer for [AssetId].
@@ -455,6 +391,8 @@ class AssetIdSerializer implements PrimitiveSerializer<AssetId> {
455391
AssetId object, {
456392
FullType specifiedType = FullType.unspecified,
457393
}) => object.toString();
394+
395+
const AssetIdSerializer();
458396
}
459397

460398
/// Serializer for [Digest].
@@ -478,4 +416,6 @@ class DigestSerializer implements PrimitiveSerializer<Digest> {
478416
Digest object, {
479417
FullType specifiedType = FullType.unspecified,
480418
}) => base64.encode(object.bytes);
419+
420+
const DigestSerializer();
481421
}

0 commit comments

Comments
 (0)