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

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

Issue 22854022: Remove the transformless phase from AssetCascade. (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
« no previous file with comments | « pkg/barback/lib/src/asset_cascade.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 16 matching lines...) Expand all
27 /// 27 ///
28 /// Building can be interrupted between phases. For example, a source is added 28 /// Building can be interrupted between phases. For example, a source is added
29 /// which starts the background process. Sometime during, say, phase 2 (which 29 /// which starts the background process. Sometime during, say, phase 2 (which
30 /// is running asynchronously) that source is modified. When the process queue 30 /// is running asynchronously) that source is modified. When the process queue
31 /// goes to advance to phase 3, it will see that modification and start the 31 /// goes to advance to phase 3, it will see that modification and start the
32 /// waterfall from the beginning again. 32 /// waterfall from the beginning again.
33 class Phase { 33 class Phase {
34 /// The cascade that owns this phase. 34 /// The cascade that owns this phase.
35 final AssetCascade cascade; 35 final AssetCascade cascade;
36 36
37 /// This phase's position relative to the other phases. Zero-based.
38 final int _index;
39
40 /// The transformers that can access [inputs]. 37 /// The transformers that can access [inputs].
41 /// 38 ///
42 /// Their outputs will be available to the next phase. 39 /// Their outputs will be available to the next phase.
43 final List<Transformer> _transformers; 40 final List<Transformer> _transformers;
44 41
45 /// The inputs that are available for transforms in this phase to consume. 42 /// The inputs that are available for transforms in this phase to consume.
46 /// 43 ///
47 /// For the first phase, these will be the source assets. For all other 44 /// For the first phase, these will be the source assets. For all other
48 /// phases, they will be the outputs from the previous phase. 45 /// phases, they will be the outputs from the previous phase.
49 final _inputs = new Map<AssetId, AssetNode>(); 46 final _inputs = new Map<AssetId, AssetNode>();
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 /// sometimes redundant with the events collected from [_transforms], but this 98 /// sometimes redundant with the events collected from [_transforms], but this
102 /// stream is necessary for new and removed inputs, and the transform stream 99 /// stream is necessary for new and removed inputs, and the transform stream
103 /// is necessary for modified secondary inputs. 100 /// is necessary for modified secondary inputs.
104 final _onDirtyController = new StreamController.broadcast(sync: true); 101 final _onDirtyController = new StreamController.broadcast(sync: true);
105 102
106 /// The phase after this one. 103 /// The phase after this one.
107 /// 104 ///
108 /// Outputs from this phase will be passed to it. 105 /// Outputs from this phase will be passed to it.
109 final Phase _next; 106 final Phase _next;
110 107
111 Phase(this.cascade, this._index, this._transformers, this._next) { 108 Phase(this.cascade, this._transformers, this._next) {
112 _onDirtyPool.add(_onDirtyController.stream); 109 _onDirtyPool.add(_onDirtyController.stream);
113 } 110 }
114 111
115 /// Adds a new asset as an input for this phase. 112 /// Adds a new asset as an input for this phase.
116 /// 113 ///
117 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase 114 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase
118 /// will automatically begin determining which transforms can consume it as a 115 /// will automatically begin determining which transforms can consume it as a
119 /// primary input. The transforms themselves won't be applied until [process] 116 /// primary input. The transforms themselves won't be applied until [process]
120 /// is called, however. 117 /// is called, however.
121 /// 118 ///
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 /// Gets the asset node for an input [id]. 170 /// Gets the asset node for an input [id].
174 /// 171 ///
175 /// If an input with that ID cannot be found, returns null. 172 /// If an input with that ID cannot be found, returns null.
176 Future<AssetNode> getInput(AssetId id) { 173 Future<AssetNode> getInput(AssetId id) {
177 return newFuture(() { 174 return newFuture(() {
178 if (id.package == cascade.package) return _inputs[id]; 175 if (id.package == cascade.package) return _inputs[id];
179 return cascade.graph.getAssetNode(id); 176 return cascade.graph.getAssetNode(id);
180 }); 177 });
181 } 178 }
182 179
180 /// Gets the asset node for an output [id].
181 ///
182 /// If an output with that ID cannot be found, returns null.
183 Future<AssetNode> getOutput(AssetId id) {
184 return newFuture(() {
185 if (id.package != cascade.package) return cascade.graph.getAssetNode(id);
186 if (!_outputs.containsKey(id)) return null;
187 return _outputs[id].first;
188 });
189 }
190
191 /// Returns all currently-available output assets for this phase.
192 AssetSet get availableOutputs {
193 return new AssetSet.from(_outputs.values
194 .map((queue) => queue.first)
195 .where((node) => node.state.isAvailable)
196 .map((node) => node.asset));
197 }
Bob Nystrom 2013/08/19 20:46:36 Nit, but I think we generally put getters above th
nweiz 2013/08/20 00:26:26 Done.
198
183 /// Asynchronously determines which transformers can consume [node] as a 199 /// Asynchronously determines which transformers can consume [node] as a
184 /// primary input and creates transforms for them. 200 /// primary input and creates transforms for them.
185 /// 201 ///
186 /// This ensures that if [node] is modified or removed during or after the 202 /// This ensures that if [node] is modified or removed during or after the
187 /// time it takes to adjust its transformers, they're appropriately 203 /// time it takes to adjust its transformers, they're appropriately
188 /// re-adjusted. Its progress can be tracked in [_adjustTransformersFutures]. 204 /// re-adjusted. Its progress can be tracked in [_adjustTransformersFutures].
189 void _adjustTransformers(AssetNode node) { 205 void _adjustTransformers(AssetNode node) {
190 // Mark the phase as dirty. This may not actually end up creating any new 206 // Mark the phase as dirty. This may not actually end up creating any new
191 // transforms, but we want adding or removing a source asset to consistently 207 // transforms, but we want adding or removing a source asset to consistently
192 // kick off a build, even if that build does nothing. 208 // kick off a build, even if that build does nothing.
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 } 332 }
317 333
318 Future _waitForInputs() { 334 Future _waitForInputs() {
319 if (_adjustTransformersFutures.isEmpty) return new Future.value(); 335 if (_adjustTransformersFutures.isEmpty) return new Future.value();
320 return Future.wait(_adjustTransformersFutures.values) 336 return Future.wait(_adjustTransformersFutures.values)
321 .then((_) => _waitForInputs()); 337 .then((_) => _waitForInputs());
322 } 338 }
323 339
324 /// Applies all currently wired up and dirty transforms. 340 /// Applies all currently wired up and dirty transforms.
325 Future _processTransforms() { 341 Future _processTransforms() {
326 if (_next == null) return;
327
328 var newPassThroughs = _passThroughControllers.values 342 var newPassThroughs = _passThroughControllers.values
329 .map((controller) => controller.node) 343 .map((controller) => controller.node)
330 .where((output) { 344 .where((output) {
331 return !_outputs.containsKey(output.id) || 345 return !_outputs.containsKey(output.id) ||
332 !_outputs[output.id].contains(output); 346 !_outputs[output.id].contains(output);
333 }).toSet(); 347 }).toSet();
334 348
335 // Convert this to a list so we can safely modify _transforms while 349 // Convert this to a list so we can safely modify _transforms while
336 // iterating over it. 350 // iterating over it.
337 var dirtyTransforms = 351 var dirtyTransforms =
338 flatten(_transforms.values.map((transforms) => transforms.toList())) 352 flatten(_transforms.values.map((transforms) => transforms.toList()))
339 .where((transform) => transform.isDirty).toList(); 353 .where((transform) => transform.isDirty).toList();
340 354
341 if (dirtyTransforms.isEmpty && newPassThroughs.isEmpty) return null; 355 if (dirtyTransforms.isEmpty && newPassThroughs.isEmpty) return null;
342 356
343 var collisions = _passAssetsThrough(newPassThroughs); 357 var collisions = new Set<AssetId>();
358 for (var output in newPassThroughs) {
359 if (_addOutput(output)) collisions.add(output.id);
360 }
361
344 return Future.wait(dirtyTransforms.map((transform) { 362 return Future.wait(dirtyTransforms.map((transform) {
345 return transform.apply().then((outputs) { 363 return transform.apply().then((outputs) {
346 for (var output in outputs) { 364 for (var output in outputs) {
347 if (_outputs.containsKey(output.id)) { 365 if (_addOutput(output)) collisions.add(output.id);
348 _outputs[output.id].add(output);
349 collisions.add(output.id);
350 } else {
351 _outputs[output.id] = new Queue<AssetNode>.from([output]);
352 _next.addInput(output);
353 }
354
355 _handleOutputRemoval(output);
356 } 366 }
357 }); 367 });
358 })).then((_) { 368 })).then((_) {
359 // Report collisions in a deterministic order. 369 // Report collisions in a deterministic order.
360 collisions = collisions.toList(); 370 collisions = collisions.toList();
361 collisions.sort((a, b) => a.compareTo(b)); 371 collisions.sort((a, b) => a.compareTo(b));
362 for (var collision in collisions) { 372 for (var collision in collisions) {
363 // Ensure that there's still a collision. It's possible it was resolved 373 // Ensure that there's still a collision. It's possible it was resolved
364 // while another transform was running. 374 // while another transform was running.
365 if (_outputs[collision].length <= 1) continue; 375 if (_outputs[collision].length <= 1) continue;
366 cascade.reportError(new AssetCollisionException( 376 cascade.reportError(new AssetCollisionException(
367 _outputs[collision].where((asset) => asset.transform != null) 377 _outputs[collision].where((asset) => asset.transform != null)
368 .map((asset) => asset.transform.info), 378 .map((asset) => asset.transform.info),
369 collision)); 379 collision));
370 } 380 }
371 }); 381 });
372 } 382 }
373 383
374 /// Pass all new assets that aren't consumed by transforms through to the next 384 /// Add [output] as an output of this phase, forwarding it to the next phase
375 /// phase. 385 /// if necessary.
376 /// 386 ///
377 /// Returns a set of asset ids that have collisions between new passed-through 387 /// Returns whether or not [output] collides with another pre-existing output.
378 /// assets and pre-existing transform outputs. 388 bool _addOutput(AssetNode output) {
379 Set<AssetId> _passAssetsThrough(Set<AssetId> newPassThroughs) { 389 _handleOutputRemoval(output);
380 var collisions = new Set<AssetId>();
381 for (var output in newPassThroughs) {
382 if (_outputs.containsKey(output.id)) {
383 // There shouldn't be another pass-through asset with the same id.
384 assert(!_outputs[output.id].any((asset) => asset.transform == null));
385 390
386 _outputs[output.id].add(output); 391 if (_outputs.containsKey(output.id)) {
387 collisions.add(output.id); 392 _outputs[output.id].add(output);
388 } else { 393 return true;
389 _outputs[output.id] = new Queue<AssetNode>.from([output]); 394 }
390 _next.addInput(output);
391 }
392 395
393 _handleOutputRemoval(output); 396 _outputs[output.id] = new Queue<AssetNode>.from([output]);
394 } 397 if (_next != null) _next.addInput(output);
395 return collisions; 398 return false;
396 } 399 }
397 400
398 /// Properly resolve collisions when [output] is removed. 401 /// Properly resolve collisions when [output] is removed.
399 void _handleOutputRemoval(AssetNode output) { 402 void _handleOutputRemoval(AssetNode output) {
400 output.whenRemoved.then((_) { 403 output.whenRemoved.then((_) {
401 var assets = _outputs[output.id]; 404 var assets = _outputs[output.id];
402 if (assets.length == 1) { 405 if (assets.length == 1) {
403 assert(assets.single == output); 406 assert(assets.single == output);
404 _outputs.remove(output.id); 407 _outputs.remove(output.id);
405 return; 408 return;
406 } 409 }
407 410
408 // If there was more than one asset, we're resolving a collision -- 411 // If there was more than one asset, we're resolving a collision --
409 // possibly partially. 412 // possibly partially.
410 var wasFirst = assets.first == output; 413 var wasFirst = assets.first == output;
411 assets.remove(output); 414 assets.remove(output);
412 415
413 // If this was the first asset, we need to pass the next asset 416 // If this was the first asset, we need to pass the next asset
414 // (chronologically) to the next phase. Pump the event queue first to give 417 // (chronologically) to the next phase. Pump the event queue first to give
415 // [_next] a chance to handle the removal of its input before getting a 418 // [_next] a chance to handle the removal of its input before getting a
416 // new input. 419 // new input.
417 if (wasFirst) { 420 if (wasFirst && _next != null) {
418 newFuture(() => _next.addInput(assets.first)); 421 newFuture(() => _next.addInput(assets.first));
419 } 422 }
420 423
421 // If there's still a collision, report it. This lets the user know 424 // If there's still a collision, report it. This lets the user know
422 // if they've successfully resolved the collision or not. 425 // if they've successfully resolved the collision or not.
423 if (assets.length > 1) { 426 if (assets.length > 1) {
424 // Pump the event queue to ensure that the removal of the input triggers 427 // Pump the event queue to ensure that the removal of the input triggers
425 // a new build to which we can attach the error. 428 // a new build to which we can attach the error.
426 newFuture(() => cascade.reportError(new AssetCollisionException( 429 newFuture(() => cascade.reportError(new AssetCollisionException(
427 assets.where((asset) => asset.transform != null) 430 assets.where((asset) => asset.transform != null)
428 .map((asset) => asset.transform.info), 431 .map((asset) => asset.transform.info),
429 output.id))); 432 output.id)));
430 } 433 }
431 }); 434 });
432 } 435 }
433 } 436 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/asset_cascade.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698