| 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 pub.source; | 5 library pub.source; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:pub_semver/pub_semver.dart'; | 9 import 'package:pub_semver/pub_semver.dart'; |
| 10 | 10 |
| 11 import 'package.dart'; | 11 import 'package.dart'; |
| 12 import 'pubspec.dart'; | 12 import 'pubspec.dart'; |
| 13 import 'system_cache.dart'; | 13 import 'system_cache.dart'; |
| 14 import 'utils.dart'; |
| 14 | 15 |
| 15 /// A source from which to get packages. | 16 /// A source from which to get packages. |
| 16 /// | 17 /// |
| 17 /// Each source has many packages that it looks up using [PackageId]s. Sources | 18 /// Each source has many packages that it looks up using [PackageId]s. Sources |
| 18 /// that inherit this directly (currently just [PathSource]) are *uncached* | 19 /// that inherit this directly (currently just [PathSource]) are *uncached* |
| 19 /// sources. They deliver a package directly to the package that depends on it. | 20 /// sources. They deliver a package directly to the package that depends on it. |
| 20 /// | 21 /// |
| 21 /// Other sources are *cached* sources. These extend [CachedSource]. When a | 22 /// Other sources are *cached* sources. These extend [CachedSource]. When a |
| 22 /// package needs a dependency from a cached source, it is first installed in | 23 /// package needs a dependency from a cached source, it is first installed in |
| 23 /// the [SystemCache] and then acquired from there. | 24 /// the [SystemCache] and then acquired from there. |
| 25 /// |
| 26 /// ## Subclassing |
| 27 /// |
| 28 /// All sources should extend this class. In addition to defining the behavior |
| 29 /// of various methods, sources define the structure of package descriptions |
| 30 /// used in [PackageRef]s, [PackageDep]s, and [PackageId]s. There are three |
| 31 /// distinct types of description, although in practice most sources use the |
| 32 /// same format for one or more of these: |
| 33 /// |
| 34 /// * User descriptions. These are included in pubspecs and usually written by |
| 35 /// hand. They're typically more flexible in the formats they allow to |
| 36 /// optimize for ease of authoring. |
| 37 /// |
| 38 /// * Reference descriptions. These are the descriptions in [PackageRef]s and |
| 39 /// [PackageDep]. They're parsed directly from user descriptions using |
| 40 /// [parseRef], and so add no additional information. |
| 41 /// |
| 42 /// * ID descriptions. These are the descriptions in [PackageId]s, which |
| 43 /// uniquely identify and provide the means to locate the concrete code of a |
| 44 /// package. They may contain additional expensive-to-compute information |
| 45 /// relative to the corresponding reference descriptions. These are the |
| 46 /// descriptions stored in lock files. |
| 24 abstract class Source { | 47 abstract class Source { |
| 25 /// The name of the source. | 48 /// The name of the source. |
| 26 /// | 49 /// |
| 27 /// Should be lower-case, suitable for use in a filename, and unique accross | 50 /// Should be lower-case, suitable for use in a filename, and unique accross |
| 28 /// all sources. | 51 /// all sources. |
| 29 String get name; | 52 String get name; |
| 30 | 53 |
| 31 /// Whether this source can choose between multiple versions of the same | 54 /// Whether this source can choose between multiple versions of the same |
| 32 /// package during version solving. | 55 /// package during version solving. |
| 33 /// | 56 /// |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 /// Note that this does *not* require the packages to be downloaded locally, | 110 /// Note that this does *not* require the packages to be downloaded locally, |
| 88 /// which is the point. This is used during version resolution to determine | 111 /// which is the point. This is used during version resolution to determine |
| 89 /// which package versions are available to be downloaded (or already | 112 /// which package versions are available to be downloaded (or already |
| 90 /// downloaded). | 113 /// downloaded). |
| 91 /// | 114 /// |
| 92 /// By default, this assumes that each description has a single version and | 115 /// By default, this assumes that each description has a single version and |
| 93 /// uses [describe] to get that version. | 116 /// uses [describe] to get that version. |
| 94 /// | 117 /// |
| 95 /// This method is effectively protected: subclasses must implement it, but | 118 /// This method is effectively protected: subclasses must implement it, but |
| 96 /// external code should not call this. Instead, call [getVersions]. | 119 /// external code should not call this. Instead, call [getVersions]. |
| 97 Future<List<PackageId>> doGetVersions(PackageRef ref) async { | 120 Future<List<PackageId>> doGetVersions(PackageRef ref); |
| 98 var pubspec = await describe(ref.atVersion(Version.none)); | |
| 99 return [ref.atVersion(pubspec.version)]; | |
| 100 } | |
| 101 | 121 |
| 102 /// Loads the (possibly remote) pubspec for the package version identified by | 122 /// Loads the (possibly remote) pubspec for the package version identified by |
| 103 /// [id]. | 123 /// [id]. |
| 104 /// | 124 /// |
| 105 /// This may be called for packages that have not yet been downloaded during | 125 /// This may be called for packages that have not yet been downloaded during |
| 106 /// the version resolution process. Its results are automatically memoized. | 126 /// the version resolution process. Its results are automatically memoized. |
| 107 /// | 127 /// |
| 128 /// Throws a [DataException] if the pubspec's version doesn't match [id]'s |
| 129 /// version. |
| 130 /// |
| 108 /// Sources should not override this. Instead, they implement [doDescribe]. | 131 /// Sources should not override this. Instead, they implement [doDescribe]. |
| 109 Future<Pubspec> describe(PackageId id) async { | 132 Future<Pubspec> describe(PackageId id) async { |
| 110 if (id.isRoot) throw new ArgumentError("Cannot describe the root package."); | 133 if (id.isRoot) throw new ArgumentError("Cannot describe the root package."); |
| 111 if (id.source != name) { | 134 if (id.source != name) { |
| 112 throw new ArgumentError("Package $id does not use source $name."); | 135 throw new ArgumentError("Package $id does not use source $name."); |
| 113 } | 136 } |
| 114 | 137 |
| 115 var pubspec = _pubspecs[id]; | 138 var pubspec = _pubspecs[id]; |
| 116 if (pubspec != null) return pubspec; | 139 if (pubspec != null) return pubspec; |
| 117 | 140 |
| 118 // Delegate to the overridden one. | 141 // Delegate to the overridden one. |
| 119 pubspec = await doDescribe(id); | 142 pubspec = await doDescribe(id); |
| 120 _pubspecs[id.atVersion(pubspec.version)] = pubspec; | 143 if (pubspec.version != id.version) { |
| 144 dataError("The pubspec for $id has version ${pubspec.version}."); |
| 145 } |
| 146 |
| 147 _pubspecs[id] = pubspec; |
| 121 return pubspec; | 148 return pubspec; |
| 122 } | 149 } |
| 123 | 150 |
| 124 /// Loads the (possibly remote) pubspec for the package version identified by | 151 /// Loads the (possibly remote) pubspec for the package version identified by |
| 125 /// [id]. | 152 /// [id]. |
| 126 /// | 153 /// |
| 127 /// This may be called for packages that have not yet been downloaded during | 154 /// This may be called for packages that have not yet been downloaded during |
| 128 /// the version resolution process. | 155 /// the version resolution process. |
| 129 /// | 156 /// |
| 130 /// This method is effectively protected: subclasses must implement it, but | 157 /// This method is effectively protected: subclasses must implement it, but |
| 131 /// external code should not call this. Instead, call [describe]. | 158 /// external code should not call this. Instead, call [describe]. |
| 132 Future<Pubspec> doDescribe(PackageId id); | 159 Future<Pubspec> doDescribe(PackageId id); |
| 133 | 160 |
| 134 /// Ensures [id] is available locally and creates a symlink at [symlink] | 161 /// Ensures [id] is available locally and creates a symlink at [symlink] |
| 135 /// pointing it. | 162 /// pointing it. |
| 136 Future get(PackageId id, String symlink); | 163 Future get(PackageId id, String symlink); |
| 137 | 164 |
| 138 /// Returns the directory where this package can (or could) be found locally. | 165 /// Returns the directory where this package can (or could) be found locally. |
| 139 /// | 166 /// |
| 140 /// If the source is cached, this will be a path in the system cache. | 167 /// If the source is cached, this will be a path in the system cache. |
| 141 /// Depending on the source, this may throw an [ArgumentError] if [id] isn't | |
| 142 /// resolved using [resolveId]. | |
| 143 String getDirectory(PackageId id); | 168 String getDirectory(PackageId id); |
| 144 | 169 |
| 145 /// Gives the source a chance to interpret and validate the description for | 170 /// Parses a [PackageRef] from a name and a user-provided [description]. |
| 146 /// a package coming from this source. | |
| 147 /// | 171 /// |
| 148 /// When a [Pubspec] or [LockFile] is parsed, it reads in the description for | 172 /// When a [Pubspec] is parsed, it reads in the description for each |
| 149 /// each dependency. It is up to the dependency's [Source] to determine how | 173 /// dependency. It is up to the dependency's [Source] to determine how that |
| 150 /// that should be interpreted. This will be called during parsing to validate | 174 /// should be interpreted. This will be called during parsing to validate that |
| 151 /// that the given [description] is well-formed according to this source, and | 175 /// the given [description] is well-formed according to this source, and to |
| 152 /// to give the source a chance to canonicalize the description. | 176 /// give the source a chance to canonicalize the description. |
| 153 /// | 177 /// |
| 154 /// [containingPath] is the path to the local file (pubspec or lockfile) | 178 /// [containingPath] is the path to the local file (pubspec or lockfile) |
| 155 /// where this description appears. It may be `null` if the description is | 179 /// where this description appears. It may be `null` if the description is |
| 156 /// coming from some in-memory source (such as pulling down a pubspec from | 180 /// coming from some in-memory source (such as pulling down a pubspec from |
| 157 /// pub.dartlang.org). | 181 /// pub.dartlang.org). |
| 158 /// | 182 /// |
| 159 /// It should return if a (possibly modified) valid description, or throw a | 183 /// The description in the returned [PackageRef] need bear no resemblance to |
| 160 /// [FormatException] if not valid. | 184 /// the original user-provided description. |
| 161 /// | 185 /// |
| 162 /// [fromLockFile] is true when the description comes from a [LockFile], to | 186 /// Throws a [FormatException] if the description is not valid. |
| 163 /// allow the source to use lockfile-specific descriptions via [resolveId]. | 187 PackageRef parseRef(String name, description, {String containingPath}); |
| 164 dynamic parseDescription(String containingPath, description, | 188 |
| 165 {bool fromLockFile: false}); | 189 /// Parses a [PackageId] from a name and a serialized description. |
| 190 /// |
| 191 /// This only accepts descriptions serialized using [serializeDescription]. It |
| 192 /// should not be used with user-authored descriptions. |
| 193 /// |
| 194 /// Throws a [FormatException] if the description is not valid. |
| 195 PackageId parseId(String name, Version version, description); |
| 166 | 196 |
| 167 /// When a [LockFile] is serialized, it uses this method to get the | 197 /// When a [LockFile] is serialized, it uses this method to get the |
| 168 /// [description] in the right format. | 198 /// [description] in the right format. |
| 169 /// | 199 /// |
| 170 /// [containingPath] is the containing directory of the root package. | 200 /// [containingPath] is the containing directory of the root package. |
| 171 dynamic serializeDescription(String containingPath, description) { | 201 dynamic serializeDescription(String containingPath, description) { |
| 172 return description; | 202 return description; |
| 173 } | 203 } |
| 174 | 204 |
| 175 /// When a package [description] is shown to the user, this is called to | 205 /// When a package [description] is shown to the user, this is called to |
| 176 /// convert it into a human-friendly form. | 206 /// convert it into a human-friendly form. |
| 177 /// | 207 /// |
| 178 /// By default, it just converts the description to a string, but sources | 208 /// By default, it just converts the description to a string, but sources |
| 179 /// may customize this. [containingPath] is the containing directory of the | 209 /// may customize this. [containingPath] is the containing directory of the |
| 180 /// root package. | 210 /// root package. |
| 181 String formatDescription(String containingPath, description) { | 211 String formatDescription(String containingPath, description) { |
| 182 return description.toString(); | 212 return description.toString(); |
| 183 } | 213 } |
| 184 | 214 |
| 185 /// Returns whether or not [description1] describes the same package as | 215 /// Returns whether or not [description1] describes the same package as |
| 186 /// [description2] for this source. | 216 /// [description2] for this source. |
| 187 /// | 217 /// |
| 188 /// This method should be light-weight. It doesn't need to validate that | 218 /// This method should be light-weight. It doesn't need to validate that |
| 189 /// either package exists. | 219 /// either package exists. |
| 220 /// |
| 221 /// Note that either description may be a reference description or an ID |
| 222 /// description; they need not be the same type. ID descriptions should be |
| 223 /// considered equal to the reference descriptions that produced them. |
| 190 bool descriptionsEqual(description1, description2); | 224 bool descriptionsEqual(description1, description2); |
| 191 | 225 |
| 192 /// Resolves [id] to a more possibly more precise that will uniquely identify | |
| 193 /// a package regardless of when the package is requested. | |
| 194 /// | |
| 195 /// For some sources, [PackageId]s can point to different chunks of code at | |
| 196 /// different times. This takes such an [id] and returns a future that | |
| 197 /// completes to a [PackageId] that will uniquely specify a single chunk of | |
| 198 /// code forever. | |
| 199 /// | |
| 200 /// For example, [GitSource] might take an [id] with description | |
| 201 /// `http://github.com/dart-lang/some-lib.git` and return an id with a | |
| 202 /// description that includes the current commit of the Git repository. | |
| 203 /// | |
| 204 /// Pub calls this after getting a package, so the source can use the local | |
| 205 /// package to determine information about the resolved id. | |
| 206 /// | |
| 207 /// The returned [PackageId] may have a description field that's invalid | |
| 208 /// according to [parseDescription], although it must still be serializable | |
| 209 /// to JSON and YAML. It must also be equal to [id] according to | |
| 210 /// [descriptionsEqual]. | |
| 211 /// | |
| 212 /// By default, this just returns [id]. | |
| 213 Future<PackageId> resolveId(PackageId id) => new Future.value(id); | |
| 214 | |
| 215 /// Returns whether [id] is fully-resolved, according to [resolveId]. | |
| 216 bool isResolved(PackageId id) => true; | |
| 217 | |
| 218 /// Stores [pubspec] so it's returned when [describe] is called with [id]. | 226 /// Stores [pubspec] so it's returned when [describe] is called with [id]. |
| 219 /// | 227 /// |
| 220 /// This is notionally protected; it should only be called by subclasses. | 228 /// This is notionally protected; it should only be called by subclasses. |
| 221 void memoizePubspec(PackageId id, Pubspec pubspec) { | 229 void memoizePubspec(PackageId id, Pubspec pubspec) { |
| 222 _pubspecs[id] = pubspec; | 230 _pubspecs[id] = pubspec; |
| 223 } | 231 } |
| 224 | 232 |
| 225 /// Returns the source's name. | 233 /// Returns the source's name. |
| 226 String toString() => name; | 234 String toString() => name; |
| 227 } | 235 } |
| OLD | NEW |