| 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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 48 bool get isDirty => _state != _State.NOT_PRIMARY && | 48 bool get isDirty => _state != _State.NOT_PRIMARY && |
| 49 _state != _State.APPLIED && _state != _State.DECLARED; | 49 _state != _State.APPLIED && _state != _State.DECLARED; |
| 50 | 50 |
| 51 /// Whether this transform is deferred. | 51 /// Whether this transform is deferred. |
| 52 /// | 52 /// |
| 53 /// A transform is deferred if either its transformer is lazy or if its | 53 /// A transform is deferred if either its transformer is lazy or if its |
| 54 /// transformer is declaring and its primary input comes from a deferred | 54 /// transformer is declaring and its primary input comes from a deferred |
| 55 /// transformer. | 55 /// transformer. |
| 56 final bool deferred; | 56 final bool deferred; |
| 57 | 57 |
| 58 /// Whether this is a deferred transform waiting for [force] to be called to | 58 /// Whether this transform has been forced since it last finished applying. |
| 59 /// generate inputs. | |
| 60 /// | 59 /// |
| 61 /// This defaults to `true` for deferred transforms and `false` otherwise. | 60 /// A transform being forced means it should run until it generates outputs |
| 62 /// During or after running `isPrimary` or `declareOutputs`, this may become | 61 /// and is no longer dirty. This is always true for non-[deferred] |
| 63 /// `false`, indicating that the transform has been forced and should generate | 62 /// transformers, since they always need to eagerly generate outputs. |
| 64 /// outputs as soon as possible. It will only be set back to `true` if an | 63 bool _forced; |
| 65 /// input changes *after* `apply` has completed. | |
| 66 bool _awaitingForce; | |
| 67 | 64 |
| 68 /// The subscriptions to each input's [AssetNode.onStateChange] stream. | 65 /// The subscriptions to each input's [AssetNode.onStateChange] stream. |
| 69 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); | 66 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); |
| 70 | 67 |
| 71 /// The controllers for the asset nodes emitted by this node. | 68 /// The controllers for the asset nodes emitted by this node. |
| 72 final _outputControllers = new Map<AssetId, AssetNodeController>(); | 69 final _outputControllers = new Map<AssetId, AssetNodeController>(); |
| 73 | 70 |
| 74 /// The ids of inputs the transformer tried and failed to read last time it | 71 /// The ids of inputs the transformer tried and failed to read last time it |
| 75 /// ran. | 72 /// ran. |
| 76 final _missingInputs = new Set<AssetId>(); | 73 final _missingInputs = new Set<AssetId>(); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its | 125 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its |
| 129 /// [declareOutputs] has been run successfully. | 126 /// [declareOutputs] has been run successfully. |
| 130 Set<AssetId> _declaredOutputs; | 127 Set<AssetId> _declaredOutputs; |
| 131 | 128 |
| 132 TransformNode(this.phase, Transformer transformer, AssetNode primary, | 129 TransformNode(this.phase, Transformer transformer, AssetNode primary, |
| 133 this._location) | 130 this._location) |
| 134 : transformer = transformer, | 131 : transformer = transformer, |
| 135 primary = primary, | 132 primary = primary, |
| 136 deferred = transformer is LazyTransformer || | 133 deferred = transformer is LazyTransformer || |
| 137 (transformer is DeclaringTransformer && primary.deferred) { | 134 (transformer is DeclaringTransformer && primary.deferred) { |
| 138 _awaitingForce = deferred; | 135 _forced = !deferred; |
| 139 | 136 |
| 140 _onLogPool.add(_onLogController.stream); | 137 _onLogPool.add(_onLogController.stream); |
| 141 | 138 |
| 142 _primarySubscription = primary.onStateChange.listen((state) { | 139 _primarySubscription = primary.onStateChange.listen((state) { |
| 143 if (state.isRemoved) { | 140 if (state.isRemoved) { |
| 144 remove(); | 141 remove(); |
| 145 } else { | 142 } else { |
| 146 if (state.isDirty && !deferred) primary.force(); | 143 if (state.isDirty && !deferred) primary.force(); |
| 147 // If this is deferred but applying, that means it must have been | 144 // If this is deferred but applying, that means it must have been |
| 148 // forced, so we should ensure its input remains forced as well. | 145 // forced, so we should ensure its input remains forced as well. |
| 149 if (deferred && _state == _State.APPLYING) primary.force(); | 146 if (deferred && _forced && _state == _State.APPLYING) primary.force(); |
| 150 _dirty(); | 147 _dirty(); |
| 151 } | 148 } |
| 152 }); | 149 }); |
| 153 | 150 |
| 154 _phaseSubscription = phase.previous.onAsset.listen((node) { | 151 _phaseSubscription = phase.previous.onAsset.listen((node) { |
| 155 if (!_missingInputs.contains(node.id)) return; | 152 if (!_missingInputs.contains(node.id)) return; |
| 156 if (!deferred) node.force(); | 153 if (!deferred) node.force(); |
| 157 _dirty(); | 154 _dirty(); |
| 158 }); | 155 }); |
| 159 | 156 |
| (...skipping 22 matching lines...) Expand all Loading... |
| 182 _clearOutputs(); | 179 _clearOutputs(); |
| 183 if (_passThroughController != null) { | 180 if (_passThroughController != null) { |
| 184 _passThroughController.setRemoved(); | 181 _passThroughController.setRemoved(); |
| 185 _passThroughController = null; | 182 _passThroughController = null; |
| 186 } | 183 } |
| 187 } | 184 } |
| 188 | 185 |
| 189 /// If [this] is deferred, ensures that its concrete outputs will be | 186 /// If [this] is deferred, ensures that its concrete outputs will be |
| 190 /// generated. | 187 /// generated. |
| 191 void force() { | 188 void force() { |
| 192 if (!_awaitingForce) return; | 189 if (_forced || _state == _State.APPLIED) return; |
| 193 primary.force(); | 190 primary.force(); |
| 194 _awaitingForce = false; | 191 _forced = true; |
| 195 _dirty(); | 192 _dirty(); |
| 196 } | 193 } |
| 197 | 194 |
| 198 /// Marks this transform as dirty. | 195 /// Marks this transform as dirty. |
| 199 /// | 196 /// |
| 200 /// This causes all of the transform's outputs to be marked as dirty as well. | 197 /// This causes all of the transform's outputs to be marked as dirty as well. |
| 201 void _dirty() { | 198 void _dirty() { |
| 202 if (_state == _State.NOT_PRIMARY) { | 199 if (_state == _State.NOT_PRIMARY) { |
| 203 _emitPassThrough(); | 200 _emitPassThrough(); |
| 204 return; | 201 return; |
| 205 } | 202 } |
| 206 | 203 |
| 207 // If we're in the process of running [isPrimary] or [declareOutputs], we | 204 // If we're in the process of running [isPrimary] or [declareOutputs], we |
| 208 // already know that [apply] needs to be run so there's nothing we need to | 205 // already know that [apply] needs to be run so there's nothing we need to |
| 209 // mark as dirty. | 206 // mark as dirty. |
| 210 if (_state == _State.DECLARING) return; | 207 if (_state == _State.DECLARING) return; |
| 211 | 208 |
| 212 // If we're waiting until [force] is called to run [apply], we don't want to | 209 // If [transformer] is declaring but not lazy and [primary] is available, we |
| 213 // run [apply] too early. | 210 // do want to start running [apply] even if [force] hasn't been called, |
| 214 if (_awaitingForce) return; | 211 // since [transformer] should run eagerly if possible. |
| 212 var canRunDeclaringEagerly = |
| 213 transformer is! LazyTransformer && primary.state.isAvailable; |
| 214 if (!_forced && !canRunDeclaringEagerly) { |
| 215 // [forced] should only ever be false for a deferred transform. |
| 216 assert(deferred); |
| 215 | 217 |
| 216 if (_state == _State.APPLIED && deferred) { | 218 // If we've finished applying, transition to DECLARED, indicating that we |
| 217 // Transition to DECLARED, indicating that we know what outputs [apply] | 219 // know what outputs [apply] will emit but we're waiting to emit them |
| 218 // will emit but we're waiting to emit them concretely until [force] is | 220 // concretely until [force] is called. If we're still applying, we'll |
| 219 // called. | 221 // transition to DECLARED once we finish. |
| 220 _state = _State.DECLARED; | 222 if (_state == _State.APPLIED) _state = _State.DECLARED; |
| 221 _awaitingForce = true; | |
| 222 for (var controller in _outputControllers.values) { | 223 for (var controller in _outputControllers.values) { |
| 223 controller.setLazy(force); | 224 controller.setLazy(force); |
| 224 } | 225 } |
| 225 return; | 226 return; |
| 226 } | 227 } |
| 227 | 228 |
| 228 if (_passThroughController != null) _passThroughController.setDirty(); | 229 if (_passThroughController != null) _passThroughController.setDirty(); |
| 229 for (var controller in _outputControllers.values) { | 230 for (var controller in _outputControllers.values) { |
| 230 controller.setDirty(); | 231 controller.setDirty(); |
| 231 } | 232 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 250 // is so a broken transformer doesn't take down the whole graph. | 251 // is so a broken transformer doesn't take down the whole graph. |
| 251 phase.cascade.reportError(_wrapException(error, stackTrace)); | 252 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 252 | 253 |
| 253 return false; | 254 return false; |
| 254 }).then((isPrimary) { | 255 }).then((isPrimary) { |
| 255 if (_isRemoved) return null; | 256 if (_isRemoved) return null; |
| 256 if (isPrimary) { | 257 if (isPrimary) { |
| 257 if (!deferred) primary.force(); | 258 if (!deferred) primary.force(); |
| 258 return _declareOutputs().then((_) { | 259 return _declareOutputs().then((_) { |
| 259 if (_isRemoved) return; | 260 if (_isRemoved) return; |
| 260 if (_awaitingForce) { | 261 if (_forced) { |
| 262 _apply(); |
| 263 } else { |
| 261 _state = _State.DECLARED; | 264 _state = _State.DECLARED; |
| 262 _onDoneController.add(null); | 265 _onDoneController.add(null); |
| 263 } else { | |
| 264 _apply(); | |
| 265 } | 266 } |
| 266 }); | 267 }); |
| 267 } | 268 } |
| 268 | 269 |
| 269 _emitPassThrough(); | 270 _emitPassThrough(); |
| 270 _state = _State.NOT_PRIMARY; | 271 _state = _State.NOT_PRIMARY; |
| 271 _onDoneController.add(null); | 272 _onDoneController.add(null); |
| 272 }); | 273 }); |
| 273 } | 274 } |
| 274 | 275 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 291 .where((id) => id.package != phase.cascade.package).toSet(); | 292 .where((id) => id.package != phase.cascade.package).toSet(); |
| 292 for (var id in invalidIds) { | 293 for (var id in invalidIds) { |
| 293 _declaredOutputs.remove(id); | 294 _declaredOutputs.remove(id); |
| 294 // TODO(nweiz): report this as a warning rather than a failing error. | 295 // TODO(nweiz): report this as a warning rather than a failing error. |
| 295 phase.cascade.reportError(new InvalidOutputException(info, id)); | 296 phase.cascade.reportError(new InvalidOutputException(info, id)); |
| 296 } | 297 } |
| 297 | 298 |
| 298 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); | 299 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); |
| 299 | 300 |
| 300 for (var id in _declaredOutputs) { | 301 for (var id in _declaredOutputs) { |
| 301 var controller = _awaitingForce | 302 var controller = _forced |
| 302 ? new AssetNodeController.lazy(id, force, this) | 303 ? new AssetNodeController(id, this) |
| 303 : new AssetNodeController(id, this); | 304 : new AssetNodeController.lazy(id, force, this); |
| 304 _outputControllers[id] = controller; | 305 _outputControllers[id] = controller; |
| 305 _onAssetController.add(controller.node); | 306 _onAssetController.add(controller.node); |
| 306 } | 307 } |
| 307 }).catchError((error, stackTrace) { | 308 }).catchError((error, stackTrace) { |
| 308 if (_isRemoved) return; | 309 if (_isRemoved) return; |
| 309 phase.cascade.reportError(_wrapException(error, stackTrace)); | 310 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 310 }); | 311 }); |
| 311 } | 312 } |
| 312 | 313 |
| 313 /// Applies this transform. | 314 /// Applies this transform. |
| 314 void _apply() { | 315 void _apply() { |
| 315 assert(!_isRemoved && !_awaitingForce); | 316 assert(!_isRemoved); |
| 316 | 317 |
| 317 // Clear input subscriptions here as well as in [_process] because [_apply] | 318 // Clear input subscriptions here as well as in [_process] because [_apply] |
| 318 // may be restarted independently if only a secondary input changes. | 319 // may be restarted independently if only a secondary input changes. |
| 319 _clearInputSubscriptions(); | 320 _clearInputSubscriptions(); |
| 320 _state = _State.APPLYING; | 321 _state = _State.APPLYING; |
| 321 _runApply().then((hadError) { | 322 _runApply().then((hadError) { |
| 322 if (_isRemoved) return; | 323 if (_isRemoved) return; |
| 323 | 324 |
| 325 if (_state == _State.DECLARED) return; |
| 326 |
| 324 if (_state == _State.NEEDS_APPLY) { | 327 if (_state == _State.NEEDS_APPLY) { |
| 325 _apply(); | 328 _apply(); |
| 326 return; | 329 return; |
| 327 } | 330 } |
| 328 | 331 |
| 332 if (deferred) _forced = false; |
| 333 |
| 329 assert(_state == _State.APPLYING); | 334 assert(_state == _State.APPLYING); |
| 330 if (hadError) { | 335 if (hadError) { |
| 331 _clearOutputs(); | 336 _clearOutputs(); |
| 332 // If the transformer threw an error, we don't want to emit the | 337 // If the transformer threw an error, we don't want to emit the |
| 333 // pass-through asset in case it will be overwritten by the transformer. | 338 // pass-through asset in case it will be overwritten by the transformer. |
| 334 // However, if the transformer declared that it wouldn't overwrite or | 339 // However, if the transformer declared that it wouldn't overwrite or |
| 335 // consume the pass-through asset, we can safely emit it. | 340 // consume the pass-through asset, we can safely emit it. |
| 336 if (_declaredOutputs != null && !_consumePrimary && | 341 if (_declaredOutputs != null && !_consumePrimary && |
| 337 !_declaredOutputs.contains(primary.id)) { | 342 !_declaredOutputs.contains(primary.id)) { |
| 338 _emitPassThrough(); | 343 _emitPassThrough(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 /// Returns whether or not an error occurred while running the transformer. | 377 /// Returns whether or not an error occurred while running the transformer. |
| 373 Future<bool> _runApply() { | 378 Future<bool> _runApply() { |
| 374 var transformController = new TransformController(this); | 379 var transformController = new TransformController(this); |
| 375 _onLogPool.add(transformController.onLog); | 380 _onLogPool.add(transformController.onLog); |
| 376 | 381 |
| 377 return primary.whenAvailable((_) { | 382 return primary.whenAvailable((_) { |
| 378 if (_isRemoved) return null; | 383 if (_isRemoved) return null; |
| 379 _state = _State.APPLYING; | 384 _state = _State.APPLYING; |
| 380 return syncFuture(() => transformer.apply(transformController.transform)); | 385 return syncFuture(() => transformer.apply(transformController.transform)); |
| 381 }).then((_) { | 386 }).then((_) { |
| 382 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; | 387 if (deferred && !_forced && !primary.state.isAvailable) { |
| 388 _state = _State.DECLARED; |
| 389 _onDoneController.add(null); |
| 390 return false; |
| 391 } |
| 392 |
| 393 if (_isRemoved) return false; |
| 394 if (_state == _State.NEEDS_APPLY) return false; |
| 395 if (_state == _State.DECLARING) return false; |
| 383 if (transformController.loggedError) return true; | 396 if (transformController.loggedError) return true; |
| 384 _handleApplyResults(transformController); | 397 _handleApplyResults(transformController); |
| 385 return false; | 398 return false; |
| 386 }).catchError((error, stackTrace) { | 399 }).catchError((error, stackTrace) { |
| 387 // If the transform became dirty while processing, ignore any errors from | 400 // If the transform became dirty while processing, ignore any errors from |
| 388 // it. | 401 // it. |
| 389 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; | 402 if (_state == _State.NEEDS_APPLY || _isRemoved) return false; |
| 390 | 403 |
| 391 // Catch all transformer errors and pipe them to the results stream. This | 404 // Catch all transformer errors and pipe them to the results stream. This |
| 392 // is so a broken transformer doesn't take down the whole graph. | 405 // is so a broken transformer doesn't take down the whole graph. |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 549 /// | 562 /// |
| 550 /// This will never transition to another state. | 563 /// This will never transition to another state. |
| 551 static final NOT_PRIMARY = const _State._("not primary"); | 564 static final NOT_PRIMARY = const _State._("not primary"); |
| 552 | 565 |
| 553 final String name; | 566 final String name; |
| 554 | 567 |
| 555 const _State._(this.name); | 568 const _State._(this.name); |
| 556 | 569 |
| 557 String toString() => name; | 570 String toString() => name; |
| 558 } | 571 } |
| OLD | NEW |