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

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

Issue 23311006: Add the ability to dynamically modify transforms to barback. (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 import 'dart:collection'; 8 import 'dart:collection';
9 9
10 import 'asset.dart'; 10 import 'asset.dart';
(...skipping 18 matching lines...) Expand all
29 /// is running asynchronously) that source is modified. When the process queue 29 /// is running asynchronously) that source is modified. When the process queue
30 /// goes to advance to phase 3, it will see that modification and start the 30 /// goes to advance to phase 3, it will see that modification and start the
31 /// waterfall from the beginning again. 31 /// waterfall from the beginning again.
32 class Phase { 32 class Phase {
33 /// The cascade that owns this phase. 33 /// The cascade that owns this phase.
34 final AssetCascade cascade; 34 final AssetCascade cascade;
35 35
36 /// The transformers that can access [inputs]. 36 /// The transformers that can access [inputs].
37 /// 37 ///
38 /// Their outputs will be available to the next phase. 38 /// Their outputs will be available to the next phase.
39 final List<Transformer> _transformers; 39 final Set<Transformer> _transformers;
40 40
41 /// The inputs that are available for transforms in this phase to consume. 41 /// The inputs that are available for transforms in this phase to consume.
42 /// 42 ///
43 /// For the first phase, these will be the source assets. For all other 43 /// For the first phase, these will be the source assets. For all other
44 /// phases, they will be the outputs from the previous phase. 44 /// phases, they will be the outputs from the previous phase.
45 final _inputs = new Map<AssetId, AssetNode>(); 45 final _inputs = new Map<AssetId, AssetNode>();
46 46
47 /// The transforms currently applicable to assets in [inputs], indexed by 47 /// The transforms currently applicable to assets in [inputs], indexed by
48 /// the ids of their primary inputs. 48 /// the ids of their primary inputs.
49 /// 49 ///
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 /// 95 ///
96 /// This is used whenever an input is added, changed, or removed. It's 96 /// This is used whenever an input is added, changed, or removed. It's
97 /// sometimes redundant with the events collected from [_transforms], but this 97 /// sometimes redundant with the events collected from [_transforms], but this
98 /// stream is necessary for new and removed inputs, and the transform stream 98 /// stream is necessary for new and removed inputs, and the transform stream
99 /// is necessary for modified secondary inputs. 99 /// is necessary for modified secondary inputs.
100 final _onDirtyController = new StreamController.broadcast(sync: true); 100 final _onDirtyController = new StreamController.broadcast(sync: true);
101 101
102 /// The phase after this one. 102 /// The phase after this one.
103 /// 103 ///
104 /// Outputs from this phase will be passed to it. 104 /// Outputs from this phase will be passed to it.
105 final Phase _next; 105 Phase get next => _next;
106 Phase _next;
106 107
107 /// Returns all currently-available output assets for this phase. 108 /// Returns all currently-available output assets for this phase.
108 AssetSet get availableOutputs { 109 AssetSet get availableOutputs {
109 return new AssetSet.from(_outputs.values 110 return new AssetSet.from(_outputs.values
110 .map((queue) => queue.first) 111 .map((queue) => queue.first)
111 .where((node) => node.state.isAvailable) 112 .where((node) => node.state.isAvailable)
112 .map((node) => node.asset)); 113 .map((node) => node.asset));
113 } 114 }
114 115
115 Phase(this.cascade, this._transformers, this._next) { 116 Phase(this.cascade, Iterable<Transformer> transformers)
117 : _transformers = transformers.toSet() {
116 _onDirtyPool.add(_onDirtyController.stream); 118 _onDirtyPool.add(_onDirtyController.stream);
117 } 119 }
118 120
119 /// Adds a new asset as an input for this phase. 121 /// Adds a new asset as an input for this phase.
120 /// 122 ///
121 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase 123 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase
122 /// will automatically begin determining which transforms can consume it as a 124 /// will automatically begin determining which transforms can consume it as a
123 /// primary input. The transforms themselves won't be applied until [process] 125 /// primary input. The transforms themselves won't be applied until [process]
124 /// is called, however. 126 /// is called, however.
125 /// 127 ///
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
188 /// 190 ///
189 /// If an output with that ID cannot be found, returns null. 191 /// If an output with that ID cannot be found, returns null.
190 Future<AssetNode> getOutput(AssetId id) { 192 Future<AssetNode> getOutput(AssetId id) {
191 return newFuture(() { 193 return newFuture(() {
192 if (id.package != cascade.package) return cascade.graph.getAssetNode(id); 194 if (id.package != cascade.package) return cascade.graph.getAssetNode(id);
193 if (!_outputs.containsKey(id)) return null; 195 if (!_outputs.containsKey(id)) return null;
194 return _outputs[id].first; 196 return _outputs[id].first;
195 }); 197 });
196 } 198 }
197 199
200 /// Set this phase's transformers to [transformers].
201 void updateTransformers(Iterable<Transformer> transformers) {
202 _onDirtyController.add(null);
203
204 var newTransformers = transformers.toSet();
205 var oldTransformers = _transformers.toSet();
206 for (var removedTransformer in
207 oldTransformers.difference(newTransformers)) {
208 _transformers.remove(removedTransformer);
209
210 // Remove old transforms for which [removedTransformer] was a transformer.
211 for (var id in _inputs.keys) {
212 // If the transformers are being adjusted for [id], it will
213 // automatically pick up on [removedTransformer] being gone.
214 if (_adjustTransformersFutures.containsKey(id)) continue;
215
216 _transforms[id].removeWhere((transform) {
217 if (transform.transformer != removedTransformer) return false;
218 transform.remove();
219 return true;
220 });
221
222 if (!_transforms[id].isEmpty) continue;
223 _passThroughControllers.putIfAbsent(id, () {
224 return new AssetNodeController.available(
225 _inputs[id].asset, _inputs[id].transform);
226 });
227 }
228 }
229
230 var brandNewTransformers = newTransformers.difference(oldTransformers);
231 if (brandNewTransformers.isEmpty) return;
232 brandNewTransformers.forEach(_transformers.add);
233
234 // If there are any new transformers, start re-adjusting the transforms for
235 // all inputs so we pick up which inputs the new transformers apply to.
236 _inputs.forEach((id, node) {
237 if (_adjustTransformersFutures.containsKey(id)) return;
238 _adjustTransformers(node);
239 });
240 }
241
242 /// Add a new phase after this one with [transformers].
243 ///
244 /// This may only be called on a phase with no phase following it.
245 Phase addPhase(Iterable<Transformer> transformers) {
246 assert(_next == null);
247 _next = new Phase(cascade, transformers);
248 for (var output in _outputs.values.map((queue) => queue.first)) {
Bob Nystrom 2013/08/20 19:08:54 The map here seems a bit gratuitous. How about jus
nweiz 2013/08/20 21:39:17 Done.
249 _next.addInput(output);
250 }
251 return _next;
252 }
253
198 /// Asynchronously determines which transformers can consume [node] as a 254 /// Asynchronously determines which transformers can consume [node] as a
199 /// primary input and creates transforms for them. 255 /// primary input and creates transforms for them.
200 /// 256 ///
201 /// This ensures that if [node] is modified or removed during or after the 257 /// This ensures that if [node] is modified or removed during or after the
202 /// time it takes to adjust its transformers, they're appropriately 258 /// time it takes to adjust its transformers, they're appropriately
203 /// re-adjusted. Its progress can be tracked in [_adjustTransformersFutures]. 259 /// re-adjusted. Its progress can be tracked in [_adjustTransformersFutures].
204 void _adjustTransformers(AssetNode node) { 260 void _adjustTransformers(AssetNode node) {
205 // Mark the phase as dirty. This may not actually end up creating any new 261 // Mark the phase as dirty. This may not actually end up creating any new
206 // transforms, but we want adding or removing a source asset to consistently 262 // transforms, but we want adding or removing a source asset to consistently
207 // kick off a build, even if that build does nothing. 263 // kick off a build, even if that build does nothing.
208 _onDirtyController.add(null); 264 _onDirtyController.add(null);
209 265
210 // If there's a pass-through for this node, mark it dirty while we figure 266 // If there's a pass-through for this node, mark it dirty while we figure
211 // out whether we need to add any transforms for it. 267 // out whether we need to add any transforms for it.
212 var controller = _passThroughControllers[node.id]; 268 var controller = _passThroughControllers[node.id];
213 if (controller != null) controller.setDirty(); 269 if (controller != null) controller.setDirty();
214 270
215 // Once the input is available, hook up transformers for it. If it changes 271 // Once the input is available, hook up transformers for it. If it changes
216 // while that's happening, try again. 272 // while that's happening, try again.
217 _adjustTransformersFutures[node.id] = node.tryUntilStable((asset) { 273 _adjustTransformersFutures[node.id] = _tryUntilStable(node,
274 (asset, transformers) {
218 var oldTransformers = _transforms[node.id] 275 var oldTransformers = _transforms[node.id]
219 .map((transform) => transform.transformer).toSet(); 276 .map((transform) => transform.transformer).toSet();
220 277
221 return _removeStaleTransforms(asset) 278 return _removeStaleTransforms(asset, transformers).then((_) =>
222 .then((_) => _addFreshTransforms(node, oldTransformers)); 279 _addFreshTransforms(node, transformers, oldTransformers));
223 }).then((_) { 280 }).then((_) {
224 _adjustPassThrough(node); 281 _adjustPassThrough(node);
225 282
226 // Now all the transforms are set up correctly and the asset is available 283 // Now all the transforms are set up correctly and the asset is available
227 // for the time being. Set up handlers for when the asset changes in the 284 // for the time being. Set up handlers for when the asset changes in the
228 // future. 285 // future.
229 node.onStateChange.first.then((state) { 286 node.onStateChange.first.then((state) {
230 if (state.isRemoved) { 287 if (state.isRemoved) {
231 _onDirtyController.add(null); 288 _onDirtyController.add(null);
232 _transforms.remove(node.id); 289 _transforms.remove(node.id);
(...skipping 18 matching lines...) Expand all
251 _adjustTransformersFutures.remove(node.id); 308 _adjustTransformersFutures.remove(node.id);
252 }); 309 });
253 310
254 // Don't top-level errors coming from the input processing. Any errors will 311 // Don't top-level errors coming from the input processing. Any errors will
255 // eventually be piped through [process]'s returned Future. 312 // eventually be piped through [process]'s returned Future.
256 _adjustTransformersFutures[node.id].catchError((_) {}); 313 _adjustTransformersFutures[node.id].catchError((_) {});
257 } 314 }
258 315
259 // Remove any old transforms that used to have [asset] as a primary asset but 316 // Remove any old transforms that used to have [asset] as a primary asset but
260 // no longer apply to its new contents. 317 // no longer apply to its new contents.
261 Future _removeStaleTransforms(Asset asset) { 318 Future _removeStaleTransforms(Asset asset, Set<Transformer> transformers) {
262 return Future.wait(_transforms[asset.id].map((transform) { 319 return Future.wait(_transforms[asset.id].map((transform) {
263 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to 320 return newFuture(() {
264 // results. 321 if (!transformers.contains(transform.transformer)) return false;
265 return transform.transformer.isPrimary(asset).then((isPrimary) { 322
323 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to
324 // results.
325 return transform.transformer.isPrimary(asset);
326 }).then((isPrimary) {
266 if (isPrimary) return; 327 if (isPrimary) return;
267 _transforms[asset.id].remove(transform); 328 _transforms[asset.id].remove(transform);
268 _onDirtyPool.remove(transform.onDirty); 329 _onDirtyPool.remove(transform.onDirty);
269 transform.remove(); 330 transform.remove();
270 }); 331 });
271 })); 332 }));
272 } 333 }
273 334
274 // Add new transforms for transformers that consider [node]'s asset to be a 335 // Add new transforms for transformers that consider [node]'s asset to be a
275 // primary input. 336 // primary input.
276 // 337 //
277 // [oldTransformers] is the set of transformers that had [node] as a primary 338 // [oldTransformers] is the set of transformers for which there were
278 // input prior to this. They don't need to be checked, since they were removed 339 // transforms that had [node] as a primary input prior to this. They don't
279 // or preserved in [_removeStaleTransforms]. 340 // need to be checked, since their transforms were removed or preserved in
280 Future _addFreshTransforms(AssetNode node, Set<Transformer> oldTransformers) { 341 // [_removeStaleTransforms].
281 return Future.wait(_transformers.map((transformer) { 342 Future _addFreshTransforms(AssetNode node, Set<Transformer> transformers,
343 Set<Transformer> oldTransformers) {
344 return Future.wait(transformers.map((transformer) {
282 if (oldTransformers.contains(transformer)) return new Future.value(); 345 if (oldTransformers.contains(transformer)) return new Future.value();
283 346
284 // If the asset is unavailable, the results of this [_adjustTransformers] 347 // If the asset is unavailable, the results of this [_adjustTransformers]
285 // run will be discarded, so we can just short-circuit. 348 // run will be discarded, so we can just short-circuit.
286 if (node.asset == null) return new Future.value(); 349 if (node.asset == null) return new Future.value();
287 350
288 // We can safely access [node.asset] here even though it might have 351 // We can safely access [node.asset] here even though it might have
289 // changed since (as above) if it has, [_adjustTransformers] will just be 352 // changed since (as above) if it has, [_adjustTransformers] will just be
290 // re-run. 353 // re-run.
291 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to 354 // TODO(rnystrom): Catch all errors from isPrimary() and redirect to
(...skipping 22 matching lines...) Expand all
314 } else { 377 } else {
315 _passThroughControllers[node.id] = 378 _passThroughControllers[node.id] =
316 new AssetNodeController.available(node.asset, node.transform); 379 new AssetNodeController.available(node.asset, node.transform);
317 } 380 }
318 } else { 381 } else {
319 var controller = _passThroughControllers.remove(node.id); 382 var controller = _passThroughControllers.remove(node.id);
320 if (controller != null) controller.setRemoved(); 383 if (controller != null) controller.setRemoved();
321 } 384 }
322 } 385 }
323 386
387 /// Like [AssetNode.tryUntilStable], but also re-runs [callback] if this
388 /// phase's transformers are modified.
389 Future _tryUntilStable(AssetNode node,
390 Future callback(Asset asset, Set<Transformer> transformers)) {
391 var oldTransformers;
392 return node.tryUntilStable((asset) {
393 oldTransformers = _transformers.toSet();
394 return callback(asset, _transformers);
395 }).then((result) {
396 if (setEquals(oldTransformers, _transformers)) return result;
397 return _tryUntilStable(node, callback);
398 });
399 }
400
324 /// Processes this phase. 401 /// Processes this phase.
325 /// 402 ///
326 /// Returns a future that completes when processing is done. If there is 403 /// Returns a future that completes when processing is done. If there is
327 /// nothing to process, returns `null`. 404 /// nothing to process, returns `null`.
328 Future process() { 405 Future process() {
329 if (_adjustTransformersFutures.isEmpty) return _processTransforms(); 406 if (_adjustTransformersFutures.isEmpty) return _processTransforms();
330 return _waitForInputs().then((_) => _processTransforms()); 407 return _waitForInputs().then((_) => _processTransforms());
331 } 408 }
332 409
333 Future _waitForInputs() { 410 Future _waitForInputs() {
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
426 // Pump the event queue to ensure that the removal of the input triggers 503 // Pump the event queue to ensure that the removal of the input triggers
427 // a new build to which we can attach the error. 504 // a new build to which we can attach the error.
428 newFuture(() => cascade.reportError(new AssetCollisionException( 505 newFuture(() => cascade.reportError(new AssetCollisionException(
429 assets.where((asset) => asset.transform != null) 506 assets.where((asset) => asset.transform != null)
430 .map((asset) => asset.transform.info), 507 .map((asset) => asset.transform.info),
431 output.id))); 508 output.id)));
432 } 509 }
433 }); 510 });
434 } 511 }
435 } 512 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698