OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 pub.barback.dependency_computer; | 5 library pub.barback.dependency_computer; |
6 | 6 |
7 import 'package:barback/barback.dart'; | 7 import 'package:barback/barback.dart'; |
8 import 'package:path/path.dart' as p; | 8 import 'package:path/path.dart' as p; |
9 | 9 |
10 import '../dart.dart'; | 10 import '../dart.dart'; |
(...skipping 26 matching lines...) Expand all Loading... |
37 final _transformersNeededByPackages = new Map<String, Set<TransformerId>>(); | 37 final _transformersNeededByPackages = new Map<String, Set<TransformerId>>(); |
38 | 38 |
39 /// The set of all packages that neither use transformers themselves nor | 39 /// The set of all packages that neither use transformers themselves nor |
40 /// import packages that use transformers. | 40 /// import packages that use transformers. |
41 /// | 41 /// |
42 /// This is precomputed before any package computers are loaded. | 42 /// This is precomputed before any package computers are loaded. |
43 final _untransformedPackages = new Set<String>(); | 43 final _untransformedPackages = new Set<String>(); |
44 | 44 |
45 DependencyComputer(this._graph) { | 45 DependencyComputer(this._graph) { |
46 for (var package in ordered(_graph.packages.keys)) { | 46 for (var package in ordered(_graph.packages.keys)) { |
47 if (_graph.transitiveDependencies(package).every((dependency) => | 47 if (_graph.transitiveDependencies( |
48 dependency.pubspec.transformers.isEmpty)) { | 48 package).every((dependency) => dependency.pubspec.transformers.isEmpty
)) { |
49 _untransformedPackages.add(package); | 49 _untransformedPackages.add(package); |
50 } | 50 } |
51 } | 51 } |
52 | 52 |
53 ordered(_graph.packages.keys).forEach(_loadPackageComputer); | 53 ordered(_graph.packages.keys).forEach(_loadPackageComputer); |
54 } | 54 } |
55 | 55 |
56 /// Returns a dependency graph for [transformers], or for all transformers if | 56 /// Returns a dependency graph for [transformers], or for all transformers if |
57 /// [transformers] is `null`. | 57 /// [transformers] is `null`. |
58 /// | 58 /// |
59 /// This graph is represented by a map whose keys are the vertices and whose | 59 /// This graph is represented by a map whose keys are the vertices and whose |
60 /// values are sets representing edges from the given vertex. Each vertex is a | 60 /// values are sets representing edges from the given vertex. Each vertex is a |
61 /// [TransformerId]. If there's an edge from `T1` to `T2`, then `T2` must be | 61 /// [TransformerId]. If there's an edge from `T1` to `T2`, then `T2` must be |
62 /// loaded before `T1` can be loaded. | 62 /// loaded before `T1` can be loaded. |
63 /// | 63 /// |
64 /// The returned graph is transitively closed. That is, if there's an edge | 64 /// The returned graph is transitively closed. That is, if there's an edge |
65 /// from `T1` to `T2` and an edge from `T2` to `T3`, there's also an edge from | 65 /// from `T1` to `T2` and an edge from `T2` to `T3`, there's also an edge from |
66 /// `T1` to `T2`. | 66 /// `T1` to `T2`. |
67 Map<TransformerId, Set<TransformerId>> transformersNeededByTransformers( | 67 Map<TransformerId, Set<TransformerId>> |
68 [Iterable<TransformerId> transformers]) { | 68 transformersNeededByTransformers([Iterable<TransformerId> transformers]) { |
69 var result = {}; | 69 var result = {}; |
70 | 70 |
71 if (transformers == null) { | 71 if (transformers == null) { |
72 transformers = ordered(_graph.packages.keys).expand((packageName) { | 72 transformers = ordered(_graph.packages.keys).expand((packageName) { |
73 var package = _graph.packages[packageName]; | 73 var package = _graph.packages[packageName]; |
74 return package.pubspec.transformers.expand((phase) { | 74 return package.pubspec.transformers.expand((phase) { |
75 return phase.expand((config) { | 75 return phase.expand((config) { |
76 var id = config.id; | 76 var id = config.id; |
77 if (id.isBuiltInTransformer) return []; | 77 if (id.isBuiltInTransformer) return []; |
78 if (id.package != _graph.entrypoint.root.name && | 78 if (id.package != _graph.entrypoint.root.name && |
(...skipping 10 matching lines...) Expand all Loading... |
89 result[id] = _transformersNeededByTransformer(id); | 89 result[id] = _transformersNeededByTransformer(id); |
90 } | 90 } |
91 return result; | 91 return result; |
92 } | 92 } |
93 | 93 |
94 /// Returns the set of all transformers needed to load the library identified | 94 /// Returns the set of all transformers needed to load the library identified |
95 /// by [id]. | 95 /// by [id]. |
96 Set<TransformerId> transformersNeededByLibrary(AssetId id) { | 96 Set<TransformerId> transformersNeededByLibrary(AssetId id) { |
97 var library = _graph.packages[id.package].path(p.fromUri(id.path)); | 97 var library = _graph.packages[id.package].path(p.fromUri(id.path)); |
98 _loadPackageComputer(id.package); | 98 _loadPackageComputer(id.package); |
99 return _packageComputers[id.package].transformersNeededByLibrary(library) | 99 return _packageComputers[id.package].transformersNeededByLibrary( |
100 .where((id) => !id.isBuiltInTransformer).toSet(); | 100 library).where((id) => !id.isBuiltInTransformer).toSet(); |
101 } | 101 } |
102 | 102 |
103 /// Returns the set of all transformers that need to be loaded before [id] is | 103 /// Returns the set of all transformers that need to be loaded before [id] is |
104 /// loaded. | 104 /// loaded. |
105 Set<TransformerId> _transformersNeededByTransformer(TransformerId id) { | 105 Set<TransformerId> _transformersNeededByTransformer(TransformerId id) { |
106 if (id.isBuiltInTransformer) return new Set(); | 106 if (id.isBuiltInTransformer) return new Set(); |
107 _loadPackageComputer(id.package); | 107 _loadPackageComputer(id.package); |
108 return _packageComputers[id.package]._transformersNeededByTransformer(id); | 108 return _packageComputers[id.package]._transformersNeededByTransformer(id); |
109 } | 109 } |
110 | 110 |
111 /// Returns the set of all transformers that need to be loaded before | 111 /// Returns the set of all transformers that need to be loaded before |
112 /// [packageUri] (a "package:" URI) can be safely imported from an external | 112 /// [packageUri] (a "package:" URI) can be safely imported from an external |
113 /// package. | 113 /// package. |
114 Set<TransformerId> _transformersNeededByPackageUri(Uri packageUri) { | 114 Set<TransformerId> _transformersNeededByPackageUri(Uri packageUri) { |
115 var components = p.split(p.fromUri(packageUri.path)); | 115 var components = p.split(p.fromUri(packageUri.path)); |
116 var packageName = components.first; | 116 var packageName = components.first; |
117 if (_untransformedPackages.contains(packageName)) return new Set(); | 117 if (_untransformedPackages.contains(packageName)) return new Set(); |
118 | 118 |
119 var package = _graph.packages[packageName]; | 119 var package = _graph.packages[packageName]; |
120 if (package == null) { | 120 if (package == null) { |
121 // TODO(nweiz): include source range information here. | 121 // TODO(nweiz): include source range information here. |
122 fail('A transformer imported unknown package "$packageName" (in ' | 122 fail( |
123 '"$packageUri").'); | 123 'A transformer imported unknown package "$packageName" (in ' '"$packag
eUri").'); |
124 } | 124 } |
125 | 125 |
126 var library = package.path('lib', p.joinAll(components.skip(1))); | 126 var library = package.path('lib', p.joinAll(components.skip(1))); |
127 | 127 |
128 _loadPackageComputer(packageName); | 128 _loadPackageComputer(packageName); |
129 return _packageComputers[packageName].transformersNeededByLibrary(library); | 129 return _packageComputers[packageName].transformersNeededByLibrary(library); |
130 } | 130 } |
131 | 131 |
132 /// Returns the set of all transformers that need to be loaded before | 132 /// Returns the set of all transformers that need to be loaded before |
133 /// everything in [rootPackage] can be used. | 133 /// everything in [rootPackage] can be used. |
(...skipping 27 matching lines...) Expand all Loading... |
161 var id = config.id; | 161 var id = config.id; |
162 if (id.isBuiltInTransformer) continue; | 162 if (id.isBuiltInTransformer) continue; |
163 if (_loadingPackageComputers.contains(id.package)) { | 163 if (_loadingPackageComputers.contains(id.package)) { |
164 throw new CycleException("$packageName is transformed by $id"); | 164 throw new CycleException("$packageName is transformed by $id"); |
165 } | 165 } |
166 results.add(id); | 166 results.add(id); |
167 } | 167 } |
168 } | 168 } |
169 | 169 |
170 var dependencies = packageName == _graph.entrypoint.root.name ? | 170 var dependencies = packageName == _graph.entrypoint.root.name ? |
171 package.immediateDependencies : package.dependencies; | 171 package.immediateDependencies : |
| 172 package.dependencies; |
172 for (var dep in dependencies) { | 173 for (var dep in dependencies) { |
173 try { | 174 try { |
174 traversePackage(dep.name); | 175 traversePackage(dep.name); |
175 } on CycleException catch (error) { | 176 } on CycleException catch (error) { |
176 throw error.prependStep("$packageName depends on ${dep.name}"); | 177 throw error.prependStep("$packageName depends on ${dep.name}"); |
177 } | 178 } |
178 } | 179 } |
179 } | 180 } |
180 | 181 |
181 traversePackage(rootPackage); | 182 traversePackage(rootPackage); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 /// A cache of the results of [_transformersNeededByTransformer]. | 233 /// A cache of the results of [_transformersNeededByTransformer]. |
233 final _transformersNeededByTransformers = | 234 final _transformersNeededByTransformers = |
234 new Map<TransformerId, Set<TransformerId>>(); | 235 new Map<TransformerId, Set<TransformerId>>(); |
235 | 236 |
236 /// A cache of the results of [_getTransitiveExternalDirectives]. | 237 /// A cache of the results of [_getTransitiveExternalDirectives]. |
237 /// | 238 /// |
238 /// This is invalidated whenever [_applicableTransformers] changes. | 239 /// This is invalidated whenever [_applicableTransformers] changes. |
239 final _transitiveExternalDirectives = new Map<String, Set<Uri>>(); | 240 final _transitiveExternalDirectives = new Map<String, Set<Uri>>(); |
240 | 241 |
241 _PackageDependencyComputer(DependencyComputer dependencyComputer, | 242 _PackageDependencyComputer(DependencyComputer dependencyComputer, |
242 String packageName) | 243 String packageName) |
243 : _dependencyComputer = dependencyComputer, | 244 : _dependencyComputer = dependencyComputer, |
244 _package = dependencyComputer._graph.packages[packageName] { | 245 _package = dependencyComputer._graph.packages[packageName] { |
245 // If [_package] uses its own transformers, there will be fewer transformers | 246 // If [_package] uses its own transformers, there will be fewer transformers |
246 // running on [_package] while its own transformers are loading than there | 247 // running on [_package] while its own transformers are loading than there |
247 // will be once all its transformers are finished loading. To handle this, | 248 // will be once all its transformers are finished loading. To handle this, |
248 // we run [_transformersNeededByTransformer] to pre-populate | 249 // we run [_transformersNeededByTransformer] to pre-populate |
249 // [_transformersNeededByLibraries] while [_applicableTransformers] is | 250 // [_transformersNeededByLibraries] while [_applicableTransformers] is |
250 // smaller. | 251 // smaller. |
251 for (var phase in _package.pubspec.transformers) { | 252 for (var phase in _package.pubspec.transformers) { |
252 for (var config in phase) { | 253 for (var config in phase) { |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 Set<TransformerId> transformersNeededByLibrary(String library) { | 305 Set<TransformerId> transformersNeededByLibrary(String library) { |
305 library = p.normalize(library); | 306 library = p.normalize(library); |
306 if (_activeLibraries.contains(library)) return new Set(); | 307 if (_activeLibraries.contains(library)) return new Set(); |
307 _activeLibraries.add(library); | 308 _activeLibraries.add(library); |
308 | 309 |
309 try { | 310 try { |
310 var externalDirectives = _getTransitiveExternalDirectives(library); | 311 var externalDirectives = _getTransitiveExternalDirectives(library); |
311 if (externalDirectives == null) { | 312 if (externalDirectives == null) { |
312 var rootName = _dependencyComputer._graph.entrypoint.root.name; | 313 var rootName = _dependencyComputer._graph.entrypoint.root.name; |
313 var dependencies = _package.name == rootName ? | 314 var dependencies = _package.name == rootName ? |
314 _package.immediateDependencies : _package.dependencies; | 315 _package.immediateDependencies : |
| 316 _package.dependencies; |
315 | 317 |
316 // If anything transitively imported/exported by [library] within this | 318 // If anything transitively imported/exported by [library] within this |
317 // package is modified by a transformer, we don't know what it will | 319 // package is modified by a transformer, we don't know what it will |
318 // load, so we take the conservative approach and say it depends on | 320 // load, so we take the conservative approach and say it depends on |
319 // everything. | 321 // everything. |
320 return _applicableTransformers.map((config) => config.id).toSet().union( | 322 return _applicableTransformers.map( |
321 unionAll(dependencies.map((dep) { | 323 (config) => config.id).toSet().union(unionAll(dependencies.map((dep)
{ |
322 try { | 324 try { |
323 return _dependencyComputer._transformersNeededByPackage(dep.name); | 325 return _dependencyComputer._transformersNeededByPackage(dep.name); |
324 } on CycleException catch (error) { | 326 } on CycleException catch (error) { |
325 throw error.prependStep("${_package.name} depends on ${dep.name}"); | 327 throw error.prependStep("${_package.name} depends on ${dep.name}"); |
326 } | 328 } |
327 }))); | 329 }))); |
328 } else { | 330 } else { |
329 // If nothing's transformed, then we only depend on the transformers | 331 // If nothing's transformed, then we only depend on the transformers |
330 // used by the external packages' libraries that we import or export. | 332 // used by the external packages' libraries that we import or export. |
331 return unionAll(externalDirectives.map((uri) { | 333 return unionAll(externalDirectives.map((uri) { |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 traverseLibrary(rootLibrary) ? results : null; | 396 traverseLibrary(rootLibrary) ? results : null; |
395 return _transitiveExternalDirectives[rootLibrary]; | 397 return _transitiveExternalDirectives[rootLibrary]; |
396 } | 398 } |
397 | 399 |
398 /// Returns the set of all imports or exports in [library]. | 400 /// Returns the set of all imports or exports in [library]. |
399 /// | 401 /// |
400 /// If [library] is modified by a transformer, this will return `null`. | 402 /// If [library] is modified by a transformer, this will return `null`. |
401 Set<Uri> _getDirectives(String library) { | 403 Set<Uri> _getDirectives(String library) { |
402 var libraryUri = p.toUri(p.normalize(library)); | 404 var libraryUri = p.toUri(p.normalize(library)); |
403 var relative = p.toUri(_package.relative(library)).path; | 405 var relative = p.toUri(_package.relative(library)).path; |
404 if (_applicableTransformers.any((config) => | 406 if (_applicableTransformers.any( |
405 config.canTransform(relative))) { | 407 (config) => config.canTransform(relative))) { |
406 _directives[libraryUri] = null; | 408 _directives[libraryUri] = null; |
407 return null; | 409 return null; |
408 } | 410 } |
409 | 411 |
410 // Check the cache *after* checking [_applicableTransformers] because | 412 // Check the cache *after* checking [_applicableTransformers] because |
411 // [_applicableTransformers] changes over time so the directives may be | 413 // [_applicableTransformers] changes over time so the directives may be |
412 // invalidated. | 414 // invalidated. |
413 if (_directives.containsKey(libraryUri)) return _directives[libraryUri]; | 415 if (_directives.containsKey(libraryUri)) return _directives[libraryUri]; |
414 | 416 |
415 // If a nonexistent library is imported, it will probably be generated by a | 417 // If a nonexistent library is imported, it will probably be generated by a |
416 // transformer. | 418 // transformer. |
417 if (!fileExists(library)) { | 419 if (!fileExists(library)) { |
418 _directives[libraryUri] = null; | 420 _directives[libraryUri] = null; |
419 return null; | 421 return null; |
420 } | 422 } |
421 | 423 |
422 _directives[libraryUri] = | 424 _directives[libraryUri] = parseImportsAndExports( |
423 parseImportsAndExports(readTextFile(library), name: library) | 425 readTextFile(library), |
424 .map((directive) => Uri.parse(directive.uri.stringValue)) | 426 name: library).map((directive) => Uri.parse(directive.uri.stringValue)).
toSet(); |
425 .toSet(); | |
426 return _directives[libraryUri]; | 427 return _directives[libraryUri]; |
427 } | 428 } |
428 } | 429 } |
OLD | NEW |