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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library barback.phase; 5 library barback.phase;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection';
8 9
9 import 'asset.dart'; 10 import 'asset.dart';
10 import 'asset_cascade.dart'; 11 import 'asset_cascade.dart';
11 import 'asset_id.dart'; 12 import 'asset_id.dart';
12 import 'asset_node.dart'; 13 import 'asset_node.dart';
13 import 'asset_set.dart'; 14 import 'asset_set.dart';
14 import 'errors.dart'; 15 import 'errors.dart';
15 import 'stream_pool.dart'; 16 import 'stream_pool.dart';
16 import 'transform_node.dart'; 17 import 'transform_node.dart';
17 import 'transformer.dart'; 18 import 'transformer.dart';
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 /// Whenever an asset is added or modified, we need to asynchronously 62 /// Whenever an asset is added or modified, we need to asynchronously
62 /// determine which transformers can use it as their primary input. We can't 63 /// determine which transformers can use it as their primary input. We can't
63 /// start processing until we know which transformers to run, and this allows 64 /// start processing until we know which transformers to run, and this allows
64 /// us to wait until we do. 65 /// us to wait until we do.
65 var _adjustTransformersFutures = new Map<AssetId, Future>(); 66 var _adjustTransformersFutures = new Map<AssetId, Future>();
66 67
67 /// New asset nodes that were added while [_adjustTransformers] was still 68 /// New asset nodes that were added while [_adjustTransformers] was still
68 /// being run on an old version of that asset. 69 /// being run on an old version of that asset.
69 var _pendingNewInputs = new Map<AssetId, AssetNode>(); 70 var _pendingNewInputs = new Map<AssetId, AssetNode>();
70 71
71 /// The ids of assets that are emitted by transforms in this phase. 72 /// A map of output ids to the asset node outputs for those ids.
72 /// 73 ///
73 /// This is used to detect collisions where multiple transforms emit the same 74 /// Usually there's only one node for a given output id. However, it's
74 /// output. 75 /// 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.
75 final _outputs = new Set<AssetId>(); 76 /// 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.
77 /// the other nodes so that if that output is removed, we know which asset to
78 /// replace it with.
79 final _outputs = new Map<AssetId, Queue<AssetNode>>();
76 80
77 /// A stream that emits an event whenever this phase becomes dirty and needs 81 /// A stream that emits an event whenever this phase becomes dirty and needs
78 /// to be run. 82 /// to be run.
79 /// 83 ///
80 /// This may emit events when the phase was already dirty or while processing 84 /// This may emit events when the phase was already dirty or while processing
81 /// transforms. Events are emitted synchronously to ensure that the dirty 85 /// transforms. Events are emitted synchronously to ensure that the dirty
82 /// state is thoroughly propagated as soon as any assets are changed. 86 /// state is thoroughly propagated as soon as any assets are changed.
83 Stream get onDirty => _onDirtyPool.stream; 87 Stream get onDirty => _onDirtyPool.stream;
84 final _onDirtyPool = new StreamPool.broadcast(); 88 final _onDirtyPool = new StreamPool.broadcast();
85 89
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 307
304 /// Applies all currently wired up and dirty transforms. 308 /// Applies all currently wired up and dirty transforms.
305 Future _processTransforms() { 309 Future _processTransforms() {
306 // Convert this to a list so we can safely modify _transforms while 310 // Convert this to a list so we can safely modify _transforms while
307 // iterating over it. 311 // iterating over it.
308 var dirtyTransforms = 312 var dirtyTransforms =
309 flatten(_transforms.values.map((transforms) => transforms.toList())) 313 flatten(_transforms.values.map((transforms) => transforms.toList()))
310 .where((transform) => transform.isDirty).toList(); 314 .where((transform) => transform.isDirty).toList();
311 if (dirtyTransforms.isEmpty) return null; 315 if (dirtyTransforms.isEmpty) return null;
312 316
313 return Future.wait(dirtyTransforms.map((transform) => transform.apply())) 317 var collisions = new Set<AssetId>();
314 .then((allNewOutputs) { 318 return Future.wait(dirtyTransforms.map((transform) {
315 var newOutputs = allNewOutputs.reduce((set1, set2) => set1.union(set2)); 319 return transform.apply().then((outputs) {
320 for (var output in outputs) {
321 if (_outputs.containsKey(output.id)) {
322 _outputs[output.id].add(output);
323 collisions.add(output.id);
324 } else {
325 _outputs[output.id] = new Queue<AssetNode>.from([output]);
326 _next.addInput(output);
327 }
316 328
317 var collisions = new Set<AssetId>(); 329 _handleOutputRemoval(output);
318 for (var newOutput in newOutputs) {
319 if (_outputs.contains(newOutput.id)) {
320 collisions.add(newOutput.id);
321 } else {
322 _next.addInput(newOutput);
323 _outputs.add(newOutput.id);
324 newOutput.whenRemoved.then((_) => _outputs.remove(newOutput.id));
325 } 330 }
326 } 331 });
327 332 })).then((_) {
328 // Report collisions in a deterministic order. 333 // Report collisions in a deterministic order.
329 collisions = collisions.toList(); 334 collisions = collisions.toList();
330 collisions.sort((a, b) => a.toString().compareTo(b.toString())); 335 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.
331 for (var collision in collisions) { 336 for (var collision in collisions) {
337 // Ensure that there's still a collision. It's possible it was resolved
338 // while another transform was running.
339 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.
332 cascade.reportError(new AssetCollisionException(collision)); 340 cascade.reportError(new AssetCollisionException(collision));
333 // TODO(rnystrom): Define what happens after a collision occurs.
334 } 341 }
335 }); 342 });
336 } 343 }
344
345 /// Properly resolve collisions when [output] is removed.
346 void _handleOutputRemoval(AssetNode output) {
347 output.whenRemoved.then((_) {
348 var assets = _outputs[output.id];
349 var wasFirst = assets.first == output;
350 assets.remove(output);
351
352 // If this wasn't the first asset, then a collision was resolved,
353 // possibly partially.
354 if (wasFirst) {
355 // If this is the only asset with this id, we're no longer
356 // producing that output. Otherwise, there was a collision, and we
357 // should return whatever asset was emitted after the one that was
358 // just removed.
359 if (assets.isEmpty) {
360 _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.
361 } else {
362 // Pump the event queue to give [_next] a chance to handle the removal
363 // of its input before getting a new input.
364 newFuture(() => _next.addInput(assets.first));
365 }
366 }
367
368 // If there's still a collision, report it. This lets the user know
369 // if they've successfully resolved the collision or not.
370 if (assets.length > 1) {
371 // Pump the event queue to ensure that the removal of the input triggers
372 // a new build to which we can attach the error.
373 newFuture(() =>
374 cascade.reportError(new AssetCollisionException(output.id)));
375 }
376 });
377 }
337 } 378 }
OLDNEW
« 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