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

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: Code review changes. 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
« no previous file with comments | « pkg/barback/lib/src/asset_id.dart ('k') | pkg/barback/lib/src/transform_node.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 in this phase to output an asset with
75 final _outputs = new Set<AssetId>(); 76 /// the same id. In that case, the chronologically first output emitted is
77 /// passed forward. We keep track of the other nodes so that if that output is
78 /// removed, we know which asset to 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.compareTo(b));
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 <= 1) continue;
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 if (assets.length == 1) {
350 assert(assets.single == output);
351 _outputs.remove(output.id);
352 return;
353 }
354
355 // If there was more than one asset, we're resolving a collision --
356 // possibly partially.
357 var wasFirst = assets.first == output;
358 assets.remove(output);
359
360 // If this was the first asset, we need to pass the next asset
361 // (chronologically) to the next phase. Pump the event queue first to give
362 // [_next] a chance to handle the removal of its input before getting a
363 // new input.
364 if (wasFirst) newFuture(() => _next.addInput(assets.first));
365
366 // If there's still a collision, report it. This lets the user know
367 // if they've successfully resolved the collision or not.
368 if (assets.length > 1) {
369 // Pump the event queue to ensure that the removal of the input triggers
370 // a new build to which we can attach the error.
371 newFuture(() =>
372 cascade.reportError(new AssetCollisionException(output.id)));
373 }
374 });
375 }
337 } 376 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/asset_id.dart ('k') | pkg/barback/lib/src/transform_node.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698