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

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: 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_group.dart'; 22 import 'transformer_group.dart';
23 import 'transformer_sorter.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 sorters for this phase.
58 final _sorters = new Map<Transformer, TransformerSorter>();
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 /// [_sorters] or [_groups]. It's possible that a given asset was consumed by
72 /// that input isn't consumed by any transformers, it will be forwarded 71 /// a group and not an individual transformer, and so shouldn't be forwarded
73 /// through the PhaseInput. However, it's possible that it was consumed by a 72 /// 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 sorter, we must be able to distinguish it from other outputs with the
78 /// the same id. To do so, we check if its origin is in [_inputOrigins]. If 76 /// same id. To do so, we check if its origin is in [_inputOrigins]. If so,
79 /// so, it's been forwarded unmodified. 77 /// 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 (_sorters.isEmpty && _groups.isEmpty) {
91 return _inputs.any((input) => input.state.isDirty) ?
92 NodeStatus.RUNNING : NodeStatus.IDLE;
93 }
94
95 var sorterStatus = NodeStatus.dirtiest(
96 _sorters.values.map((sorter) => sorter.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(sorterStatus)
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(node, _sorters.length, _groups.length);
165 node, _transformers.length, _groups.length);
166 _forwarders[node.id] = forwarder; 168 _forwarders[node.id] = forwarder;
167 forwarder.onAsset.listen(_handleOutputWithoutForwarder); 169 forwarder.onAsset.listen(_handleOutputWithoutForwarder);
168 if (forwarder.output != null) { 170 if (forwarder.output != null) {
169 _handleOutputWithoutForwarder(forwarder.output); 171 _handleOutputWithoutForwarder(forwarder.output);
170 } 172 }
171 173
172 _inputOrigins.add(node.origin); 174 _inputOrigins.add(node.origin);
173 var input = new PhaseInput(this, node, "$_location.$_index"); 175 _inputs.add(node);
174 _inputs[node.id] = input; 176 node.onStateChange.listen((state) {
175 input.input.whenRemoved(() { 177 if (state.isRemoved) {
176 _inputOrigins.remove(node.origin); 178 _inputOrigins.remove(node.origin);
177 _inputs.remove(node.id); 179 _forwarders.remove(node.id).remove();
178 _forwarders.remove(node.id).remove(); 180 }
179 _streams.changeStatus(status); 181 _streams.changeStatus(status);
180 }); 182 });
181 input.onAsset.listen(_handleOutput);
182 _streams.onLogPool.add(input.onLog);
183 input.onStatusChange.listen((_) => _streams.changeStatus(status));
184 183
185 input.updateTransformers(_transformers); 184 for (var sorter in _sorters.values) {
186 185 sorter.addInput(node);
186 }
187 for (var group in _groups.values) { 187 for (var group in _groups.values) {
188 group.addInput(node); 188 group.addInput(node);
189 } 189 }
190 } 190 }
191 191
192 // TODO(nweiz): If the output is available when this is called, it's 192 // 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 193 // 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 194 // 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 195 // avoid this, we should have this and the methods it calls take explicit
196 // callbacks, as in [AssetNode.whenAvailable]. 196 // 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 228 // Otherwise, store a completer for the asset node. If it's generated in
229 // the future, we'll complete this completer. 229 // the future, we'll complete this completer.
230 var completer = _pendingOutputRequests.putIfAbsent(id, 230 var completer = _pendingOutputRequests.putIfAbsent(id,
231 () => new Completer.sync()); 231 () => new Completer.sync());
232 return completer.future; 232 return completer.future;
233 }); 233 });
234 } 234 }
235 235
236 /// Set this phase's transformers to [transformers]. 236 /// Set this phase's transformers to [transformers].
237 void updateTransformers(Iterable transformers) { 237 void updateTransformers(Iterable transformers) {
238 var actualTransformers = transformers.where((op) => op is Transformer); 238 var newTransformers = transformers.where((op) => op is Transformer)
239 _transformers.clear(); 239 .toSet();
240 _transformers.addAll(actualTransformers); 240 var oldTransformers = _sorters.keys.toSet();
241 for (var input in _inputs.values) { 241 for (var removed in oldTransformers.difference(newTransformers)) {
242 input.updateTransformers(actualTransformers); 242 _sorters.remove(removed).remove();
243 }
244
245 for (var transformer in newTransformers.difference(oldTransformers)) {
246 var sorter = new TransformerSorter(
247 this, transformer, "$_location.$_index");
248 _sorters[transformer] = sorter;
249 sorter.onAsset.listen(_handleOutput);
250 _streams.onLogPool.add(sorter.onLog);
251 sorter.onStatusChange.listen((_) => _streams.changeStatus(status));
252 for (var input in _inputs) {
253 sorter.addInput(input);
254 }
243 } 255 }
244 256
245 var newGroups = transformers.where((op) => op is TransformerGroup) 257 var newGroups = transformers.where((op) => op is TransformerGroup)
246 .toSet(); 258 .toSet();
247 var oldGroups = _groups.keys.toSet(); 259 var oldGroups = _groups.keys.toSet();
248 for (var removed in oldGroups.difference(newGroups)) { 260 for (var removed in oldGroups.difference(newGroups)) {
249 _groups.remove(removed).remove(); 261 _groups.remove(removed).remove();
250 } 262 }
251 263
252 for (var added in newGroups.difference(oldGroups)) { 264 for (var added in newGroups.difference(oldGroups)) {
253 var runner = new GroupRunner(cascade, added, "$_location.$_index"); 265 var runner = new GroupRunner(cascade, added, "$_location.$_index");
254 _groups[added] = runner; 266 _groups[added] = runner;
255 runner.onAsset.listen(_handleOutput); 267 runner.onAsset.listen(_handleOutput);
256 _streams.onLogPool.add(runner.onLog); 268 _streams.onLogPool.add(runner.onLog);
257 runner.onStatusChange.listen((_) => _streams.changeStatus(status)); 269 runner.onStatusChange.listen((_) => _streams.changeStatus(status));
258 for (var input in _inputs.values) { 270 for (var input in _inputs) {
259 runner.addInput(input.input); 271 runner.addInput(input);
260 } 272 }
261 } 273 }
262 274
263 for (var forwarder in _forwarders.values) { 275 for (var forwarder in _forwarders.values) {
264 forwarder.updateTransformers(_transformers.length, _groups.length); 276 forwarder.updateTransformers(_sorters.length, _groups.length);
265 } 277 }
278
279 _streams.changeStatus(status);
266 } 280 }
267 281
268 /// Force all [LazyTransformer]s' transforms in this phase to begin producing 282 /// Force all [LazyTransformer]s' transforms in this phase to begin producing
269 /// concrete assets. 283 /// concrete assets.
270 void forceAllTransforms() { 284 void forceAllTransforms() {
285 for (var sorter in _sorters.values) {
286 sorter.forceAllTransforms();
287 }
288
271 for (var group in _groups.values) { 289 for (var group in _groups.values) {
272 group.forceAllTransforms(); 290 group.forceAllTransforms();
273 } 291 }
274
275 for (var input in _inputs.values) {
276 input.forceAllTransforms();
277 }
278 } 292 }
279 293
280 /// Add a new phase after this one. 294 /// Add a new phase after this one.
281 /// 295 ///
282 /// This may only be called on a phase with no phase following it. 296 /// This may only be called on a phase with no phase following it.
283 Phase addPhase() { 297 Phase addPhase() {
284 var next = new Phase._(cascade, _location, _index + 1, this); 298 var next = new Phase._(cascade, _location, _index + 1, this);
285 for (var output in _outputs.values.toList()) { 299 for (var output in _outputs.values.toList()) {
286 // Remove [output]'s listeners because now they should get the asset from 300 // Remove [output]'s listeners because now they should get the asset from
287 // [next], rather than this phase. Any transforms consuming [output] will 301 // [next], rather than this phase. Any transforms consuming [output] will
288 // be re-run and will consume the output from the new final phase. 302 // be re-run and will consume the output from the new final phase.
289 output.removeListeners(); 303 output.removeListeners();
290 } 304 }
291 return next; 305 return next;
292 } 306 }
293 307
294 /// Mark this phase as removed. 308 /// Mark this phase as removed.
295 /// 309 ///
296 /// This will remove all the phase's outputs. 310 /// This will remove all the phase's outputs.
297 void remove() { 311 void remove() {
298 for (var input in _inputs.values.toList()) { 312 for (var sorter in _sorters.values.toList()) {
299 input.remove(); 313 sorter.remove();
300 } 314 }
301 for (var group in _groups.values) { 315 for (var group in _groups.values) {
302 group.remove(); 316 group.remove();
303 } 317 }
304 _streams.close(); 318 _streams.close();
305 if (_previousStatusSubscription != null) { 319 if (_previousStatusSubscription != null) {
306 _previousStatusSubscription.cancel(); 320 _previousStatusSubscription.cancel();
307 } 321 }
308 if (_previousOnAssetSubscription != null) { 322 if (_previousOnAssetSubscription != null) {
309 _previousOnAssetSubscription.cancel(); 323 _previousOnAssetSubscription.cancel();
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
360 assert(asset.state.isDirty); 374 assert(asset.state.isDirty);
361 asset.force(); 375 asset.force();
362 asset.whenStateChanges().then((state) { 376 asset.whenStateChanges().then((state) {
363 if (state.isRemoved) return getOutput(asset.id); 377 if (state.isRemoved) return getOutput(asset.id);
364 return asset; 378 return asset;
365 }).then(request.complete).catchError(request.completeError); 379 }).then(request.complete).catchError(request.completeError);
366 } 380 }
367 381
368 String toString() => "phase $_location.$_index"; 382 String toString() => "phase $_location.$_index";
369 } 383 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698