| 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.transform_node; | 5 library barback.transform_node; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'asset.dart'; | 9 import 'asset.dart'; |
| 10 import 'asset_id.dart'; | 10 import 'asset_id.dart'; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 final String _location; | 40 final String _location; |
| 41 | 41 |
| 42 /// The subscription to [primary]'s [AssetNode.onStateChange] stream. | 42 /// The subscription to [primary]'s [AssetNode.onStateChange] stream. |
| 43 StreamSubscription _primarySubscription; | 43 StreamSubscription _primarySubscription; |
| 44 | 44 |
| 45 /// The subscription to [phase]'s [Phase.onAsset] stream. | 45 /// The subscription to [phase]'s [Phase.onAsset] stream. |
| 46 StreamSubscription<AssetNode> _phaseSubscription; | 46 StreamSubscription<AssetNode> _phaseSubscription; |
| 47 | 47 |
| 48 /// How far along [this] is in processing its assets. | 48 /// How far along [this] is in processing its assets. |
| 49 NodeStatus get status { | 49 NodeStatus get status { |
| 50 if (_state == _State.NOT_PRIMARY || _state == _State.APPLIED || | 50 if (_state == _State.APPLIED || _state == _State.DECLARED) { |
| 51 _state == _State.DECLARED) { | |
| 52 return NodeStatus.IDLE; | 51 return NodeStatus.IDLE; |
| 53 } | 52 } |
| 54 | 53 |
| 55 if (_declaring && _state != _State.DECLARING) { | 54 if (_declaring && _state != _State.DECLARING) { |
| 56 return NodeStatus.MATERIALIZING; | 55 return NodeStatus.MATERIALIZING; |
| 57 } else { | 56 } else { |
| 58 return NodeStatus.RUNNING; | 57 return NodeStatus.RUNNING; |
| 59 } | 58 } |
| 60 } | 59 } |
| 61 | 60 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 123 /// | 122 /// |
| 124 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its | 123 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its |
| 125 /// [declareOutputs] has been run successfully. | 124 /// [declareOutputs] has been run successfully. |
| 126 Set<AssetId> _declaredOutputs; | 125 Set<AssetId> _declaredOutputs; |
| 127 | 126 |
| 128 TransformNode(this.phase, Transformer transformer, AssetNode primary, | 127 TransformNode(this.phase, Transformer transformer, AssetNode primary, |
| 129 this._location) | 128 this._location) |
| 130 : transformer = transformer, | 129 : transformer = transformer, |
| 131 primary = primary { | 130 primary = primary { |
| 132 _forced = transformer is! DeclaringTransformer; | 131 _forced = transformer is! DeclaringTransformer; |
| 132 if (_forced) primary.force(); |
| 133 | 133 |
| 134 _primarySubscription = primary.onStateChange.listen((state) { | 134 _primarySubscription = primary.onStateChange.listen((state) { |
| 135 if (state.isRemoved) { | 135 if (state.isRemoved) { |
| 136 remove(); | 136 remove(); |
| 137 } else { | 137 } else { |
| 138 if (_forced) primary.force(); | 138 if (_forced) primary.force(); |
| 139 _dirty(); | 139 _dirty(); |
| 140 } | 140 } |
| 141 }); | 141 }); |
| 142 | 142 |
| 143 _phaseSubscription = phase.previous.onAsset.listen((node) { | 143 _phaseSubscription = phase.previous.onAsset.listen((node) { |
| 144 if (!_missingInputs.contains(node.id)) return; | 144 if (!_missingInputs.contains(node.id)) return; |
| 145 if (_forced) node.force(); | 145 if (_forced) node.force(); |
| 146 _dirty(); | 146 _dirty(); |
| 147 }); | 147 }); |
| 148 | 148 |
| 149 _isPrimary(); | 149 _declareOutputs().then((_) { |
| 150 if (_forced || _canRunDeclaringEagerly) { |
| 151 _apply(); |
| 152 } else { |
| 153 _state = _State.DECLARED; |
| 154 _streams.changeStatus(NodeStatus.IDLE); |
| 155 } |
| 156 }); |
| 150 } | 157 } |
| 151 | 158 |
| 152 /// The [TransformInfo] describing this node. | 159 /// The [TransformInfo] describing this node. |
| 153 /// | 160 /// |
| 154 /// [TransformInfo] is the publicly-visible representation of a transform | 161 /// [TransformInfo] is the publicly-visible representation of a transform |
| 155 /// node. | 162 /// node. |
| 156 TransformInfo get info => new TransformInfo(transformer, primary.id); | 163 TransformInfo get info => new TransformInfo(transformer, primary.id); |
| 157 | 164 |
| 158 /// Marks this transform as removed. | 165 /// Marks this transform as removed. |
| 159 /// | 166 /// |
| (...skipping 19 matching lines...) Expand all Loading... |
| 179 if (_forced || _state == _State.APPLIED) return; | 186 if (_forced || _state == _State.APPLIED) return; |
| 180 primary.force(); | 187 primary.force(); |
| 181 _forced = true; | 188 _forced = true; |
| 182 if (_state == _State.DECLARED) _dirty(); | 189 if (_state == _State.DECLARED) _dirty(); |
| 183 } | 190 } |
| 184 | 191 |
| 185 /// Marks this transform as dirty. | 192 /// Marks this transform as dirty. |
| 186 /// | 193 /// |
| 187 /// This causes all of the transform's outputs to be marked as dirty as well. | 194 /// This causes all of the transform's outputs to be marked as dirty as well. |
| 188 void _dirty() { | 195 void _dirty() { |
| 189 if (_state == _State.NOT_PRIMARY) { | 196 // If we're in the process of running [declareOutputs], we already know that |
| 190 _emitPassThrough(); | 197 // [apply] needs to be run so there's nothing we need to mark as dirty. |
| 191 return; | |
| 192 } | |
| 193 | |
| 194 // If we're in the process of running [isPrimary] or [declareOutputs], we | |
| 195 // already know that [apply] needs to be run so there's nothing we need to | |
| 196 // mark as dirty. | |
| 197 if (_state == _State.DECLARING) return; | 198 if (_state == _State.DECLARING) return; |
| 198 | 199 |
| 199 if (!_forced && !_canRunDeclaringEagerly) { | 200 if (!_forced && !_canRunDeclaringEagerly) { |
| 200 // [forced] should only ever be false for a declaring transformer. | 201 // [forced] should only ever be false for a declaring transformer. |
| 201 assert(_declaring); | 202 assert(_declaring); |
| 202 | 203 |
| 203 // If we've finished applying, transition to MATERIALIZING, indicating | 204 // If we've finished applying, transition to MATERIALIZING, indicating |
| 204 // that we know what outputs [apply] will emit but we're waiting to emit | 205 // that we know what outputs [apply] will emit but we're waiting to emit |
| 205 // them concretely until [force] is called. If we're still applying, we'll | 206 // them concretely until [force] is called. If we're still applying, we'll |
| 206 // transition to MATERIALIZING once we finish. | 207 // transition to MATERIALIZING once we finish. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 220 if (_state == _State.APPLIED) { | 221 if (_state == _State.APPLIED) { |
| 221 if (_declaredOutputs != null) _emitDeclaredOutputs(); | 222 if (_declaredOutputs != null) _emitDeclaredOutputs(); |
| 222 _apply(); | 223 _apply(); |
| 223 } else if (_state == _State.DECLARED) { | 224 } else if (_state == _State.DECLARED) { |
| 224 _apply(); | 225 _apply(); |
| 225 } else { | 226 } else { |
| 226 _state = _State.NEEDS_APPLY; | 227 _state = _State.NEEDS_APPLY; |
| 227 } | 228 } |
| 228 } | 229 } |
| 229 | 230 |
| 230 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the | |
| 231 /// result. | |
| 232 /// | |
| 233 /// This will also run [_declareOutputs] and/or [_apply] as appropriate. | |
| 234 void _isPrimary() { | |
| 235 syncFuture(() => transformer.isPrimary(primary.id)) | |
| 236 .catchError((error, stackTrace) { | |
| 237 if (_isRemoved) return false; | |
| 238 | |
| 239 // Catch all transformer errors and pipe them to the results stream. This | |
| 240 // is so a broken transformer doesn't take down the whole graph. | |
| 241 phase.cascade.reportError(_wrapException(error, stackTrace)); | |
| 242 | |
| 243 return false; | |
| 244 }).then((isPrimary) { | |
| 245 if (_isRemoved) return null; | |
| 246 if (isPrimary) { | |
| 247 if (_forced) primary.force(); | |
| 248 return _declareOutputs().then((_) { | |
| 249 if (_isRemoved) return; | |
| 250 if (_forced || _canRunDeclaringEagerly) { | |
| 251 _apply(); | |
| 252 } else { | |
| 253 _state = _State.DECLARED; | |
| 254 _streams.changeStatus(NodeStatus.IDLE); | |
| 255 } | |
| 256 }); | |
| 257 } | |
| 258 | |
| 259 _emitPassThrough(); | |
| 260 _state = _State.NOT_PRIMARY; | |
| 261 _streams.changeStatus(NodeStatus.IDLE); | |
| 262 }); | |
| 263 } | |
| 264 | |
| 265 /// Runs [transform.declareOutputs] and emits the resulting assets as dirty | 231 /// Runs [transform.declareOutputs] and emits the resulting assets as dirty |
| 266 /// assets. | 232 /// assets. |
| 267 Future _declareOutputs() { | 233 Future _declareOutputs() { |
| 268 if (transformer is! DeclaringTransformer) return new Future.value(); | 234 if (transformer is! DeclaringTransformer) return new Future.value(); |
| 269 | 235 |
| 270 var controller = new DeclaringTransformController(this); | 236 var controller = new DeclaringTransformController(this); |
| 271 return syncFuture(() { | 237 return syncFuture(() { |
| 272 return (transformer as DeclaringTransformer) | 238 return (transformer as DeclaringTransformer) |
| 273 .declareOutputs(controller.transform); | 239 .declareOutputs(controller.transform); |
| 274 }).then((_) { | 240 }).then((_) { |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 510 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); | 476 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); |
| 511 } | 477 } |
| 512 | 478 |
| 513 String toString() => | 479 String toString() => |
| 514 "transform node in $_location for $transformer on $primary ($_state, " | 480 "transform node in $_location for $transformer on $primary ($_state, " |
| 515 "$status, ${_forced ? '' : 'un'}forced)"; | 481 "$status, ${_forced ? '' : 'un'}forced)"; |
| 516 } | 482 } |
| 517 | 483 |
| 518 /// The enum of states that [TransformNode] can be in. | 484 /// The enum of states that [TransformNode] can be in. |
| 519 class _State { | 485 class _State { |
| 520 /// The transform is running [Transformer.isPrimary] followed by | 486 /// The transform is running [DeclaringTransformer.declareOutputs]. |
| 521 /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]). | |
| 522 /// | 487 /// |
| 523 /// This is the initial state of the transformer, and it will only occur once | 488 /// This is the initial state of the transformer, and it will only occur once |
| 524 /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs] | 489 /// since [DeclaringTransformer.declareOutputs] is independent of the contents |
| 525 /// are independent of the contents of the primary input. Once the two methods | 490 /// of the primary input. Once the method finishes running, this will |
| 526 /// finish running, this will transition to [NOT_PRIMARY] if the input isn't | 491 /// transition to [APPLYING] if the transform is non-lazy and the input is |
| 527 /// primary, [DECLARED] if the transform is deferred, and [APPLYING] | 492 /// available, and [DECLARED] otherwise. |
| 528 /// otherwise. | 493 /// |
| 529 static final DECLARING = const _State._("computing isPrimary"); | 494 /// Non-declaring transformers will transition out of this state and into |
| 495 /// [APPLYING] immediately. |
| 496 static final DECLARING = const _State._("declaring outputs"); |
| 530 | 497 |
| 531 /// The transform is deferred and has run | 498 /// The transform is deferred and has run |
| 532 /// [DeclaringTransformer.declareOutputs] but hasn't yet been forced. | 499 /// [DeclaringTransformer.declareOutputs] but hasn't yet been forced. |
| 533 /// | 500 /// |
| 534 /// This will transition to [APPLYING] when one of the outputs has been | 501 /// This will transition to [APPLYING] when one of the outputs has been |
| 535 /// forced. | 502 /// forced. |
| 536 static final DECLARED = const _State._("declared"); | 503 static final DECLARED = const _State._("declared"); |
| 537 | 504 |
| 538 /// The transform is running [Transformer.apply]. | 505 /// The transform is running [Transformer.apply]. |
| 539 /// | 506 /// |
| (...skipping 13 matching lines...) Expand all Loading... |
| 553 /// emitted an error. | 520 /// emitted an error. |
| 554 /// | 521 /// |
| 555 /// If the transformer is deferred, the [TransformNode] can also be in this | 522 /// If the transformer is deferred, the [TransformNode] can also be in this |
| 556 /// state when [Transformer.declareOutputs] has been run but | 523 /// state when [Transformer.declareOutputs] has been run but |
| 557 /// [Transformer.apply] has not. | 524 /// [Transformer.apply] has not. |
| 558 /// | 525 /// |
| 559 /// If an input changes, this will transition to [DECLARED] if the transform | 526 /// If an input changes, this will transition to [DECLARED] if the transform |
| 560 /// is deferred and [APPLYING] otherwise. | 527 /// is deferred and [APPLYING] otherwise. |
| 561 static final APPLIED = const _State._("applied"); | 528 static final APPLIED = const _State._("applied"); |
| 562 | 529 |
| 563 /// The transform has finished running [Transformer.isPrimary], which returned | |
| 564 /// `false`. | |
| 565 /// | |
| 566 /// This will never transition to another state. | |
| 567 static final NOT_PRIMARY = const _State._("not primary"); | |
| 568 | |
| 569 final String name; | 530 final String name; |
| 570 | 531 |
| 571 const _State._(this.name); | 532 const _State._(this.name); |
| 572 | 533 |
| 573 String toString() => name; | 534 String toString() => name; |
| 574 } | 535 } |
| OLD | NEW |