Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Unified Diff: third_party/pkg/barback-0.13.0/lib/src/transform_node.dart

Issue 291843011: Run pub tests against older versions of barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/pkg/barback-0.13.0/lib/src/transform_node.dart
diff --git a/third_party/pkg/barback-0.13.0/lib/src/transform_node.dart b/third_party/pkg/barback-0.13.0/lib/src/transform_node.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e3d9067dd7d473c4aeb53c435e2699dc8adfe2c1
--- /dev/null
+++ b/third_party/pkg/barback-0.13.0/lib/src/transform_node.dart
@@ -0,0 +1,515 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library barback.transform_node;
+
+import 'dart:async';
+
+import 'asset.dart';
+import 'asset_id.dart';
+import 'asset_node.dart';
+import 'declaring_transform.dart';
+import 'errors.dart';
+import 'lazy_transformer.dart';
+import 'log.dart';
+import 'phase.dart';
+import 'stream_pool.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.
+///
+/// Keeps track of whether it's dirty and needs to be run and which assets it
+/// depends on.
+class TransformNode {
+ /// The [Phase] that this transform runs in.
+ final Phase phase;
+
+ /// The [Transformer] to apply to this node's inputs.
+ final Transformer transformer;
+
+ /// The node for the primary asset this transform depends on.
+ final AssetNode primary;
+
+ /// A string describing the location of [this] in the transformer graph.
+ final String _location;
+
+ /// The subscription to [primary]'s [AssetNode.onStateChange] stream.
+ StreamSubscription _primarySubscription;
+
+ /// The subscription to [phase]'s [Phase.onAsset] stream.
+ StreamSubscription<AssetNode> _phaseSubscription;
+
+ /// Whether [this] is dirty and still has more processing to do.
+ bool get isDirty => !_state.isDone;
+
+ /// Whether [transformer] is lazy and this transform has yet to be forced.
+ bool _isLazy;
+
+ /// The subscriptions to each input's [AssetNode.onStateChange] stream.
+ final _inputSubscriptions = new Map<AssetId, StreamSubscription>();
+
+ /// The controllers for the asset nodes emitted by this node.
+ final _outputControllers = new Map<AssetId, AssetNodeController>();
+
+ final _missingInputs = new Set<AssetId>();
+
+ /// The controller that's used to pass [primary] through [this] if it's not
+ /// consumed or overwritten.
+ ///
+ /// This needs an intervening controller to ensure that the output can be
+ /// marked dirty when determining whether [this] will consume or overwrite it,
+ /// and be marked removed if it does. [_passThroughController] will be null
+ /// if the asset is not being passed through.
+ AssetNodeController _passThroughController;
+
+ /// A stream that emits an event whenever [this] is no longer dirty.
+ ///
+ /// This is synchronous in order to guarantee that it will emit an event as
+ /// soon as [isDirty] flips from `true` to `false`.
+ Stream get onDone => _onDoneController.stream;
+ final _onDoneController = new StreamController.broadcast(sync: true);
+
+ /// A stream that emits any new assets emitted by [this].
+ ///
+ /// Assets are emitted synchronously to ensure that any changes are thoroughly
+ /// propagated as soon as they occur.
+ Stream<AssetNode> get onAsset => _onAssetController.stream;
+ final _onAssetController =
+ new StreamController<AssetNode>.broadcast(sync: true);
+
+ /// A stream that emits an event whenever this transform logs an entry.
+ ///
+ /// This is synchronous because error logs can cause the transform to fail, so
+ /// we need to ensure that their processing isn't delayed until after the
+ /// transform or build has finished.
+ Stream<LogEntry> get onLog => _onLogPool.stream;
+ final _onLogPool = new StreamPool<LogEntry>.broadcast();
+
+ /// The current state of [this].
+ var _state = _TransformNodeState.PROCESSING;
+
+ /// Whether [this] has been marked as removed.
+ bool get _isRemoved => _onAssetController.isClosed;
+
+ /// Whether the most recent run of this transform has declared that it
+ /// consumes the primary input.
+ ///
+ /// Defaults to `false`. This is not meaningful unless [_state] is
+ /// [_TransformNodeState.APPLIED].
+ bool _consumePrimary = false;
+
+ TransformNode(this.phase, Transformer transformer, this.primary,
+ this._location)
+ : transformer = transformer,
+ _isLazy = transformer is LazyTransformer {
+ _primarySubscription = primary.onStateChange.listen((state) {
+ if (state.isRemoved) {
+ remove();
+ } else {
+ _dirty(primaryChanged: true);
+ }
+ });
+
+ _phaseSubscription = phase.previous.onAsset.listen((node) {
+ if (_missingInputs.contains(node.id)) _dirty(primaryChanged: false);
+ });
+
+ _process();
+ }
+
+ /// The [TransformInfo] describing this node.
+ ///
+ /// [TransformInfo] is the publicly-visible representation of a transform
+ /// node.
+ TransformInfo get info => new TransformInfo(transformer, primary.id);
+
+ /// Marks this transform as removed.
+ ///
+ /// This causes all of the transform's outputs to be marked as removed as
+ /// well. Normally this will be automatically done internally based on events
+ /// from the primary input, but it's possible for a transform to no longer be
+ /// valid even if its primary input still exists.
+ void remove() {
+ _onAssetController.close();
+ _onDoneController.close();
+ _primarySubscription.cancel();
+ _phaseSubscription.cancel();
+ _clearInputSubscriptions();
+ _clearOutputs();
+ if (_passThroughController != null) {
+ _passThroughController.setRemoved();
+ _passThroughController = null;
+ }
+ }
+
+ /// If [transformer] is lazy, ensures that its concrete outputs will be
+ /// generated.
+ void force() {
+ // 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(primaryChanged: false);
+ }
+
+ /// Marks this transform as dirty.
+ ///
+ /// This causes all of the transform's outputs to be marked as dirty as well.
+ /// [primaryChanged] should be true if and only if [this] was set dirty
+ /// because [primary] changed.
+ void _dirty({bool primaryChanged: false}) {
+ if (!primaryChanged && _state.isNotPrimary) return;
+
+ if (_passThroughController != null) _passThroughController.setDirty();
+ for (var controller in _outputControllers.values) {
+ controller.setDirty();
+ }
+
+ if (_state.isDone) {
+ if (primaryChanged) {
+ _process();
+ } else {
+ _apply();
+ }
+ } else if (primaryChanged) {
+ _state = _TransformNodeState.NEEDS_IS_PRIMARY;
+ } else if (!_state.needsIsPrimary) {
+ _state = _TransformNodeState.NEEDS_APPLY;
+ }
+ }
+
+ /// Determines whether [primary] is primary for [transformer], and if so runs
+ /// [transformer.apply].
+ void _process() {
+ // Clear all the old input subscriptions. If an input is re-used, we'll
+ // re-subscribe.
+ _clearInputSubscriptions();
+ _state = _TransformNodeState.PROCESSING;
+ primary.whenAvailable((_) {
+ _state = _TransformNodeState.PROCESSING;
+ return transformer.isPrimary(primary.asset.id);
+ }).catchError((error, stackTrace) {
+ // If the transform became dirty while processing, ignore any errors from
+ // it.
+ if (_state.needsIsPrimary || _isRemoved) return false;
+
+ // 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(_wrapException(error, stackTrace));
+
+ return false;
+ }).then((isPrimary) {
+ if (_isRemoved) return;
+ if (_state.needsIsPrimary) {
+ _process();
+ } else if (isPrimary) {
+ _apply();
+ } else {
+ _clearOutputs();
+ _emitPassThrough();
+ _state = _TransformNodeState.NOT_PRIMARY;
+ _onDoneController.add(null);
+ }
+ });
+ }
+
+ /// Applies this transform.
+ void _apply() {
+ assert(!_onAssetController.isClosed);
+
+ // Clear input subscriptions here as well as in [_process] because [_apply]
+ // may be restarted independently if only a secondary input changes.
+ _clearInputSubscriptions();
+ _state = _TransformNodeState.PROCESSING;
+ primary.whenAvailable((_) {
+ if (_state.needsIsPrimary) return null;
+ _state = _TransformNodeState.PROCESSING;
+ // TODO(nweiz): If [transformer] is a [DeclaringTransformer] but not a
+ // [LazyTransformer], we can get some mileage out of doing a declarative
+ // first so we know how to hook up the assets.
+ if (_isLazy) return _declareLazy();
+ return _applyImmediate();
+ }).catchError((error, stackTrace) {
+ // If the transform became dirty while processing, ignore any errors from
+ // it.
+ if (!_state.isProcessing || _isRemoved) return false;
+
+ // 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(_wrapException(error, stackTrace));
+ return true;
+ }).then((hadError) {
+ if (_isRemoved) return;
+
+ if (_state.needsIsPrimary) {
+ _process();
+ } else if (_state.needsApply) {
+ _apply();
+ } else {
+ assert(_state.isProcessing);
+ if (hadError) {
+ _clearOutputs();
+ _dontEmitPassThrough();
+ }
+
+ _state = _TransformNodeState.APPLIED;
+ _onDoneController.add(null);
+ }
+ });
+ }
+
+ /// Gets the asset for an input [id].
+ ///
+ /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+ Future<Asset> getInput(AssetId id) {
+ return phase.previous.getOutput(id).then((node) {
+ // Throw if the input isn't found. This ensures the transformer's apply
+ // is exited. We'll then catch this and report it through the proper
+ // results stream.
+ if (node == null) {
+ _missingInputs.add(id);
+ throw new AssetNotFoundException(id);
+ }
+
+ _inputSubscriptions.putIfAbsent(node.id, () {
+ return node.onStateChange.listen((_) => _dirty(primaryChanged: false));
+ });
+
+ return node.asset;
+ });
+ }
+
+ /// Applies the transform so that it produces concrete (as opposed to lazy)
+ /// outputs.
+ ///
+ /// Returns whether or not the transformer logged an error.
+ Future<bool> _applyImmediate() {
+ var transformController = new TransformController(this);
+ _onLogPool.add(transformController.onLog);
+
+ return syncFuture(() {
+ return transformer.apply(transformController.transform);
+ }).then((_) {
+ if (!_state.isProcessing || _onAssetController.isClosed) return false;
+ if (transformController.loggedError) return true;
+
+ _consumePrimary = transformController.consumePrimary;
+
+ var newOutputs = transformController.outputs;
+ // 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();
+ }
+
+ // Emit or stop emitting the pass-through asset between removing and
+ // adding outputs to ensure there are no collisions.
+ if (!newOutputs.containsId(primary.id)) {
+ _emitPassThrough();
+ } else {
+ _dontEmitPassThrough();
+ }
+
+ // 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;
+ _onAssetController.add(controller.node);
+ }
+ }
+
+ return false;
+ });
+ }
+
+ /// Applies the transform in declarative mode so that it produces lazy
+ /// outputs.
+ ///
+ /// Returns whether or not the transformer logged an error.
+ Future<bool> _declareLazy() {
+ var transformController = new DeclaringTransformController(this);
+
+ return syncFuture(() {
+ return (transformer as LazyTransformer)
+ .declareOutputs(transformController.transform);
+ }).then((_) {
+ if (!_state.isProcessing || _onAssetController.isClosed) return false;
+ if (transformController.loggedError) return true;
+
+ _consumePrimary = transformController.consumePrimary;
+
+ var newIds = transformController.outputIds;
+ 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();
+ }
+
+ // Emit or stop emitting the pass-through asset between removing and
+ // adding outputs to ensure there are no collisions.
+ if (!newIds.contains(primary.id)) {
+ _emitPassThrough();
+ } else {
+ _dontEmitPassThrough();
+ }
+
+ for (var id in newIds) {
+ var controller = _outputControllers[id];
+ if (controller != null) {
+ controller.setLazy(force);
+ } else {
+ var controller = new AssetNodeController.lazy(id, force, this);
+ _outputControllers[id] = controller;
+ _onAssetController.add(controller.node);
+ }
+ }
+
+ return false;
+ });
+ }
+
+ /// Cancels all subscriptions to secondary input nodes.
+ void _clearInputSubscriptions() {
+ _missingInputs.clear();
+ for (var subscription in _inputSubscriptions.values) {
+ subscription.cancel();
+ }
+ _inputSubscriptions.clear();
+ }
+
+ /// Removes all output assets.
+ void _clearOutputs() {
+ // Remove all the previously-emitted assets.
+ for (var controller in _outputControllers.values) {
+ controller.setRemoved();
+ }
+ _outputControllers.clear();
+ }
+
+ /// Emit the pass-through asset if it's not being emitted already.
+ void _emitPassThrough() {
+ assert(!_outputControllers.containsKey(primary.id));
+
+ if (_consumePrimary) return;
+ if (_passThroughController == null) {
+ _passThroughController = new AssetNodeController.from(primary);
+ _onAssetController.add(_passThroughController.node);
+ } else {
+ _passThroughController.setAvailable(primary.asset);
+ }
+ }
+
+ /// Stop emitting the pass-through asset if it's being emitted already.
+ void _dontEmitPassThrough() {
+ if (_passThroughController == null) return;
+ _passThroughController.setRemoved();
+ _passThroughController = null;
+ }
+
+ BarbackException _wrapException(error, StackTrace stackTrace) {
+ if (error is! AssetNotFoundException) {
+ return new TransformerException(info, error, stackTrace);
+ } else {
+ return new MissingInputException(info, error.id);
+ }
+ }
+
+ String toString() =>
+ "transform node in $_location for $transformer on $primary";
+}
+
+/// The enum of states that [TransformNode] can be in.
+class _TransformNodeState {
+ /// The transform node is running [Transformer.isPrimary] or
+ /// [Transformer.apply] and doesn't need to re-run them.
+ ///
+ /// If there are no external changes by the time the processing finishes, this
+ /// will transition to [APPLIED] or [NOT_PRIMARY] depending on the result of
+ /// [Transformer.isPrimary]. If the primary input changes, this will
+ /// transition to [NEEDS_IS_PRIMARY]. If a secondary input changes, this will
+ /// transition to [NEEDS_APPLY].
+ static final PROCESSING = const _TransformNodeState._("processing");
+
+ /// The transform is running [Transformer.isPrimary] or [Transformer.apply],
+ /// but since it started the primary input changed, so it will need to re-run
+ /// [Transformer.isPrimary].
+ ///
+ /// This will always transition to [Transformer.PROCESSING].
+ static final NEEDS_IS_PRIMARY =
+ const _TransformNodeState._("needs isPrimary");
+
+ /// The transform is running [Transformer.apply], but since it started a
+ /// secondary input changed, so it will need to re-run [Transformer.apply].
+ ///
+ /// If there are no external changes by the time [Transformer.apply] finishes,
+ /// this will transition to [PROCESSING]. If the primary input changes, this
+ /// will transition to [NEEDS_IS_PRIMARY].
+ static final NEEDS_APPLY = const _TransformNodeState._("needs apply");
+
+ /// The transform has finished running [Transformer.apply], whether or not it
+ /// emitted an error.
+ ///
+ /// If the primary input or a secondary input changes, this will transition to
+ /// [PROCESSING].
+ static final APPLIED = const _TransformNodeState._("applied");
+
+ /// The transform has finished running [Transformer.isPrimary], which returned
+ /// `false`.
+ ///
+ /// If the primary input changes, this will transition to [PROCESSING].
+ static final NOT_PRIMARY = const _TransformNodeState._("not primary");
+
+ /// Whether [this] is [PROCESSING].
+ bool get isProcessing => this == _TransformNodeState.PROCESSING;
+
+ /// Whether [this] is [NEEDS_IS_PRIMARY].
+ bool get needsIsPrimary => this == _TransformNodeState.NEEDS_IS_PRIMARY;
+
+ /// Whether [this] is [NEEDS_APPLY].
+ bool get needsApply => this == _TransformNodeState.NEEDS_APPLY;
+
+ /// Whether [this] is [APPLIED].
+ bool get isApplied => this == _TransformNodeState.APPLIED;
+
+ /// Whether [this] is [NOT_PRIMARY].
+ bool get isNotPrimary => this == _TransformNodeState.NOT_PRIMARY;
+
+ /// Whether the transform has finished running [Transformer.isPrimary] and
+ /// [Transformer.apply].
+ ///
+ /// Specifically, whether [this] is [APPLIED] or [NOT_PRIMARY].
+ bool get isDone => isApplied || isNotPrimary;
+
+ final String name;
+
+ const _TransformNodeState._(this.name);
+
+ String toString() => name;
+}
« no previous file with comments | « third_party/pkg/barback-0.13.0/lib/src/transform_logger.dart ('k') | third_party/pkg/barback-0.13.0/lib/src/transformer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698