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.graph.transform_node; | 5 library barback.graph.transform_node; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import '../asset/asset.dart'; | 9 import '../asset/asset.dart'; |
| 10 import '../asset/asset_id.dart'; | 10 import '../asset/asset_id.dart'; |
| 11 import '../asset/asset_node.dart'; | 11 import '../asset/asset_node.dart'; |
| 12 import '../asset/asset_node_set.dart'; | |
| 12 import '../errors.dart'; | 13 import '../errors.dart'; |
| 13 import '../log.dart'; | 14 import '../log.dart'; |
| 14 import '../transformer/aggregate_transform.dart'; | 15 import '../transformer/aggregate_transform.dart'; |
| 16 import '../transformer/aggregate_transformer.dart'; | |
| 15 import '../transformer/declaring_aggregate_transform.dart'; | 17 import '../transformer/declaring_aggregate_transform.dart'; |
| 16 import '../transformer/declaring_transform.dart'; | 18 import '../transformer/declaring_aggregate_transformer.dart'; |
| 17 import '../transformer/declaring_transformer.dart'; | 19 import '../transformer/lazy_aggregate_transformer.dart'; |
| 18 import '../transformer/lazy_transformer.dart'; | 20 import '../utils.dart'; |
| 19 import '../transformer/transform.dart'; | |
| 20 import '../transformer/transformer.dart'; | |
| 21 import 'node_status.dart'; | 21 import 'node_status.dart'; |
| 22 import 'node_streams.dart'; | 22 import 'node_streams.dart'; |
| 23 import 'phase.dart'; | 23 import 'phase.dart'; |
| 24 | 24 |
| 25 /// Describes a transform on a set of assets and its relationship to the build | 25 /// Describes a transform on a set of assets and its relationship to the build |
| 26 /// dependency graph. | 26 /// dependency graph. |
| 27 /// | 27 /// |
| 28 /// Keeps track of whether it's dirty and needs to be run and which assets it | 28 /// Keeps track of whether it's dirty and needs to be run and which assets it |
| 29 /// depends on. | 29 /// depends on. |
| 30 class TransformNode { | 30 class TransformNode { |
| 31 /// The aggregate key for this node. | 31 /// The aggregate key for this node. |
| 32 final String key; | 32 final String key; |
| 33 | 33 |
| 34 /// The [Phase] that this transform runs in. | 34 /// The [Phase] that this transform runs in. |
| 35 final Phase phase; | 35 final Phase phase; |
| 36 | 36 |
| 37 /// The [Transformer] to apply to this node's inputs. | 37 /// The [AggregateTransformer] to apply to this node's inputs. |
| 38 final Transformer transformer; | 38 final AggregateTransformer transformer; |
| 39 | 39 |
| 40 /// The node for the primary asset this transform depends on. | 40 /// The primary asset nodes this transform runs on. |
| 41 final AssetNode primary; | 41 final _primaries = new AssetNodeSet(); |
| 42 | 42 |
| 43 /// A string describing the location of [this] in the transformer graph. | 43 /// A string describing the location of [this] in the transformer graph. |
| 44 final String _location; | 44 final String _location; |
| 45 | 45 |
| 46 /// The subscription to [primary]'s [AssetNode.onStateChange] stream. | 46 /// The subscription to the [_primaries]' [AssetNode.onStateChange] streams. |
| 47 StreamSubscription _primarySubscription; | 47 final _primarySubscriptions = new Map<AssetId, StreamSubscription>(); |
| 48 | 48 |
| 49 /// The subscription to [phase]'s [Phase.onAsset] stream. | 49 /// The subscription to [phase]'s [Phase.onAsset] stream. |
| 50 StreamSubscription<AssetNode> _phaseSubscription; | 50 StreamSubscription<AssetNode> _phaseAssetSubscription; |
| 51 | |
| 52 /// The subscription to [phase]'s [Phase.onStatusChange] stream. | |
| 53 StreamSubscription<NodeStatus> _phaseStatusSubscription; | |
| 51 | 54 |
| 52 /// How far along [this] is in processing its assets. | 55 /// How far along [this] is in processing its assets. |
| 53 NodeStatus get status { | 56 NodeStatus get status { |
| 54 if (_state == _State.APPLIED || _state == _State.DECLARED) { | 57 if (_state == _State.APPLIED || _state == _State.DECLARED) { |
| 55 return NodeStatus.IDLE; | 58 return NodeStatus.IDLE; |
| 56 } | 59 } |
| 57 | 60 |
| 58 if (_declaring && _state != _State.DECLARING) { | 61 if (_declaring && _state != _State.DECLARING && |
| 62 _state != _State.NEEDS_DECLARE) { | |
| 59 return NodeStatus.MATERIALIZING; | 63 return NodeStatus.MATERIALIZING; |
| 60 } else { | 64 } else { |
| 61 return NodeStatus.RUNNING; | 65 return NodeStatus.RUNNING; |
| 62 } | 66 } |
| 63 } | 67 } |
| 64 | 68 |
| 69 /// The [TransformInfo] describing this node. | |
| 70 /// | |
| 71 /// [TransformInfo] is the publicly-visible representation of a transform | |
| 72 /// node. | |
| 73 TransformInfo get info => new TransformInfo(transformer, | |
| 74 new AssetId(phase.cascade.package, key)); | |
| 75 | |
| 65 /// Whether this is a declaring transform. | 76 /// Whether this is a declaring transform. |
| 66 /// | 77 /// |
| 67 /// This is usually identical to `transformer is DeclaringTransformer`, but if | 78 /// This is usually identical to `transformer is |
| 68 /// a declaring and non-lazy transformer emits an error during | 79 /// DeclaringAggregateTransformer`, but if a declaring and non-lazy |
| 69 /// `declareOutputs` it's treated as though it wasn't declaring. | 80 /// transformer emits an error during `declareOutputs` it's treated as though |
| 70 bool get _declaring => transformer is DeclaringTransformer && | 81 /// it wasn't declaring. |
| 82 bool get _declaring => transformer is DeclaringAggregateTransformer && | |
| 71 (_state == _State.DECLARING || _declaredOutputs != null); | 83 (_state == _State.DECLARING || _declaredOutputs != null); |
| 72 | 84 |
| 73 /// Whether this transform has been forced since it last finished applying. | 85 /// Whether this transform has been forced since it last finished applying. |
| 74 /// | 86 /// |
| 75 /// A transform being forced means it should run until it generates outputs | 87 /// A transform being forced means it should run until it generates outputs |
| 76 /// and is no longer dirty. This is always true for non-declaring | 88 /// and is no longer dirty. This is always true for non-declaring |
| 77 /// transformers, since they always need to eagerly generate outputs. | 89 /// transformers, since they always need to eagerly generate outputs. |
| 78 bool _forced; | 90 bool _forced; |
| 79 | 91 |
| 80 /// The subscriptions to each input's [AssetNode.onStateChange] stream. | 92 /// The subscriptions to each secondary input's [AssetNode.onStateChange] |
| 81 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); | 93 /// stream. |
| 94 final _secondarySubscriptions = new Map<AssetId, StreamSubscription>(); | |
| 82 | 95 |
| 83 /// The controllers for the asset nodes emitted by this node. | 96 /// The controllers for the asset nodes emitted by this node. |
| 84 final _outputControllers = new Map<AssetId, AssetNodeController>(); | 97 final _outputControllers = new Map<AssetId, AssetNodeController>(); |
| 85 | 98 |
| 86 /// The ids of inputs the transformer tried and failed to read last time it | 99 /// The ids of inputs the transformer tried and failed to read last time it |
| 87 /// ran. | 100 /// ran. |
| 88 final _missingInputs = new Set<AssetId>(); | 101 final _missingInputs = new Set<AssetId>(); |
| 89 | 102 |
| 90 /// The controller that's used to pass [primary] through [this] if it's not | 103 /// The controllers that are used to pass each primary input through [this] if |
| 91 /// consumed or overwritten. | 104 /// it's not consumed or overwritten. |
| 92 /// | 105 /// |
| 93 /// This needs an intervening controller to ensure that the output can be | 106 /// This needs an intervening controller to ensure that the output can be |
| 94 /// marked dirty when determining whether [this] will consume or overwrite it, | 107 /// marked dirty when determining whether [this] will consume or overwrite it, |
| 95 /// and be marked removed if it does. [_passThroughController] will be null | 108 /// and be marked removed if it does. No pass-through controller will exist |
| 96 /// if the asset is not being passed through. | 109 /// for primary inputs that are not being passed through. |
| 97 AssetNodeController _passThroughController; | 110 final _passThroughControllers = new Map<AssetId, AssetNodeController>(); |
| 98 | 111 |
| 99 /// The asset node for this transform. | 112 /// The asset node for this transform. |
| 100 final _streams = new NodeStreams(); | 113 final _streams = new NodeStreams(); |
| 101 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange; | 114 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange; |
| 102 Stream<AssetNode> get onAsset => _streams.onAsset; | 115 Stream<AssetNode> get onAsset => _streams.onAsset; |
| 103 Stream<LogEntry> get onLog => _streams.onLog; | 116 Stream<LogEntry> get onLog => _streams.onLog; |
| 104 | 117 |
| 105 /// The current state of [this]. | 118 /// The current state of [this]. |
| 106 var _state = _State.DECLARING; | 119 var _state = _State.DECLARED; |
| 107 | 120 |
| 108 /// Whether [this] has been marked as removed. | 121 /// Whether [this] has been marked as removed. |
| 109 bool get _isRemoved => _streams.onAssetController.isClosed; | 122 bool get _isRemoved => _streams.onAssetController.isClosed; |
| 110 | 123 |
| 111 // If [transformer] is declaring but not lazy and [primary] is available, we | 124 // If [transformer] is declaring but not lazy and [primary] is available, we |
| 112 // can run [apply] even if [force] hasn't been called, since [transformer] | 125 // can run [apply] even if [force] hasn't been called, since [transformer] |
| 113 // should run eagerly if possible. | 126 // should run eagerly if possible. |
| 114 bool get _canRunDeclaringEagerly => | 127 bool get _canRunDeclaringEagerly => |
| 115 _declaring && transformer is! LazyTransformer && | 128 _declaring && transformer is! LazyAggregateTransformer && |
| 116 primary.state.isAvailable; | 129 _primaries.every((input) => input.state.isAvailable); |
| 117 | 130 |
| 118 /// Whether the most recent run of this transform has declared that it | 131 /// Which primary inputs the most recent run of this transform has declared |
| 119 /// consumes the primary input. | 132 /// that it consumes. |
| 120 /// | 133 /// |
| 121 /// Defaults to `false`. This is not meaningful unless [_state] is | 134 /// This starts out `null`, indicating that the transform hasn't declared |
| 122 /// [_State.APPLIED] or [_State.DECLARED]. | 135 /// anything yet. This is not meaningful unless [_state] is [_State.APPLIED] |
| 123 bool _consumePrimary = false; | 136 /// or [_State.DECLARED]. |
| 137 Set<AssetId> _consumedPrimaries; | |
| 124 | 138 |
| 125 /// The set of output ids that [transformer] declared it would emit. | 139 /// The set of output ids that [transformer] declared it would emit. |
| 126 /// | 140 /// |
| 127 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its | 141 /// This is only non-null if [transformer] is a |
| 128 /// [declareOutputs] has been run successfully. | 142 /// [DeclaringAggregateTransformer] and its [declareOutputs] has been run |
| 143 /// successfully. | |
| 129 Set<AssetId> _declaredOutputs; | 144 Set<AssetId> _declaredOutputs; |
| 130 | 145 |
| 131 TransformNode(this.phase, Transformer transformer, AssetNode primary, | 146 /// The controller for the currently-running |
| 132 this._location) | 147 /// [DeclaringAggregateTransformer.declareOutputs] call's |
| 133 : key = primary.id.path, | 148 /// [DeclaringAggregateTransform]. |
| 134 transformer = transformer, | 149 /// |
| 135 primary = primary { | 150 /// This will be non-`null` when |
| 136 _forced = transformer is! DeclaringTransformer; | 151 /// [DeclaringAggregateTransformer.declareOutputs] is running. This means that |
| 137 if (_forced) primary.force(); | 152 /// it's always non-`null` when [_state] is [_State.DECLARING], sometimes |
| 153 /// non-`null` when it's [_State.NEEDS_DECLARE], and always `null` otherwise. | |
| 154 DeclaringAggregateTransformController _declareController; | |
| 138 | 155 |
| 139 _primarySubscription = primary.onStateChange.listen((state) { | 156 /// The controller for the currently-running [AggregateTransformer.apply] call 's |
|
Bob Nystrom
2014/05/08 20:30:48
Long line.
nweiz
2014/05/08 21:12:36
Done.
| |
| 140 if (state.isRemoved) { | 157 /// [AggregateTransform]. |
| 141 remove(); | 158 /// |
| 142 } else { | 159 /// This will be non-`null` when [AggregateTransform.apply] is running, which |
| 143 if (_forced) primary.force(); | 160 /// means that it's always non-`null` when [_state] is [_State.APPLYING] or |
| 144 _dirty(); | 161 /// [_State.NEEDS_APPLY], sometimes non-`null` when it's |
| 145 } | 162 /// [_State.NEEDS_DECLARE], and always `null` otherwise. |
| 146 }); | 163 AggregateTransformController _applyController; |
| 147 | 164 |
| 148 _phaseSubscription = phase.previous.onAsset.listen((node) { | 165 TransformNode(this.phase, this.transformer, this.key, this._location) { |
| 166 _forced = transformer is! DeclaringAggregateTransformer; | |
| 167 | |
| 168 _phaseAssetSubscription = phase.previous.onAsset.listen((node) { | |
| 149 if (!_missingInputs.contains(node.id)) return; | 169 if (!_missingInputs.contains(node.id)) return; |
| 150 if (_forced) node.force(); | 170 if (_forced) node.force(); |
| 151 _dirty(); | 171 _dirty(); |
| 152 }); | 172 }); |
| 153 | 173 |
| 154 _declareOutputs().then((_) { | 174 _phaseStatusSubscription = phase.previous.onStatusChange.listen((status) { |
| 155 if (_forced || _canRunDeclaringEagerly) { | 175 if (status == NodeStatus.RUNNING) return; |
| 156 _apply(); | 176 |
| 157 } else { | 177 _maybeFinishDeclareController(); |
| 158 _state = _State.DECLARED; | 178 _maybeFinishApplyController(); |
| 159 _streams.changeStatus(NodeStatus.IDLE); | |
| 160 } | |
| 161 }); | 179 }); |
| 180 | |
| 181 _run(); | |
| 162 } | 182 } |
| 163 | 183 |
| 164 /// The [TransformInfo] describing this node. | 184 /// Adds [input] as a primary input for this node. |
| 165 /// | 185 void addInput(AssetNode input) { |
|
Bob Nystrom
2014/05/08 20:30:48
addInput -> addPrimary?
nweiz
2014/05/08 21:12:36
Done.
| |
| 166 /// [TransformInfo] is the publicly-visible representation of a transform | 186 _primaries.add(input); |
| 167 /// node. | 187 if (_forced) input.force(); |
| 168 TransformInfo get info => new TransformInfo(transformer, primary.id); | 188 |
| 189 _primarySubscriptions[input.id] = input.onStateChange | |
| 190 .listen((_) => _onPrimaryStateChange(input)); | |
| 191 | |
| 192 if (_state == _State.DECLARING && !_declareController.isDone) { | |
| 193 // If we're running `declareOutputs` and its id stream isn't closed yet, | |
| 194 // pass this in as another id. | |
| 195 _declareController.addId(input.id); | |
| 196 _maybeFinishDeclareController(); | |
| 197 } else if (_state == _State.APPLYING) { | |
| 198 // If we're running `apply`, we need to wait until [input] is available | |
| 199 // before we pass it into the stream. If it's available now, great; if | |
| 200 // not, [_onPrimaryStateChange] will handle it. | |
| 201 if (!input.state.isAvailable) return; | |
| 202 _onPrimaryStateChange(input); | |
| 203 _maybeFinishApplyController(); | |
| 204 } else { | |
| 205 // Otherwise, a new input means we'll need to re-run `declareOutputs`. | |
| 206 _restartRun(); | |
| 207 } | |
| 208 } | |
| 169 | 209 |
| 170 /// Marks this transform as removed. | 210 /// Marks this transform as removed. |
| 171 /// | 211 /// |
| 172 /// This causes all of the transform's outputs to be marked as removed as | 212 /// This causes all of the transform's outputs to be marked as removed as |
| 173 /// well. Normally this will be automatically done internally based on events | 213 /// well. Normally this will be automatically done internally based on events |
| 174 /// from the primary input, but it's possible for a transform to no longer be | 214 /// from the primary input, but it's possible for a transform to no longer be |
| 175 /// valid even if its primary input still exists. | 215 /// valid even if its primary input still exists. |
| 176 void remove() { | 216 void remove() { |
| 177 _streams.close(); | 217 _streams.close(); |
| 178 _primarySubscription.cancel(); | 218 _phaseAssetSubscription.cancel(); |
| 179 _phaseSubscription.cancel(); | 219 _phaseStatusSubscription.cancel(); |
| 180 _clearInputSubscriptions(); | 220 if (_declareController != null) _declareController.cancel(); |
| 221 if (_applyController != null) _applyController.cancel(); | |
| 222 _clearSecondarySubscriptions(); | |
| 181 _clearOutputs(); | 223 _clearOutputs(); |
| 182 if (_passThroughController != null) { | 224 |
| 183 _passThroughController.setRemoved(); | 225 for (var subscription in _primarySubscriptions.values) { |
| 184 _passThroughController = null; | 226 subscription.cancel(); |
| 185 } | 227 } |
| 228 _primarySubscriptions.clear(); | |
| 229 | |
| 230 for (var controller in _passThroughControllers.values) { | |
| 231 controller.setRemoved(); | |
| 232 } | |
| 233 _passThroughControllers.clear(); | |
| 186 } | 234 } |
| 187 | 235 |
| 188 /// If [this] is deferred, ensures that its concrete outputs will be | 236 /// If [this] is deferred, ensures that its concrete outputs will be |
| 189 /// generated. | 237 /// generated. |
| 190 void force() { | 238 void force() { |
| 191 if (_forced || _state == _State.APPLIED) return; | 239 if (_forced || _state == _State.APPLIED) return; |
| 192 primary.force(); | 240 for (var input in _primaries) { |
| 241 input.force(); | |
| 242 } | |
| 243 | |
| 193 _forced = true; | 244 _forced = true; |
| 194 if (_state == _State.DECLARED) _dirty(); | 245 if (_state == _State.DECLARED) _apply(); |
| 195 } | 246 } |
| 196 | 247 |
| 197 /// Marks this transform as dirty. | 248 /// Marks this transform as dirty. |
| 198 /// | 249 /// |
| 199 /// This causes all of the transform's outputs to be marked as dirty as well. | 250 /// Specifically, this should be called when one of the transform's inputs' |
| 251 /// contents change, or when a secondary input is removed. Primary inputs | |
| 252 /// being added or removed are handled by [addInput] and | |
| 253 /// [_onPrimaryStateChange]. | |
| 200 void _dirty() { | 254 void _dirty() { |
| 201 // If we're in the process of running [declareOutputs], we already know that | 255 if (_state == _State.DECLARING || _state == _State.NEEDS_DECLARE || |
| 202 // [apply] needs to be run so there's nothing we need to mark as dirty. | 256 _state == _State.NEEDS_APPLY) { |
| 203 if (_state == _State.DECLARING) return; | 257 // If we already know that [_apply] needs to be run, there's nothing to do |
| 258 // here. | |
| 259 return; | |
| 260 } | |
| 204 | 261 |
| 205 if (!_forced && !_canRunDeclaringEagerly) { | 262 if (!_forced && !_canRunDeclaringEagerly) { |
| 206 // [forced] should only ever be false for a declaring transformer. | 263 // [forced] should only ever be false for a declaring transformer. |
| 207 assert(_declaring); | 264 assert(_declaring); |
| 208 | 265 |
| 209 // If we've finished applying, transition to MATERIALIZING, indicating | 266 // If we've finished applying, transition to DECLARED, indicating that we |
| 210 // that we know what outputs [apply] will emit but we're waiting to emit | 267 // know what outputs [apply] will emit but we're waiting to emit them |
| 211 // them concretely until [force] is called. If we're still applying, we'll | 268 // concretely until [force] is called. If we're still applying, we'll |
| 212 // transition to MATERIALIZING once we finish. | 269 // transition to DECLARED once we finish. |
| 213 if (_state == _State.APPLIED) _state = _State.DECLARED; | 270 if (_state == _State.APPLIED) _state = _State.DECLARED; |
| 214 for (var controller in _outputControllers.values) { | 271 for (var controller in _outputControllers.values) { |
| 215 controller.setLazy(force); | 272 controller.setLazy(force); |
| 216 } | 273 } |
| 217 _emitDeclaredOutputs(); | 274 _emitDeclaredOutputs(); |
| 218 return; | 275 return; |
| 219 } | 276 } |
| 220 | 277 |
| 221 if (_passThroughController != null) _passThroughController.setDirty(); | |
| 222 for (var controller in _outputControllers.values) { | |
| 223 controller.setDirty(); | |
| 224 } | |
| 225 | |
| 226 if (_state == _State.APPLIED) { | 278 if (_state == _State.APPLIED) { |
| 227 if (_declaredOutputs != null) _emitDeclaredOutputs(); | 279 if (_declaredOutputs != null) _emitDeclaredOutputs(); |
| 228 _apply(); | 280 _apply(); |
| 229 } else if (_state == _State.DECLARED) { | 281 } else if (_state == _State.DECLARED) { |
| 230 _apply(); | 282 _apply(); |
| 231 } else { | 283 } else { |
| 232 _state = _State.NEEDS_APPLY; | 284 _state = _State.NEEDS_APPLY; |
| 233 } | 285 } |
| 234 } | 286 } |
| 235 | 287 |
| 288 /// The callback called when [input]'s state changes. | |
| 289 void _onPrimaryStateChange(AssetNode input) { | |
| 290 if (input.state.isRemoved) { | |
| 291 _primarySubscriptions.remove(input.id); | |
| 292 | |
| 293 if (_primaries.isEmpty) { | |
| 294 // If there are no more primary inputs, there's no more use for this | |
| 295 // node in the graph. It will be re-created by its | |
| 296 // [TransformerClassifier] if a new input with [key] is added. | |
| 297 remove(); | |
| 298 return; | |
| 299 } | |
| 300 | |
| 301 // Any change to the number of primary inputs requires that we re-run the | |
| 302 // transformation. | |
| 303 _restartRun(); | |
| 304 } else if (input.state.isAvailable) { | |
| 305 if (_state == _State.DECLARED && _canRunDeclaringEagerly) { | |
| 306 // If [this] is fully declared but hasn't started applying, this input | |
| 307 // becoming available may mean that all inputs are available, in which | |
| 308 // case we can run apply eagerly. | |
| 309 _apply(); | |
| 310 return; | |
| 311 } | |
| 312 | |
| 313 // If we're not actively passing concrete assets to the transformer, the | |
| 314 // distinction between a dirty asset and an available one isn't relevant. | |
| 315 if (_state != _State.APPLYING) return; | |
| 316 | |
| 317 if (_applyController.isDone) { | |
| 318 // If we get a new asset after we've closed the asset stream, we need to | |
| 319 // re-run declare and then apply. | |
| 320 _restartRun(); | |
| 321 } else { | |
| 322 // If the new asset comes before the asset stream is done, we can just | |
| 323 // pass it to the stream. | |
| 324 _applyController.addInput(input.asset); | |
| 325 _maybeFinishApplyController(); | |
| 326 } | |
| 327 } else { | |
| 328 if (_forced) input.force(); | |
| 329 if (_state == _State.APPLYING && !_applyController.addedId(input.id)) { | |
| 330 // If the input hasn't yet been added to the transform's input stream, | |
| 331 // there's no need to consider the transformation dirty. | |
| 332 return; | |
| 333 } | |
| 334 _dirty(); | |
| 335 } | |
| 336 } | |
| 337 | |
| 338 /// Run the entire transformation, including both `declareOutputs` (if | |
| 339 /// applicable) and `apply`. | |
| 340 void _run() { | |
| 341 assert(_state != _State.DECLARING); | |
| 342 assert(_state != _State.APPLYING); | |
| 343 | |
| 344 _markOutputsDirty(); | |
| 345 _declareOutputs(() { | |
| 346 if (_forced || _canRunDeclaringEagerly) { | |
| 347 _apply(); | |
| 348 } else { | |
| 349 _state = _State.DECLARED; | |
| 350 _streams.changeStatus(NodeStatus.IDLE); | |
| 351 } | |
| 352 }); | |
| 353 } | |
| 354 | |
| 355 /// Restart the entire transformation, including `declareOutputs` if | |
| 356 /// applicable. | |
| 357 void _restartRun() { | |
| 358 if (_state == _State.DECLARED || _state == _State.APPLIED) { | |
| 359 // If we're currently idle, we can restart the transformation immediately. | |
| 360 _run(); | |
| 361 return; | |
| 362 } | |
| 363 | |
| 364 // If we're actively running `declareOutputs` or `apply`, cancel the | |
| 365 // transforms and transition to `NEEDS_DECLARE`. Once the transformer's | |
| 366 // method returns, we'll transition to `DECLARING`. | |
| 367 if (_declareController != null) _declareController.cancel(); | |
| 368 if (_applyController != null) _applyController.cancel(); | |
| 369 _state = _State.NEEDS_DECLARE; | |
| 370 } | |
| 371 | |
| 236 /// Runs [transform.declareOutputs] and emits the resulting assets as dirty | 372 /// Runs [transform.declareOutputs] and emits the resulting assets as dirty |
| 237 /// assets. | 373 /// assets. |
| 238 Future _declareOutputs() { | 374 /// |
| 239 if (transformer is! DeclaringTransformer) return new Future.value(); | 375 /// Calls [callback] when it's finished. This doesn't return a future so that |
| 376 /// [callback] is called synchronously if there are no outputs to declare. If | |
| 377 /// [this] is removed while inputs are being declared, [callback] will not be | |
| 378 /// called. | |
| 379 void _declareOutputs(void callback()) { | |
| 380 if (transformer is! DeclaringAggregateTransformer) { | |
| 381 callback(); | |
| 382 return; | |
| 383 } | |
| 240 | 384 |
| 385 _state = _State.DECLARING; | |
| 241 var controller = new DeclaringAggregateTransformController(this); | 386 var controller = new DeclaringAggregateTransformController(this); |
| 387 _declareController = controller; | |
|
Bob Nystrom
2014/05/08 20:30:48
No reason to make a local for this.
nweiz
2014/05/08 21:12:36
The instance variable gets nulled out in the whenC
Bob Nystrom
2014/05/08 23:56:13
Ah, right. I didn't notice it was used down below.
| |
| 242 _streams.onLogPool.add(controller.onLog); | 388 _streams.onLogPool.add(controller.onLog); |
| 243 controller.idController.add(primary.id); | 389 for (var primary in _primaries) { |
| 244 return newDeclaringTransform(controller.transform).then((transform) { | 390 controller.addId(primary.id); |
| 245 return (transformer as DeclaringTransformer).declareOutputs(transform); | 391 } |
| 392 | |
| 393 syncFuture(() { | |
| 394 return (transformer as DeclaringAggregateTransformer) | |
| 395 .declareOutputs(controller.transform); | |
| 396 }).whenComplete(() { | |
| 397 controller.cancel(); | |
|
Bob Nystrom
2014/05/08 20:30:48
Document that calling cancel here deliberately eve
nweiz
2014/05/08 21:12:36
Done.
| |
| 398 _declareController = null; | |
| 246 }).then((_) { | 399 }).then((_) { |
| 247 if (_isRemoved) return; | 400 if (_isRemoved) return; |
| 248 if (controller.loggedError) return; | 401 if (_state == _State.NEEDS_DECLARE) { |
| 402 _declareOutputs(callback); | |
| 403 return; | |
| 404 } | |
| 249 | 405 |
| 250 _consumePrimary = controller.consumedPrimaries.contains(primary.id); | 406 if (controller.loggedError) { |
|
Bob Nystrom
2014/05/08 20:30:48
Document something like: "If declaration fails, fa
nweiz
2014/05/08 21:12:36
Done.
| |
| 407 if (transformer is! LazyAggregateTransformer) _forced = true; | |
| 408 callback(); | |
| 409 return; | |
| 410 } | |
| 411 | |
| 412 _consumedPrimaries = controller.consumedPrimaries; | |
| 251 _declaredOutputs = controller.outputIds; | 413 _declaredOutputs = controller.outputIds; |
| 252 var invalidIds = _declaredOutputs | 414 var invalidIds = _declaredOutputs |
| 253 .where((id) => id.package != phase.cascade.package).toSet(); | 415 .where((id) => id.package != phase.cascade.package).toSet(); |
| 254 for (var id in invalidIds) { | 416 for (var id in invalidIds) { |
| 255 _declaredOutputs.remove(id); | 417 _declaredOutputs.remove(id); |
| 256 // TODO(nweiz): report this as a warning rather than a failing error. | 418 // TODO(nweiz): report this as a warning rather than a failing error. |
| 257 phase.cascade.reportError(new InvalidOutputException(info, id)); | 419 phase.cascade.reportError(new InvalidOutputException(info, id)); |
| 258 } | 420 } |
| 259 | 421 |
| 260 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); | 422 for (var primary in _primaries) { |
| 423 if (_declaredOutputs.contains(primary.id)) continue; | |
| 424 _emitPassThrough(primary.id); | |
| 425 } | |
| 261 _emitDeclaredOutputs(); | 426 _emitDeclaredOutputs(); |
| 427 callback(); | |
| 262 }).catchError((error, stackTrace) { | 428 }).catchError((error, stackTrace) { |
| 263 if (_isRemoved) return; | 429 if (_isRemoved) return; |
| 264 if (transformer is! LazyTransformer) _forced = true; | 430 if (transformer is! LazyAggregateTransformer) _forced = true; |
| 265 phase.cascade.reportError(_wrapException(error, stackTrace)); | 431 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 432 callback(); | |
| 266 }); | 433 }); |
| 267 } | 434 } |
| 268 | 435 |
| 269 /// Emits a dirty asset node for all outputs that were declared by the | 436 /// Emits a dirty asset node for all outputs that were declared by the |
| 270 /// transformer. | 437 /// transformer. |
| 271 /// | 438 /// |
| 272 /// This won't emit any outputs for which there already exist output | 439 /// This won't emit any outputs for which there already exist output |
| 273 /// controllers. It should only be called for transforms that have declared | 440 /// controllers. It should only be called for transforms that have declared |
| 274 /// their outputs. | 441 /// their outputs. |
| 275 void _emitDeclaredOutputs() { | 442 void _emitDeclaredOutputs() { |
| 276 assert(_declaredOutputs != null); | 443 assert(_declaredOutputs != null); |
| 277 for (var id in _declaredOutputs) { | 444 for (var id in _declaredOutputs) { |
| 278 if (_outputControllers.containsKey(id)) continue; | 445 if (_outputControllers.containsKey(id)) continue; |
| 279 var controller = _forced | 446 var controller = _forced |
| 280 ? new AssetNodeController(id, this) | 447 ? new AssetNodeController(id, this) |
| 281 : new AssetNodeController.lazy(id, force, this); | 448 : new AssetNodeController.lazy(id, force, this); |
| 282 _outputControllers[id] = controller; | 449 _outputControllers[id] = controller; |
| 283 _streams.onAssetController.add(controller.node); | 450 _streams.onAssetController.add(controller.node); |
| 284 } | 451 } |
| 285 } | 452 } |
| 286 | 453 |
| 454 //// Mark all emitted and passed-through outputs of this transform as dirty. | |
| 455 void _markOutputsDirty() { | |
| 456 for (var controller in _passThroughControllers.values) { | |
| 457 controller.setDirty(); | |
| 458 } | |
| 459 for (var controller in _outputControllers.values) { | |
| 460 if (_forced) { | |
| 461 controller.setDirty(); | |
| 462 } else { | |
| 463 controller.setLazy(force); | |
| 464 } | |
| 465 } | |
| 466 } | |
| 467 | |
| 287 /// Applies this transform. | 468 /// Applies this transform. |
| 288 void _apply() { | 469 void _apply() { |
| 289 assert(!_isRemoved); | 470 assert(!_isRemoved); |
| 290 | 471 |
| 291 // Clear input subscriptions here as well as in [_process] because [_apply] | 472 _markOutputsDirty(); |
| 292 // may be restarted independently if only a secondary input changes. | 473 _clearSecondarySubscriptions(); |
| 293 _clearInputSubscriptions(); | |
| 294 _state = _State.APPLYING; | 474 _state = _State.APPLYING; |
| 295 _streams.changeStatus(status); | 475 _streams.changeStatus(status); |
| 296 _runApply().then((hadError) { | 476 _runApply().then((hadError) { |
| 297 if (_isRemoved) return; | 477 if (_isRemoved) return; |
| 298 | 478 |
| 299 if (_state == _State.DECLARED) return; | 479 if (_state == _State.DECLARED) return; |
| 300 | 480 |
| 301 if (_state == _State.NEEDS_APPLY) { | 481 if (_state == _State.NEEDS_DECLARE) { |
| 302 _apply(); | 482 _run(); |
| 303 return; | 483 return; |
| 304 } | 484 } |
| 305 | 485 |
|
Bob Nystrom
2014/05/08 20:30:48
Document this please. Something like: "If we got i
nweiz
2014/05/08 21:12:36
Done.
| |
| 486 if (_state == _State.NEEDS_APPLY) { | |
| 487 if (_forced || _canRunDeclaringEagerly) { | |
| 488 _apply(); | |
| 489 } else { | |
| 490 _state = _State.DECLARED; | |
| 491 } | |
| 492 return; | |
| 493 } | |
| 494 | |
| 306 if (_declaring) _forced = false; | 495 if (_declaring) _forced = false; |
| 307 | 496 |
| 308 assert(_state == _State.APPLYING); | 497 assert(_state == _State.APPLYING); |
| 309 if (hadError) { | 498 if (hadError) { |
| 310 _clearOutputs(); | 499 _clearOutputs(); |
| 311 // If the transformer threw an error, we don't want to emit the | 500 // If the transformer threw an error, we don't want to emit the |
| 312 // pass-through asset in case it will be overwritten by the transformer. | 501 // pass-through assets in case they'll be overwritten by the |
| 313 // However, if the transformer declared that it wouldn't overwrite or | 502 // transformer. However, if the transformer declared that it wouldn't |
| 314 // consume the pass-through asset, we can safely emit it. | 503 // overwrite or consume a pass-through asset, we can safely emit it. |
| 315 if (_declaredOutputs != null && !_consumePrimary && | 504 if (_declaredOutputs != null) { |
| 316 !_declaredOutputs.contains(primary.id)) { | 505 for (var id in _primaries.map((node) => node.id)) { |
|
Bob Nystrom
2014/05/08 20:30:48
Nit: Using map() here seems a bit gratuitous. Why
nweiz
2014/05/08 21:12:36
Done.
| |
| 317 _emitPassThrough(); | 506 if (_consumedPrimaries.contains(id) || |
| 318 } else { | 507 _declaredOutputs.contains(id)) { |
| 319 _dontEmitPassThrough(); | 508 _dontEmitPassThrough(id); |
| 509 } else { | |
| 510 _emitPassThrough(id); | |
| 511 } | |
| 512 } | |
| 320 } | 513 } |
| 321 } | 514 } |
| 322 | 515 |
| 323 _state = _State.APPLIED; | 516 _state = _State.APPLIED; |
| 324 _streams.changeStatus(NodeStatus.IDLE); | 517 _streams.changeStatus(NodeStatus.IDLE); |
| 325 }); | 518 }); |
| 326 } | 519 } |
| 327 | 520 |
| 328 /// Gets the asset for an input [id]. | 521 /// Gets the asset for an input [id]. |
| 329 /// | 522 /// |
| 330 /// If an input with [id] cannot be found, throws an [AssetNotFoundException]. | 523 /// If an input with [id] cannot be found, throws an [AssetNotFoundException]. |
| 331 Future<Asset> getInput(AssetId id) { | 524 Future<Asset> getInput(AssetId id) { |
| 332 return phase.previous.getOutput(id).then((node) { | 525 return phase.previous.getOutput(id).then((node) { |
| 333 // Throw if the input isn't found. This ensures the transformer's apply | 526 // Throw if the input isn't found. This ensures the transformer's apply |
| 334 // is exited. We'll then catch this and report it through the proper | 527 // is exited. We'll then catch this and report it through the proper |
| 335 // results stream. | 528 // results stream. |
| 336 if (node == null) { | 529 if (node == null) { |
| 337 _missingInputs.add(id); | 530 _missingInputs.add(id); |
| 338 throw new AssetNotFoundException(id); | 531 throw new AssetNotFoundException(id); |
| 339 } | 532 } |
| 340 | 533 |
| 341 _inputSubscriptions.putIfAbsent(node.id, () { | 534 _secondarySubscriptions.putIfAbsent(node.id, () { |
| 342 return node.onStateChange.listen((state) => _dirty()); | 535 return node.onStateChange.listen((_) => _dirty()); |
| 343 }); | 536 }); |
| 344 | 537 |
| 345 return node.asset; | 538 return node.asset; |
| 346 }); | 539 }); |
| 347 } | 540 } |
| 348 | 541 |
| 349 /// Run [Transformer.apply] as soon as [primary] is available. | 542 /// Run [Transformer.apply] as soon as [primary] is available. |
|
Bob Nystrom
2014/05/08 20:30:48
Update doc comment.
nweiz
2014/05/08 21:12:36
Done.
| |
| 350 /// | 543 /// |
| 351 /// Returns whether or not an error occurred while running the transformer. | 544 /// Returns whether or not an error occurred while running the transformer. |
| 352 Future<bool> _runApply() { | 545 Future<bool> _runApply() { |
| 353 var controller = new AggregateTransformController(this); | 546 var controller = new AggregateTransformController(this); |
| 547 _applyController = controller; | |
| 354 _streams.onLogPool.add(controller.onLog); | 548 _streams.onLogPool.add(controller.onLog); |
| 549 for (var primary in _primaries) { | |
| 550 if (!primary.state.isAvailable) continue; | |
| 551 controller.addInput(primary.asset); | |
| 552 } | |
| 355 | 553 |
| 356 return primary.whenAvailable((_) { | 554 return syncFuture(() { |
| 357 if (_isRemoved) return null; | 555 return transformer.apply(controller.transform); |
| 358 _state = _State.APPLYING; | 556 }).whenComplete(() { |
| 359 controller.inputController.add(primary.asset); | 557 controller.cancel(); |
|
Bob Nystrom
2014/05/08 20:30:48
Document that doing this deliberately even on succ
nweiz
2014/05/08 21:12:36
Done.
| |
| 360 return newTransform(controller.transform).then((transform) { | 558 _applyController = null; |
| 361 return transformer.apply(transform); | |
| 362 }); | |
| 363 }).then((_) { | 559 }).then((_) { |
| 364 if (!_forced && !primary.state.isAvailable) { | 560 assert(_state != _State.DECLARED); |
| 561 assert(_state != _State.APPLIED); | |
| 562 | |
| 563 if (!_forced && _primaries.any((node) => !node.state.isAvailable)) { | |
| 365 _state = _State.DECLARED; | 564 _state = _State.DECLARED; |
| 366 _streams.changeStatus(NodeStatus.IDLE); | 565 _streams.changeStatus(NodeStatus.IDLE); |
| 367 return false; | 566 return false; |
| 368 } | 567 } |
| 369 | 568 |
| 370 if (_isRemoved) return false; | 569 if (_isRemoved) return false; |
| 371 if (_state == _State.NEEDS_APPLY) return false; | 570 if (_state == _State.NEEDS_APPLY) return false; |
| 372 if (_state == _State.DECLARING) return false; | 571 if (_state == _State.NEEDS_DECLARE) return false; |
|
Bob Nystrom
2014/05/08 20:30:48
What if _state is DECLARING? Can that still happen
nweiz
2014/05/08 21:12:36
There's no way to transition to DECLARING until `a
Bob Nystrom
2014/05/08 23:56:13
Add an assert above then?
nweiz
2014/05/19 21:11:09
Done.
nweiz
2014/05/19 21:11:09
Done.
| |
| 373 if (controller.loggedError) return true; | 572 if (controller.loggedError) return true; |
| 374 _handleApplyResults(controller); | 573 _handleApplyResults(controller); |
| 375 return false; | 574 return false; |
| 376 }).catchError((error, stackTrace) { | 575 }).catchError((error, stackTrace) { |
| 377 // If the transform became dirty while processing, ignore any errors from | 576 // If the transform became dirty while processing, ignore any errors from |
| 378 // it. | 577 // it. |
| 379 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; | 578 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; |
| 380 | 579 |
| 381 // Catch all transformer errors and pipe them to the results stream. This | 580 // Catch all transformer errors and pipe them to the results stream. This |
| 382 // is so a broken transformer doesn't take down the whole graph. | 581 // is so a broken transformer doesn't take down the whole graph. |
| 383 phase.cascade.reportError(_wrapException(error, stackTrace)); | 582 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 384 return true; | 583 return true; |
| 385 }); | 584 }); |
| 386 } | 585 } |
| 387 | 586 |
| 388 /// Handle the results of running [Transformer.apply]. | 587 /// Handle the results of running [Transformer.apply]. |
| 389 /// | 588 /// |
| 390 /// [controller] should be the controller for the [AggegateTransform] passed | 589 /// [controller] should be the controller for the [AggegateTransform] passed |
| 391 /// to [AggregateTransformer.apply]. | 590 /// to [AggregateTransformer.apply]. |
| 392 void _handleApplyResults(AggregateTransformController controller) { | 591 void _handleApplyResults(AggregateTransformController controller) { |
| 393 _consumePrimary = controller.consumedPrimaries.contains(primary.id); | 592 _consumedPrimaries = controller.consumedPrimaries; |
| 394 | 593 |
| 395 var newOutputs = controller.outputs; | 594 var newOutputs = controller.outputs; |
| 396 // Any ids that are for a different package are invalid. | 595 // Any ids that are for a different package are invalid. |
| 397 var invalidIds = newOutputs | 596 var invalidIds = newOutputs |
| 398 .map((asset) => asset.id) | 597 .map((asset) => asset.id) |
| 399 .where((id) => id.package != phase.cascade.package) | 598 .where((id) => id.package != phase.cascade.package) |
| 400 .toSet(); | 599 .toSet(); |
| 401 for (var id in invalidIds) { | 600 for (var id in invalidIds) { |
| 402 newOutputs.removeId(id); | 601 newOutputs.removeId(id); |
| 403 // TODO(nweiz): report this as a warning rather than a failing error. | 602 // TODO(nweiz): report this as a warning rather than a failing error. |
| 404 phase.cascade.reportError(new InvalidOutputException(info, id)); | 603 phase.cascade.reportError(new InvalidOutputException(info, id)); |
| 405 } | 604 } |
| 406 | 605 |
| 407 // Remove outputs that used to exist but don't anymore. | 606 // Remove outputs that used to exist but don't anymore. |
| 408 for (var id in _outputControllers.keys.toList()) { | 607 for (var id in _outputControllers.keys.toList()) { |
| 409 if (newOutputs.containsId(id)) continue; | 608 if (newOutputs.containsId(id)) continue; |
| 410 _outputControllers.remove(id).setRemoved(); | 609 _outputControllers.remove(id).setRemoved(); |
| 411 } | 610 } |
| 412 | 611 |
| 413 // Emit or stop emitting the pass-through asset between removing and | 612 // Emit or stop emitting pass-through assets between removing and adding |
| 414 // adding outputs to ensure there are no collisions. | 613 // outputs to ensure there are no collisions. |
| 415 if (!_consumePrimary && !newOutputs.containsId(primary.id)) { | 614 for (var id in _primaries.map((node) => node.id)) { |
| 416 _emitPassThrough(); | 615 if (_consumedPrimaries.contains(id) || newOutputs.containsId(id)) { |
| 417 } else { | 616 _dontEmitPassThrough(id); |
| 418 _dontEmitPassThrough(); | 617 } else { |
| 618 _emitPassThrough(id); | |
|
Bob Nystrom
2014/05/08 20:30:48
What do you think of:
_emitPassThrough -> _passTh
nweiz
2014/05/08 21:12:36
Done.
| |
| 619 } | |
| 419 } | 620 } |
| 420 | 621 |
| 421 // Store any new outputs or new contents for existing outputs. | 622 // Store any new outputs or new contents for existing outputs. |
| 422 for (var asset in newOutputs) { | 623 for (var asset in newOutputs) { |
| 423 var controller = _outputControllers[asset.id]; | 624 var controller = _outputControllers[asset.id]; |
| 424 if (controller != null) { | 625 if (controller != null) { |
| 425 controller.setAvailable(asset); | 626 controller.setAvailable(asset); |
| 426 } else { | 627 } else { |
| 427 var controller = new AssetNodeController.available(asset, this); | 628 var controller = new AssetNodeController.available(asset, this); |
| 428 _outputControllers[asset.id] = controller; | 629 _outputControllers[asset.id] = controller; |
| 429 _streams.onAssetController.add(controller.node); | 630 _streams.onAssetController.add(controller.node); |
| 430 } | 631 } |
| 431 } | 632 } |
| 432 } | 633 } |
| 433 | 634 |
| 434 /// Cancels all subscriptions to secondary input nodes. | 635 /// Cancels all subscriptions to secondary input nodes. |
| 435 void _clearInputSubscriptions() { | 636 void _clearSecondarySubscriptions() { |
| 436 _missingInputs.clear(); | 637 _missingInputs.clear(); |
| 437 for (var subscription in _inputSubscriptions.values) { | 638 for (var subscription in _secondarySubscriptions.values) { |
| 438 subscription.cancel(); | 639 subscription.cancel(); |
| 439 } | 640 } |
| 440 _inputSubscriptions.clear(); | 641 _secondarySubscriptions.clear(); |
| 441 } | 642 } |
| 442 | 643 |
| 443 /// Removes all output assets. | 644 /// Removes all output assets. |
| 444 void _clearOutputs() { | 645 void _clearOutputs() { |
| 445 // Remove all the previously-emitted assets. | 646 // Remove all the previously-emitted assets. |
| 446 for (var controller in _outputControllers.values) { | 647 for (var controller in _outputControllers.values) { |
| 447 controller.setRemoved(); | 648 controller.setRemoved(); |
| 448 } | 649 } |
| 449 _outputControllers.clear(); | 650 _outputControllers.clear(); |
| 450 } | 651 } |
| 451 | 652 |
| 452 /// Emit the pass-through asset if it's not being emitted already. | 653 /// Emit the pass-through node for the primary input [id] if it's not being |
| 453 void _emitPassThrough() { | 654 /// emitted already. |
| 454 assert(!_outputControllers.containsKey(primary.id)); | 655 void _emitPassThrough(AssetId id) { |
| 656 assert(!_outputControllers.containsKey(id)); | |
| 455 | 657 |
| 456 if (_consumePrimary) return; | 658 if (_consumedPrimaries.contains(id)) return; |
| 457 if (_passThroughController == null) { | 659 var controller = _passThroughControllers[id]; |
| 458 _passThroughController = new AssetNodeController.from(primary); | 660 var primary = _primaries[id]; |
| 459 _streams.onAssetController.add(_passThroughController.node); | 661 if (controller == null) { |
| 662 controller = new AssetNodeController.from(primary); | |
| 663 _passThroughControllers[id] = controller; | |
| 664 _streams.onAssetController.add(controller.node); | |
| 460 } else if (primary.state.isDirty) { | 665 } else if (primary.state.isDirty) { |
| 461 _passThroughController.setDirty(); | 666 controller.setDirty(); |
| 462 } else if (!_passThroughController.node.state.isAvailable) { | 667 } else if (!controller.node.state.isAvailable) { |
| 463 _passThroughController.setAvailable(primary.asset); | 668 controller.setAvailable(primary.asset); |
| 464 } | 669 } |
| 465 } | 670 } |
| 466 | 671 |
| 467 /// Stop emitting the pass-through asset if it's being emitted already. | 672 /// Stops emitting the pass-through node for the primary input [id] if it's |
| 468 void _dontEmitPassThrough() { | 673 /// being emitted. |
| 469 if (_passThroughController == null) return; | 674 void _dontEmitPassThrough(AssetId id) { |
| 470 _passThroughController.setRemoved(); | 675 var controller = _passThroughControllers.remove(id); |
| 471 _passThroughController = null; | 676 if (controller == null) return; |
| 677 controller.setRemoved(); | |
| 678 } | |
| 679 | |
| 680 /// If `declareOutputs` is running and all previous phases have declared their | |
| 681 /// outputs, mark [_declareController] as done. | |
| 682 void _maybeFinishDeclareController() { | |
| 683 if (_declareController == null) return; | |
| 684 if (phase.previous.status == NodeStatus.RUNNING) return; | |
| 685 _declareController.done(); | |
| 686 } | |
| 687 | |
| 688 /// If `apply` is running, all previous phases have declared their outputs, | |
| 689 /// and all primary inputs are available and thus have been passed to the | |
| 690 /// transformer, mark [_declareController] as done. | |
|
Bob Nystrom
2014/05/08 20:30:48
_applyController.
nweiz
2014/05/08 21:12:36
Done.
| |
| 691 void _maybeFinishApplyController() { | |
| 692 if (_applyController == null) return; | |
| 693 if (_primaries.any((input) => !input.state.isAvailable)) return; | |
| 694 if (phase.previous.status == NodeStatus.RUNNING) return; | |
| 695 _applyController.done(); | |
| 472 } | 696 } |
| 473 | 697 |
| 474 BarbackException _wrapException(error, StackTrace stackTrace) { | 698 BarbackException _wrapException(error, StackTrace stackTrace) { |
| 475 if (error is! AssetNotFoundException) { | 699 if (error is! AssetNotFoundException) { |
| 476 return new TransformerException(info, error, stackTrace); | 700 return new TransformerException(info, error, stackTrace); |
| 477 } else { | 701 } else { |
| 478 return new MissingInputException(info, error.id); | 702 return new MissingInputException(info, error.id); |
| 479 } | 703 } |
| 480 } | 704 } |
| 481 | 705 |
| 482 /// Emit a warning about the transformer on [id]. | |
| 483 void _warn(String message) { | |
| 484 _streams.onLogController.add( | |
| 485 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); | |
| 486 } | |
| 487 | |
| 488 String toString() => | 706 String toString() => |
| 489 "transform node in $_location for $transformer on $primary ($_state, " | 707 "transform node in $_location for $transformer on ${info.primaryId} " |
| 490 "$status, ${_forced ? '' : 'un'}forced)"; | 708 "($_state, $status, ${_forced ? '' : 'un'}forced)"; |
| 491 } | 709 } |
| 492 | 710 |
| 493 /// The enum of states that [TransformNode] can be in. | 711 /// The enum of states that [TransformNode] can be in. |
| 494 class _State { | 712 class _State { |
| 495 /// The transform is running [DeclaringTransformer.declareOutputs]. | 713 /// The transform is running [DeclaringAggregateTransformer.declareOutputs]. |
| 496 /// | 714 /// |
| 497 /// This is the initial state of the transformer, and it will only occur once | 715 /// If the set of primary inputs changes while in this state, it will |
|
Bob Nystrom
2014/05/08 20:30:48
Is this true of any change, or just modifications/
nweiz
2014/05/08 21:12:36
A new primary will only reset it if the id stream
| |
| 498 /// since [DeclaringTransformer.declareOutputs] is independent of the contents | 716 /// transition to [NEEDS_DECLARE]. If the [TransformNode] is still in this |
| 499 /// of the primary input. Once the method finishes running, this will | 717 /// state when `declareOutputs` finishes running, it will transition to |
| 500 /// transition to [APPLYING] if the transform is non-lazy and the input is | 718 /// [APPLYING] if the transform is non-lazy and all of its primary inputs are |
| 501 /// available, and [DECLARED] otherwise. | 719 /// available, and [DECLARED] otherwise. |
| 502 /// | 720 /// |
| 503 /// Non-declaring transformers will transition out of this state and into | 721 /// Non-declaring transformers will transition out of this state and into |
| 504 /// [APPLYING] immediately. | 722 /// [APPLYING] immediately. |
| 505 static final DECLARING = const _State._("declaring outputs"); | 723 static const DECLARING = const _State._("declaring outputs"); |
| 724 | |
| 725 /// The transform is running [AggregateTransformer.declareOutputs] or | |
| 726 /// [AggregateTransform.apply], but a primary input was added or removed after | |
| 727 /// it started, so it will need to re-run `declareOutputs`. | |
| 728 /// | |
| 729 /// The [TransformNode] will transition to [DECLARING] once `declareOutputs` | |
| 730 /// or `apply` finishes running. | |
| 731 static const NEEDS_DECLARE = const _State._("needs declare"); | |
| 506 | 732 |
| 507 /// The transform is deferred and has run | 733 /// The transform is deferred and has run |
| 508 /// [DeclaringTransformer.declareOutputs] but hasn't yet been forced. | 734 /// [DeclaringAggregateTransformer.declareOutputs] but hasn't yet been forced. |
| 509 /// | 735 /// |
| 510 /// This will transition to [APPLYING] when one of the outputs has been | 736 /// The [TransformNode] will transition to [APPLYING] when one of the outputs |
| 511 /// forced. | 737 /// has been forced or if the transformer is non-lazy and all of its primary |
| 512 static final DECLARED = const _State._("declared"); | 738 /// inputs become available. |
| 739 static const DECLARED = const _State._("declared"); | |
| 513 | 740 |
| 514 /// The transform is running [Transformer.apply]. | 741 /// The transform is running [AggregateTransformer.apply]. |
| 515 /// | 742 /// |
| 516 /// If an input changes while in this state, it will transition to | 743 /// If an input's contents change or a secondary input is added or removed |
| 517 /// [NEEDS_APPLY]. If the [TransformNode] is still in this state when | 744 /// while in this state, the [TransformNode] will transition to [NEEDS_APPLY]. |
| 518 /// [Transformer.apply] finishes running, it will transition to [APPLIED]. | 745 /// If a primary input is added or removed, it will transition to |
| 519 static final APPLYING = const _State._("applying"); | 746 /// [NEEDS_DECLARE]. If the it's still in this state when `apply` finishes |
|
Bob Nystrom
2014/05/08 20:30:48
"the it's" -> "it's"
nweiz
2014/05/08 21:12:36
Done.
| |
| 747 /// running, it will transition to [APPLIED]. | |
| 748 static const APPLYING = const _State._("applying"); | |
| 520 | 749 |
| 521 /// The transform is running [Transformer.apply], but an input changed after | 750 /// The transform is running [AggregateTransformer.apply], but an input's |
| 522 /// it started, so it will need to re-run [Transformer.apply]. | 751 /// contents changed or a secondary input was added or removed after it |
| 752 /// started, so it will need to re-run `apply`. | |
| 523 /// | 753 /// |
| 524 /// This will transition to [APPLYING] once [Transformer.apply] finishes | 754 /// If a primary input is added or removed while in this state, the |
| 525 /// running. | 755 /// [TranformNode] will transition to [NEEDS_DECLARE]. If it's still in this |
| 526 static final NEEDS_APPLY = const _State._("needs apply"); | 756 /// state when `apply` finishes running, it will transition to [APPLYING]. |
| 757 static const NEEDS_APPLY = const _State._("needs apply"); | |
| 527 | 758 |
| 528 /// The transform has finished running [Transformer.apply], whether or not it | 759 /// The transform has finished running [AggregateTransformer.apply], whether |
| 529 /// emitted an error. | 760 /// or not it emitted an error. |
| 530 /// | 761 /// |
| 531 /// If the transformer is deferred, the [TransformNode] can also be in this | 762 /// If an input's contents change or a secondary input is added or removed, |
| 532 /// state when [Transformer.declareOutputs] has been run but | 763 /// the [TransformNode] will transition to [DECLARED] if the transform is |
| 533 /// [Transformer.apply] has not. | 764 /// declaring and [APPLYING] otherwise. If a primary input is added or |
| 534 /// | 765 /// removed, this will transition to [DECLARING]. |
| 535 /// If an input changes, this will transition to [DECLARED] if the transform | 766 static const APPLIED = const _State._("applied"); |
| 536 /// is deferred and [APPLYING] otherwise. | |
| 537 static final APPLIED = const _State._("applied"); | |
| 538 | 767 |
| 539 final String name; | 768 final String name; |
| 540 | 769 |
| 541 const _State._(this.name); | 770 const _State._(this.name); |
| 542 | 771 |
| 543 String toString() => name; | 772 String toString() => name; |
| 544 } | 773 } |
| OLD | NEW |