| Index: pkg/barback/lib/src/transform_node.dart
|
| diff --git a/pkg/barback/lib/src/transform_node.dart b/pkg/barback/lib/src/transform_node.dart
|
| index 76b29bdffd5d21f62de8b8c7d6ab63bb8a678210..4e2e79d65694a735810ca9a9403af65ff3a5a8b4 100644
|
| --- a/pkg/barback/lib/src/transform_node.dart
|
| +++ b/pkg/barback/lib/src/transform_node.dart
|
| @@ -12,11 +12,14 @@ import 'asset.dart';
|
| import 'asset_id.dart';
|
| import 'asset_node.dart';
|
| import 'asset_set.dart';
|
| +import 'dry_run_transform.dart';
|
| import 'errors.dart';
|
| +import 'lazy_transformer.dart';
|
| import 'log.dart';
|
| import 'phase.dart';
|
| import 'transform.dart';
|
| import 'transformer.dart';
|
| +import 'utils.dart';
|
|
|
| /// Describes a transform on a set of assets and its relationship to the build
|
| /// dependency graph.
|
| @@ -41,6 +44,10 @@ class TransformNode {
|
| bool get isDirty => _isDirty;
|
| var _isDirty = true;
|
|
|
| + /// Whether [transformer] is lazy and this transform has yet to be
|
| + /// materialized.
|
| + bool _isLazy;
|
| +
|
| /// The subscriptions to each input's [AssetNode.onStateChange] stream.
|
| var _inputSubscriptions = new Map<AssetId, StreamSubscription>();
|
|
|
| @@ -64,7 +71,9 @@ class TransformNode {
|
| Stream<LogEntry> get onLog => _onLogController.stream;
|
| final _onLogController = new StreamController<LogEntry>.broadcast(sync: true);
|
|
|
| - TransformNode(this.phase, this.transformer, this.primary) {
|
| + TransformNode(this.phase, Transformer transformer, this.primary)
|
| + : transformer = transformer,
|
| + _isLazy = transformer is LazyTransformer {
|
| _primarySubscription = primary.onStateChange.listen((state) {
|
| if (state.isRemoved) {
|
| remove();
|
| @@ -98,6 +107,15 @@ class TransformNode {
|
| }
|
| }
|
|
|
| + /// If [transform] is lazy, ensures that its outputs are materialized.
|
| + void materialize() {
|
| + // TODO(nweiz): we might want to have a timeout after which, if the
|
| + // transform's outputs have gone unused, we switch it back to lazy mode.
|
| + if (!_isLazy) return;
|
| + _isLazy = false;
|
| + _dirty();
|
| + }
|
| +
|
| /// Marks this transform as dirty.
|
| ///
|
| /// This causes all of the transform's outputs to be marked as dirty as well.
|
| @@ -116,9 +134,6 @@ class TransformNode {
|
| Future<Set<AssetNode>> apply() {
|
| assert(!_onDirtyController.isClosed);
|
|
|
| - var newOutputs = new AssetSet();
|
| - var transform = createTransform(this, newOutputs, _log);
|
| -
|
| // Clear all the old input subscriptions. If an input is re-used, we'll
|
| // re-subscribe.
|
| for (var subscription in _inputSubscriptions.values) {
|
| @@ -128,25 +143,26 @@ class TransformNode {
|
|
|
| _isDirty = false;
|
|
|
| - return transformer.apply(transform).catchError((error, stack) {
|
| + return syncFuture(() {
|
| + // TODO(nweiz): If [transformer] is a [DryRunTransformer] but not a
|
| + // [LazyTransformer], we can get some mileage out of doing a dry run first
|
| + // so we know how to hook up the assets.
|
| + if (_isLazy) return _applyLazy();
|
| + return _applyImmediate();
|
| + }).catchError((error, stackTrace) {
|
| // If the transform became dirty while processing, ignore any errors from
|
| // it.
|
| - if (_isDirty) return;
|
| + if (_isDirty) return new Set();
|
|
|
| if (error is! MissingInputException) {
|
| - error = new TransformerException(info, error, stack);
|
| + error = new TransformerException(info, error, stackTrace);
|
| }
|
|
|
| - // Catch all transformer errors and pipe them to the results stream.
|
| - // This is so a broken transformer doesn't take down the whole graph.
|
| + // Catch all transformer errors and pipe them to the results stream. This
|
| + // is so a broken transformer doesn't take down the whole graph.
|
| phase.cascade.reportError(error);
|
|
|
| - // Don't allow partial results from a failed transform.
|
| - newOutputs.clear();
|
| - }).then((_) {
|
| - if (_isDirty) return new Set();
|
| -
|
| - return _adjustOutputs(newOutputs);
|
| + return new Set();
|
| });
|
| }
|
|
|
| @@ -177,40 +193,87 @@ class TransformNode {
|
| });
|
| }
|
|
|
| - /// Adjusts the outputs of the transform to reflect the outputs emitted on its
|
| - /// most recent run.
|
| - Set<AssetNode> _adjustOutputs(AssetSet newOutputs) {
|
| - // Any ids that are for a different package are invalid.
|
| - var invalidIds = newOutputs
|
| - .map((asset) => asset.id)
|
| - .where((id) => id.package != phase.cascade.package)
|
| - .toSet();
|
| - for (var id in invalidIds) {
|
| - newOutputs.removeId(id);
|
| - // TODO(nweiz): report this as a warning rather than a failing error.
|
| - phase.cascade.reportError(new InvalidOutputException(info, id));
|
| - }
|
| + /// Applies the transform so that it produces materialized (as opposed to
|
| + /// lazy) outputs.
|
| + Future<Set<AssetNode>> _applyImmediate() {
|
| + var newOutputs = new AssetSet();
|
| + var transform = new Transform(this, newOutputs, _log);
|
|
|
| - // Remove outputs that used to exist but don't anymore.
|
| - for (var id in _outputControllers.keys.toList()) {
|
| - if (newOutputs.containsId(id)) continue;
|
| - _outputControllers.remove(id).setRemoved();
|
| - }
|
| + return syncFuture(() => transformer.apply(transform)).then((_) {
|
| + if (_isDirty) return new Set();
|
|
|
| - var brandNewOutputs = new Set<AssetNode>();
|
| - // Store any new outputs or new contents for existing outputs.
|
| - for (var asset in newOutputs) {
|
| - var controller = _outputControllers[asset.id];
|
| - if (controller != null) {
|
| - controller.setAvailable(asset);
|
| - } else {
|
| - var controller = new AssetNodeController.available(asset, this);
|
| - _outputControllers[asset.id] = controller;
|
| - brandNewOutputs.add(controller.node);
|
| + // Any ids that are for a different package are invalid.
|
| + var invalidIds = newOutputs
|
| + .map((asset) => asset.id)
|
| + .where((id) => id.package != phase.cascade.package)
|
| + .toSet();
|
| + for (var id in invalidIds) {
|
| + newOutputs.removeId(id);
|
| + // TODO(nweiz): report this as a warning rather than a failing error.
|
| + phase.cascade.reportError(new InvalidOutputException(info, id));
|
| + }
|
| +
|
| + // Remove outputs that used to exist but don't anymore.
|
| + for (var id in _outputControllers.keys.toList()) {
|
| + if (newOutputs.containsId(id)) continue;
|
| + _outputControllers.remove(id).setRemoved();
|
| }
|
| - }
|
|
|
| - return brandNewOutputs;
|
| + var brandNewOutputs = new Set<AssetNode>();
|
| + // Store any new outputs or new contents for existing outputs.
|
| + for (var asset in newOutputs) {
|
| + var controller = _outputControllers[asset.id];
|
| + if (controller != null) {
|
| + controller.setAvailable(asset);
|
| + } else {
|
| + var controller = new AssetNodeController.available(asset, this);
|
| + _outputControllers[asset.id] = controller;
|
| + brandNewOutputs.add(controller.node);
|
| + }
|
| + }
|
| +
|
| + return brandNewOutputs;
|
| + });
|
| + }
|
| +
|
| + /// Applies the transform in dry-run mode so that it produces lazy outputs.
|
| + Future<Set<AssetNode>> _applyLazy() {
|
| + var newIds = new Set();
|
| + var transform = new DryRunTransform(this, newIds, _log);
|
| +
|
| + return syncFuture(() {
|
| + return (transformer as LazyTransformer).dryRun(transform);
|
| + }).then((_) {
|
| + if (_isDirty) return new Set();
|
| +
|
| + var invalidIds =
|
| + newIds.where((id) => id.package != phase.cascade.package).toSet();
|
| + for (var id in invalidIds) {
|
| + newIds.remove(id);
|
| + // TODO(nweiz): report this as a warning rather than a failing error.
|
| + phase.cascade.reportError(new InvalidOutputException(info, id));
|
| + }
|
| +
|
| + // Remove outputs that used to exist but don't anymore.
|
| + for (var id in _outputControllers.keys.toList()) {
|
| + if (newIds.contains(id)) continue;
|
| + _outputControllers.remove(id).setRemoved();
|
| + }
|
| +
|
| + var brandNewOutputs = new Set<AssetNode>();
|
| + for (var id in newIds) {
|
| + var controller = _outputControllers[id];
|
| + if (controller != null) {
|
| + controller.setLazy(materialize);
|
| + } else {
|
| + var controller = new AssetNodeController.lazy(id, materialize, this);
|
| + _outputControllers[id] = controller;
|
| + brandNewOutputs.add(controller.node);
|
| + }
|
| + }
|
| +
|
| + return brandNewOutputs;
|
| + });
|
| }
|
|
|
| void _log(AssetId asset, LogLevel level, String message, Span span) {
|
|
|