| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |