| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 /** | 5 /** |
| 6 * A named, versioned, unit of code and resource reuse. | 6 * A named, versioned, unit of code and resource reuse. |
| 7 */ | 7 */ |
| 8 class Package { | 8 class Package implements Hashable { |
| 9 /** |
| 10 * Loads the package whose root directory is [packageDir]. |
| 11 */ |
| 12 static Future<Package> load(String packageDir) { |
| 13 final pubspecPath = join(packageDir, 'pubspec'); |
| 14 |
| 15 return _parsePubspec(pubspecPath).transform((dependencies) { |
| 16 return new Package._(packageDir, dependencies); |
| 17 }); |
| 18 } |
| 19 |
| 20 /** |
| 21 * The path to the directory containing the package. |
| 22 */ |
| 23 final String dir; |
| 24 |
| 9 /** | 25 /** |
| 10 * The name of the package. | 26 * The name of the package. |
| 11 */ | 27 */ |
| 12 final String name; | 28 final String name; |
| 13 | 29 |
| 14 /** | 30 /** |
| 15 * The names of the packages that this package depends on. This is what is | 31 * The names of the packages that this package depends on. This is what is |
| 16 * specified in the pubspec when this package depends on another. | 32 * specified in the pubspec when this package depends on another. |
| 17 */ | 33 */ |
| 18 // TODO(rnystrom): When packages are versioned and sourced, this will likely | 34 // TODO(rnystrom): When packages are versioned and sourced, this will likely |
| 19 // be something more than just a string. | 35 // be something more than just a string. |
| 20 final Collection<String> dependencies; | 36 final Collection<String> dependencies; |
| 21 | 37 |
| 22 /** | 38 /** |
| 23 * The cache where this package is contained. | 39 * Constructs a package. This should not be called directly. Instead, acquire |
| 40 * packages from [load()]. |
| 24 */ | 41 */ |
| 25 final PackageCache _cache; | 42 Package._(String dir, this.dependencies) |
| 43 : dir = dir, |
| 44 name = basename(dir); |
| 26 | 45 |
| 27 /** | 46 /** |
| 28 * Constructs a package. This should not be called directly. Instead, acquire | 47 * Reads and returns all of the packages this package immediately depends on. |
| 29 * packages from [PackageCache]. | |
| 30 */ | 48 */ |
| 31 Package._(this._cache, this.name, this.dependencies); | 49 Future<Collection<Package>> loadDependencies(PackageCache cache) { |
| 50 return Futures.wait(dependencies.map((name) => cache.find(name))); |
| 51 } |
| 32 | 52 |
| 33 /** | 53 /** |
| 34 * Reads and returns all of the packages this package depends on. | 54 * Walks the entire dependency graph starting at this package and returns a |
| 55 * [Set] of all of the packages dependend on by this one, directly or |
| 56 * indirectly. This package is included in the result set. |
| 35 */ | 57 */ |
| 36 Future<Collection<Package>> readDependencies() { | 58 Future<Set<Package>> traverseDependencies(PackageCache cache) { |
| 37 return Futures.wait(dependencies.map((name) => _cache.find(name))); | 59 final completer = new Completer<Set<Package>>(); |
| 60 final packages = new Set<Package>(); |
| 61 |
| 62 var pendingAsyncCalls = 0; |
| 63 |
| 64 walkPackage(Package package) { |
| 65 // Skip packages we've already traversed. |
| 66 if (packages.contains(package)) return; |
| 67 |
| 68 // Add the package. |
| 69 packages.add(package); |
| 70 |
| 71 // Recurse into its dependencies. |
| 72 pendingAsyncCalls++; |
| 73 package.loadDependencies(cache).then((dependencies) { |
| 74 for (final dependency in dependencies) { |
| 75 walkPackage(dependency); |
| 76 } |
| 77 |
| 78 pendingAsyncCalls--; |
| 79 if (pendingAsyncCalls == 0) completer.complete(packages); |
| 80 }); |
| 81 } |
| 82 |
| 83 walkPackage(this); |
| 84 |
| 85 return completer.future; |
| 86 } |
| 87 |
| 88 /** |
| 89 * Generates a hashcode for the package. |
| 90 */ |
| 91 int hashCode() => name.hashCode(); |
| 92 |
| 93 /** |
| 94 * Returns a debug string for the package. |
| 95 */ |
| 96 String toString() => '$name ($dir)'; |
| 97 |
| 98 /** |
| 99 * Parses the pubspec at the given path and returns the list of package |
| 100 * dependencies it exposes. |
| 101 */ |
| 102 static Future<List<String>> _parsePubspec(String path) { |
| 103 final completer = new Completer<List<String>>(); |
| 104 |
| 105 // TODO(rnystrom): Handle the directory not existing. |
| 106 // TODO(rnystrom): Error-handling. |
| 107 final readFuture = readTextFile(path); |
| 108 readFuture.handleException((error) { |
| 109 // If there is no pubspec, we implicitly treat that as a package with no |
| 110 // dependencies. |
| 111 // TODO(rnystrom): Distinguish file not found from other real errors. |
| 112 completer.complete(<String>[]); |
| 113 return true; |
| 114 }); |
| 115 |
| 116 readFuture.then((pubspec) { |
| 117 // TODO(rnystrom): Use YAML parser when ready. For now, it's just a flat |
| 118 // list of newline-separated strings. |
| 119 final dependencyNames = pubspec.split('\n'). |
| 120 map((name) => name.trim()). |
| 121 filter((name) => (name != null) && (name != '')); |
| 122 |
| 123 completer.complete(dependencyNames); |
| 124 }); |
| 125 |
| 126 return completer.future; |
| 38 } | 127 } |
| 39 } | 128 } |
| OLD | NEW |