| 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'; |
| 11 import 'asset_node.dart'; | 11 import 'asset_node.dart'; |
| 12 import 'declaring_transform.dart'; | 12 import 'declaring_transform.dart'; |
| 13 import 'declaring_transformer.dart'; | 13 import 'declaring_transformer.dart'; |
| 14 import 'errors.dart'; | 14 import 'errors.dart'; |
| 15 import 'lazy_transformer.dart'; | 15 import 'lazy_transformer.dart'; |
| 16 import 'log.dart'; | 16 import 'log.dart'; |
| 17 import 'node_status.dart'; |
| 17 import 'node_streams.dart'; | 18 import 'node_streams.dart'; |
| 18 import 'phase.dart'; | 19 import 'phase.dart'; |
| 19 import 'transform.dart'; | 20 import 'transform.dart'; |
| 20 import 'transformer.dart'; | 21 import 'transformer.dart'; |
| 21 import 'utils.dart'; | 22 import 'utils.dart'; |
| 22 | 23 |
| 23 /// Describes a transform on a set of assets and its relationship to the build | 24 /// Describes a transform on a set of assets and its relationship to the build |
| 24 /// dependency graph. | 25 /// dependency graph. |
| 25 /// | 26 /// |
| 26 /// Keeps track of whether it's dirty and needs to be run and which assets it | 27 /// Keeps track of whether it's dirty and needs to be run and which assets it |
| (...skipping 10 matching lines...) Expand all Loading... |
| 37 | 38 |
| 38 /// A string describing the location of [this] in the transformer graph. | 39 /// A string describing the location of [this] in the transformer graph. |
| 39 final String _location; | 40 final String _location; |
| 40 | 41 |
| 41 /// The subscription to [primary]'s [AssetNode.onStateChange] stream. | 42 /// The subscription to [primary]'s [AssetNode.onStateChange] stream. |
| 42 StreamSubscription _primarySubscription; | 43 StreamSubscription _primarySubscription; |
| 43 | 44 |
| 44 /// The subscription to [phase]'s [Phase.onAsset] stream. | 45 /// The subscription to [phase]'s [Phase.onAsset] stream. |
| 45 StreamSubscription<AssetNode> _phaseSubscription; | 46 StreamSubscription<AssetNode> _phaseSubscription; |
| 46 | 47 |
| 47 /// Whether [this] is dirty and still has more processing to do. | 48 /// How far along [this] is in processing its assets. |
| 48 bool get isDirty => _state != _State.NOT_PRIMARY && | 49 NodeStatus get status { |
| 49 _state != _State.APPLIED && _state != _State.DECLARED; | 50 if (_state == _State.NOT_PRIMARY || _state == _State.APPLIED || |
| 51 _state == _State.DECLARED) { |
| 52 return NodeStatus.IDLE; |
| 53 } |
| 54 |
| 55 if (transformer is DeclaringTransformer && _state != _State.DECLARING) { |
| 56 return NodeStatus.MATERIALIZING; |
| 57 } else { |
| 58 return NodeStatus.RUNNING; |
| 59 } |
| 60 } |
| 50 | 61 |
| 51 /// Whether this transform is deferred. | 62 /// Whether this transform is deferred. |
| 52 /// | 63 /// |
| 53 /// A transform is deferred if either its transformer is lazy or if its | 64 /// 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 | 65 /// transformer is declaring and its primary input comes from a deferred |
| 55 /// transformer. | 66 /// transformer. |
| 56 final bool deferred; | 67 final bool deferred; |
| 57 | 68 |
| 58 /// Whether this transform has been forced since it last finished applying. | 69 /// Whether this transform has been forced since it last finished applying. |
| 59 /// | 70 /// |
| (...skipping 16 matching lines...) Expand all Loading... |
| 76 /// consumed or overwritten. | 87 /// consumed or overwritten. |
| 77 /// | 88 /// |
| 78 /// This needs an intervening controller to ensure that the output can be | 89 /// This needs an intervening controller to ensure that the output can be |
| 79 /// marked dirty when determining whether [this] will consume or overwrite it, | 90 /// marked dirty when determining whether [this] will consume or overwrite it, |
| 80 /// and be marked removed if it does. [_passThroughController] will be null | 91 /// and be marked removed if it does. [_passThroughController] will be null |
| 81 /// if the asset is not being passed through. | 92 /// if the asset is not being passed through. |
| 82 AssetNodeController _passThroughController; | 93 AssetNodeController _passThroughController; |
| 83 | 94 |
| 84 /// The asset node for this transform. | 95 /// The asset node for this transform. |
| 85 final _streams = new NodeStreams(); | 96 final _streams = new NodeStreams(); |
| 86 Stream get onDone => _streams.onDone; | 97 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange; |
| 87 Stream<AssetNode> get onAsset => _streams.onAsset; | 98 Stream<AssetNode> get onAsset => _streams.onAsset; |
| 88 Stream<LogEntry> get onLog => _streams.onLog; | 99 Stream<LogEntry> get onLog => _streams.onLog; |
| 89 | 100 |
| 90 /// The current state of [this]. | 101 /// The current state of [this]. |
| 91 var _state = _State.DECLARING; | 102 var _state = _State.DECLARING; |
| 92 | 103 |
| 93 /// Whether [this] has been marked as removed. | 104 /// Whether [this] has been marked as removed. |
| 94 bool get _isRemoved => _streams.onAssetController.isClosed; | 105 bool get _isRemoved => _streams.onAssetController.isClosed; |
| 95 | 106 |
| 96 /// Whether the most recent run of this transform has declared that it | 107 /// Whether the most recent run of this transform has declared that it |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 | 195 |
| 185 // If [transformer] is declaring but not lazy and [primary] is available, we | 196 // If [transformer] is declaring but not lazy and [primary] is available, we |
| 186 // do want to start running [apply] even if [force] hasn't been called, | 197 // do want to start running [apply] even if [force] hasn't been called, |
| 187 // since [transformer] should run eagerly if possible. | 198 // since [transformer] should run eagerly if possible. |
| 188 var canRunDeclaringEagerly = | 199 var canRunDeclaringEagerly = |
| 189 transformer is! LazyTransformer && primary.state.isAvailable; | 200 transformer is! LazyTransformer && primary.state.isAvailable; |
| 190 if (!_forced && !canRunDeclaringEagerly) { | 201 if (!_forced && !canRunDeclaringEagerly) { |
| 191 // [forced] should only ever be false for a deferred transform. | 202 // [forced] should only ever be false for a deferred transform. |
| 192 assert(deferred); | 203 assert(deferred); |
| 193 | 204 |
| 194 // If we've finished applying, transition to DECLARED, indicating that we | 205 // If we've finished applying, transition to MATERIALIZING, indicating |
| 195 // know what outputs [apply] will emit but we're waiting to emit them | 206 // that we know what outputs [apply] will emit but we're waiting to emit |
| 196 // concretely until [force] is called. If we're still applying, we'll | 207 // them concretely until [force] is called. If we're still applying, we'll |
| 197 // transition to DECLARED once we finish. | 208 // transition to MATERIALIZING once we finish. |
| 198 if (_state == _State.APPLIED) _state = _State.DECLARED; | 209 if (_state == _State.APPLIED) _state = _State.DECLARED; |
| 199 for (var controller in _outputControllers.values) { | 210 for (var controller in _outputControllers.values) { |
| 200 controller.setLazy(force); | 211 controller.setLazy(force); |
| 201 } | 212 } |
| 213 _emitDeclaredOutputs(); |
| 202 return; | 214 return; |
| 203 } | 215 } |
| 204 | 216 |
| 205 if (_passThroughController != null) _passThroughController.setDirty(); | 217 if (_passThroughController != null) _passThroughController.setDirty(); |
| 206 for (var controller in _outputControllers.values) { | 218 for (var controller in _outputControllers.values) { |
| 207 controller.setDirty(); | 219 controller.setDirty(); |
| 208 } | 220 } |
| 209 | 221 |
| 210 if (_state == _State.APPLIED || _state == _State.DECLARED) { | 222 if (_state == _State.APPLIED) { |
| 223 if (_declaredOutputs != null) _emitDeclaredOutputs(); |
| 224 _apply(); |
| 225 } else if (_state == _State.DECLARED) { |
| 211 _apply(); | 226 _apply(); |
| 212 } else { | 227 } else { |
| 213 _state = _State.NEEDS_APPLY; | 228 _state = _State.NEEDS_APPLY; |
| 214 } | 229 } |
| 215 } | 230 } |
| 216 | 231 |
| 217 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the | 232 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the |
| 218 /// result. | 233 /// result. |
| 219 /// | 234 /// |
| 220 /// This will also run [_declareOutputs] and/or [_apply] as appropriate. | 235 /// This will also run [_declareOutputs] and/or [_apply] as appropriate. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 231 }).then((isPrimary) { | 246 }).then((isPrimary) { |
| 232 if (_isRemoved) return null; | 247 if (_isRemoved) return null; |
| 233 if (isPrimary) { | 248 if (isPrimary) { |
| 234 if (!deferred) primary.force(); | 249 if (!deferred) primary.force(); |
| 235 return _declareOutputs().then((_) { | 250 return _declareOutputs().then((_) { |
| 236 if (_isRemoved) return; | 251 if (_isRemoved) return; |
| 237 if (_forced) { | 252 if (_forced) { |
| 238 _apply(); | 253 _apply(); |
| 239 } else { | 254 } else { |
| 240 _state = _State.DECLARED; | 255 _state = _State.DECLARED; |
| 241 _streams.onDoneController.add(null); | 256 _streams.changeStatus(NodeStatus.IDLE); |
| 242 } | 257 } |
| 243 }); | 258 }); |
| 244 } | 259 } |
| 245 | 260 |
| 246 _emitPassThrough(); | 261 _emitPassThrough(); |
| 247 _state = _State.NOT_PRIMARY; | 262 _state = _State.NOT_PRIMARY; |
| 248 _streams.onDoneController.add(null); | 263 _streams.changeStatus(NodeStatus.IDLE); |
| 249 }); | 264 }); |
| 250 } | 265 } |
| 251 | 266 |
| 252 /// Runs [transform.declareOutputs] and emits the resulting assets as dirty | 267 /// Runs [transform.declareOutputs] and emits the resulting assets as dirty |
| 253 /// assets. | 268 /// assets. |
| 254 Future _declareOutputs() { | 269 Future _declareOutputs() { |
| 255 if (transformer is! DeclaringTransformer) return new Future.value(); | 270 if (transformer is! DeclaringTransformer) return new Future.value(); |
| 256 | 271 |
| 257 var controller = new DeclaringTransformController(this); | 272 var controller = new DeclaringTransformController(this); |
| 258 return syncFuture(() { | 273 return syncFuture(() { |
| 259 return (transformer as DeclaringTransformer) | 274 return (transformer as DeclaringTransformer) |
| 260 .declareOutputs(controller.transform); | 275 .declareOutputs(controller.transform); |
| 261 }).then((_) { | 276 }).then((_) { |
| 262 if (_isRemoved) return; | 277 if (_isRemoved) return; |
| 263 if (controller.loggedError) return; | 278 if (controller.loggedError) return; |
| 264 | 279 |
| 265 _consumePrimary = controller.consumePrimary; | 280 _consumePrimary = controller.consumePrimary; |
| 266 _declaredOutputs = controller.outputIds; | 281 _declaredOutputs = controller.outputIds; |
| 267 var invalidIds = _declaredOutputs | 282 var invalidIds = _declaredOutputs |
| 268 .where((id) => id.package != phase.cascade.package).toSet(); | 283 .where((id) => id.package != phase.cascade.package).toSet(); |
| 269 for (var id in invalidIds) { | 284 for (var id in invalidIds) { |
| 270 _declaredOutputs.remove(id); | 285 _declaredOutputs.remove(id); |
| 271 // TODO(nweiz): report this as a warning rather than a failing error. | 286 // TODO(nweiz): report this as a warning rather than a failing error. |
| 272 phase.cascade.reportError(new InvalidOutputException(info, id)); | 287 phase.cascade.reportError(new InvalidOutputException(info, id)); |
| 273 } | 288 } |
| 274 | 289 |
| 275 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); | 290 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); |
| 276 | 291 _emitDeclaredOutputs(); |
| 277 for (var id in _declaredOutputs) { | |
| 278 var controller = _forced | |
| 279 ? new AssetNodeController(id, this) | |
| 280 : new AssetNodeController.lazy(id, force, this); | |
| 281 _outputControllers[id] = controller; | |
| 282 _streams.onAssetController.add(controller.node); | |
| 283 } | |
| 284 }).catchError((error, stackTrace) { | 292 }).catchError((error, stackTrace) { |
| 285 if (_isRemoved) return; | 293 if (_isRemoved) return; |
| 286 phase.cascade.reportError(_wrapException(error, stackTrace)); | 294 phase.cascade.reportError(_wrapException(error, stackTrace)); |
| 287 }); | 295 }); |
| 288 } | 296 } |
| 289 | 297 |
| 298 /// Emits a dirty asset node for all outputs that were declared by the |
| 299 /// transformer. |
| 300 /// |
| 301 /// This won't emit any outputs for which there already exist output |
| 302 /// controllers. It should only be called for transforms that have declared |
| 303 /// their outputs. |
| 304 void _emitDeclaredOutputs() { |
| 305 assert(_declaredOutputs != null); |
| 306 for (var id in _declaredOutputs) { |
| 307 if (_outputControllers.containsKey(id)) continue; |
| 308 var controller = _forced |
| 309 ? new AssetNodeController(id, this) |
| 310 : new AssetNodeController.lazy(id, force, this); |
| 311 _outputControllers[id] = controller; |
| 312 _streams.onAssetController.add(controller.node); |
| 313 } |
| 314 } |
| 315 |
| 290 /// Applies this transform. | 316 /// Applies this transform. |
| 291 void _apply() { | 317 void _apply() { |
| 292 assert(!_isRemoved); | 318 assert(!_isRemoved); |
| 293 | 319 |
| 294 // Clear input subscriptions here as well as in [_process] because [_apply] | 320 // Clear input subscriptions here as well as in [_process] because [_apply] |
| 295 // may be restarted independently if only a secondary input changes. | 321 // may be restarted independently if only a secondary input changes. |
| 296 _clearInputSubscriptions(); | 322 _clearInputSubscriptions(); |
| 297 _state = _State.APPLYING; | 323 _state = _State.APPLYING; |
| 324 _streams.changeStatus(status); |
| 298 _runApply().then((hadError) { | 325 _runApply().then((hadError) { |
| 299 if (_isRemoved) return; | 326 if (_isRemoved) return; |
| 300 | 327 |
| 301 if (_state == _State.DECLARED) return; | 328 if (_state == _State.DECLARED) return; |
| 302 | 329 |
| 303 if (_state == _State.NEEDS_APPLY) { | 330 if (_state == _State.NEEDS_APPLY) { |
| 304 _apply(); | 331 _apply(); |
| 305 return; | 332 return; |
| 306 } | 333 } |
| 307 | 334 |
| 308 if (deferred) _forced = false; | 335 if (deferred) _forced = false; |
| 309 | 336 |
| 310 assert(_state == _State.APPLYING); | 337 assert(_state == _State.APPLYING); |
| 311 if (hadError) { | 338 if (hadError) { |
| 312 _clearOutputs(); | 339 _clearOutputs(); |
| 313 // If the transformer threw an error, we don't want to emit the | 340 // If the transformer threw an error, we don't want to emit the |
| 314 // pass-through asset in case it will be overwritten by the transformer. | 341 // pass-through asset in case it will be overwritten by the transformer. |
| 315 // However, if the transformer declared that it wouldn't overwrite or | 342 // However, if the transformer declared that it wouldn't overwrite or |
| 316 // consume the pass-through asset, we can safely emit it. | 343 // consume the pass-through asset, we can safely emit it. |
| 317 if (_declaredOutputs != null && !_consumePrimary && | 344 if (_declaredOutputs != null && !_consumePrimary && |
| 318 !_declaredOutputs.contains(primary.id)) { | 345 !_declaredOutputs.contains(primary.id)) { |
| 319 _emitPassThrough(); | 346 _emitPassThrough(); |
| 320 } else { | 347 } else { |
| 321 _dontEmitPassThrough(); | 348 _dontEmitPassThrough(); |
| 322 } | 349 } |
| 323 } | 350 } |
| 324 | 351 |
| 325 _state = _State.APPLIED; | 352 _state = _State.APPLIED; |
| 326 _streams.onDoneController.add(null); | 353 _streams.changeStatus(NodeStatus.IDLE); |
| 327 }); | 354 }); |
| 328 } | 355 } |
| 329 | 356 |
| 330 /// Gets the asset for an input [id]. | 357 /// Gets the asset for an input [id]. |
| 331 /// | 358 /// |
| 332 /// If an input with [id] cannot be found, throws an [AssetNotFoundException]. | 359 /// If an input with [id] cannot be found, throws an [AssetNotFoundException]. |
| 333 Future<Asset> getInput(AssetId id) { | 360 Future<Asset> getInput(AssetId id) { |
| 334 return phase.previous.getOutput(id).then((node) { | 361 return phase.previous.getOutput(id).then((node) { |
| 335 // Throw if the input isn't found. This ensures the transformer's apply | 362 // Throw if the input isn't found. This ensures the transformer's apply |
| 336 // is exited. We'll then catch this and report it through the proper | 363 // is exited. We'll then catch this and report it through the proper |
| (...skipping 18 matching lines...) Expand all Loading... |
| 355 var transformController = new TransformController(this); | 382 var transformController = new TransformController(this); |
| 356 _streams.onLogPool.add(transformController.onLog); | 383 _streams.onLogPool.add(transformController.onLog); |
| 357 | 384 |
| 358 return primary.whenAvailable((_) { | 385 return primary.whenAvailable((_) { |
| 359 if (_isRemoved) return null; | 386 if (_isRemoved) return null; |
| 360 _state = _State.APPLYING; | 387 _state = _State.APPLYING; |
| 361 return syncFuture(() => transformer.apply(transformController.transform)); | 388 return syncFuture(() => transformer.apply(transformController.transform)); |
| 362 }).then((_) { | 389 }).then((_) { |
| 363 if (deferred && !_forced && !primary.state.isAvailable) { | 390 if (deferred && !_forced && !primary.state.isAvailable) { |
| 364 _state = _State.DECLARED; | 391 _state = _State.DECLARED; |
| 365 _streams.onDoneController.add(null); | 392 _streams.changeStatus(NodeStatus.IDLE); |
| 366 return false; | 393 return false; |
| 367 } | 394 } |
| 368 | 395 |
| 369 if (_isRemoved) return false; | 396 if (_isRemoved) return false; |
| 370 if (_state == _State.NEEDS_APPLY) return false; | 397 if (_state == _State.NEEDS_APPLY) return false; |
| 371 if (_state == _State.DECLARING) return false; | 398 if (_state == _State.DECLARING) return false; |
| 372 if (transformController.loggedError) return true; | 399 if (transformController.loggedError) return true; |
| 373 _handleApplyResults(transformController); | 400 _handleApplyResults(transformController); |
| 374 return false; | 401 return false; |
| 375 }).catchError((error, stackTrace) { | 402 }).catchError((error, stackTrace) { |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 538 /// | 565 /// |
| 539 /// This will never transition to another state. | 566 /// This will never transition to another state. |
| 540 static final NOT_PRIMARY = const _State._("not primary"); | 567 static final NOT_PRIMARY = const _State._("not primary"); |
| 541 | 568 |
| 542 final String name; | 569 final String name; |
| 543 | 570 |
| 544 const _State._(this.name); | 571 const _State._(this.name); |
| 545 | 572 |
| 546 String toString() => name; | 573 String toString() => name; |
| 547 } | 574 } |
| OLD | NEW |