Chromium Code Reviews| 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.group_runner; | 5 library barback.group_runner; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'asset_cascade.dart'; | 9 import 'asset_cascade.dart'; |
| 10 import 'asset_node.dart'; | 10 import 'asset_node.dart'; |
| 11 import 'log.dart'; | 11 import 'log.dart'; |
| 12 import 'phase.dart'; | 12 import 'phase.dart'; |
| 13 import 'stream_pool.dart'; | 13 import 'stream_pool.dart'; |
| 14 import 'transformer_group.dart'; | 14 import 'transformer_group.dart'; |
| 15 | 15 |
| 16 /// A class that processes all of the phases in a single transformer group. | 16 /// A class that processes all of the phases in a single transformer group. |
| 17 /// | 17 /// |
| 18 /// A group takes many inputs, processes them, and emits many outputs. | 18 /// A group takes many inputs, processes them, and emits many outputs. |
| 19 class GroupRunner { | 19 class GroupRunner { |
| 20 /// The group this runner runs. | 20 /// The group this runner runs. |
| 21 final TransformerGroup _group; | 21 final TransformerGroup _group; |
| 22 | 22 |
| 23 /// A string describing the location of [this] in the transformer graph. | 23 /// A string describing the location of [this] in the transformer graph. |
| 24 final String _location; | 24 final String _location; |
| 25 | 25 |
| 26 /// The phases defined by this group. | 26 /// The phases defined by this group. |
| 27 final _phases = new List<Phase>(); | 27 final _phases = new List<Phase>(); |
| 28 | 28 |
| 29 /// A stream that emits an event whenever this group becomes dirty and needs | 29 /// Whether [this] is dirty and still has more processing to do. |
| 30 /// to be run. | 30 bool get isDirty => _phases.any((phase) => phase.isDirty); |
| 31 | |
| 32 /// A stream that emits an event whenever [this] is no longer dirty. | |
| 31 /// | 33 /// |
| 32 /// This may emit events when the group was already dirty or while processing | 34 /// This is synchronous in order to guarantee that it will emit an event as |
| 33 /// transforms. Events are emitted synchronously to ensure that the dirty | 35 /// soon as [isDirty] flips from `true` to `false`. |
| 34 /// state is thoroughly propagated as soon as any assets are changed. | 36 Stream get onDone => _onDoneController.stream; |
| 35 Stream get onDirty => _onDirtyPool.stream; | 37 final _onDoneController = new StreamController.broadcast(sync: true); |
| 36 final _onDirtyPool = new StreamPool.broadcast(); | |
| 37 | 38 |
| 38 /// Whether this group is dirty and needs to be run. | 39 /// A stream that emits any new assets emitted by [this]. |
| 39 bool get isDirty => _phases.any((phase) => phase.isDirty); | 40 /// |
| 41 /// Assets are emitted synchronously to ensure that any changes are thoroughly | |
| 42 /// propagated as soon as they occur. | |
| 43 Stream<AssetNode> get onAsset => _onAssetPool.stream; | |
| 44 final _onAssetPool = new StreamPool<AssetNode>(); | |
| 40 | 45 |
| 41 /// A stream that emits an event whenever any transforms in this group logs | 46 /// A stream that emits an event whenever any transforms in this group logs |
| 42 /// an entry. | 47 /// an entry. |
| 43 Stream<LogEntry> get onLog => _onLogPool.stream; | 48 Stream<LogEntry> get onLog => _onLogPool.stream; |
| 44 final _onLogPool = new StreamPool<LogEntry>.broadcast(); | 49 final _onLogPool = new StreamPool<LogEntry>.broadcast(); |
| 45 | 50 |
| 46 // TODO(nweiz): move to a more push-based way of propagating outputs and get | |
| 47 // rid of this. Once that's done, see if we can unify GroupRunner and | |
| 48 // AssetCascade. | |
| 49 /// The set of outputs that has been returned by [process]. | |
| 50 /// | |
| 51 /// [process] is expected to only return new outputs, so this is used to | |
| 52 /// ensure that it does so. | |
| 53 final _alreadyEmittedOutputs = new Set<AssetNode>(); | |
| 54 | |
| 55 GroupRunner(AssetCascade cascade, this._group, this._location) { | 51 GroupRunner(AssetCascade cascade, this._group, this._location) { |
| 56 var lastPhase = new Phase(cascade, _group.phases.first, _location); | 52 _addPhase(new Phase(cascade, _location), _group.phases.first); |
| 57 _phases.add(lastPhase); | |
| 58 for (var phase in _group.phases.skip(1)) { | 53 for (var phase in _group.phases.skip(1)) { |
| 59 lastPhase = lastPhase.addPhase(phase); | 54 _addPhase(_phases.last.addPhase(), phase); |
| 60 _phases.add(lastPhase); | |
| 61 } | |
| 62 | |
| 63 for (var phase in _phases) { | |
| 64 _onDirtyPool.add(phase.onDirty); | |
| 65 _onLogPool.add(phase.onLog); | |
| 66 } | 55 } |
| 67 } | 56 } |
| 68 | 57 |
| 58 /// Add a phase with [contents] to [this]'s list of phases. | |
| 59 /// | |
| 60 /// [contents] should be an inner [Iterable] from a [TransformGroup.phases] | |
| 61 /// value. | |
| 62 void _addPhase(Phase phase, Iterable contents) { | |
| 63 _phases.add(phase); | |
| 64 _onAssetPool.add(phase.onAsset); | |
| 65 _onLogPool.add(phase.onLog); | |
| 66 phase.onDone.listen((_) { | |
|
Bob Nystrom
2014/03/05 22:13:25
Do we need to listen to this on all phases, or jus
nweiz
2014/03/06 00:29:08
It's possible for a middle phase to go dirty and t
| |
| 67 if (!isDirty) _onDoneController.add(null); | |
| 68 }); | |
| 69 phase.updateTransformers(contents); | |
| 70 } | |
| 71 | |
| 69 /// Force all [LazyTransformer]s' transforms in this group to begin producing | 72 /// Force all [LazyTransformer]s' transforms in this group to begin producing |
| 70 /// concrete assets. | 73 /// concrete assets. |
| 71 void forceAllTransforms() { | 74 void forceAllTransforms() { |
| 72 for (var phase in _phases) { | 75 for (var phase in _phases) { |
| 73 phase.forceAllTransforms(); | 76 phase.forceAllTransforms(); |
| 74 } | 77 } |
| 75 } | 78 } |
| 76 | 79 |
| 77 /// Adds a new asset as an input for this group. | 80 /// Adds a new asset as an input for this group. |
| 78 void addInput(AssetNode node) { | 81 void addInput(AssetNode node) { |
| 79 _phases.first.addInput(node); | 82 _phases.first.addInput(node); |
| 80 } | 83 } |
| 81 | 84 |
| 82 /// Removes this group and all sub-phases within it. | 85 /// Removes this group and all sub-phases within it. |
| 83 void remove() { | 86 void remove() { |
| 84 _phases.first.remove(); | 87 _phases.first.remove(); |
| 85 } | 88 } |
| 86 | 89 |
| 87 /// Processes this group. | |
| 88 /// | |
| 89 /// Returns a future that completes with any new outputs produced by the | |
| 90 /// group. | |
| 91 Future<Set<AssetNode>> process() { | |
| 92 // Process the first phase that needs to do work. | |
| 93 for (var phase in _phases) { | |
| 94 var future = phase.process(); | |
| 95 if (future != null) return future.then((_) => process()); | |
| 96 } | |
| 97 | |
| 98 // If we get here, all phases are done processing. | |
| 99 var newOutputs = _phases.last.availableOutputs | |
| 100 .difference(_alreadyEmittedOutputs); | |
| 101 for (var output in newOutputs) { | |
| 102 output.whenRemoved(() => _alreadyEmittedOutputs.remove(output)); | |
| 103 } | |
| 104 _alreadyEmittedOutputs.addAll(newOutputs); | |
| 105 | |
| 106 return new Future.value(newOutputs); | |
| 107 } | |
| 108 | |
| 109 String toString() => "group in phase $_location for $_group"; | 90 String toString() => "group in phase $_location for $_group"; |
| 110 } | 91 } |
| OLD | NEW |