Chromium Code Reviews| 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 | 6 |
| 7 import 'package:pub_semver/pub_semver.dart'; | 7 import 'package:pub_semver/pub_semver.dart'; |
| 8 | 8 |
| 9 import 'package.dart'; | 9 import 'package.dart'; |
| 10 import 'pubspec.dart'; | 10 import 'pubspec.dart'; |
| 11 import 'system_cache.dart'; | 11 import 'system_cache.dart'; |
| 12 import 'utils.dart'; | 12 import 'utils.dart'; |
| 13 | 13 |
| 14 /// A source from which to get packages. | 14 /// A source from which to get packages. |
| 15 /// | 15 /// |
| 16 /// Each source has many packages that it looks up using [PackageId]s. Sources | 16 /// Each source has many packages that it looks up using [PackageId]s. Sources |
| 17 /// that inherit this directly (currently just [PathSource]) are *uncached* | 17 /// that inherit this directly (currently just [PathSource]) are *uncached* |
| 18 /// sources. They deliver a package directly to the package that depends on it. | 18 /// sources. They deliver a package directly to the package that depends on it. |
| 19 /// | 19 /// |
| 20 /// Other sources are *cached* sources. These extend [CachedSource]. When a | 20 /// Other sources are *cached* sources. These extend [CachedSource]. When a |
| 21 /// package needs a dependency from a cached source, it is first installed in | 21 /// package needs a dependency from a cached source, it is first installed in |
| 22 /// the [SystemCache] and then acquired from there. | 22 /// the [SystemCache] and then acquired from there. |
| 23 /// | 23 /// |
| 24 /// Each user-visible source has two classes: a [Source] that knows how to do | |
| 25 /// filesystem-independent operations like parsing and comparing descriptions, | |
| 26 /// and a [LiveSource] that knows how to actually install (and potentially | |
| 27 /// download) those packages. Only the [LiveSource] has access to the | |
| 28 /// [SystemCache]. | |
| 29 /// | |
| 24 /// ## Subclassing | 30 /// ## Subclassing |
| 25 /// | 31 /// |
| 26 /// All sources should extend this class. In addition to defining the behavior | 32 /// All [Source]s should extend this class and all [LiveSource]s should extend |
| 27 /// of various methods, sources define the structure of package descriptions | 33 /// [LiveSource]. In addition to defining the behavior of various methods, |
| 28 /// used in [PackageRef]s, [PackageDep]s, and [PackageId]s. There are three | 34 /// sources define the structure of package descriptions used in [PackageRef]s, |
| 29 /// distinct types of description, although in practice most sources use the | 35 /// [PackageDep]s, and [PackageId]s. There are three distinct types of |
| 30 /// same format for one or more of these: | 36 /// description, although in practice most sources use the same format for one |
| 37 /// or more of these: | |
| 31 /// | 38 /// |
| 32 /// * User descriptions. These are included in pubspecs and usually written by | 39 /// * User descriptions. These are included in pubspecs and usually written by |
| 33 /// hand. They're typically more flexible in the formats they allow to | 40 /// hand. They're typically more flexible in the formats they allow to |
| 34 /// optimize for ease of authoring. | 41 /// optimize for ease of authoring. |
| 35 /// | 42 /// |
| 36 /// * Reference descriptions. These are the descriptions in [PackageRef]s and | 43 /// * Reference descriptions. These are the descriptions in [PackageRef]s and |
| 37 /// [PackageDep]. They're parsed directly from user descriptions using | 44 /// [PackageDep]. They're parsed directly from user descriptions using |
| 38 /// [parseRef], and so add no additional information. | 45 /// [parseRef], and so add no additional information. |
| 39 /// | 46 /// |
| 40 /// * ID descriptions. These are the descriptions in [PackageId]s, which | 47 /// * ID descriptions. These are the descriptions in [PackageId]s, which |
| 41 /// uniquely identify and provide the means to locate the concrete code of a | 48 /// uniquely identify and provide the means to locate the concrete code of a |
| 42 /// package. They may contain additional expensive-to-compute information | 49 /// package. They may contain additional expensive-to-compute information |
| 43 /// relative to the corresponding reference descriptions. These are the | 50 /// relative to the corresponding reference descriptions. These are the |
| 44 /// descriptions stored in lock files. | 51 /// descriptions stored in lock files. |
| 45 abstract class Source { | 52 abstract class Source { |
| 46 /// The name of the source. | 53 /// The name of the source. |
| 47 /// | 54 /// |
| 48 /// Should be lower-case, suitable for use in a filename, and unique accross | 55 /// Should be lower-case, suitable for use in a filename, and unique accross |
| 49 /// all sources. | 56 /// all sources. |
| 50 String get name; | 57 String get name; |
| 51 | 58 |
| 52 /// Whether this source can choose between multiple versions of the same | 59 /// Whether this source can choose between multiple versions of the same |
| 53 /// package during version solving. | 60 /// package during version solving. |
| 54 /// | 61 /// |
| 55 /// Defaults to `false`. | 62 /// Defaults to `false`. |
| 56 final bool hasMultipleVersions = false; | 63 final bool hasMultipleVersions = false; |
| 57 | 64 |
| 58 /// Whether or not this source is the default source. | |
| 59 bool get isDefault => systemCache.sources.defaultSource == this; | |
| 60 | |
| 61 /// A cache of pubspecs described by [describe]. | |
| 62 final _pubspecs = <PackageId, Pubspec>{}; | |
| 63 | |
| 64 /// The system cache with which this source is registered. | |
| 65 SystemCache get systemCache { | |
| 66 assert(_systemCache != null); | |
| 67 return _systemCache; | |
| 68 } | |
| 69 | |
| 70 /// The system cache variable. | |
| 71 /// | |
| 72 /// Set by [_bind]. | |
| 73 SystemCache _systemCache; | |
| 74 | |
| 75 /// Records the system cache to which this source belongs. | 65 /// Records the system cache to which this source belongs. |
| 76 /// | 66 /// |
| 77 /// This should only be called once for each source, by | 67 /// This should only be called once for each source, by |
| 78 /// [SystemCache.register]. It should not be overridden by base classes. | 68 /// [SystemCache.register]. It should not be overridden by base classes. |
| 79 void bind(SystemCache systemCache) { | 69 LiveSource bind(SystemCache systemCache); |
| 80 assert(_systemCache == null); | 70 |
| 81 this._systemCache = systemCache; | 71 /// Parses a [PackageRef] from a name and a user-provided [description]. |
| 72 /// | |
| 73 /// When a [Pubspec] is parsed, it reads in the description for each | |
| 74 /// dependency. It is up to the dependency's [Source] to determine how that | |
| 75 /// should be interpreted. This will be called during parsing to validate that | |
| 76 /// the given [description] is well-formed according to this source, and to | |
| 77 /// give the source a chance to canonicalize the description. | |
| 78 /// | |
| 79 /// [containingPath] is the path to the local file (pubspec or lockfile) | |
| 80 /// where this description appears. It may be `null` if the description is | |
| 81 /// coming from some in-memory source (such as pulling down a pubspec from | |
| 82 /// pub.dartlang.org). | |
| 83 /// | |
| 84 /// The description in the returned [PackageRef] need bear no resemblance to | |
| 85 /// the original user-provided description. | |
| 86 /// | |
| 87 /// Throws a [FormatException] if the description is not valid. | |
| 88 PackageRef parseRef(String name, description, {String containingPath}); | |
| 89 | |
| 90 /// Parses a [PackageId] from a name and a serialized description. | |
| 91 /// | |
| 92 /// This only accepts descriptions serialized using [serializeDescription]. It | |
| 93 /// should not be used with user-authored descriptions. | |
| 94 /// | |
| 95 /// Throws a [FormatException] if the description is not valid. | |
| 96 PackageId parseId(String name, Version version, description); | |
| 97 | |
| 98 /// When a [LockFile] is serialized, it uses this method to get the | |
| 99 /// [description] in the right format. | |
| 100 /// | |
| 101 /// [containingPath] is the containing directory of the root package. | |
| 102 dynamic serializeDescription(String containingPath, description) { | |
| 103 return description; | |
| 82 } | 104 } |
| 83 | 105 |
| 106 /// When a package [description] is shown to the user, this is called to | |
| 107 /// convert it into a human-friendly form. | |
| 108 /// | |
| 109 /// By default, it just converts the description to a string, but sources | |
| 110 /// may customize this. [containingPath] is the containing directory of the | |
| 111 /// root package. | |
| 112 String formatDescription(String containingPath, description) { | |
| 113 return description.toString(); | |
| 114 } | |
| 115 | |
| 116 /// Returns whether or not [description1] describes the same package as | |
| 117 /// [description2] for this source. | |
| 118 /// | |
| 119 /// This method should be light-weight. It doesn't need to validate that | |
| 120 /// either package exists. | |
| 121 /// | |
| 122 /// Note that either description may be a reference description or an ID | |
| 123 /// description; they need not be the same type. ID descriptions should be | |
| 124 /// considered equal to the reference descriptions that produced them. | |
| 125 bool descriptionsEqual(description1, description2); | |
| 126 | |
| 127 /// Returns the source's name. | |
| 128 String toString() => name; | |
| 129 } | |
| 130 | |
| 131 /// A source bound to a [SystemCache]. | |
| 132 abstract class LiveSource { | |
|
Bob Nystrom
2016/06/14 23:21:55
"LiveSource" feels a little weird to me. It's sort
nweiz
2016/06/20 20:46:08
"Provider" means more or less the same thing as "S
Bob Nystrom
2016/06/20 21:20:31
Yeah, that bugged me too. I do feel they are a lit
nweiz
2016/06/20 22:03:18
Going with BoundSource as discussed offline.
| |
| 133 /// The unbound source that produced [this]. | |
| 134 Source get source; | |
| 135 | |
| 136 /// The system cache to which [this] is bound. | |
| 137 SystemCache get systemCache; | |
| 138 | |
| 84 /// Get the IDs of all versions that match [ref]. | 139 /// Get the IDs of all versions that match [ref]. |
| 85 /// | 140 /// |
| 86 /// Note that this does *not* require the packages to be downloaded locally, | 141 /// Note that this does *not* require the packages to be downloaded locally, |
| 87 /// which is the point. This is used during version resolution to determine | 142 /// which is the point. This is used during version resolution to determine |
| 88 /// which package versions are available to be downloaded (or already | 143 /// which package versions are available to be downloaded (or already |
| 89 /// downloaded). | 144 /// downloaded). |
| 90 /// | 145 /// |
| 91 /// By default, this assumes that each description has a single version and | 146 /// By default, this assumes that each description has a single version and |
| 92 /// uses [describe] to get that version. | 147 /// uses [describe] to get that version. |
| 93 /// | 148 /// |
| 94 /// Sources should not override this. Instead, they implement [doGetVersions]. | 149 /// Sources should not override this. Instead, they implement [doGetVersions]. |
| 95 Future<List<PackageId>> getVersions(PackageRef ref) { | 150 Future<List<PackageId>> getVersions(PackageRef ref) { |
| 96 if (ref.isRoot) { | 151 if (ref.isRoot) { |
| 97 throw new ArgumentError("Cannot get versions for the root package."); | 152 throw new ArgumentError("Cannot get versions for the root package."); |
| 98 } | 153 } |
| 99 if (ref.source != name) { | 154 if (ref.source != source.name) { |
| 100 throw new ArgumentError("Package $ref does not use source $name."); | 155 throw new ArgumentError("Package $ref does not use source ${source.name}." ); |
| 101 } | 156 } |
| 102 | 157 |
| 103 return doGetVersions(ref); | 158 return doGetVersions(ref); |
| 104 } | 159 } |
| 105 | 160 |
| 106 /// Get the IDs of all versions that match [ref]. | 161 /// Get the IDs of all versions that match [ref]. |
| 107 /// | 162 /// |
| 108 /// Note that this does *not* require the packages to be downloaded locally, | 163 /// Note that this does *not* require the packages to be downloaded locally, |
| 109 /// which is the point. This is used during version resolution to determine | 164 /// which is the point. This is used during version resolution to determine |
| 110 /// which package versions are available to be downloaded (or already | 165 /// which package versions are available to be downloaded (or already |
| 111 /// downloaded). | 166 /// downloaded). |
| 112 /// | 167 /// |
| 113 /// By default, this assumes that each description has a single version and | 168 /// By default, this assumes that each description has a single version and |
| 114 /// uses [describe] to get that version. | 169 /// uses [describe] to get that version. |
| 115 /// | 170 /// |
| 116 /// This method is effectively protected: subclasses must implement it, but | 171 /// This method is effectively protected: subclasses must implement it, but |
| 117 /// external code should not call this. Instead, call [getVersions]. | 172 /// external code should not call this. Instead, call [getVersions]. |
| 118 Future<List<PackageId>> doGetVersions(PackageRef ref); | 173 Future<List<PackageId>> doGetVersions(PackageRef ref); |
| 119 | 174 |
| 175 /// A cache of pubspecs described by [describe]. | |
| 176 final _pubspecs = <PackageId, Pubspec>{}; | |
| 177 | |
| 120 /// Loads the (possibly remote) pubspec for the package version identified by | 178 /// Loads the (possibly remote) pubspec for the package version identified by |
| 121 /// [id]. | 179 /// [id]. |
| 122 /// | 180 /// |
| 123 /// This may be called for packages that have not yet been downloaded during | 181 /// This may be called for packages that have not yet been downloaded during |
| 124 /// the version resolution process. Its results are automatically memoized. | 182 /// the version resolution process. Its results are automatically memoized. |
| 125 /// | 183 /// |
| 126 /// Throws a [DataException] if the pubspec's version doesn't match [id]'s | 184 /// Throws a [DataException] if the pubspec's version doesn't match [id]'s |
| 127 /// version. | 185 /// version. |
| 128 /// | 186 /// |
| 129 /// Sources should not override this. Instead, they implement [doDescribe]. | 187 /// Sources should not override this. Instead, they implement [doDescribe]. |
| 130 Future<Pubspec> describe(PackageId id) async { | 188 Future<Pubspec> describe(PackageId id) async { |
| 131 if (id.isRoot) throw new ArgumentError("Cannot describe the root package."); | 189 if (id.isRoot) throw new ArgumentError("Cannot describe the root package."); |
| 132 if (id.source != name) { | 190 if (id.source != source.name) { |
| 133 throw new ArgumentError("Package $id does not use source $name."); | 191 throw new ArgumentError("Package $id does not use source ${source.name}.") ; |
| 134 } | 192 } |
| 135 | 193 |
| 136 var pubspec = _pubspecs[id]; | 194 var pubspec = _pubspecs[id]; |
| 137 if (pubspec != null) return pubspec; | 195 if (pubspec != null) return pubspec; |
| 138 | 196 |
| 139 // Delegate to the overridden one. | 197 // Delegate to the overridden one. |
| 140 pubspec = await doDescribe(id); | 198 pubspec = await doDescribe(id); |
| 141 if (pubspec.version != id.version) { | 199 if (pubspec.version != id.version) { |
| 142 dataError("The pubspec for $id has version ${pubspec.version}."); | 200 dataError("The pubspec for $id has version ${pubspec.version}."); |
| 143 } | 201 } |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 158 | 216 |
| 159 /// Ensures [id] is available locally and creates a symlink at [symlink] | 217 /// Ensures [id] is available locally and creates a symlink at [symlink] |
| 160 /// pointing it. | 218 /// pointing it. |
| 161 Future get(PackageId id, String symlink); | 219 Future get(PackageId id, String symlink); |
| 162 | 220 |
| 163 /// Returns the directory where this package can (or could) be found locally. | 221 /// Returns the directory where this package can (or could) be found locally. |
| 164 /// | 222 /// |
| 165 /// If the source is cached, this will be a path in the system cache. | 223 /// If the source is cached, this will be a path in the system cache. |
| 166 String getDirectory(PackageId id); | 224 String getDirectory(PackageId id); |
| 167 | 225 |
| 168 /// Parses a [PackageRef] from a name and a user-provided [description]. | |
| 169 /// | |
| 170 /// When a [Pubspec] is parsed, it reads in the description for each | |
| 171 /// dependency. It is up to the dependency's [Source] to determine how that | |
| 172 /// should be interpreted. This will be called during parsing to validate that | |
| 173 /// the given [description] is well-formed according to this source, and to | |
| 174 /// give the source a chance to canonicalize the description. | |
| 175 /// | |
| 176 /// [containingPath] is the path to the local file (pubspec or lockfile) | |
| 177 /// where this description appears. It may be `null` if the description is | |
| 178 /// coming from some in-memory source (such as pulling down a pubspec from | |
| 179 /// pub.dartlang.org). | |
| 180 /// | |
| 181 /// The description in the returned [PackageRef] need bear no resemblance to | |
| 182 /// the original user-provided description. | |
| 183 /// | |
| 184 /// Throws a [FormatException] if the description is not valid. | |
| 185 PackageRef parseRef(String name, description, {String containingPath}); | |
| 186 | |
| 187 /// Parses a [PackageId] from a name and a serialized description. | |
| 188 /// | |
| 189 /// This only accepts descriptions serialized using [serializeDescription]. It | |
| 190 /// should not be used with user-authored descriptions. | |
| 191 /// | |
| 192 /// Throws a [FormatException] if the description is not valid. | |
| 193 PackageId parseId(String name, Version version, description); | |
| 194 | |
| 195 /// When a [LockFile] is serialized, it uses this method to get the | |
| 196 /// [description] in the right format. | |
| 197 /// | |
| 198 /// [containingPath] is the containing directory of the root package. | |
| 199 dynamic serializeDescription(String containingPath, description) { | |
| 200 return description; | |
| 201 } | |
| 202 | |
| 203 /// When a package [description] is shown to the user, this is called to | |
| 204 /// convert it into a human-friendly form. | |
| 205 /// | |
| 206 /// By default, it just converts the description to a string, but sources | |
| 207 /// may customize this. [containingPath] is the containing directory of the | |
| 208 /// root package. | |
| 209 String formatDescription(String containingPath, description) { | |
| 210 return description.toString(); | |
| 211 } | |
| 212 | |
| 213 /// Returns whether or not [description1] describes the same package as | |
| 214 /// [description2] for this source. | |
| 215 /// | |
| 216 /// This method should be light-weight. It doesn't need to validate that | |
| 217 /// either package exists. | |
| 218 /// | |
| 219 /// Note that either description may be a reference description or an ID | |
| 220 /// description; they need not be the same type. ID descriptions should be | |
| 221 /// considered equal to the reference descriptions that produced them. | |
| 222 bool descriptionsEqual(description1, description2); | |
| 223 | |
| 224 /// 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]. |
| 225 /// | 227 /// |
| 226 /// This is notionally protected; it should only be called by subclasses. | 228 /// This is notionally protected; it should only be called by subclasses. |
| 227 void memoizePubspec(PackageId id, Pubspec pubspec) { | 229 void memoizePubspec(PackageId id, Pubspec pubspec) { |
| 228 _pubspecs[id] = pubspec; | 230 _pubspecs[id] = pubspec; |
| 229 } | 231 } |
| 230 | |
| 231 /// Returns the source's name. | |
| 232 String toString() => name; | |
| 233 } | 232 } |
| OLD | NEW |