| 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 library source; | 5 library source; |
| 6 | 6 |
| 7 import 'io.dart'; | 7 import 'io.dart'; |
| 8 import 'package.dart'; | 8 import 'package.dart'; |
| 9 import 'pubspec.dart'; | 9 import 'pubspec.dart'; |
| 10 import 'system_cache.dart'; | 10 import 'system_cache.dart'; |
| 11 import 'version.dart'; | 11 import 'version.dart'; |
| 12 | 12 |
| 13 /** | 13 /// A source from which to install packages. |
| 14 * A source from which to install packages. | 14 /// |
| 15 * | 15 /// Each source has many packages that it looks up using [PackageId]s. The |
| 16 * Each source has many packages that it looks up using [PackageId]s. The source | 16 /// source is responsible for installing these packages to the package cache. |
| 17 * is responsible for installing these packages to the package cache. | |
| 18 */ | |
| 19 abstract class Source { | 17 abstract class Source { |
| 20 /** | 18 /// The name of the source. Should be lower-case, suitable for use in a |
| 21 * The name of the source. Should be lower-case, suitable for use in a | 19 /// filename, and unique accross all sources. |
| 22 * filename, and unique accross all sources. | |
| 23 */ | |
| 24 String get name; | 20 String get name; |
| 25 | 21 |
| 26 /// Whether or not this source is the default source. | 22 /// Whether or not this source is the default source. |
| 27 bool get isDefault => systemCache.sources.defaultSource == this; | 23 bool get isDefault => systemCache.sources.defaultSource == this; |
| 28 | 24 |
| 29 /** | 25 /// Whether this source's packages should be cached in Pub's global cache |
| 30 * Whether this source's packages should be cached in Pub's global cache | 26 /// directory. |
| 31 * directory. | 27 /// |
| 32 * | 28 /// A source should be cached if it requires network access to retrieve |
| 33 * A source should be cached if it requires network access to retrieve | 29 /// packages. It doesn't need to be cached if all packages are available |
| 34 * packages. It doesn't need to be cached if all packages are available | 30 /// locally. |
| 35 * locally. | |
| 36 */ | |
| 37 bool get shouldCache; | 31 bool get shouldCache; |
| 38 | 32 |
| 39 /** | 33 /// The system cache with which this source is registered. |
| 40 * The system cache with which this source is registered. | |
| 41 */ | |
| 42 SystemCache get systemCache { | 34 SystemCache get systemCache { |
| 43 assert(_systemCache != null); | 35 assert(_systemCache != null); |
| 44 return _systemCache; | 36 return _systemCache; |
| 45 } | 37 } |
| 46 | 38 |
| 47 /** | 39 /// The system cache variable. Set by [_bind]. |
| 48 * The system cache variable. Set by [_bind]. | |
| 49 */ | |
| 50 SystemCache _systemCache; | 40 SystemCache _systemCache; |
| 51 | 41 |
| 52 /** | 42 /// The root directory of this source's cache within the system cache. |
| 53 * The root directory of this source's cache within the system cache. | 43 /// |
| 54 * | 44 /// This shouldn't be overridden by subclasses. |
| 55 * This shouldn't be overridden by subclasses. | |
| 56 */ | |
| 57 String get systemCacheRoot => join(systemCache.rootDir, name); | 45 String get systemCacheRoot => join(systemCache.rootDir, name); |
| 58 | 46 |
| 59 /** | 47 /// Records the system cache to which this source belongs. |
| 60 * Records the system cache to which this source belongs. | 48 /// |
| 61 * | 49 /// This should only be called once for each source, by |
| 62 * This should only be called once for each source, by [SystemCache.register]. | 50 /// [SystemCache.register]. It should not be overridden by base classes. |
| 63 * It should not be overridden by base classes. | |
| 64 */ | |
| 65 void bind(SystemCache systemCache) { | 51 void bind(SystemCache systemCache) { |
| 66 assert(_systemCache == null); | 52 assert(_systemCache == null); |
| 67 this._systemCache = systemCache; | 53 this._systemCache = systemCache; |
| 68 } | 54 } |
| 69 | 55 |
| 70 /** | 56 /// Get the list of all versions that exist for the package described by |
| 71 * Get the list of all versions that exist for the package described by | 57 /// [description]. [name] is the expected name of the package. |
| 72 * [description]. [name] is the expected name of the package. | 58 /// |
| 73 * | 59 /// Note that this does *not* require the packages to be installed, which is |
| 74 * Note that this does *not* require the packages to be installed, which is | 60 /// the point. This is used during version resolution to determine which |
| 75 * the point. This is used during version resolution to determine which | 61 /// package versions are available to be installed (or already installed). |
| 76 * package versions are available to be installed (or already installed). | 62 /// |
| 77 * | 63 /// By default, this assumes that each description has a single version and |
| 78 * By default, this assumes that each description has a single version and | 64 /// uses [describe] to get that version. |
| 79 * uses [describe] to get that version. | |
| 80 */ | |
| 81 Future<List<Version>> getVersions(String name, description) { | 65 Future<List<Version>> getVersions(String name, description) { |
| 82 return describe(new PackageId(name, this, Version.none, description)) | 66 return describe(new PackageId(name, this, Version.none, description)) |
| 83 .transform((pubspec) => [pubspec.version]); | 67 .transform((pubspec) => [pubspec.version]); |
| 84 } | 68 } |
| 85 | 69 |
| 86 /** | 70 /// Loads the (possibly remote) pubspec for the package version identified by |
| 87 * Loads the (possibly remote) pubspec for the package version identified by | 71 /// [id]. This may be called for packages that have not yet been installed |
| 88 * [id]. This may be called for packages that have not yet been installed | 72 /// during the version resolution process. |
| 89 * during the version resolution process. | 73 /// |
| 90 * | 74 /// For cached sources, by default this uses [installToSystemCache] to get the |
| 91 * For cached sources, by default this uses [installToSystemCache] to get the | 75 /// pubspec. There is no default implementation for non-cached sources; they |
| 92 * pubspec. There is no default implementation for non-cached sources; they | 76 /// must implement it manually. |
| 93 * must implement it manually. | |
| 94 */ | |
| 95 Future<Pubspec> describe(PackageId id) { | 77 Future<Pubspec> describe(PackageId id) { |
| 96 if (!shouldCache) throw "Source $name must implement describe(id)."; | 78 if (!shouldCache) throw "Source $name must implement describe(id)."; |
| 97 return installToSystemCache(id).transform((package) => package.pubspec); | 79 return installToSystemCache(id).transform((package) => package.pubspec); |
| 98 } | 80 } |
| 99 | 81 |
| 100 /** | 82 /// Installs the package identified by [id] to [path]. Returns a [Future] that |
| 101 * Installs the package identified by [id] to [path]. Returns a [Future] that | 83 /// completes when the installation was finished. The [Future] should resolve |
| 102 * completes when the installation was finished. The [Future] should resolve | 84 /// to true if the package was found in the source and false if it wasn't. For |
| 103 * to true if the package was found in the source and false if it wasn't. For | 85 /// all other error conditions, it should complete with an exception. |
| 104 * all other error conditions, it should complete with an exception. | 86 /// |
| 105 * | 87 /// [path] is guaranteed not to exist, and its parent directory is guaranteed |
| 106 * [path] is guaranteed not to exist, and its parent directory is guaranteed | 88 /// to exist. |
| 107 * to exist. | 89 /// |
| 108 * | 90 /// Note that [path] may be deleted. If re-installing a package that has |
| 109 * Note that [path] may be deleted. If re-installing a package that has | 91 /// already been installed would be costly or impossible, |
| 110 * already been installed would be costly or impossible, | 92 /// [installToSystemCache] should be implemented instead of [install]. |
| 111 * [installToSystemCache] should be implemented instead of [install]. | 93 /// |
| 112 * | 94 /// This doesn't need to be implemented if [installToSystemCache] is |
| 113 * This doesn't need to be implemented if [installToSystemCache] is | 95 /// implemented. |
| 114 * implemented. | |
| 115 */ | |
| 116 Future<bool> install(PackageId id, String path) { | 96 Future<bool> install(PackageId id, String path) { |
| 117 throw "Either install or installToSystemCache must be implemented for " | 97 throw "Either install or installToSystemCache must be implemented for " |
| 118 "source $name."; | 98 "source $name."; |
| 119 } | 99 } |
| 120 | 100 |
| 121 /** | 101 /// Installs the package identified by [id] to the system cache. This is only |
| 122 * Installs the package identified by [id] to the system cache. This is only | 102 /// called for sources with [shouldCache] set to true. |
| 123 * called for sources with [shouldCache] set to true. | 103 /// |
| 124 * | 104 /// By default, this uses [systemCacheDirectory] and [install]. |
| 125 * By default, this uses [systemCacheDirectory] and [install]. | |
| 126 */ | |
| 127 Future<Package> installToSystemCache(PackageId id) { | 105 Future<Package> installToSystemCache(PackageId id) { |
| 128 var path = systemCacheDirectory(id); | 106 var path = systemCacheDirectory(id); |
| 129 return exists(path).chain((exists) { | 107 return exists(path).chain((exists) { |
| 130 if (exists) return new Future<bool>.immediate(true); | 108 if (exists) return new Future<bool>.immediate(true); |
| 131 return ensureDir(dirname(path)).chain((_) => install(id, path)); | 109 return ensureDir(dirname(path)).chain((_) => install(id, path)); |
| 132 }).chain((found) { | 110 }).chain((found) { |
| 133 if (!found) throw 'Package $id not found.'; | 111 if (!found) throw 'Package $id not found.'; |
| 134 return Package.load(id.name, path, systemCache.sources); | 112 return Package.load(id.name, path, systemCache.sources); |
| 135 }); | 113 }); |
| 136 } | 114 } |
| 137 | 115 |
| 138 /** | 116 /// Returns the directory in the system cache that the package identified by |
| 139 * Returns the directory in the system cache that the package identified by | 117 /// [id] should be installed to. This should return a path to a subdirectory |
| 140 * [id] should be installed to. This should return a path to a subdirectory of | 118 /// of [systemCacheRoot]. |
| 141 * [systemCacheRoot]. | 119 /// |
| 142 * | 120 /// This doesn't need to be implemented if [shouldCache] is false, or if |
| 143 * This doesn't need to be implemented if [shouldCache] is false, or if | 121 /// [installToSystemCache] is implemented. |
| 144 * [installToSystemCache] is implemented. | |
| 145 */ | |
| 146 String systemCacheDirectory(PackageId id) => | 122 String systemCacheDirectory(PackageId id) => |
| 147 join(systemCacheRoot, packageName(id.description)); | 123 join(systemCacheRoot, packageName(id.description)); |
| 148 | 124 |
| 149 /** | 125 /// When a [Pubspec] or [LockFile] is parsed, it reads in the description for |
| 150 * When a [Pubspec] or [LockFile] is parsed, it reads in the description for | 126 /// each dependency. It is up to the dependency's [Source] to determine how |
| 151 * each dependency. It is up to the dependency's [Source] to determine how | 127 /// that should be interpreted. This will be called during parsing to validate |
| 152 * that should be interpreted. This will be called during parsing to validate | 128 /// that the given [description] is well-formed according to this source. It |
| 153 * that the given [description] is well-formed according to this source. It | 129 /// should return if the description is valid, or throw a [FormatException] if |
| 154 * should return if the description is valid, or throw a [FormatException] if | 130 /// not. |
| 155 * not. | 131 /// |
| 156 * | 132 /// [fromLockFile] is true when the description comes from a [LockFile], to |
| 157 * [fromLockFile] is true when the description comes from a [LockFile], to | 133 /// allow the source to use lockfile-specific descriptions via [resolveId]. |
| 158 * allow the source to use lockfile-specific descriptions via [resolveId]. | |
| 159 */ | |
| 160 void validateDescription(description, {bool fromLockFile: false}) {} | 134 void validateDescription(description, {bool fromLockFile: false}) {} |
| 161 | 135 |
| 162 /** | 136 /// Returns whether or not [description1] describes the same package as |
| 163 * Returns whether or not [description1] describes the same package as | 137 /// [description2] for this source. This method should be light-weight. It |
| 164 * [description2] for this source. This method should be light-weight. It | 138 /// doesn't need to validate that either package exists. |
| 165 * doesn't need to validate that either package exists. | 139 /// |
| 166 * | 140 /// By default, just uses regular equality. |
| 167 * By default, just uses regular equality. | |
| 168 */ | |
| 169 bool descriptionsEqual(description1, description2) => | 141 bool descriptionsEqual(description1, description2) => |
| 170 description1 == description2; | 142 description1 == description2; |
| 171 | 143 |
| 172 /** | 144 /// For some sources, [PackageId]s can point to different chunks of code at |
| 173 * For some sources, [PackageId]s can point to different chunks of code at | 145 /// different times. This takes such an [id] and returns a future that |
| 174 * different times. This takes such an [id] and returns a future that | 146 /// completes to a [PackageId] that will uniquely specify a single chunk of |
| 175 * completes to a [PackageId] that will uniquely specify a single chunk of | 147 /// code forever. |
| 176 * code forever. | 148 /// |
| 177 * | 149 /// For example, [GitSource] might take an [id] with description |
| 178 * For example, [GitSource] might take an [id] with description | 150 /// `http://github.com/dart-lang/some-lib.git` and return an id with a |
| 179 * `http://github.com/dart-lang/some-lib.git` and return an id with a | 151 /// description that includes the current commit of the Git repository. |
| 180 * description that includes the current commit of the Git repository. | 152 /// |
| 181 * | 153 /// This will be called after the package identified by [id] is installed, so |
| 182 * This will be called after the package identified by [id] is installed, so | 154 /// the source can use the installed package to determine information about |
| 183 * the source can use the installed package to determine information about the | 155 /// the resolved id. |
| 184 * resolved id. | 156 /// |
| 185 * | 157 /// The returned [PackageId] may have a description field that's invalid |
| 186 * The returned [PackageId] may have a description field that's invalid | 158 /// according to [validateDescription], although it must still be serializable |
| 187 * according to [validateDescription], although it must still be serializable | 159 /// to JSON and YAML. It must also be equal to [id] according to |
| 188 * to JSON and YAML. It must also be equal to [id] according to | 160 /// [descriptionsEqual]. |
| 189 * [descriptionsEqual]. | 161 /// |
| 190 * | 162 /// By default, this just returns [id]. |
| 191 * By default, this just returns [id]. | |
| 192 */ | |
| 193 Future<PackageId> resolveId(PackageId id) => new Future.immediate(id); | 163 Future<PackageId> resolveId(PackageId id) => new Future.immediate(id); |
| 194 | 164 |
| 195 /// Returns the source's name. | 165 /// Returns the source's name. |
| 196 String toString() => name; | 166 String toString() => name; |
| 197 } | 167 } |
| OLD | NEW |