@@ -49,6 +49,10 @@ class AssetGraph implements GeneratedAssetHider {
49
49
50
50
final BuiltMap <String , LanguageVersion ?> packageLanguageVersions;
51
51
52
+ /// The result of [computeOutputs] for reuse, or `null` if outputs have not
53
+ /// been computed.
54
+ Map <AssetId , Set <AssetId >>? _outputs;
55
+
52
56
/// All post process build steps outputs, indexed by package then
53
57
/// [PostProcessBuildStepId] .
54
58
///
@@ -129,6 +133,11 @@ class AssetGraph implements GeneratedAssetHider {
129
133
if (node == null ) throw StateError ('Missing node: $id ' );
130
134
final updatedNode = node.rebuild (updates);
131
135
_nodesByPackage[id.package]! [id.path] = updatedNode;
136
+
137
+ if (node.inputs != updatedNode.inputs) {
138
+ _outputs = null ;
139
+ }
140
+
132
141
return updatedNode;
133
142
}
134
143
@@ -145,6 +154,11 @@ class AssetGraph implements GeneratedAssetHider {
145
154
if (node == null ) return null ;
146
155
final updatedNode = node.rebuild (updates);
147
156
_nodesByPackage[id.package]! [id.path] = updatedNode;
157
+
158
+ if (node.inputs != updatedNode.inputs) {
159
+ _outputs = null ;
160
+ }
161
+
148
162
return updatedNode;
149
163
}
150
164
@@ -162,7 +176,6 @@ class AssetGraph implements GeneratedAssetHider {
162
176
// primary outputs. We only want to remove this node.
163
177
_nodesByPackage[existing.id.package]! .remove (existing.id.path);
164
178
node = node.rebuild ((b) {
165
- b.outputs.addAll (existing.outputs);
166
179
b.primaryOutputs.addAll (existing.primaryOutputs);
167
180
});
168
181
} else {
@@ -173,6 +186,10 @@ class AssetGraph implements GeneratedAssetHider {
173
186
}
174
187
}
175
188
_nodesByPackage.putIfAbsent (node.id.package, () => {})[node.id.path] = node;
189
+ if (node.inputs? .isNotEmpty ?? false ) {
190
+ _outputs = null ;
191
+ }
192
+
176
193
return node;
177
194
}
178
195
@@ -261,7 +278,8 @@ class AssetGraph implements GeneratedAssetHider {
261
278
for (var output in node.primaryOutputs.toList ()) {
262
279
_removeRecursive (output, removedIds: removedIds);
263
280
}
264
- for (var output in node.outputs) {
281
+ final outputs = computeOutputs ();
282
+ for (var output in (outputs[node.id] ?? const < AssetId > {})) {
265
283
updateNodeIfPresent (output, (nodeBuilder) {
266
284
if (nodeBuilder.type == NodeType .generated) {
267
285
nodeBuilder.generatedNodeState.inputs.remove (id);
@@ -277,23 +295,14 @@ class AssetGraph implements GeneratedAssetHider {
277
295
for (var input in node.generatedNodeState! .inputs) {
278
296
// We may have already removed this node entirely.
279
297
updateNodeIfPresent (input, (nodeBuilder) {
280
- nodeBuilder
281
- ..outputs.remove (id)
282
- ..primaryOutputs.remove (id);
298
+ nodeBuilder.primaryOutputs.remove (id);
283
299
});
284
300
}
285
- updateNode (node.generatedNodeConfiguration! .builderOptionsId, (
286
- nodeBuilder,
287
- ) {
288
- nodeBuilder.outputs.remove (id);
289
- });
290
301
} else if (node.type == NodeType .glob) {
291
302
for (var input in node.globNodeState! .inputs) {
292
303
// We may have already removed this node entirely.
293
304
updateNodeIfPresent (input, (nodeBuilder) {
294
- nodeBuilder
295
- ..outputs.remove (id)
296
- ..primaryOutputs.remove (id);
305
+ nodeBuilder.primaryOutputs.remove (id);
297
306
});
298
307
}
299
308
}
@@ -311,6 +320,34 @@ class AssetGraph implements GeneratedAssetHider {
311
320
return removedIds;
312
321
}
313
322
323
+ /// Computes node outputs: the inverse of the graph described by the `inputs`
324
+ /// fields on glob and generated nodes.
325
+ ///
326
+ /// The result is cached until any node is updated with different `inputs` or
327
+ /// [updateAndInvalidate] is called.
328
+ Map <AssetId , Set <AssetId >> computeOutputs () {
329
+ if (_outputs != null ) return _outputs! ;
330
+ final result = < AssetId , Set <AssetId >> {};
331
+ for (final node in allNodes) {
332
+ if (node.type == NodeType .generated) {
333
+ for (final input in node.generatedNodeState! .inputs) {
334
+ result.putIfAbsent (input, () => {}).add (node.id);
335
+ }
336
+ result
337
+ .putIfAbsent (
338
+ node.generatedNodeConfiguration! .builderOptionsId,
339
+ () => {},
340
+ )
341
+ .add (node.id);
342
+ } else if (node.type == NodeType .glob) {
343
+ for (final input in node.globNodeState! .inputs) {
344
+ result.putIfAbsent (input, () => {}).add (node.id);
345
+ }
346
+ }
347
+ }
348
+ return _outputs = result;
349
+ }
350
+
314
351
/// All nodes in the graph, whether source files or generated outputs.
315
352
Iterable <AssetNode > get allNodes =>
316
353
_nodesByPackage.values.expand ((pkdIds) => pkdIds.values);
@@ -408,8 +445,7 @@ class AssetGraph implements GeneratedAssetHider {
408
445
.where (
409
446
(node) =>
410
447
node.isTrackedInput &&
411
- (node.outputs.isNotEmpty ||
412
- node.primaryOutputs.isNotEmpty ||
448
+ (node.primaryOutputs.isNotEmpty ||
413
449
node.lastKnownDigest != null ),
414
450
)
415
451
.map ((node) => node.id),
@@ -458,6 +494,7 @@ class AssetGraph implements GeneratedAssetHider {
458
494
// Transitively invalidates all assets. This needs to happen after the
459
495
// structure of the graph has been updated.
460
496
var invalidatedIds = < AssetId > {};
497
+ final computedOutputs = computeOutputs ();
461
498
462
499
void invalidateNodeAndDeps (AssetId startNodeId) {
463
500
if (! invalidatedIds.add (startNodeId)) return ;
@@ -484,7 +521,8 @@ class AssetGraph implements GeneratedAssetHider {
484
521
});
485
522
486
523
if (invalidatedNode != null ) {
487
- for (final id in invalidatedNode.outputs) {
524
+ for (final id
525
+ in (computedOutputs[invalidatedNode.id] ?? const < AssetId > {})) {
488
526
if (invalidatedIds.add (id)) {
489
527
nodesToInvalidate.add (id);
490
528
}
@@ -534,6 +572,7 @@ class AssetGraph implements GeneratedAssetHider {
534
572
}
535
573
}
536
574
575
+ _outputs = null ;
537
576
return invalidatedIds;
538
577
}
539
578
@@ -681,12 +720,14 @@ class AssetGraph implements GeneratedAssetHider {
681
720
throw ArgumentError ('Expected node of type NodeType.builderOptionsNode' );
682
721
}
683
722
var removed = < AssetId > {};
723
+ Map <AssetId , Set <AssetId >>? computedOutputsBeforeRemoves;
684
724
for (var output in outputs) {
685
725
AssetNode ? existing;
686
726
// When any outputs aren't hidden we can pick up old generated outputs as
687
727
// regular `AssetNode`s, we need to delete them and all their primary
688
728
// outputs, and replace them with a `GeneratedAssetNode`.
689
729
if (contains (output)) {
730
+ computedOutputsBeforeRemoves = computeOutputs ();
690
731
existing = get (output)! ;
691
732
if (existing.type == NodeType .generated) {
692
733
final existingConfiguration = existing.generatedNodeConfiguration! ;
@@ -712,13 +753,9 @@ class AssetGraph implements GeneratedAssetHider {
712
753
isHidden: isHidden,
713
754
);
714
755
if (existing != null ) {
715
- newNode = newNode.rebuild ((b) => b..outputs.addAll (existing! .outputs));
716
756
// Ensure we set up the reverse link for NodeWithInput nodes.
717
- _addInput (existing.outputs , output);
757
+ _addInput (computedOutputsBeforeRemoves ! [output] ?? < AssetId > {} , output);
718
758
}
719
- updateNode (builderOptionsNode.id, (nodeBuilder) {
720
- nodeBuilder.outputs.add (output);
721
- });
722
759
_add (newNode);
723
760
}
724
761
return removed;
0 commit comments