Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(357)

Unified Diff: pkg/barback/lib/src/phase.dart

Issue 22685006: Better handling of asset collisions in barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/barback/lib/src/phase.dart
diff --git a/pkg/barback/lib/src/phase.dart b/pkg/barback/lib/src/phase.dart
index 82ec5213ac1608e05cfe04e84de21e6b2b4e69d1..bdc2b5c97a214edb7216e1434da8a2f61ec450a0 100644
--- a/pkg/barback/lib/src/phase.dart
+++ b/pkg/barback/lib/src/phase.dart
@@ -5,6 +5,7 @@
library barback.phase;
import 'dart:async';
+import 'dart:collection';
import 'asset.dart';
import 'asset_cascade.dart';
@@ -68,11 +69,14 @@ class Phase {
/// being run on an old version of that asset.
var _pendingNewInputs = new Map<AssetId, AssetNode>();
- /// The ids of assets that are emitted by transforms in this phase.
+ /// A map of output ids to the asset node outputs for those ids.
///
- /// This is used to detect collisions where multiple transforms emit the same
- /// output.
- final _outputs = new Set<AssetId>();
+ /// Usually there's only one node for a given output id. However, it's
+ /// possible for multiple transformers to output an asset with the same id. In
Bob Nystrom 2013/08/09 22:44:43 "transformers" -> "transformers in this phase"
nweiz 2013/08/12 21:15:44 Done.
+ /// that case, the first output emitted is passed forward. We keep track of
Bob Nystrom 2013/08/09 22:44:43 "first" -> "chronologically first"
nweiz 2013/08/12 21:15:44 Done.
+ /// the other nodes so that if that output is removed, we know which asset to
+ /// replace it with.
+ final _outputs = new Map<AssetId, Queue<AssetNode>>();
/// A stream that emits an event whenever this phase becomes dirty and needs
/// to be run.
@@ -310,27 +314,64 @@ class Phase {
.where((transform) => transform.isDirty).toList();
if (dirtyTransforms.isEmpty) return null;
- return Future.wait(dirtyTransforms.map((transform) => transform.apply()))
- .then((allNewOutputs) {
- var newOutputs = allNewOutputs.reduce((set1, set2) => set1.union(set2));
-
- var collisions = new Set<AssetId>();
- for (var newOutput in newOutputs) {
- if (_outputs.contains(newOutput.id)) {
- collisions.add(newOutput.id);
- } else {
- _next.addInput(newOutput);
- _outputs.add(newOutput.id);
- newOutput.whenRemoved.then((_) => _outputs.remove(newOutput.id));
+ var collisions = new Set<AssetId>();
+ return Future.wait(dirtyTransforms.map((transform) {
+ return transform.apply().then((outputs) {
+ for (var output in outputs) {
+ if (_outputs.containsKey(output.id)) {
+ _outputs[output.id].add(output);
+ collisions.add(output.id);
+ } else {
+ _outputs[output.id] = new Queue<AssetNode>.from([output]);
+ _next.addInput(output);
+ }
+
+ _handleOutputRemoval(output);
}
- }
-
+ });
+ })).then((_) {
// Report collisions in a deterministic order.
collisions = collisions.toList();
collisions.sort((a, b) => a.toString().compareTo(b.toString()));
Bob Nystrom 2013/08/09 22:44:43 This presumes AssetNode.toString() does something
nweiz 2013/08/12 21:15:44 I just went ahead and implemented comparable.
for (var collision in collisions) {
+ // Ensure that there's still a collision. It's possible it was resolved
+ // while another transform was running.
+ if (_outputs[collision].length < 2) return;
Bob Nystrom 2013/08/09 22:44:43 "< 2" -> "<= 1" "return" -> "continue"
nweiz 2013/08/12 21:15:44 Done.
cascade.reportError(new AssetCollisionException(collision));
- // TODO(rnystrom): Define what happens after a collision occurs.
+ }
+ });
+ }
+
+ /// Properly resolve collisions when [output] is removed.
+ void _handleOutputRemoval(AssetNode output) {
+ output.whenRemoved.then((_) {
+ var assets = _outputs[output.id];
+ var wasFirst = assets.first == output;
+ assets.remove(output);
+
+ // If this wasn't the first asset, then a collision was resolved,
+ // possibly partially.
+ if (wasFirst) {
+ // If this is the only asset with this id, we're no longer
+ // producing that output. Otherwise, there was a collision, and we
+ // should return whatever asset was emitted after the one that was
+ // just removed.
+ if (assets.isEmpty) {
+ _outputs.remove(output.id);
Bob Nystrom 2013/08/09 22:44:43 How about handling this case first and then exitin
Bob Nystrom 2013/08/09 22:44:43 Also, do we need to tell the next phase the input
nweiz 2013/08/12 21:15:44 Done.
+ } else {
+ // Pump the event queue to give [_next] a chance to handle the removal
+ // of its input before getting a new input.
+ newFuture(() => _next.addInput(assets.first));
+ }
+ }
+
+ // If there's still a collision, report it. This lets the user know
+ // if they've successfully resolved the collision or not.
+ if (assets.length > 1) {
+ // Pump the event queue to ensure that the removal of the input triggers
+ // a new build to which we can attach the error.
+ newFuture(() =>
+ cascade.reportError(new AssetCollisionException(output.id)));
}
});
}
« no previous file with comments | « no previous file | pkg/barback/lib/src/transform_node.dart » ('j') | pkg/barback/test/package_graph/errors_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698