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

Side by Side Diff: pkg/barback/lib/src/asset_graph.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.asset_graph;
6
7 import 'dart:async';
8 import 'dart:collection';
9
10 import '../barback.dart';
11 import '../transformer.dart';
nweiz 2013/06/18 23:14:46 "src" files shouldn't be importing "lib" files. Th
Bob Nystrom 2013/06/20 00:23:59 Done.
12 import 'change_batch.dart';
13 import 'phase.dart';
14
15 /// The main build dependency manager. For any given input file, it can tell
16 /// which output files are affected by it, and vice versa.
17 class AssetGraph {
18 final AssetProvider _provider;
19
20 final _phases = <Phase>[];
21
22 Stream<ProcessResult> get results => _resultsController.stream;
23 final _resultsController = new StreamController<ProcessResult>.broadcast();
24
25 /// This holds a future that completes when the build process is complete if
26 /// if it is currently running. Otherwise, it is `null`.
27 Future _processDone;
28
29 ChangeBatch _sourceChanges;
30
31 /// Creates a new [AssetGraph].
32 ///
33 /// It loads source assets using [provider] and then uses [transformerPhases]
34 /// to generate output files from them.
35 //TODO(rnystrom): Better way of specifying transformers and their ordering.
36 AssetGraph(this._provider,
37 Iterable<Iterable<Transformer>> transformerPhases) {
38 // Add phases for each transformer stage.
39 for (var transformers in transformerPhases) {
40 var phase = new Phase(this, _phases.length, transformers.toList());
41 _phases.add(phase);
42 }
43
44 // Each phase writes its outputs as inputs to the next phase after it.
45 // Add a phase at the end for the final outputs of the last phase.
46 _phases.add(new Phase(this, _phases.length, []));
47
48 // Chain them together.
49 for (var i = 0; i < _phases.length - 1; i++){
50 _phases[i].next = _phases[i + 1];
51 }
52 }
53
54 /// Gets the asset identified by [id].
55 ///
56 /// If [id] is for a generated or transformed asset, this will wait until
57 /// it has been created and return it. If the asset cannot be found, throws
58 /// [AssetNotFoundException].
59 Future<Asset> getAssetById(AssetId id) {
60 return _waitForProcess().then((_) {
61 // Find the latest phase that output this asset.
62 for (var i = _phases.length - 1; i >= 0; i--) {
63 var node = _phases[i].inputs[id];
64 if (node != null) {
65 // By the time we get here, the asset should have been built.
66 assert(node.asset != null);
67 return node.asset;
68 }
69 }
70
71 // Couldn't find it.
72 var error = new AssetNotFoundException(id);
73 reportError(error);
74 throw error;
nweiz 2013/06/18 23:14:46 My thought here was that you could *just* throw wh
Bob Nystrom 2013/06/20 00:23:59 In most cases, though, we don't want to just unwin
75 });
76 }
77
78 /// Adds [sources] to the graph's known set of source assets. Begins
79 /// applying any transforms that can consume any of the sources. If a given
80 /// source is already known, it is considered modified and all transforms
81 /// that use it will be re-applied.
82 void updateSources(Iterable<AssetId> sources) {
83 if (_sourceChanges == null) _sourceChanges = new ChangeBatch();
84 _sourceChanges.update(sources);
85
86 _waitForProcess();
87 }
88
89 /// Removes [removed] from the graph's known set of source assets.
90 void removeSources(Iterable<AssetId> removed) {
91 if (_sourceChanges == null) _sourceChanges = new ChangeBatch();
92 _sourceChanges.remove(removed);
93
94 _waitForProcess();
95 }
96
97 /// Reports a process result with the given error then throws it.
98 void reportError(error) {
99 _resultsController.add(new ProcessResult(error));
100 }
101
102 /// Starts the build process asynchronously if there is work to be done.
103 ///
104 /// Returns a future that completes with the background processing is done.
105 /// If there is no work to do, returns a future that completes immediately.
106 /// All errors that occur during processing will be caught (and routed to the
107 /// [results] stream) before they get to the returned future, so it is safe
108 /// to discard it.
109 Future _waitForProcess() {
110 if (_processDone != null) return _processDone;
111 return _processDone = _process().whenComplete(() {
nweiz 2013/06/18 23:14:46 If you're confident this won't emit errors, why ar
Bob Nystrom 2013/06/20 00:23:59 Why not?
nweiz 2013/06/20 23:06:08 Because functionally it's identical to [then], but
Bob Nystrom 2013/06/21 00:13:20 Added a catchError() too here like you suggested.
112 _processDone = null;
113 });
114 }
115
116 /// Starts the background processing. Returns a future that completes when
117 /// all assets have been processed.
118 Future _process() {
119 return _processSourceChanges().then((_) {
120 // Find the first phase that has work to do and do it.
121 var future;
122 for (var phase in _phases) {
123 future = phase.process();
124 if (future != null) break;
125 }
126
127 // If all phases are done, so are we.
128 if (future == null) return;
129
130 // Process that phase and then loop onto the next.
131 return future.then((_) => _process());
132 });
133 }
134
135 /// Processes the current batch of changes to source assets.
136 Future _processSourceChanges() {
137 // Always pump the event loop. This ensures a bunch of synchronous source
138 // changes are processed in a single batch even when the first one starts
139 // the build process.
140 return new Future(() {
141 if (_sourceChanges == null) return null;
142
143 // Take the current batch to ensure it doesn't get added to while we're
144 // processing it.
145 var changes = _sourceChanges;
146 _sourceChanges = null;
147
148 var updated = new Map<AssetId, Asset>();
149 var futures = [];
150 for (var id in changes.updated) {
151 futures.add(_provider.getAsset(id).then((asset) {
152 updated[id] = asset;
153 }));
154 }
155
156 return Future.wait(futures).then((_) {
157 _phases.first.updateInputs(updated, changes.removed);
158 });
159 });
160 }
161 }
162
163 /// The build process runs asynchronously in the background. It reports back to
164 /// the user be emitting a [Stream] of these objects. Currently, it only emits
165 /// errors.
166 class ProcessResult {
167 /// The error that occurred.
168 final error;
169
170 ProcessResult(this.error);
171 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698