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 |