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

Side by Side Diff: pkg/barback/lib/src/phase.dart

Issue 260833006: Remove PhaseInput and add TransformerSorter to barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rename "sorter" to "classifier". 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
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.phase; 5 library barback.phase;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'asset_cascade.dart'; 9 import 'asset_cascade.dart';
10 import 'asset_id.dart'; 10 import 'asset_id.dart';
11 import 'asset_node.dart'; 11 import 'asset_node.dart';
12 import 'asset_node_set.dart';
12 import 'errors.dart'; 13 import 'errors.dart';
13 import 'group_runner.dart'; 14 import 'group_runner.dart';
14 import 'log.dart'; 15 import 'log.dart';
15 import 'multiset.dart'; 16 import 'multiset.dart';
16 import 'node_status.dart'; 17 import 'node_status.dart';
17 import 'node_streams.dart'; 18 import 'node_streams.dart';
18 import 'phase_forwarder.dart'; 19 import 'phase_forwarder.dart';
19 import 'phase_input.dart';
20 import 'phase_output.dart'; 20 import 'phase_output.dart';
21 import 'transformer.dart'; 21 import 'transformer.dart';
22 import 'transformer_classifier.dart';
22 import 'transformer_group.dart'; 23 import 'transformer_group.dart';
23 import 'utils.dart'; 24 import 'utils.dart';
24 25
25 /// One phase in the ordered series of transformations in an [AssetCascade]. 26 /// One phase in the ordered series of transformations in an [AssetCascade].
26 /// 27 ///
27 /// Each phase can access outputs from previous phases and can in turn pass 28 /// Each phase can access outputs from previous phases and can in turn pass
28 /// outputs to later phases. Phases are processed strictly serially. All 29 /// outputs to later phases. Phases are processed strictly serially. All
29 /// transforms in a phase will be complete before moving on to the next phase. 30 /// transforms in a phase will be complete before moving on to the next phase.
30 /// Within a single phase, all transforms will be run in parallel. 31 /// Within a single phase, all transforms will be run in parallel.
31 /// 32 ///
32 /// Building can be interrupted between phases. For example, a source is added 33 /// Building can be interrupted between phases. For example, a source is added
33 /// which starts the background process. Sometime during, say, phase 2 (which 34 /// which starts the background process. Sometime during, say, phase 2 (which
34 /// is running asynchronously) that source is modified. When the process queue 35 /// is running asynchronously) that source is modified. When the process queue
35 /// goes to advance to phase 3, it will see that modification and start the 36 /// goes to advance to phase 3, it will see that modification and start the
36 /// waterfall from the beginning again. 37 /// waterfall from the beginning again.
37 class Phase { 38 class Phase {
38 /// The cascade that owns this phase. 39 /// The cascade that owns this phase.
39 final AssetCascade cascade; 40 final AssetCascade cascade;
40 41
41 /// A string describing the location of [this] in the transformer graph. 42 /// A string describing the location of [this] in the transformer graph.
42 final String _location; 43 final String _location;
43 44
44 /// The index of [this] in its parent cascade or group. 45 /// The index of [this] in its parent cascade or group.
45 final int _index; 46 final int _index;
46 47
47 /// The transformers that can access [inputs].
48 ///
49 /// Their outputs will be available to the next phase.
50 final _transformers = new Set<Transformer>();
51
52 /// The groups for this phase. 48 /// The groups for this phase.
53 final _groups = new Map<TransformerGroup, GroupRunner>(); 49 final _groups = new Map<TransformerGroup, GroupRunner>();
54 50
55 /// The inputs for this phase. 51 /// The inputs for this phase.
56 /// 52 ///
57 /// For the first phase, these will be the source assets. For all other 53 /// For the first phase, these will be the source assets. For all other
58 /// phases, they will be the outputs from the previous phase. 54 /// phases, they will be the outputs from the previous phase.
59 final _inputs = new Map<AssetId, PhaseInput>(); 55 final _inputs = new AssetNodeSet();
56
57 /// The transformer classifiers for this phase.
58 final _classifiers = new Map<Transformer, TransformerClassifier>();
60 59
61 /// The forwarders for this phase. 60 /// The forwarders for this phase.
62 final _forwarders = new Map<AssetId, PhaseForwarder>(); 61 final _forwarders = new Map<AssetId, PhaseForwarder>();
63 62
64 /// The outputs for this phase. 63 /// The outputs for this phase.
65 final _outputs = new Map<AssetId, PhaseOutput>(); 64 final _outputs = new Map<AssetId, PhaseOutput>();
66 65
67 /// The set of all [AssetNode.origin] properties of the input assets for this 66 /// The set of all [AssetNode.origin] properties of the input assets for this
68 /// phase. 67 /// phase.
69 /// 68 ///
70 /// This is used to determine which assets have been passed unmodified through 69 /// This is used to determine which assets have been passed unmodified through
71 /// [_inputs] or [_groups]. Each input asset has a PhaseInput in [_inputs]. If 70 /// [_classifiers] or [_groups]. It's possible that a given asset was consumed
72 /// that input isn't consumed by any transformers, it will be forwarded 71 /// by a group and not an individual transformer, and so shouldn't be
73 /// through the PhaseInput. However, it's possible that it was consumed by a 72 /// forwarded through the phase as a whole.
74 /// group, and so shouldn't be forwarded through the phase as a whole.
75 /// 73 ///
76 /// In order to detect whether an output has been forwarded through a group or 74 /// In order to detect whether an output has been forwarded through a group or
77 /// a PhaseInput, we must be able to distinguish it from other outputs with 75 /// a classifier, we must be able to distinguish it from other outputs with
78 /// the same id. To do so, we check if its origin is in [_inputOrigins]. If 76 /// the same id. To do so, we check if its origin is in [_inputOrigins]. If
79 /// so, it's been forwarded unmodified. 77 /// so, it's been forwarded unmodified.
80 final _inputOrigins = new Multiset<AssetNode>(); 78 final _inputOrigins = new Multiset<AssetNode>();
81 79
82 /// The streams exposed by this phase. 80 /// The streams exposed by this phase.
83 final _streams = new NodeStreams(); 81 final _streams = new NodeStreams();
84 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange; 82 Stream<NodeStatus> get onStatusChange => _streams.onStatusChange;
85 Stream<AssetNode> get onAsset => _streams.onAsset; 83 Stream<AssetNode> get onAsset => _streams.onAsset;
86 Stream<LogEntry> get onLog => _streams.onLog; 84 Stream<LogEntry> get onLog => _streams.onLog;
87 85
88 /// How far along [this] is in processing its assets. 86 /// How far along [this] is in processing its assets.
89 NodeStatus get status { 87 NodeStatus get status {
90 var inputStatus = NodeStatus.dirtiest( 88 // Before any transformers are added, the phase should be dirty if and only
91 _inputs.values.map((input) => input.status)); 89 // if any input is dirty.
90 if (_classifiers.isEmpty && _groups.isEmpty) {
91 return _inputs.any((input) => input.state.isDirty) ?
92 NodeStatus.RUNNING : NodeStatus.IDLE;
93 }
94
95 var classifierStatus = NodeStatus.dirtiest(
96 _classifiers.values.map((classifier) => classifier.status));
92 var groupStatus = NodeStatus.dirtiest( 97 var groupStatus = NodeStatus.dirtiest(
93 _groups.values.map((group) => group.status)); 98 _groups.values.map((group) => group.status));
94 return (previous == null ? NodeStatus.IDLE : previous.status) 99 return (previous == null ? NodeStatus.IDLE : previous.status)
95 .dirtier(inputStatus) 100 .dirtier(classifierStatus)
96 .dirtier(groupStatus); 101 .dirtier(groupStatus);
97 } 102 }
98 103
99 /// The previous phase in the cascade, or null if this is the first phase. 104 /// The previous phase in the cascade, or null if this is the first phase.
100 final Phase previous; 105 final Phase previous;
101 106
102 /// The subscription to [previous]'s [onStatusChange] stream. 107 /// The subscription to [previous]'s [onStatusChange] stream.
103 StreamSubscription _previousStatusSubscription; 108 StreamSubscription _previousStatusSubscription;
104 109
105 /// The subscription to [previous]'s [onAsset] stream. 110 /// The subscription to [previous]'s [onAsset] stream.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 /// 155 ///
151 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase 156 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase
152 /// will automatically begin determining which transforms can consume it as a 157 /// will automatically begin determining which transforms can consume it as a
153 /// primary input. The transforms themselves won't be applied until [process] 158 /// primary input. The transforms themselves won't be applied until [process]
154 /// is called, however. 159 /// is called, however.
155 /// 160 ///
156 /// This should only be used for brand-new assets or assets that have been 161 /// This should only be used for brand-new assets or assets that have been
157 /// removed and re-created. The phase will automatically handle updated assets 162 /// removed and re-created. The phase will automatically handle updated assets
158 /// using the [AssetNode.onStateChange] stream. 163 /// using the [AssetNode.onStateChange] stream.
159 void addInput(AssetNode node) { 164 void addInput(AssetNode node) {
160 if (_inputs.containsKey(node.id)) _inputs[node.id].remove();
161
162 // Each group is one channel along which an asset may be forwarded, as is 165 // Each group is one channel along which an asset may be forwarded, as is
163 // each transformer. 166 // each transformer.
164 var forwarder = new PhaseForwarder( 167 var forwarder = new PhaseForwarder(
165 node, _transformers.length, _groups.length); 168 node, _classifiers.length, _groups.length);
166 _forwarders[node.id] = forwarder; 169 _forwarders[node.id] = forwarder;
167 forwarder.onAsset.listen(_handleOutputWithoutForwarder); 170 forwarder.onAsset.listen(_handleOutputWithoutForwarder);
168 if (forwarder.output != null) { 171 if (forwarder.output != null) {
169 _handleOutputWithoutForwarder(forwarder.output); 172 _handleOutputWithoutForwarder(forwarder.output);
170 } 173 }
171 174
172 _inputOrigins.add(node.origin); 175 _inputOrigins.add(node.origin);
173 var input = new PhaseInput(this, node, "$_location.$_index"); 176 _inputs.add(node);
174 _inputs[node.id] = input; 177 node.onStateChange.listen((state) {
175 input.input.whenRemoved(() { 178 if (state.isRemoved) {
176 _inputOrigins.remove(node.origin); 179 _inputOrigins.remove(node.origin);
177 _inputs.remove(node.id); 180 _forwarders.remove(node.id).remove();
178 _forwarders.remove(node.id).remove(); 181 }
179 _streams.changeStatus(status); 182 _streams.changeStatus(status);
180 }); 183 });
181 input.onAsset.listen(_handleOutput);
182 _streams.onLogPool.add(input.onLog);
183 input.onStatusChange.listen((_) => _streams.changeStatus(status));
184 184
185 input.updateTransformers(_transformers); 185 for (var classifier in _classifiers.values) {
186 186 classifier.addInput(node);
187 }
187 for (var group in _groups.values) { 188 for (var group in _groups.values) {
188 group.addInput(node); 189 group.addInput(node);
189 } 190 }
190 } 191 }
191 192
192 // TODO(nweiz): If the output is available when this is called, it's 193 // TODO(nweiz): If the output is available when this is called, it's
193 // theoretically possible for it to become unavailable between the call and 194 // theoretically possible for it to become unavailable between the call and
194 // the return. If it does so, it won't trigger the rebuilding process. To 195 // the return. If it does so, it won't trigger the rebuilding process. To
195 // avoid this, we should have this and the methods it calls take explicit 196 // avoid this, we should have this and the methods it calls take explicit
196 // callbacks, as in [AssetNode.whenAvailable]. 197 // callbacks, as in [AssetNode.whenAvailable].
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 // Otherwise, store a completer for the asset node. If it's generated in 229 // Otherwise, store a completer for the asset node. If it's generated in
229 // the future, we'll complete this completer. 230 // the future, we'll complete this completer.
230 var completer = _pendingOutputRequests.putIfAbsent(id, 231 var completer = _pendingOutputRequests.putIfAbsent(id,
231 () => new Completer.sync()); 232 () => new Completer.sync());
232 return completer.future; 233 return completer.future;
233 }); 234 });
234 } 235 }
235 236
236 /// Set this phase's transformers to [transformers]. 237 /// Set this phase's transformers to [transformers].
237 void updateTransformers(Iterable transformers) { 238 void updateTransformers(Iterable transformers) {
238 var actualTransformers = transformers.where((op) => op is Transformer); 239 var newTransformers = transformers.where((op) => op is Transformer)
239 _transformers.clear(); 240 .toSet();
240 _transformers.addAll(actualTransformers); 241 var oldTransformers = _classifiers.keys.toSet();
241 for (var input in _inputs.values) { 242 for (var removed in oldTransformers.difference(newTransformers)) {
242 input.updateTransformers(actualTransformers); 243 _classifiers.remove(removed).remove();
244 }
245
246 for (var transformer in newTransformers.difference(oldTransformers)) {
247 var classifier = new TransformerClassifier(
248 this, transformer, "$_location.$_index");
249 _classifiers[transformer] = classifier;
250 classifier.onAsset.listen(_handleOutput);
251 _streams.onLogPool.add(classifier.onLog);
252 classifier.onStatusChange.listen((_) => _streams.changeStatus(status));
253 for (var input in _inputs) {
254 classifier.addInput(input);
255 }
243 } 256 }
244 257
245 var newGroups = transformers.where((op) => op is TransformerGroup) 258 var newGroups = transformers.where((op) => op is TransformerGroup)
246 .toSet(); 259 .toSet();
247 var oldGroups = _groups.keys.toSet(); 260 var oldGroups = _groups.keys.toSet();
248 for (var removed in oldGroups.difference(newGroups)) { 261 for (var removed in oldGroups.difference(newGroups)) {
249 _groups.remove(removed).remove(); 262 _groups.remove(removed).remove();
250 } 263 }
251 264
252 for (var added in newGroups.difference(oldGroups)) { 265 for (var added in newGroups.difference(oldGroups)) {
253 var runner = new GroupRunner(cascade, added, "$_location.$_index"); 266 var runner = new GroupRunner(cascade, added, "$_location.$_index");
254 _groups[added] = runner; 267 _groups[added] = runner;
255 runner.onAsset.listen(_handleOutput); 268 runner.onAsset.listen(_handleOutput);
256 _streams.onLogPool.add(runner.onLog); 269 _streams.onLogPool.add(runner.onLog);
257 runner.onStatusChange.listen((_) => _streams.changeStatus(status)); 270 runner.onStatusChange.listen((_) => _streams.changeStatus(status));
258 for (var input in _inputs.values) { 271 for (var input in _inputs) {
259 runner.addInput(input.input); 272 runner.addInput(input);
260 } 273 }
261 } 274 }
262 275
263 for (var forwarder in _forwarders.values) { 276 for (var forwarder in _forwarders.values) {
264 forwarder.updateTransformers(_transformers.length, _groups.length); 277 forwarder.updateTransformers(_classifiers.length, _groups.length);
265 } 278 }
279
280 _streams.changeStatus(status);
266 } 281 }
267 282
268 /// Force all [LazyTransformer]s' transforms in this phase to begin producing 283 /// Force all [LazyTransformer]s' transforms in this phase to begin producing
269 /// concrete assets. 284 /// concrete assets.
270 void forceAllTransforms() { 285 void forceAllTransforms() {
286 for (var classifier in _classifiers.values) {
287 classifier.forceAllTransforms();
288 }
289
271 for (var group in _groups.values) { 290 for (var group in _groups.values) {
272 group.forceAllTransforms(); 291 group.forceAllTransforms();
273 } 292 }
274
275 for (var input in _inputs.values) {
276 input.forceAllTransforms();
277 }
278 } 293 }
279 294
280 /// Add a new phase after this one. 295 /// Add a new phase after this one.
281 /// 296 ///
282 /// This may only be called on a phase with no phase following it. 297 /// This may only be called on a phase with no phase following it.
283 Phase addPhase() { 298 Phase addPhase() {
284 var next = new Phase._(cascade, _location, _index + 1, this); 299 var next = new Phase._(cascade, _location, _index + 1, this);
285 for (var output in _outputs.values.toList()) { 300 for (var output in _outputs.values.toList()) {
286 // Remove [output]'s listeners because now they should get the asset from 301 // Remove [output]'s listeners because now they should get the asset from
287 // [next], rather than this phase. Any transforms consuming [output] will 302 // [next], rather than this phase. Any transforms consuming [output] will
288 // be re-run and will consume the output from the new final phase. 303 // be re-run and will consume the output from the new final phase.
289 output.removeListeners(); 304 output.removeListeners();
290 } 305 }
291 return next; 306 return next;
292 } 307 }
293 308
294 /// Mark this phase as removed. 309 /// Mark this phase as removed.
295 /// 310 ///
296 /// This will remove all the phase's outputs. 311 /// This will remove all the phase's outputs.
297 void remove() { 312 void remove() {
298 for (var input in _inputs.values.toList()) { 313 for (var classifier in _classifiers.values.toList()) {
299 input.remove(); 314 classifier.remove();
300 } 315 }
301 for (var group in _groups.values) { 316 for (var group in _groups.values) {
302 group.remove(); 317 group.remove();
303 } 318 }
304 _streams.close(); 319 _streams.close();
305 if (_previousStatusSubscription != null) { 320 if (_previousStatusSubscription != null) {
306 _previousStatusSubscription.cancel(); 321 _previousStatusSubscription.cancel();
307 } 322 }
308 if (_previousOnAssetSubscription != null) { 323 if (_previousOnAssetSubscription != null) {
309 _previousOnAssetSubscription.cancel(); 324 _previousOnAssetSubscription.cancel();
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
360 assert(asset.state.isDirty); 375 assert(asset.state.isDirty);
361 asset.force(); 376 asset.force();
362 asset.whenStateChanges().then((state) { 377 asset.whenStateChanges().then((state) {
363 if (state.isRemoved) return getOutput(asset.id); 378 if (state.isRemoved) return getOutput(asset.id);
364 return asset; 379 return asset;
365 }).then(request.complete).catchError(request.completeError); 380 }).then(request.complete).catchError(request.completeError);
366 } 381 }
367 382
368 String toString() => "phase $_location.$_index"; 383 String toString() => "phase $_location.$_index";
369 } 384 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698