OLD | NEW |
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 pub.load_all_transformers; | 1 library pub.load_all_transformers; |
6 | |
7 import 'dart:async'; | 2 import 'dart:async'; |
8 | |
9 import 'package:barback/barback.dart'; | 3 import 'package:barback/barback.dart'; |
10 | |
11 import '../log.dart' as log; | 4 import '../log.dart' as log; |
12 import '../package_graph.dart'; | 5 import '../package_graph.dart'; |
13 import '../utils.dart'; | 6 import '../utils.dart'; |
14 import 'asset_environment.dart'; | 7 import 'asset_environment.dart'; |
15 import 'barback_server.dart'; | 8 import 'barback_server.dart'; |
16 import 'dart2js_transformer.dart'; | 9 import 'dart2js_transformer.dart'; |
17 import 'excluding_transformer.dart'; | 10 import 'excluding_transformer.dart'; |
18 import 'rewrite_import_transformer.dart'; | 11 import 'rewrite_import_transformer.dart'; |
19 import 'transformer_config.dart'; | 12 import 'transformer_config.dart'; |
20 import 'transformer_id.dart'; | 13 import 'transformer_id.dart'; |
21 import 'transformer_isolate.dart'; | 14 import 'transformer_isolate.dart'; |
22 import 'transformers_needed_by_transformers.dart'; | 15 import 'transformers_needed_by_transformers.dart'; |
23 | |
24 /// Loads all transformers depended on by packages in [environment]. | |
25 /// | |
26 /// This uses [environment]'s primary server to serve the Dart files from which | |
27 /// transformers are loaded, then adds the transformers to | |
28 /// `environment.barback`. | |
29 /// | |
30 /// Any built-in transformers that are provided by the environment will | |
31 /// automatically be added to the end of the root package's cascade. | |
32 Future loadAllTransformers(AssetEnvironment environment, | 16 Future loadAllTransformers(AssetEnvironment environment, |
33 BarbackServer transformerServer) { | 17 BarbackServer transformerServer) { |
34 var transformersNeededByTransformers = | 18 var transformersNeededByTransformers = |
35 computeTransformersNeededByTransformers(environment.graph); | 19 computeTransformersNeededByTransformers(environment.graph); |
36 | |
37 var buffer = new StringBuffer(); | 20 var buffer = new StringBuffer(); |
38 buffer.writeln("Transformer dependencies:"); | 21 buffer.writeln("Transformer dependencies:"); |
39 transformersNeededByTransformers.forEach((id, dependencies) { | 22 transformersNeededByTransformers.forEach((id, dependencies) { |
40 if (dependencies.isEmpty) { | 23 if (dependencies.isEmpty) { |
41 buffer.writeln("$id: -"); | 24 buffer.writeln("$id: -"); |
42 } else { | 25 } else { |
43 buffer.writeln("$id: ${toSentence(dependencies)}"); | 26 buffer.writeln("$id: ${toSentence(dependencies)}"); |
44 } | 27 } |
45 }); | 28 }); |
46 log.fine(buffer); | 29 log.fine(buffer); |
47 | |
48 var phasedTransformers = _phaseTransformers(transformersNeededByTransformers); | 30 var phasedTransformers = _phaseTransformers(transformersNeededByTransformers); |
49 | |
50 var packagesThatUseTransformers = | 31 var packagesThatUseTransformers = |
51 _packagesThatUseTransformers(environment.graph); | 32 _packagesThatUseTransformers(environment.graph); |
52 | |
53 var loader = new _TransformerLoader(environment, transformerServer); | 33 var loader = new _TransformerLoader(environment, transformerServer); |
54 | |
55 // Add a rewrite transformer for each package, so that we can resolve | |
56 // "package:" imports while loading transformers. | |
57 var rewrite = new RewriteImportTransformer(); | 34 var rewrite = new RewriteImportTransformer(); |
58 for (var package in environment.packages) { | 35 for (var package in environment.packages) { |
59 environment.barback.updateTransformers(package, [[rewrite]]); | 36 environment.barback.updateTransformers(package, [[rewrite]]); |
60 } | 37 } |
61 environment.barback.updateTransformers(r'$pub', [[rewrite]]); | 38 environment.barback.updateTransformers(r'$pub', [[rewrite]]); |
62 | |
63 return Future.forEach(phasedTransformers, (phase) { | 39 return Future.forEach(phasedTransformers, (phase) { |
64 /// Load all the transformers in [phase], then add them to the appropriate | |
65 /// locations in the transformer graphs of the packages that use them. | |
66 return loader.load(phase).then((_) { | 40 return loader.load(phase).then((_) { |
67 // Only update packages that use transformers in [phase]. | 41 var packagesToUpdate = |
68 var packagesToUpdate = unionAll(phase.map((id) => | 42 unionAll(phase.map((id) => packagesThatUseTransformers[id])); |
69 packagesThatUseTransformers[id])); | |
70 return Future.wait(packagesToUpdate.map((packageName) { | 43 return Future.wait(packagesToUpdate.map((packageName) { |
71 var package = environment.graph.packages[packageName]; | 44 var package = environment.graph.packages[packageName]; |
72 return loader.transformersForPhases(package.pubspec.transformers) | 45 return loader.transformersForPhases( |
73 .then((phases) { | 46 package.pubspec.transformers).then((phases) { |
74 | |
75 // Make sure [rewrite] is still the first phase so that future | |
76 // transformers' "package:" imports will work. | |
77 phases.insert(0, new Set.from([rewrite])); | 47 phases.insert(0, new Set.from([rewrite])); |
78 environment.barback.updateTransformers(packageName, phases); | 48 environment.barback.updateTransformers(packageName, phases); |
79 }); | 49 }); |
80 })); | 50 })); |
81 }); | 51 }); |
82 }).then((_) { | 52 }).then((_) { |
83 /// Reset the transformers for each package to get rid of [rewrite], which | |
84 /// is no longer needed. | |
85 return Future.wait(environment.graph.packages.values.map((package) { | 53 return Future.wait(environment.graph.packages.values.map((package) { |
86 return loader.transformersForPhases(package.pubspec.transformers) | 54 return loader.transformersForPhases( |
87 .then((phases) { | 55 package.pubspec.transformers).then((phases) { |
88 var transformers = environment.getBuiltInTransformers(package); | 56 var transformers = environment.getBuiltInTransformers(package); |
89 if (transformers != null) phases.add(transformers); | 57 if (transformers != null) phases.add(transformers); |
90 | 58 newFuture( |
91 // TODO(nweiz): remove the [newFuture] here when issue 17305 is fixed. | 59 () => environment.barback.updateTransformers(package.name, phases)); |
92 // If no transformer in [phases] applies to a source input, | |
93 // [updateTransformers] may cause a [BuildResult] to be scheduled for | |
94 // immediate emission. Issue 17305 means that the caller will be unable | |
95 // to receive this result unless we delay the update to after this | |
96 // function returns. | |
97 newFuture(() => | |
98 environment.barback.updateTransformers(package.name, phases)); | |
99 }); | 60 }); |
100 })); | 61 })); |
101 }); | 62 }); |
102 } | 63 } |
103 | 64 List<Set<TransformerId>> _phaseTransformers(Map<TransformerId, |
104 /// Given [transformerDependencies], a directed acyclic graph, returns a list of | 65 Set<TransformerId>> transformerDependencies) { |
105 /// "phases" (sets of transformers). | |
106 /// | |
107 /// Each phase must be fully loaded and passed to barback before the next phase | |
108 /// can be safely loaded. However, transformers within a phase can be safely | |
109 /// loaded in parallel. | |
110 List<Set<TransformerId>> _phaseTransformers( | |
111 Map<TransformerId, Set<TransformerId>> transformerDependencies) { | |
112 // A map from transformer ids to the indices of the phases that those | |
113 // transformer ids should end up in. Populated by [phaseNumberFor]. | |
114 var phaseNumbers = {}; | 66 var phaseNumbers = {}; |
115 var phases = []; | 67 var phases = []; |
116 | |
117 phaseNumberFor(id) { | 68 phaseNumberFor(id) { |
118 if (phaseNumbers.containsKey(id)) return phaseNumbers[id]; | 69 if (phaseNumbers.containsKey(id)) return phaseNumbers[id]; |
119 var dependencies = transformerDependencies[id]; | 70 var dependencies = transformerDependencies[id]; |
120 phaseNumbers[id] = dependencies.isEmpty ? 0 : | 71 phaseNumbers[id] = |
121 maxAll(dependencies.map(phaseNumberFor)) + 1; | 72 dependencies.isEmpty ? 0 : maxAll(dependencies.map(phaseNumberFor)) + 1; |
122 return phaseNumbers[id]; | 73 return phaseNumbers[id]; |
123 } | 74 } |
124 | |
125 for (var id in transformerDependencies.keys) { | 75 for (var id in transformerDependencies.keys) { |
126 var phaseNumber = phaseNumberFor(id); | 76 var phaseNumber = phaseNumberFor(id); |
127 if (phases.length <= phaseNumber) phases.length = phaseNumber + 1; | 77 if (phases.length <= phaseNumber) phases.length = phaseNumber + 1; |
128 if (phases[phaseNumber] == null) phases[phaseNumber] = new Set(); | 78 if (phases[phaseNumber] == null) phases[phaseNumber] = new Set(); |
129 phases[phaseNumber].add(id); | 79 phases[phaseNumber].add(id); |
130 } | 80 } |
131 | |
132 return phases; | 81 return phases; |
133 } | 82 } |
134 | 83 Map<TransformerId, Set<String>> _packagesThatUseTransformers(PackageGraph graph) |
135 /// Returns a map from transformer ids to all packages in [graph] that use each | 84 { |
136 /// transformer. | |
137 Map<TransformerId, Set<String>> _packagesThatUseTransformers( | |
138 PackageGraph graph) { | |
139 var results = {}; | 85 var results = {}; |
140 for (var package in graph.packages.values) { | 86 for (var package in graph.packages.values) { |
141 for (var phase in package.pubspec.transformers) { | 87 for (var phase in package.pubspec.transformers) { |
142 for (var config in phase) { | 88 for (var config in phase) { |
143 results.putIfAbsent(config.id, () => new Set()).add(package.name); | 89 results.putIfAbsent(config.id, () => new Set()).add(package.name); |
144 } | 90 } |
145 } | 91 } |
146 } | 92 } |
147 return results; | 93 return results; |
148 } | 94 } |
149 | |
150 /// A class that loads transformers defined in specific files. | |
151 class _TransformerLoader { | 95 class _TransformerLoader { |
152 final AssetEnvironment _environment; | 96 final AssetEnvironment _environment; |
153 | |
154 final BarbackServer _transformerServer; | 97 final BarbackServer _transformerServer; |
155 | |
156 final _isolates = new Map<TransformerId, TransformerIsolate>(); | 98 final _isolates = new Map<TransformerId, TransformerIsolate>(); |
157 | |
158 final _transformers = new Map<TransformerConfig, Set<Transformer>>(); | 99 final _transformers = new Map<TransformerConfig, Set<Transformer>>(); |
159 | |
160 /// The packages that use each transformer id. | |
161 /// | |
162 /// Used for error reporting. | |
163 final _transformerUsers = new Map<TransformerId, Set<String>>(); | 100 final _transformerUsers = new Map<TransformerId, Set<String>>(); |
164 | |
165 _TransformerLoader(this._environment, this._transformerServer) { | 101 _TransformerLoader(this._environment, this._transformerServer) { |
166 for (var package in _environment.graph.packages.values) { | 102 for (var package in _environment.graph.packages.values) { |
167 for (var config in unionAll(package.pubspec.transformers)) { | 103 for (var config in unionAll(package.pubspec.transformers)) { |
168 _transformerUsers.putIfAbsent(config.id, () => new Set<String>()) | 104 _transformerUsers.putIfAbsent( |
169 .add(package.name); | 105 config.id, |
| 106 () => new Set<String>()).add(package.name); |
170 } | 107 } |
171 } | 108 } |
172 } | 109 } |
173 | |
174 /// Loads a transformer plugin isolate that imports the transformer libraries | |
175 /// indicated by [ids]. | |
176 /// | |
177 /// Once the returned future completes, transformer instances from this | |
178 /// isolate can be created using [transformersFor] or [transformersForPhase]. | |
179 /// | |
180 /// This will skip any ids that have already been loaded. | |
181 Future load(Iterable<TransformerId> ids) { | 110 Future load(Iterable<TransformerId> ids) { |
182 ids = ids.where((id) => !_isolates.containsKey(id)).toList(); | 111 ids = ids.where((id) => !_isolates.containsKey(id)).toList(); |
183 if (ids.isEmpty) return new Future.value(); | 112 if (ids.isEmpty) return new Future.value(); |
184 | |
185 return log.progress("Loading ${toSentence(ids)} transformers", () { | 113 return log.progress("Loading ${toSentence(ids)} transformers", () { |
186 return TransformerIsolate.spawn(_environment, _transformerServer, ids); | 114 return TransformerIsolate.spawn(_environment, _transformerServer, ids); |
187 }).then((isolate) { | 115 }).then((isolate) { |
188 for (var id in ids) { | 116 for (var id in ids) { |
189 _isolates[id] = isolate; | 117 _isolates[id] = isolate; |
190 } | 118 } |
191 }); | 119 }); |
192 } | 120 } |
193 | |
194 /// Instantiates and returns all transformers in the library indicated by | |
195 /// [config] with the given configuration. | |
196 /// | |
197 /// If this is called before the library has been loaded into an isolate via | |
198 /// [load], it will return an empty set. | |
199 Future<Set<Transformer>> transformersFor(TransformerConfig config) { | 121 Future<Set<Transformer>> transformersFor(TransformerConfig config) { |
200 if (_transformers.containsKey(config)) { | 122 if (_transformers.containsKey(config)) { |
201 return new Future.value(_transformers[config]); | 123 return new Future.value(_transformers[config]); |
202 } else if (_isolates.containsKey(config.id)) { | 124 } else if (_isolates.containsKey(config.id)) { |
203 return _isolates[config.id].create(config).then((transformers) { | 125 return _isolates[config.id].create(config).then((transformers) { |
204 if (transformers.isNotEmpty) { | 126 if (transformers.isNotEmpty) { |
205 _transformers[config] = transformers; | 127 _transformers[config] = transformers; |
206 return transformers; | 128 return transformers; |
207 } | 129 } |
208 | |
209 var message = "No transformers"; | 130 var message = "No transformers"; |
210 if (config.configuration.isNotEmpty) { | 131 if (config.configuration.isNotEmpty) { |
211 message += " that accept configuration"; | 132 message += " that accept configuration"; |
212 } | 133 } |
213 | |
214 var location; | 134 var location; |
215 if (config.id.path == null) { | 135 if (config.id.path == null) { |
216 location = 'package:${config.id.package}/transformer.dart or ' | 136 location = |
217 'package:${config.id.package}/${config.id.package}.dart'; | 137 'package:${config.id.package}/transformer.dart or ' |
| 138 'package:${config.id.package}/${config.id.package}.dart'; |
218 } else { | 139 } else { |
219 location = 'package:$config.dart'; | 140 location = 'package:$config.dart'; |
220 } | 141 } |
221 | |
222 var users = toSentence(ordered(_transformerUsers[config.id])); | 142 var users = toSentence(ordered(_transformerUsers[config.id])); |
223 fail("$message were defined in $location,\n" | 143 fail("$message were defined in $location,\n" "required by $users."); |
224 "required by $users."); | |
225 }); | 144 }); |
226 } else if (config.id.package != '\$dart2js') { | 145 } else if (config.id.package != '\$dart2js') { |
227 return new Future.value(new Set()); | 146 return new Future.value(new Set()); |
228 } | 147 } |
229 | |
230 var transformer; | 148 var transformer; |
231 try { | 149 try { |
232 transformer = new Dart2JSTransformer.withSettings(_environment, | 150 transformer = new Dart2JSTransformer.withSettings( |
| 151 _environment, |
233 new BarbackSettings(config.configuration, _environment.mode)); | 152 new BarbackSettings(config.configuration, _environment.mode)); |
234 } on FormatException catch (error, stackTrace) { | 153 } on FormatException catch (error, stackTrace) { |
235 fail(error.message, error, stackTrace); | 154 fail(error.message, error, stackTrace); |
236 } | 155 } |
237 | 156 _transformers[config] = |
238 // Handle any exclusions. | 157 new Set.from([ExcludingTransformer.wrap(transformer, config)]); |
239 _transformers[config] = new Set.from( | |
240 [ExcludingTransformer.wrap(transformer, config)]); | |
241 return new Future.value(_transformers[config]); | 158 return new Future.value(_transformers[config]); |
242 } | 159 } |
243 | 160 Future<List<Set<Transformer>>> |
244 /// Loads all transformers defined in each phase of [phases]. | 161 transformersForPhases(Iterable<Set<TransformerConfig>> phases) { |
245 /// | |
246 /// If any library hasn't yet been loaded via [load], it will be ignored. | |
247 Future<List<Set<Transformer>>> transformersForPhases( | |
248 Iterable<Set<TransformerConfig>> phases) { | |
249 return Future.wait(phases.map((phase) { | 162 return Future.wait(phases.map((phase) { |
250 return waitAndPrintErrors(phase.map(transformersFor)).then(unionAll); | 163 return waitAndPrintErrors(phase.map(transformersFor)).then(unionAll); |
251 })).then((phases) { | 164 })).then((phases) { |
252 // Return a growable list so that callers can add phases. | |
253 return phases.toList(); | 165 return phases.toList(); |
254 }); | 166 }); |
255 } | 167 } |
256 } | 168 } |
OLD | NEW |