| 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 |