| 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 implements Hashable { | 8 class Package implements Hashable { |
| 9 /** | 9 /** |
| 10 * Loads the package whose root directory is [packageDir]. | 10 * Loads the package whose root directory is [packageDir]. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 * The path to the directory containing the package. | 21 * The path to the directory containing the package. |
| 22 */ | 22 */ |
| 23 final String dir; | 23 final String dir; |
| 24 | 24 |
| 25 /** | 25 /** |
| 26 * The name of the package. | 26 * The name of the package. |
| 27 */ | 27 */ |
| 28 final String name; | 28 final String name; |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * The names of the packages that this package depends on. This is what is | 31 * The ids of the packages that this package depends on. This is what is |
| 32 * specified in the pubspec when this package depends on another. | 32 * specified in the pubspec when this package depends on another. |
| 33 */ | 33 */ |
| 34 // TODO(rnystrom): When packages are versioned and sourced, this will likely | 34 final Collection<PackageId> dependencies; |
| 35 // be something more than just a string. | |
| 36 final Collection<String> dependencies; | |
| 37 | 35 |
| 38 /** | 36 /** |
| 39 * Constructs a package. This should not be called directly. Instead, acquire | 37 * Constructs a package. This should not be called directly. Instead, acquire |
| 40 * packages from [load()]. | 38 * packages from [load()]. |
| 41 */ | 39 */ |
| 42 Package._(String dir, this.dependencies) | 40 Package._(String dir, this.dependencies) |
| 43 : dir = dir, | 41 : dir = dir, |
| 44 name = basename(dir); | 42 name = basename(dir); |
| 45 | 43 |
| 46 /** | 44 /** |
| 47 * Reads and returns all of the packages this package immediately depends on. | |
| 48 */ | |
| 49 Future<Collection<Package>> loadDependencies(PackageCache cache) { | |
| 50 return Futures.wait(dependencies.map((name) => cache.find(name))); | |
| 51 } | |
| 52 | |
| 53 /** | |
| 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. | |
| 57 */ | |
| 58 Future<Set<Package>> traverseDependencies(PackageCache cache) { | |
| 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 dependencies.forEach(walkPackage); | |
| 75 pendingAsyncCalls--; | |
| 76 if (pendingAsyncCalls == 0) completer.complete(packages); | |
| 77 }); | |
| 78 } | |
| 79 | |
| 80 walkPackage(this); | |
| 81 | |
| 82 return completer.future; | |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * Generates a hashcode for the package. | 45 * Generates a hashcode for the package. |
| 87 */ | 46 */ |
| 88 // TODO(rnystrom): Do something more sophisticated here once we care about | 47 // TODO(rnystrom): Do something more sophisticated here once we care about |
| 89 // versioning and different package sources. | 48 // versioning and different package sources. |
| 90 int hashCode() => name.hashCode(); | 49 int hashCode() => name.hashCode(); |
| 91 | 50 |
| 92 /** | 51 /** |
| 93 * Returns a debug string for the package. | 52 * Returns a debug string for the package. |
| 94 */ | 53 */ |
| 95 String toString() => '$name ($dir)'; | 54 String toString() => '$name ($dir)'; |
| 96 | 55 |
| 97 /** | 56 /** |
| 98 * Parses the pubspec at the given path and returns the list of package | 57 * Parses the pubspec at the given path and returns the list of package |
| 99 * dependencies it exposes. | 58 * dependencies it exposes. |
| 100 */ | 59 */ |
| 101 static Future<List<String>> _parsePubspec(String path) { | 60 static Future<List<PackageId>> _parsePubspec(String path) { |
| 102 final completer = new Completer<List<String>>(); | 61 final completer = new Completer<List<PackageId>>(); |
| 103 | 62 |
| 104 // TODO(rnystrom): Handle the directory not existing. | 63 // TODO(rnystrom): Handle the directory not existing. |
| 105 // TODO(rnystrom): Error-handling. | 64 // TODO(rnystrom): Error-handling. |
| 106 final readFuture = readTextFile(path); | 65 final readFuture = readTextFile(path); |
| 107 readFuture.handleException((error) { | 66 readFuture.handleException((error) { |
| 108 // If there is no pubspec, we implicitly treat that as a package with no | 67 // If there is no pubspec, we implicitly treat that as a package with no |
| 109 // dependencies. | 68 // dependencies. |
| 110 // TODO(rnystrom): Distinguish file not found from other real errors. | 69 // TODO(rnystrom): Distinguish file not found from other real errors. |
| 111 completer.complete(<String>[]); | 70 completer.complete(<PackageId>[]); |
| 112 return true; | 71 return true; |
| 113 }); | 72 }); |
| 114 | 73 |
| 115 readFuture.then((pubspec) { | 74 readFuture.then((pubspec) { |
| 116 if (pubspec.trim() == '') { | 75 if (pubspec.trim() == '') { |
| 117 completer.complete(<String>[]); | 76 completer.complete(<String>[]); |
| 118 return; | 77 return; |
| 119 } | 78 } |
| 120 | 79 |
| 121 var parsedPubspec = loadYaml(pubspec); | 80 var parsedPubspec = loadYaml(pubspec); |
| 122 if (parsedPubspec is! Map) { | 81 if (parsedPubspec is! Map) { |
| 123 completer.completeException('The pubspec must be a YAML mapping.'); | 82 completer.completeException('The pubspec must be a YAML mapping.'); |
| 124 } | 83 } |
| 125 | 84 |
| 126 if (!parsedPubspec.containsKey('dependencies')) { | 85 if (!parsedPubspec.containsKey('dependencies')) { |
| 127 completer.complete(<String>[]); | 86 completer.complete(<String>[]); |
| 128 return; | 87 return; |
| 129 } | 88 } |
| 130 | 89 |
| 131 var dependencies = parsedPubspec['dependencies']; | 90 var dependencies = parsedPubspec['dependencies']; |
| 132 if (dependencies.some((e) => e is! String)) { | 91 if (dependencies.some((e) => e is! String)) { |
| 133 completer.completeException( | 92 completer.completeException( |
| 134 'The pubspec dependencies must be a list of package names.'); | 93 'The pubspec dependencies must be a list of package names.'); |
| 135 } | 94 } |
| 136 | 95 |
| 137 completer.complete(dependencies); | 96 var dependencyIds = |
| 97 dependencies.map((name) => new PackageId(name, Source.defaultSource)); |
| 98 completer.complete(dependencyIds); |
| 138 }); | 99 }); |
| 139 | 100 |
| 140 return completer.future; | 101 return completer.future; |
| 141 } | 102 } |
| 142 } | 103 } |
| 104 |
| 105 /** |
| 106 * A unique identifier for a package. A given package id specifies a single |
| 107 * chunk of code and resources. |
| 108 * |
| 109 * Note that it's possible for multiple package ids to point to identical |
| 110 * packages. For example, the same package may be available from multiple |
| 111 * sources. As far as Pub is concerned, those packages are different. |
| 112 */ |
| 113 // TODO(nweiz, rnystrom): this should include version eventually |
| 114 class PackageId implements Hashable, Comparable { |
| 115 /** |
| 116 * The name used by the [source] to look up the package. |
| 117 * |
| 118 * Note that this may be distinct from [name], which is the name of the |
| 119 * package itself. The [source] uses this name to locate the package and |
| 120 * returns the true package name. For example, for a Git source [fullName] |
| 121 * might be the URL "git://github.com/dart/uilib.git", while [name] would just |
| 122 * be "uilib". It would be up to the source to take the URL and extract the |
| 123 * package name. |
| 124 */ |
| 125 final String fullName; |
| 126 |
| 127 /** |
| 128 * The [Source] used to look up the package given the [fullName]. |
| 129 */ |
| 130 final Source source; |
| 131 |
| 132 PackageId(String this.fullName, Source this.source); |
| 133 |
| 134 /** |
| 135 * The name of the package being imported. Not necessarily the same as |
| 136 * [fullName]. |
| 137 */ |
| 138 String get name() => source.packageName(fullName); |
| 139 |
| 140 int hashCode() => fullName.hashCode() ^ source.name.hashCode(); |
| 141 |
| 142 bool operator ==(other) { |
| 143 if (other is! PackageId) return false; |
| 144 return other.fullName == fullName && other.source.name == source.name; |
| 145 } |
| 146 |
| 147 String toString() => "$name from ${source.name}"; |
| 148 |
| 149 int compareTo(Comparable other) { |
| 150 if (other is! PackageId) throw new IllegalArgumentException(other); |
| 151 var sourceComp = a.source.name.compareTo(b.source.name); |
| 152 if (sourceComp != 0) return sourceComp; |
| 153 return a.fullName.compareTo(b.fullName); |
| 154 } |
| 155 } |
| OLD | NEW |