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 |