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

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

Issue 21446002: Add events in barback to bubble up the dirty bit. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 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.dart'; 9 import 'asset.dart';
10 import 'asset_cascade.dart'; 10 import 'asset_cascade.dart';
11 import 'asset_id.dart'; 11 import 'asset_id.dart';
12 import 'asset_node.dart'; 12 import 'asset_node.dart';
13 import 'asset_set.dart'; 13 import 'asset_set.dart';
14 import 'errors.dart'; 14 import 'errors.dart';
15 import 'stream_pool.dart';
15 import 'transform_node.dart'; 16 import 'transform_node.dart';
16 import 'transformer.dart'; 17 import 'transformer.dart';
17 import 'utils.dart'; 18 import 'utils.dart';
18 19
19 /// One phase in the ordered series of transformations in an [AssetCascade]. 20 /// One phase in the ordered series of transformations in an [AssetCascade].
20 /// 21 ///
21 /// Each phase can access outputs from previous phases and can in turn pass 22 /// Each phase can access outputs from previous phases and can in turn pass
22 /// outputs to later phases. Phases are processed strictly serially. All 23 /// outputs to later phases. Phases are processed strictly serially. All
23 /// transforms in a phase will be complete before moving on to the next phase. 24 /// transforms in a phase will be complete before moving on to the next phase.
24 /// Within a single phase, all transforms will be run in parallel. 25 /// Within a single phase, all transforms will be run in parallel.
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 /// New asset nodes that were added while [_adjustTransformers] was still 67 /// New asset nodes that were added while [_adjustTransformers] was still
67 /// being run on an old version of that asset. 68 /// being run on an old version of that asset.
68 var _pendingNewInputs = new Map<AssetId, AssetNode>(); 69 var _pendingNewInputs = new Map<AssetId, AssetNode>();
69 70
70 /// The ids of assets that are emitted by transforms in this phase. 71 /// The ids of assets that are emitted by transforms in this phase.
71 /// 72 ///
72 /// This is used to detect collisions where multiple transforms emit the same 73 /// This is used to detect collisions where multiple transforms emit the same
73 /// output. 74 /// output.
74 final _outputs = new Set<AssetId>(); 75 final _outputs = new Set<AssetId>();
75 76
77 /// A stream that emits an event whenever this phase becomes dirty and needs
78 /// to be run.
79 ///
80 /// This may emit events when the phase was already dirty or while processing
81 /// transforms. Events are emitted synchronously to ensure that the dirty
82 /// state is thoroughly propagated as soon as any assets are changed.
83 Stream get onDirty => _onDirtyPool.stream;
84 final _onDirtyPool = new StreamPool.broadcast();
85
86 /// A controller whose stream feeds into [_onDirtyPool].
87 ///
88 /// This is used whenever an input is added, changed, or removed. It's
89 /// sometimes redundant with the events collected from [_transforms], but this
90 /// stream is necessary for new and removed inputs, and the transform stream
91 /// is necessary for modified secondary inputs.
92 final _onDirtyController = new StreamController.broadcast(sync: true);
93
76 /// The phase after this one. 94 /// The phase after this one.
77 /// 95 ///
78 /// Outputs from this phase will be passed to it. 96 /// Outputs from this phase will be passed to it.
79 final Phase _next; 97 final Phase _next;
80 98
81 Phase(this.cascade, this._index, this._transformers, this._next); 99 Phase(this.cascade, this._index, this._transformers, this._next) {
100 _onDirtyPool.addStream(_onDirtyController.stream);
101 }
82 102
83 /// Adds a new asset as an input for this phase. 103 /// Adds a new asset as an input for this phase.
84 /// 104 ///
85 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase 105 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase
86 /// will automatically begin determining which transforms can consume it as a 106 /// will automatically begin determining which transforms can consume it as a
87 /// primary input. The transforms themselves won't be applied until [process] 107 /// primary input. The transforms themselves won't be applied until [process]
88 /// is called, however. 108 /// is called, however.
89 /// 109 ///
90 /// This should only be used for brand-new assets or assets that have been 110 /// This should only be used for brand-new assets or assets that have been
91 /// removed and re-created. The phase will automatically handle updated assets 111 /// removed and re-created. The phase will automatically handle updated assets
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 return inputs[id]; 181 return inputs[id];
162 } 182 }
163 183
164 /// Asynchronously determines which transformers can consume [node] as a 184 /// Asynchronously determines which transformers can consume [node] as a
165 /// primary input and creates transforms for them. 185 /// primary input and creates transforms for them.
166 /// 186 ///
167 /// This ensures that if [node] is modified or removed during or after the 187 /// This ensures that if [node] is modified or removed during or after the
168 /// time it takes to adjust its transformers, they're appropriately 188 /// time it takes to adjust its transformers, they're appropriately
169 /// re-adjusted. Its progress can be tracked in [_adjustTransformersFutures]. 189 /// re-adjusted. Its progress can be tracked in [_adjustTransformersFutures].
170 void _adjustTransformers(AssetNode node) { 190 void _adjustTransformers(AssetNode node) {
191 // Mark the phase as dirty. This may not actually end up creating any new
192 // transforms, but we want adding or removing a source asset to consistently
193 // kick off a build, even if that build does nothing.
Bob Nystrom 2013/08/01 18:10:33 Why?
nweiz 2013/08/01 20:30:52 API consistency. If a user calls [Barback.updateSo
194 _onDirtyController.add(null);
195
171 // Once the input is available, hook up transformers for it. If it changes 196 // Once the input is available, hook up transformers for it. If it changes
172 // while that's happening, try again. 197 // while that's happening, try again.
173 _adjustTransformersFutures[node.id] = node.tryUntilStable((asset) { 198 _adjustTransformersFutures[node.id] = node.tryUntilStable((asset) {
174 var oldTransformers = _transforms[node.id] 199 var oldTransformers = _transforms[node.id]
175 .map((transform) => transform.transformer).toSet(); 200 .map((transform) => transform.transformer).toSet();
176 201
177 return _removeStaleTransforms(asset) 202 return _removeStaleTransforms(asset)
178 .then((_) => _addFreshTransforms(node, oldTransformers)); 203 .then((_) => _addFreshTransforms(node, oldTransformers));
179 }).then((_) { 204 }).then((_) {
180 // Now all the transforms are set up correctly and the asset is available 205 // Now all the transforms are set up correctly and the asset is available
181 // for the time being. Set up handlers for when the asset changes in the 206 // for the time being. Set up handlers for when the asset changes in the
182 // future. 207 // future.
183 node.onStateChange.first.then((state) { 208 node.onStateChange.first.then((state) {
184 if (state.isRemoved) { 209 if (state.isRemoved) {
185 _transforms.remove(node.id); 210 _onDirtyController.add(null);
211 _removeTransforms(node.id);
186 } else { 212 } else {
187 _adjustTransformers(node); 213 _adjustTransformers(node);
188 } 214 }
189 }).catchError((e) { 215 }).catchError((e) {
190 _adjustTransformersFutures[node.id] = new Future.error(e); 216 _adjustTransformersFutures[node.id] = new Future.error(e);
191 }); 217 });
192 }).catchError((error) { 218 }).catchError((error) {
193 if (error is! AssetNotFoundException || error.id != node.id) throw error; 219 if (error is! AssetNotFoundException || error.id != node.id) throw error;
194 220
195 // If the asset is removed, [tryUntilStable] will throw an 221 // If the asset is removed, [tryUntilStable] will throw an
196 // [AssetNotFoundException]. In that case, just remove all transforms for 222 // [AssetNotFoundException]. In that case, just remove all transforms for
197 // the node. 223 // the node.
198 _transforms.remove(node.id); 224 _removeTransforms(node.id);
199 }).whenComplete(() { 225 }).whenComplete(() {
200 _adjustTransformersFutures.remove(node.id); 226 _adjustTransformersFutures.remove(node.id);
201 }); 227 });
202 228
203 // Don't top-level errors coming from the input processing. Any errors will 229 // Don't top-level errors coming from the input processing. Any errors will
204 // eventually be piped through [process]'s returned Future. 230 // eventually be piped through [process]'s returned Future.
205 _adjustTransformersFutures[node.id].catchError((_) {}); 231 _adjustTransformersFutures[node.id].catchError((_) {});
206 } 232 }
207 233
234 /// Remove all transforms for the asset identified by [id].
235 void _removeTransforms(AssetId id) {
236 for (var transform in _transforms.remove(id)) {
237 _onDirtyPool.removeStream(transform.onDirty);
238 }
239 }
240
208 // Remove any old transforms that used to have [asset] as a primary asset but 241 // Remove any old transforms that used to have [asset] as a primary asset but
209 // no longer apply to its new contents. 242 // no longer apply to its new contents.
210 Future _removeStaleTransforms(Asset asset) { 243 Future _removeStaleTransforms(Asset asset) {
211 return Future.wait(_transforms[asset.id].map((transform) { 244 return Future.wait(_transforms[asset.id].map((transform) {
212 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to 245 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to
213 // results. 246 // results.
214 return transform.transformer.isPrimary(asset).then((isPrimary) { 247 return transform.transformer.isPrimary(asset).then((isPrimary) {
215 if (isPrimary) return; 248 if (isPrimary) return;
216 _transforms[asset.id].remove(transform); 249 _transforms[asset.id].remove(transform);
250 _onDirtyPool.removeStream(transform.onDirty);
217 transform.remove(); 251 transform.remove();
218 }); 252 });
219 })); 253 }));
220 } 254 }
221 255
222 // Add new transforms for transformers that consider [node]'s asset to be a 256 // Add new transforms for transformers that consider [node]'s asset to be a
223 // primary input. 257 // primary input.
224 // 258 //
225 // [oldTransformers] is the set of transformers that had [node] as a primary 259 // [oldTransformers] is the set of transformers that had [node] as a primary
226 // input prior to this. They don't need to be checked, since they were removed 260 // input prior to this. They don't need to be checked, since they were removed
227 // or preserved in [_removeStaleTransforms]. 261 // or preserved in [_removeStaleTransforms].
228 Future _addFreshTransforms(AssetNode node, Set<Transformer> oldTransformers) { 262 Future _addFreshTransforms(AssetNode node, Set<Transformer> oldTransformers) {
229 return Future.wait(_transformers.map((transformer) { 263 return Future.wait(_transformers.map((transformer) {
230 if (oldTransformers.contains(transformer)) return new Future.value(); 264 if (oldTransformers.contains(transformer)) return new Future.value();
231 265
232 // If the asset is unavailable, the results of this [_adjustTransformers] 266 // If the asset is unavailable, the results of this [_adjustTransformers]
233 // run will be discarded, so we can just short-circuit. 267 // run will be discarded, so we can just short-circuit.
234 if (node.asset == null) return new Future.value(); 268 if (node.asset == null) return new Future.value();
235 269
236 // We can safely access [node.asset] here even though it might have 270 // We can safely access [node.asset] here even though it might have
237 // changed since (as above) if it has, [_adjustTransformers] will just be 271 // changed since (as above) if it has, [_adjustTransformers] will just be
238 // re-run. 272 // re-run.
239 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to 273 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to
240 // results. 274 // results.
241 return transformer.isPrimary(node.asset).then((isPrimary) { 275 return transformer.isPrimary(node.asset).then((isPrimary) {
242 if (!isPrimary) return; 276 if (!isPrimary) return;
243 _transforms[node.id].add(new TransformNode(this, transformer, node)); 277 var transform = new TransformNode(this, transformer, node);
278 _transforms[node.id].add(transform);
279 _onDirtyPool.addStream(transform.onDirty);
244 }); 280 });
245 })); 281 }));
246 } 282 }
247 283
248 /// Processes this phase. 284 /// Processes this phase.
249 /// 285 ///
250 /// Returns a future that completes when processing is done. If there is 286 /// Returns a future that completes when processing is done. If there is
251 /// nothing to process, returns `null`. 287 /// nothing to process, returns `null`.
252 Future process() { 288 Future process() {
253 if (_adjustTransformersFutures.isEmpty) return _processTransforms(); 289 if (_adjustTransformersFutures.isEmpty) return _processTransforms();
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 // Report collisions in a deterministic order. 323 // Report collisions in a deterministic order.
288 collisions = collisions.toList(); 324 collisions = collisions.toList();
289 collisions.sort((a, b) => a.toString().compareTo(b.toString())); 325 collisions.sort((a, b) => a.toString().compareTo(b.toString()));
290 for (var collision in collisions) { 326 for (var collision in collisions) {
291 cascade.reportError(new AssetCollisionException(collision)); 327 cascade.reportError(new AssetCollisionException(collision));
292 // TODO(rnystrom): Define what happens after a collision occurs. 328 // TODO(rnystrom): Define what happens after a collision occurs.
293 } 329 }
294 }); 330 });
295 } 331 }
296 } 332 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698