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

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

Powered by Google App Engine
This is Rietveld 408576698