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 '../asset/asset_node_set.dart'; |
13 import '../errors.dart'; | 13 import '../errors.dart'; |
14 import '../log.dart'; | 14 import '../log.dart'; |
15 import '../transformer/aggregate_transform.dart'; | 15 import '../transformer/aggregate_transform.dart'; |
16 import '../transformer/aggregate_transformer.dart'; | 16 import '../transformer/aggregate_transformer.dart'; |
17 import '../transformer/declaring_aggregate_transform.dart'; | 17 import '../transformer/declaring_aggregate_transform.dart'; |
18 import '../transformer/declaring_aggregate_transformer.dart'; | 18 import '../transformer/declaring_aggregate_transformer.dart'; |
19 import '../transformer/lazy_aggregate_transformer.dart'; | 19 import '../transformer/lazy_aggregate_transformer.dart'; |
20 import '../utils.dart'; | 20 import '../utils.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 import 'transformer_classifier.dart'; | 24 import 'transformer_classifier.dart'; |
25 | 25 |
| 26 /// Every `_applyLogDuration`, we will issue a fine log entry letting the user |
| 27 /// know that the transform is still executing. |
| 28 const _applyLogDuration = const Duration(seconds: 10); |
| 29 |
26 /// Describes a transform on a set of assets and its relationship to the build | 30 /// Describes a transform on a set of assets and its relationship to the build |
27 /// dependency graph. | 31 /// dependency graph. |
28 /// | 32 /// |
29 /// Keeps track of whether it's dirty and needs to be run and which assets it | 33 /// Keeps track of whether it's dirty and needs to be run and which assets it |
30 /// depends on. | 34 /// depends on. |
31 class TransformNode { | 35 class TransformNode { |
32 /// The aggregate key for this node. | 36 /// The aggregate key for this node. |
33 final String key; | 37 final String key; |
34 | 38 |
35 /// The [TransformerClassifier] that [this] belongs to. | 39 /// The [TransformerClassifier] that [this] belongs to. |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 | 168 |
165 /// The controller for the currently-running [AggregateTransformer.apply] | 169 /// The controller for the currently-running [AggregateTransformer.apply] |
166 /// call's [AggregateTransform]. | 170 /// call's [AggregateTransform]. |
167 /// | 171 /// |
168 /// This will be non-`null` when [AggregateTransform.apply] is running, which | 172 /// This will be non-`null` when [AggregateTransform.apply] is running, which |
169 /// means that it's always non-`null` when [_state] is [_State.APPLYING] or | 173 /// means that it's always non-`null` when [_state] is [_State.APPLYING] or |
170 /// [_State.NEEDS_APPLY], sometimes non-`null` when it's | 174 /// [_State.NEEDS_APPLY], sometimes non-`null` when it's |
171 /// [_State.NEEDS_DECLARE], and always `null` otherwise. | 175 /// [_State.NEEDS_DECLARE], and always `null` otherwise. |
172 AggregateTransformController _applyController; | 176 AggregateTransformController _applyController; |
173 | 177 |
174 /// The number of secondary inputs that have been requested but not yet | 178 /// Map to track pending requests for secondary inputs. |
175 /// produced. | 179 /// |
176 int _pendingSecondaryInputs = 0; | 180 /// Keys are the secondary inputs that have been requested but not yet |
| 181 /// produced. Values are the number of requests for that input. |
| 182 final _pendingSecondaryInputs = <AssetId, int>{}; |
177 | 183 |
178 /// A stopwatch that tracks the total time spent in a transformer's `apply` | 184 /// A stopwatch that tracks the total time spent in a transformer's `apply` |
179 /// function. | 185 /// function. |
180 final _timeInTransformer = new Stopwatch(); | 186 final _timeInTransformer = new Stopwatch(); |
181 | 187 |
182 /// A stopwatch that tracks the time in a transformer's `apply` function spent | 188 /// A stopwatch that tracks the time in a transformer's `apply` function spent |
183 /// waiting for [getInput] calls to complete. | 189 /// waiting for [getInput] calls to complete. |
184 final _timeAwaitingInputs = new Stopwatch(); | 190 final _timeAwaitingInputs = new Stopwatch(); |
185 | 191 |
186 TransformNode(this.classifier, this.transformer, this.key, this._location) { | 192 TransformNode(this.classifier, this.transformer, this.key, this._location) { |
(...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
570 _state = _State.APPLIED; | 576 _state = _State.APPLIED; |
571 _streams.changeStatus(NodeStatus.IDLE); | 577 _streams.changeStatus(NodeStatus.IDLE); |
572 }); | 578 }); |
573 } | 579 } |
574 | 580 |
575 /// Gets the asset for an input [id]. | 581 /// Gets the asset for an input [id]. |
576 /// | 582 /// |
577 /// If an input with [id] cannot be found, throws an [AssetNotFoundException]. | 583 /// If an input with [id] cannot be found, throws an [AssetNotFoundException]. |
578 Future<Asset> getInput(AssetId id) { | 584 Future<Asset> getInput(AssetId id) { |
579 _timeAwaitingInputs.start(); | 585 _timeAwaitingInputs.start(); |
580 _pendingSecondaryInputs++; | 586 _pendingSecondaryInputs[id] = _pendingSecondaryInputs.containsKey(id) |
| 587 ? _pendingSecondaryInputs[id] + 1 |
| 588 : 1; |
581 return phase.previous.getOutput(id).then((node) { | 589 return phase.previous.getOutput(id).then((node) { |
582 // Throw if the input isn't found. This ensures the transformer's apply | 590 // Throw if the input isn't found. This ensures the transformer's apply |
583 // is exited. We'll then catch this and report it through the proper | 591 // is exited. We'll then catch this and report it through the proper |
584 // results stream. | 592 // results stream. |
585 if (node == null) { | 593 if (node == null) { |
586 _missingInputs.add(id); | 594 _missingInputs.add(id); |
587 | 595 |
588 // If this id is for an asset in another package, subscribe to that | 596 // If this id is for an asset in another package, subscribe to that |
589 // package's asset cascade so when it starts emitting the id we know to | 597 // package's asset cascade so when it starts emitting the id we know to |
590 // re-run the transformer. | 598 // re-run the transformer. |
(...skipping 12 matching lines...) Expand all Loading... |
603 | 611 |
604 throw new AssetNotFoundException(id); | 612 throw new AssetNotFoundException(id); |
605 } | 613 } |
606 | 614 |
607 _secondarySubscriptions.putIfAbsent(node.id, () { | 615 _secondarySubscriptions.putIfAbsent(node.id, () { |
608 return node.onStateChange.listen((_) => _dirty()); | 616 return node.onStateChange.listen((_) => _dirty()); |
609 }); | 617 }); |
610 | 618 |
611 return node.asset; | 619 return node.asset; |
612 }).whenComplete(() { | 620 }).whenComplete(() { |
613 _pendingSecondaryInputs--; | 621 assert(_pendingSecondaryInputs.containsKey(id)); |
614 if (_pendingSecondaryInputs != 0) _timeAwaitingInputs.stop(); | 622 if (_pendingSecondaryInputs[id] == 1) { |
| 623 _pendingSecondaryInputs.remove(id); |
| 624 } else { |
| 625 _pendingSecondaryInputs[id]--; |
| 626 } |
| 627 if (_pendingSecondaryInputs.isEmpty) _timeAwaitingInputs.stop(); |
615 }); | 628 }); |
616 } | 629 } |
617 | 630 |
618 /// Run [AggregateTransformer.apply]. | 631 /// Run [AggregateTransformer.apply]. |
619 /// | 632 /// |
620 /// Returns whether or not an error occurred while running the transformer. | 633 /// Returns whether or not an error occurred while running the transformer. |
621 Future<bool> _runApply() { | 634 Future<bool> _runApply() { |
622 var controller = new AggregateTransformController(this); | 635 var controller = new AggregateTransformController(this); |
623 _applyController = controller; | 636 _applyController = controller; |
624 _streams.onLogPool.add(controller.onLog); | 637 _streams.onLogPool.add(controller.onLog); |
625 for (var primary in _primaries) { | 638 for (var primary in _primaries) { |
626 if (!primary.state.isAvailable) continue; | 639 if (!primary.state.isAvailable) continue; |
627 controller.addInput(primary.asset); | 640 controller.addInput(primary.asset); |
628 } | 641 } |
629 _maybeFinishApplyController(); | 642 _maybeFinishApplyController(); |
630 | 643 |
| 644 var transformCounterTimer; |
| 645 |
631 return syncFuture(() { | 646 return syncFuture(() { |
632 _timeInTransformer.reset(); | 647 _timeInTransformer.reset(); |
633 _timeAwaitingInputs.reset(); | 648 _timeAwaitingInputs.reset(); |
634 _timeInTransformer.start(); | 649 _timeInTransformer.start(); |
| 650 |
| 651 transformCounterTimer = new Timer.periodic(_applyLogDuration, (_) { |
| 652 if (_streams.onLogController.isClosed || |
| 653 !_timeInTransformer.isRunning) { |
| 654 return; |
| 655 } |
| 656 |
| 657 var message = new StringBuffer("Not yet complete after " |
| 658 "${niceDuration(_timeInTransformer.elapsed)}"); |
| 659 if (_pendingSecondaryInputs.isNotEmpty) { |
| 660 message.write(", waiting on input(s) " |
| 661 "${_pendingSecondaryInputs.keys.join(", ")}"); |
| 662 } |
| 663 _streams.onLogController.add(new LogEntry( |
| 664 info, |
| 665 info.primaryId, |
| 666 LogLevel.FINE, |
| 667 message.toString(), |
| 668 null)); |
| 669 }); |
| 670 |
635 return transformer.apply(controller.transform); | 671 return transformer.apply(controller.transform); |
636 }).whenComplete(() { | 672 }).whenComplete(() { |
| 673 transformCounterTimer.cancel(); |
637 _timeInTransformer.stop(); | 674 _timeInTransformer.stop(); |
638 _timeAwaitingInputs.stop(); | 675 _timeAwaitingInputs.stop(); |
639 | 676 |
640 // Cancel the controller here even if `apply` wasn't interrupted. Since | 677 // Cancel the controller here even if `apply` wasn't interrupted. Since |
641 // the apply is finished, we want to close out the controller's streams. | 678 // the apply is finished, we want to close out the controller's streams. |
642 controller.cancel(); | 679 controller.cancel(); |
643 _applyController = null; | 680 _applyController = null; |
644 }).then((_) { | 681 }).then((_) { |
645 assert(_state != _State.DECLARED); | 682 assert(_state != _State.DECLARED); |
646 assert(_state != _State.DECLARING); | 683 assert(_state != _State.DECLARING); |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
877 /// declaring and [APPLYING] otherwise. If a primary input is added or | 914 /// declaring and [APPLYING] otherwise. If a primary input is added or |
878 /// removed, this will transition to [DECLARING]. | 915 /// removed, this will transition to [DECLARING]. |
879 static const APPLIED = const _State._("applied"); | 916 static const APPLIED = const _State._("applied"); |
880 | 917 |
881 final String name; | 918 final String name; |
882 | 919 |
883 const _State._(this.name); | 920 const _State._(this.name); |
884 | 921 |
885 String toString() => name; | 922 String toString() => name; |
886 } | 923 } |
OLD | NEW |