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

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

Issue 16854005: First pass at build dependency graph for barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Revise. Created 7 years, 6 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library barback.phase;
6
7 import 'dart:async';
8
9 import '../barback.dart';
10 import '../transformer.dart';
11 import 'asset_graph.dart';
12 import 'asset_node.dart';
13 import 'transform_node.dart';
14
15 /// The transforms in a processing graph are organized into a series of phases.
16 /// Each phase can access outputs from previous phases and can in turn pass
17 /// outputs to later phases.
18 ///
19 /// Phases are processed strictly serially. All transforms in a phase will be
20 /// complete before moving on to the next phase. Within a single phase, all
21 /// transforms will be run in parallel.
22 ///
23 /// Building can be interrupted between phases. For example, an source is added
nweiz 2013/06/18 23:14:46 "a source"
Bob Nystrom 2013/06/20 00:23:59 Done.
24 /// which starts the background process. Sometime during phase 2 (which is
nweiz 2013/06/18 23:14:46 It's not clear what "the background process" refer
Bob Nystrom 2013/06/20 00:23:59 Arbitrary number for the example.
25 /// running asynchronously) that source is modified. When the process queue
26 /// goes to advance to phase 3, it will see that modification and start the
27 /// waterfall from the beginning again.
28 class Phase {
29 /// The graph that owns this phase.
30 final AssetGraph graph;
31
32 /// This phase's position relative to the other phases. Zero-based.
33 final int index;
nweiz 2013/06/18 23:14:46 Is this actually used anywhere? It seems weird to
Bob Nystrom 2013/06/20 00:23:59 I end up using it for debug printing. I can remove
34
35 /// The transformers that can access [inputs]. Their outputs will be
36 /// available to the next phase.
37 final List<Transformer> transformers;
38
39 /// The inputs that are available for transforms in this phase to consume.
40 /// For the first phase, these will be the source assets. For all other
41 /// phases, they will be the outputs from the previous phase.
42 final inputs = new Map<AssetId, AssetNode>();
43
44 /// The transforms currently applicable to assets in [inputs]. These are the
45 /// transforms that have been "wired up": they represent a repeatable
46 /// transformation of a single concrete set of inputs. "dart2js" is a
47 /// transformer. "dart2js on web/main.dart" is a transform.
48 final transforms = new Set<TransformNode>();
49
50 /// The nodes that are new in this phase since the last time [process] was
51 /// called. When we process, we'll check these to see if we can hang new
52 /// transforms off them.
53 final newInputs = new Set<AssetNode>();
nweiz 2013/06/18 23:14:46 Seems like most of these fields should be private.
Bob Nystrom 2013/06/20 00:23:59 Done.
54
55 /// The phase after this one. Outputs from this phase will be passed to it.
56 Phase next;
nweiz 2013/06/18 23:14:46 It's weird that this is mutable. If you construct
Bob Nystrom 2013/06/20 00:23:59 Done.
57
58 Phase(this.graph, this.index, this.transformers);
59
60 /// Updates the phase's inputs with [updated] and removes [removed]. This
61 /// marks any affected [transforms] as dirty or discards them if their inputs
62 /// are removed.
63 void updateInputs(Map<AssetId, Asset> updated, Set<AssetId> removed) {
64 // Remove any nodes that are no longer being output. Handle removals first
65 // in case there are assets that were removed by one transform but updated
66 // by another. In that case, the update should win.
67 for (var id in removed) {
68 var node = inputs.remove(id);
69
70 // Every transform that was using it is dirty now.
71 if (node != null) {
72 node.consumers.forEach((consumer) => consumer.isDirty = true);
73 }
74 }
75
76 // Update and new or modified assets.
77 updated.forEach((id, asset) {
78 var node = inputs.putIfAbsent(id, () => new AssetNode(id));
79
80 // If it's a new node, remember that so we can see if any new transforms
81 // will consume it.
82 if (node.asset == null) newInputs.add(node);
83
84 node.updateAsset(asset);
85 });
86 }
87
88 /// Processes this phase. For all new inputs, it tries to see if there are
89 /// transformers that can consume them. Then all applicable transforms are
90 /// applied.
91 ///
92 /// Returns a future that completes when processing is done. If there is
93 /// nothing to process, returns `null`.
94 Future process() {
95 var future = _processNewInputs();
96 if (future == null) {
97 return _processTransforms();
98 }
99
100 return future.then((_) => _processTransforms());
101 }
102
103 /// Creates new transforms for any new inputs that are applicable.
104 Future _processNewInputs() {
105 if (newInputs.isEmpty) return null;
106
107 var futures = [];
108 for (var node in newInputs) {
109 for (var transformer in transformers) {
110 futures.add(transformer.isPrimary(node.id).then((isPrimary) {
111 if (!isPrimary) return;
112 var transform = new TransformNode(this, transformer, node);
113 node.consumers.add(transform);
114 transforms.add(transform);
115 }));
116 }
117 }
118
119 newInputs.clear();
120
121 return Future.wait(futures);
122 }
123
124 /// Applies all currently wired up and dirty transforms. Passes their outputs
125 /// to the next phase.
126 Future _processTransforms() {
127 var dirtyTransforms = transforms.where((transform) => transform.isDirty);
128 if (dirtyTransforms.isEmpty) return null;
129
130 return Future.wait(dirtyTransforms.map(
131 (transform) => transform.apply())).then((transformOutputs) {
nweiz 2013/06/18 23:14:46 I think this would be a little cleaner formatted l
Bob Nystrom 2013/06/20 00:23:59 Done.
132 // Collect all of the outputs. Since the transforms are run in parallel,
133 // we have to be careful here to ensure that the result is deterministic
134 // and not influenced by the order that transforms complete.
135 var updated = new Map<AssetId, Asset>();
136 var removed = new Set<AssetId>();
137 var collisions = new Set<AssetId>();
138
139 // Handle the generated outputs of all transforms first.
140 for (var outputs in transformOutputs) {
141 // Collect the outputs of all transformers together.
142 outputs.updated.forEach((id, asset) {
143 if (updated.containsKey(id)) {
144 // Report a collision.
145 collisions.add(id);
146 } else {
147 // TODO(rnystrom): In the case of a collision, the asset that
148 // "wins" is chosen non-deterministically. Do something better.
149 updated[id] = asset;
150 }
151 });
152
153 // Track any assets no longer output by this transform. We don't
154 // handle the case where *another* transform generates the asset
155 // no longer generated by this one. updateInputs() handles that.
156 removed.addAll(outputs.removed);
157 }
158
159 // Report any collisions in deterministic order.
160 collisions = collisions.toList();
161 collisions.sort((a, b) => a.toString().compareTo(b.toString()));
162 for (var collision in collisions) {
163 graph.reportError(new AssetCollisionException(collision));
164 // TODO(rnystrom): Define what happens after a collision occurs.
165 }
166
167 // Pass the outputs to the next phase.
168 next.updateInputs(updated, removed);
169 });
170 }
171 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698