@@ -49,6 +49,10 @@ class AssetGraph implements GeneratedAssetHider {
4949
5050 final BuiltMap <String , LanguageVersion ?> packageLanguageVersions;
5151
52+ /// The result of [computeOutputs] for reuse, or `null` if outputs have not
53+ /// been computed.
54+ Map <AssetId , Set <AssetId >>? _outputs;
55+
5256 /// All post process build steps outputs, indexed by package then
5357 /// [PostProcessBuildStepId] .
5458 ///
@@ -129,6 +133,11 @@ class AssetGraph implements GeneratedAssetHider {
129133 if (node == null ) throw StateError ('Missing node: $id ' );
130134 final updatedNode = node.rebuild (updates);
131135 _nodesByPackage[id.package]! [id.path] = updatedNode;
136+
137+ if (node.inputs != updatedNode.inputs) {
138+ _outputs = null ;
139+ }
140+
132141 return updatedNode;
133142 }
134143
@@ -145,6 +154,11 @@ class AssetGraph implements GeneratedAssetHider {
145154 if (node == null ) return null ;
146155 final updatedNode = node.rebuild (updates);
147156 _nodesByPackage[id.package]! [id.path] = updatedNode;
157+
158+ if (node.inputs != updatedNode.inputs) {
159+ _outputs = null ;
160+ }
161+
148162 return updatedNode;
149163 }
150164
@@ -162,7 +176,6 @@ class AssetGraph implements GeneratedAssetHider {
162176 // primary outputs. We only want to remove this node.
163177 _nodesByPackage[existing.id.package]! .remove (existing.id.path);
164178 node = node.rebuild ((b) {
165- b.outputs.addAll (existing.outputs);
166179 b.primaryOutputs.addAll (existing.primaryOutputs);
167180 });
168181 } else {
@@ -173,6 +186,10 @@ class AssetGraph implements GeneratedAssetHider {
173186 }
174187 }
175188 _nodesByPackage.putIfAbsent (node.id.package, () => {})[node.id.path] = node;
189+ if (node.inputs? .isNotEmpty ?? false ) {
190+ _outputs = null ;
191+ }
192+
176193 return node;
177194 }
178195
@@ -261,7 +278,8 @@ class AssetGraph implements GeneratedAssetHider {
261278 for (var output in node.primaryOutputs.toList ()) {
262279 _removeRecursive (output, removedIds: removedIds);
263280 }
264- for (var output in node.outputs) {
281+ final outputs = computeOutputs ();
282+ for (var output in (outputs[node.id] ?? const < AssetId > {})) {
265283 updateNodeIfPresent (output, (nodeBuilder) {
266284 if (nodeBuilder.type == NodeType .generated) {
267285 nodeBuilder.generatedNodeState.inputs.remove (id);
@@ -277,23 +295,14 @@ class AssetGraph implements GeneratedAssetHider {
277295 for (var input in node.generatedNodeState! .inputs) {
278296 // We may have already removed this node entirely.
279297 updateNodeIfPresent (input, (nodeBuilder) {
280- nodeBuilder
281- ..outputs.remove (id)
282- ..primaryOutputs.remove (id);
298+ nodeBuilder.primaryOutputs.remove (id);
283299 });
284300 }
285- updateNode (node.generatedNodeConfiguration! .builderOptionsId, (
286- nodeBuilder,
287- ) {
288- nodeBuilder.outputs.remove (id);
289- });
290301 } else if (node.type == NodeType .glob) {
291302 for (var input in node.globNodeState! .inputs) {
292303 // We may have already removed this node entirely.
293304 updateNodeIfPresent (input, (nodeBuilder) {
294- nodeBuilder
295- ..outputs.remove (id)
296- ..primaryOutputs.remove (id);
305+ nodeBuilder.primaryOutputs.remove (id);
297306 });
298307 }
299308 }
@@ -311,6 +320,34 @@ class AssetGraph implements GeneratedAssetHider {
311320 return removedIds;
312321 }
313322
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+
314351 /// All nodes in the graph, whether source files or generated outputs.
315352 Iterable <AssetNode > get allNodes =>
316353 _nodesByPackage.values.expand ((pkdIds) => pkdIds.values);
@@ -408,8 +445,7 @@ class AssetGraph implements GeneratedAssetHider {
408445 .where (
409446 (node) =>
410447 node.isTrackedInput &&
411- (node.outputs.isNotEmpty ||
412- node.primaryOutputs.isNotEmpty ||
448+ (node.primaryOutputs.isNotEmpty ||
413449 node.lastKnownDigest != null ),
414450 )
415451 .map ((node) => node.id),
@@ -458,6 +494,7 @@ class AssetGraph implements GeneratedAssetHider {
458494 // Transitively invalidates all assets. This needs to happen after the
459495 // structure of the graph has been updated.
460496 var invalidatedIds = < AssetId > {};
497+ final computedOutputs = computeOutputs ();
461498
462499 void invalidateNodeAndDeps (AssetId startNodeId) {
463500 if (! invalidatedIds.add (startNodeId)) return ;
@@ -484,7 +521,8 @@ class AssetGraph implements GeneratedAssetHider {
484521 });
485522
486523 if (invalidatedNode != null ) {
487- for (final id in invalidatedNode.outputs) {
524+ for (final id
525+ in (computedOutputs[invalidatedNode.id] ?? const < AssetId > {})) {
488526 if (invalidatedIds.add (id)) {
489527 nodesToInvalidate.add (id);
490528 }
@@ -534,6 +572,7 @@ class AssetGraph implements GeneratedAssetHider {
534572 }
535573 }
536574
575+ _outputs = null ;
537576 return invalidatedIds;
538577 }
539578
@@ -681,12 +720,14 @@ class AssetGraph implements GeneratedAssetHider {
681720 throw ArgumentError ('Expected node of type NodeType.builderOptionsNode' );
682721 }
683722 var removed = < AssetId > {};
723+ Map <AssetId , Set <AssetId >>? computedOutputsBeforeRemoves;
684724 for (var output in outputs) {
685725 AssetNode ? existing;
686726 // When any outputs aren't hidden we can pick up old generated outputs as
687727 // regular `AssetNode`s, we need to delete them and all their primary
688728 // outputs, and replace them with a `GeneratedAssetNode`.
689729 if (contains (output)) {
730+ computedOutputsBeforeRemoves = computeOutputs ();
690731 existing = get (output)! ;
691732 if (existing.type == NodeType .generated) {
692733 final existingConfiguration = existing.generatedNodeConfiguration! ;
@@ -712,13 +753,9 @@ class AssetGraph implements GeneratedAssetHider {
712753 isHidden: isHidden,
713754 );
714755 if (existing != null ) {
715- newNode = newNode.rebuild ((b) => b..outputs.addAll (existing! .outputs));
716756 // Ensure we set up the reverse link for NodeWithInput nodes.
717- _addInput (existing.outputs , output);
757+ _addInput (computedOutputsBeforeRemoves ! [output] ?? < AssetId > {} , output);
718758 }
719- updateNode (builderOptionsNode.id, (nodeBuilder) {
720- nodeBuilder.outputs.add (output);
721- });
722759 _add (newNode);
723760 }
724761 return removed;
0 commit comments