Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(299)

Side by Side Diff: utils/pub/version_solver.dart

Issue 11638010: Convert /** comments to /// in pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Respond to review. Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « utils/pub/version.dart ('k') | utils/pub/yaml/composer.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /** 5 /// Attempts to resolve a set of version constraints for a package dependency
6 * Attempts to resolve a set of version constraints for a package dependency 6 /// graph and select an appropriate set of best specific versions for all
7 * graph and select an appropriate set of best specific versions for all 7 /// dependent packages. It works iteratively and tries to reach a stable
8 * dependent packages. It works iteratively and tries to reach a stable 8 /// solution where the constraints of all dependencies are met. If it fails to
9 * solution where the constraints of all dependencies are met. If it fails to 9 /// reach a solution after a certain number of iterations, it assumes the
10 * reach a solution after a certain number of iterations, it assumes the 10 /// dependency graph is unstable and reports and error.
11 * dependency graph is unstable and reports and error. 11 ///
12 * 12 /// There are two fundamental operations in the process of iterating over the
13 * There are two fundamental operations in the process of iterating over the 13 /// graph:
14 * graph: 14 ///
15 * 15 /// 1. Changing the selected concrete version of some package. (This includes
16 * 1. Changing the selected concrete version of some package. (This includes 16 /// adding and removing a package too, which is considering changing the
17 * adding and removing a package too, which is considering changing the 17 /// version to or from "none".) In other words, a node has changed.
18 * version to or from "none".) In other words, a node has changed. 18 /// 2. Changing the version constraint that one package places on another. In
19 * 2. Changing the version constraint that one package places on another. In 19 /// other words, and edge has changed.
20 * other words, and edge has changed. 20 ///
21 * 21 /// Both of these events have a corresponding (potentional) async operation and
22 * Both of these events have a corresponding (potentional) async operation and 22 /// roughly cycle back and forth between each other. When we change the version
23 * roughly cycle back and forth between each other. When we change the version 23 /// of package changes, we asynchronously load the pubspec for the new version.
24 * of package changes, we asynchronously load the pubspec for the new version. 24 /// When that's done, we compare the dependencies of the new version versus the
25 * When that's done, we compare the dependencies of the new version versus the 25 /// old one. For everything that differs, we change those constraints between
26 * old one. For everything that differs, we change those constraints between 26 /// this package and that dependency.
27 * this package and that dependency. 27 ///
28 * 28 /// When a constraint on a package changes, we re-calculate the overall
29 * When a constraint on a package changes, we re-calculate the overall 29 /// constraint on that package. I.e. with a shared dependency, we intersect all
30 * constraint on that package. I.e. with a shared dependency, we intersect all 30 /// of the constraints that its depending packages place on it. If that overall
31 * of the constraints that its depending packages place on it. If that overall 31 /// constraint changes (say from "<3.0.0" to "<2.5.0"), then the currently
32 * constraint changes (say from "<3.0.0" to "<2.5.0"), then the currently 32 /// picked version for that package may fall outside of the new constraint. If
33 * picked version for that package may fall outside of the new constraint. If 33 /// that happens, we find the new best version that meets the updated constraint
34 * that happens, we find the new best version that meets the updated constraint 34 /// and then the change the package to use that version. That cycles back up to
35 * and then the change the package to use that version. That cycles back up to 35 /// the beginning again.
36 * the beginning again.
37 */
38 library version_solver; 36 library version_solver;
39 37
40 import 'dart:json'; 38 import 'dart:json';
41 import 'dart:math'; 39 import 'dart:math';
42 import 'lock_file.dart'; 40 import 'lock_file.dart';
43 import 'log.dart' as log; 41 import 'log.dart' as log;
44 import 'package.dart'; 42 import 'package.dart';
45 import 'pubspec.dart'; 43 import 'pubspec.dart';
46 import 'root_source.dart'; 44 import 'root_source.dart';
47 import 'source.dart'; 45 import 'source.dart';
48 import 'source_registry.dart'; 46 import 'source_registry.dart';
49 import 'utils.dart'; 47 import 'utils.dart';
50 import 'version.dart'; 48 import 'version.dart';
51 49
52 /** 50 /// Attempts to select the best concrete versions for all of the transitive
53 * Attempts to select the best concrete versions for all of the transitive 51 /// dependencies of [root] taking into account all of the [VersionConstraint]s
54 * dependencies of [root] taking into account all of the [VersionConstraint]s 52 /// that those dependencies place on each other and the requirements imposed by
55 * that those dependencies place on each other and the requirements imposed by 53 /// [lockFile]. If successful, completes to a [Map] that maps package names to
56 * [lockFile]. If successful, completes to a [Map] that maps package names to 54 /// the selected version for that package. If it fails, the future will complete
57 * the selected version for that package. If it fails, the future will complete 55 /// with a [NoVersionException], [DisjointConstraintException], or
58 * with a [NoVersionException], [DisjointConstraintException], or 56 /// [CouldNotSolveException].
59 * [CouldNotSolveException].
60 */
61 Future<List<PackageId>> resolveVersions(SourceRegistry sources, Package root, 57 Future<List<PackageId>> resolveVersions(SourceRegistry sources, Package root,
62 LockFile lockFile) { 58 LockFile lockFile) {
63 log.message('Resolving dependencies...'); 59 log.message('Resolving dependencies...');
64 return new VersionSolver(sources, root, lockFile).solve(); 60 return new VersionSolver(sources, root, lockFile).solve();
65 } 61 }
66 62
67 class VersionSolver { 63 class VersionSolver {
68 final SourceRegistry _sources; 64 final SourceRegistry _sources;
69 final Package _root; 65 final Package _root;
70 final LockFile lockFile; 66 final LockFile lockFile;
71 final PubspecCache _pubspecs; 67 final PubspecCache _pubspecs;
72 final Map<String, Dependency> _packages; 68 final Map<String, Dependency> _packages;
73 final Queue<WorkItem> _work; 69 final Queue<WorkItem> _work;
74 int _numIterations = 0; 70 int _numIterations = 0;
75 71
76 VersionSolver(SourceRegistry sources, this._root, this.lockFile) 72 VersionSolver(SourceRegistry sources, this._root, this.lockFile)
77 : _sources = sources, 73 : _sources = sources,
78 _pubspecs = new PubspecCache(sources), 74 _pubspecs = new PubspecCache(sources),
79 _packages = <String, Dependency>{}, 75 _packages = <String, Dependency>{},
80 _work = new Queue<WorkItem>(); 76 _work = new Queue<WorkItem>();
81 77
82 /** 78 /// Tell the version solver to use the most recent version of [package] that
83 * Tell the version solver to use the most recent version of [package] that 79 /// exists in whatever source it's installed from. If that version violates
84 * exists in whatever source it's installed from. If that version violates 80 /// constraints imposed by other dependencies, an error will be raised when
85 * constraints imposed by other dependencies, an error will be raised when 81 /// solving the versions, even if an earlier compatible version exists.
86 * solving the versions, even if an earlier compatible version exists.
87 */
88 void useLatestVersion(String package) { 82 void useLatestVersion(String package) {
89 // TODO(nweiz): How do we want to detect and handle unknown dependencies 83 // TODO(nweiz): How do we want to detect and handle unknown dependencies
90 // here? 84 // here?
91 getDependency(package).useLatestVersion = true; 85 getDependency(package).useLatestVersion = true;
92 lockFile.packages.remove(package); 86 lockFile.packages.remove(package);
93 } 87 }
94 88
95 Future<List<PackageId>> solve() { 89 Future<List<PackageId>> solve() {
96 // Kick off the work by adding the root package at its concrete version to 90 // Kick off the work by adding the root package at its concrete version to
97 // the dependency graph. 91 // the dependency graph.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 _work.add(work); 128 _work.add(work);
135 } 129 }
136 130
137 Dependency getDependency(String package) { 131 Dependency getDependency(String package) {
138 // There can be unused dependencies in the graph, so just create an empty 132 // There can be unused dependencies in the graph, so just create an empty
139 // one if needed. 133 // one if needed.
140 _packages.putIfAbsent(package, () => new Dependency(package)); 134 _packages.putIfAbsent(package, () => new Dependency(package));
141 return _packages[package]; 135 return _packages[package];
142 } 136 }
143 137
144 /** 138 /// Sets the best selected version of [package] to [version].
145 * Sets the best selected version of [package] to [version].
146 */
147 void setVersion(String package, Version version) { 139 void setVersion(String package, Version version) {
148 _packages[package].version = version; 140 _packages[package].version = version;
149 } 141 }
150 142
151 /** 143 /// Returns the most recent version of [dependency] that satisfies all of its
152 * Returns the most recent version of [dependency] that satisfies all of its 144 /// version constraints.
153 * version constraints.
154 */
155 Future<Version> getBestVersion(Dependency dependency) { 145 Future<Version> getBestVersion(Dependency dependency) {
156 return dependency.getVersions().transform((versions) { 146 return dependency.getVersions().transform((versions) {
157 var best = null; 147 var best = null;
158 for (var version in versions) { 148 for (var version in versions) {
159 if (dependency.useLatestVersion || 149 if (dependency.useLatestVersion ||
160 dependency.constraint.allows(version)) { 150 dependency.constraint.allows(version)) {
161 if (best == null || version > best) best = version; 151 if (best == null || version > best) best = version;
162 } 152 }
163 } 153 }
164 154
165 // TODO(rnystrom): Better exception. 155 // TODO(rnystrom): Better exception.
166 if (best == null) { 156 if (best == null) {
167 if (tryUnlockDepender(dependency)) return null; 157 if (tryUnlockDepender(dependency)) return null;
168 throw new NoVersionException(dependency.name, dependency.constraint, 158 throw new NoVersionException(dependency.name, dependency.constraint,
169 dependency._refs); 159 dependency._refs);
170 } else if (!dependency.constraint.allows(best)) { 160 } else if (!dependency.constraint.allows(best)) {
171 if (tryUnlockDepender(dependency)) return null; 161 if (tryUnlockDepender(dependency)) return null;
172 throw new CouldNotUpdateException( 162 throw new CouldNotUpdateException(
173 dependency.name, dependency.constraint, best); 163 dependency.name, dependency.constraint, best);
174 } 164 }
175 165
176 return best; 166 return best;
177 }); 167 });
178 } 168 }
179 169
180 /** 170 /// Looks for a package that depends (transitively) on [dependency] and has
181 * Looks for a package that depends (transitively) on [dependency] and has its 171 /// its version locked in the lockfile. If one is found, enqueues an
182 * version locked in the lockfile. If one is found, enqueues an 172 /// [UnlockPackage] work item for it and returns true. Otherwise, returns
183 * [UnlockPackage] work item for it and returns true. Otherwise, returns 173 /// false.
184 * false. 174 ///
185 * 175 /// This does a breadth-first search; immediate dependers will be unlocked
186 * This does a breadth-first search; immediate dependers will be unlocked 176 /// first, followed by transitive dependers.
187 * first, followed by transitive dependers.
188 */
189 bool tryUnlockDepender(Dependency dependency, [Set<String> seen]) { 177 bool tryUnlockDepender(Dependency dependency, [Set<String> seen]) {
190 if (seen == null) seen = new Set(); 178 if (seen == null) seen = new Set();
191 // Avoid an infinite loop if there are circular dependencies. 179 // Avoid an infinite loop if there are circular dependencies.
192 if (seen.contains(dependency.name)) return false; 180 if (seen.contains(dependency.name)) return false;
193 seen.add(dependency.name); 181 seen.add(dependency.name);
194 182
195 for (var dependerName in dependency.dependers) { 183 for (var dependerName in dependency.dependers) {
196 var depender = getDependency(dependerName); 184 var depender = getDependency(dependerName);
197 var locked = lockFile.packages[dependerName]; 185 var locked = lockFile.packages[dependerName];
198 if (locked != null && depender.version == locked.version) { 186 if (locked != null && depender.version == locked.version) {
(...skipping 18 matching lines...) Expand all
217 dep.source.descriptionsEqual( 205 dep.source.descriptionsEqual(
218 description, lockedPackage.description)) { 206 description, lockedPackage.description)) {
219 description = lockedPackage.description; 207 description = lockedPackage.description;
220 } 208 }
221 209
222 return new PackageId(dep.name, dep.source, dep.version, description); 210 return new PackageId(dep.name, dep.source, dep.version, description);
223 }); 211 });
224 } 212 }
225 } 213 }
226 214
227 /** 215 /// The constraint solver works by iteratively processing a queue of work items.
228 * The constraint solver works by iteratively processing a queue of work items. 216 /// Each item is a single atomic change to the dependency graph. Handling them
229 * Each item is a single atomic change to the dependency graph. Handling them 217 /// in a queue lets us handle asynchrony (resolving versions requires
230 * in a queue lets us handle asynchrony (resolving versions requires information 218 /// information from servers) as well as avoid deeply nested recursion.
231 * from servers) as well as avoid deeply nested recursion.
232 */
233 abstract class WorkItem { 219 abstract class WorkItem {
234 /** 220 /// Processes this work item. Returns a future that completes when the work is
235 * Processes this work item. Returns a future that completes when the work is 221 /// done. If `null` is returned, that means the work has completed
236 * done. If `null` is returned, that means the work has completed 222 /// synchronously and the next item can be started immediately.
237 * synchronously and the next item can be started immediately.
238 */
239 Future process(VersionSolver solver); 223 Future process(VersionSolver solver);
240 } 224 }
241 225
242 /** 226 /// The best selected version for a package has changed to [version]. If the
243 * The best selected version for a package has changed to [version]. If the 227 /// previous version of the package is `null`, that means the package is being
244 * previous version of the package is `null`, that means the package is being 228 /// added to the graph. If [version] is `null`, it is being removed.
245 * added to the graph. If [version] is `null`, it is being removed.
246 */
247 class ChangeVersion implements WorkItem { 229 class ChangeVersion implements WorkItem {
248 /// The name of the package whose version is being changed. 230 /// The name of the package whose version is being changed.
249 final String package; 231 final String package;
250 232
251 /** 233 /// The source of the package whose version is changing.
252 * The source of the package whose version is changing.
253 */
254 final Source source; 234 final Source source;
255 235
256 /** 236 /// The description identifying the package whose version is changing.
257 * The description identifying the package whose version is changing.
258 */
259 final description; 237 final description;
260 238
261 /** 239 /// The new selected version.
262 * The new selected version.
263 */
264 final Version version; 240 final Version version;
265 241
266 ChangeVersion(this.package, this.source, this.description, this.version) { 242 ChangeVersion(this.package, this.source, this.description, this.version) {
267 if (source == null) throw "null source"; 243 if (source == null) throw "null source";
268 } 244 }
269 245
270 Future process(VersionSolver solver) { 246 Future process(VersionSolver solver) {
271 log.fine("Changing $package to version $version."); 247 log.fine("Changing $package to version $version.");
272 248
273 var dependency = solver.getDependency(package); 249 var dependency = solver.getDependency(package);
(...skipping 22 matching lines...) Expand all
296 } 272 }
297 273
298 // Everything that's left is a depdendency that's only in the new 274 // Everything that's left is a depdendency that's only in the new
299 // version of the package. 275 // version of the package.
300 for (var newRef in newDependencyRefs.values) { 276 for (var newRef in newDependencyRefs.values) {
301 solver.enqueue(new AddConstraint(package, newRef)); 277 solver.enqueue(new AddConstraint(package, newRef));
302 } 278 }
303 }); 279 });
304 } 280 }
305 281
306 /** 282 /// Get the dependencies at [version] of the package being changed.
307 * Get the dependencies at [version] of the package being changed.
308 */
309 Future<Map<String, PackageRef>> getDependencyRefs(VersionSolver solver, 283 Future<Map<String, PackageRef>> getDependencyRefs(VersionSolver solver,
310 Version version) { 284 Version version) {
311 // If there is no version, it means no package, so no dependencies. 285 // If there is no version, it means no package, so no dependencies.
312 if (version == null) { 286 if (version == null) {
313 return 287 return
314 new Future<Map<String, PackageRef>>.immediate(<String, PackageRef>{}); 288 new Future<Map<String, PackageRef>>.immediate(<String, PackageRef>{});
315 } 289 }
316 290
317 var id = new PackageId(package, source, version, description); 291 var id = new PackageId(package, source, version, description);
318 return solver._pubspecs.load(id).transform((pubspec) { 292 return solver._pubspecs.load(id).transform((pubspec) {
319 var dependencies = <String, PackageRef>{}; 293 var dependencies = <String, PackageRef>{};
320 for (var dependency in pubspec.dependencies) { 294 for (var dependency in pubspec.dependencies) {
321 dependencies[dependency.name] = dependency; 295 dependencies[dependency.name] = dependency;
322 } 296 }
323 return dependencies; 297 return dependencies;
324 }); 298 });
325 } 299 }
326 } 300 }
327 301
328 /** 302 /// A constraint that a depending package places on a dependent package has
329 * A constraint that a depending package places on a dependent package has 303 /// changed.
330 * changed. 304 ///
331 * 305 /// This is an abstract class that contains logic for updating the dependency
332 * This is an abstract class that contains logic for updating the dependency 306 /// graph once a dependency has changed. Changing the dependency is the
333 * graph once a dependency has changed. Changing the dependency is the 307 /// responsibility of subclasses.
334 * responsibility of subclasses.
335 */
336 abstract class ChangeConstraint implements WorkItem { 308 abstract class ChangeConstraint implements WorkItem {
337 Future process(VersionSolver solver); 309 Future process(VersionSolver solver);
338 310
339 void undo(VersionSolver solver); 311 void undo(VersionSolver solver);
340 312
341 Future _processChange(VersionSolver solver, Dependency oldDependency, 313 Future _processChange(VersionSolver solver, Dependency oldDependency,
342 Dependency newDependency) { 314 Dependency newDependency) {
343 var name = newDependency.name; 315 var name = newDependency.name;
344 var source = oldDependency.source != null ? 316 var source = oldDependency.source != null ?
345 oldDependency.source : newDependency.source; 317 oldDependency.source : newDependency.source;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 return solver.getBestVersion(newDependency).transform((best) { 367 return solver.getBestVersion(newDependency).transform((best) {
396 if (best == null) { 368 if (best == null) {
397 undo(solver); 369 undo(solver);
398 } else if (newDependency.version != best) { 370 } else if (newDependency.version != best) {
399 solver.enqueue(new ChangeVersion(name, source, description, best)); 371 solver.enqueue(new ChangeVersion(name, source, description, best));
400 } 372 }
401 }); 373 });
402 } 374 }
403 } 375 }
404 376
405 /** 377 /// The constraint given by [ref] is being placed by [depender].
406 * The constraint given by [ref] is being placed by [depender].
407 */
408 class AddConstraint extends ChangeConstraint { 378 class AddConstraint extends ChangeConstraint {
409 /** 379 /// The package that has the dependency.
410 * The package that has the dependency.
411 */
412 final String depender; 380 final String depender;
413 381
414 /** 382 /// The package being depended on and the constraints being placed on it. The
415 * The package being depended on and the constraints being placed on it. The 383 /// source, version, and description in this ref are all considered
416 * source, version, and description in this ref are all considered constraints 384 /// constraints on the dependent package.
417 * on the dependent package.
418 */
419 final PackageRef ref; 385 final PackageRef ref;
420 386
421 AddConstraint(this.depender, this.ref); 387 AddConstraint(this.depender, this.ref);
422 388
423 Future process(VersionSolver solver) { 389 Future process(VersionSolver solver) {
424 log.fine("Adding $depender's constraint $ref."); 390 log.fine("Adding $depender's constraint $ref.");
425 391
426 var dependency = solver.getDependency(ref.name); 392 var dependency = solver.getDependency(ref.name);
427 var oldDependency = dependency.clone(); 393 var oldDependency = dependency.clone();
428 dependency.placeConstraint(depender, ref); 394 dependency.placeConstraint(depender, ref);
429 return _processChange(solver, oldDependency, dependency); 395 return _processChange(solver, oldDependency, dependency);
430 } 396 }
431 397
432 void undo(VersionSolver solver) { 398 void undo(VersionSolver solver) {
433 solver.getDependency(ref.name).removeConstraint(depender); 399 solver.getDependency(ref.name).removeConstraint(depender);
434 } 400 }
435 } 401 }
436 402
437 /** 403 /// [depender] is no longer placing a constraint on [dependent].
438 * [depender] is no longer placing a constraint on [dependent].
439 */
440 class RemoveConstraint extends ChangeConstraint { 404 class RemoveConstraint extends ChangeConstraint {
441 /** 405 /// The package that was placing a constraint on [dependent].
442 * The package that was placing a constraint on [dependent].
443 */
444 String depender; 406 String depender;
445 407
446 /** 408 /// The package that was being depended on.
447 * The package that was being depended on.
448 */
449 String dependent; 409 String dependent;
450 410
451 /** The constraint that was removed. */ 411 /// The constraint that was removed.
452 PackageRef _removed; 412 PackageRef _removed;
453 413
454 RemoveConstraint(this.depender, this.dependent); 414 RemoveConstraint(this.depender, this.dependent);
455 415
456 Future process(VersionSolver solver) { 416 Future process(VersionSolver solver) {
457 log.fine("Removing $depender's constraint ($_removed) on $dependent."); 417 log.fine("Removing $depender's constraint ($_removed) on $dependent.");
458 418
459 var dependency = solver.getDependency(dependent); 419 var dependency = solver.getDependency(dependent);
460 var oldDependency = dependency.clone(); 420 var oldDependency = dependency.clone();
461 _removed = dependency.removeConstraint(depender); 421 _removed = dependency.removeConstraint(depender);
462 return _processChange(solver, oldDependency, dependency); 422 return _processChange(solver, oldDependency, dependency);
463 } 423 }
464 424
465 void undo(VersionSolver solver) { 425 void undo(VersionSolver solver) {
466 solver.getDependency(dependent).placeConstraint(depender, _removed); 426 solver.getDependency(dependent).placeConstraint(depender, _removed);
467 } 427 }
468 } 428 }
469 429
470 /** [package]'s version is no longer constrained by the lockfile. */ 430 /// [package]'s version is no longer constrained by the lockfile.
471 class UnlockPackage implements WorkItem { 431 class UnlockPackage implements WorkItem {
472 /** The package being unlocked. */ 432 /// The package being unlocked.
473 Dependency package; 433 Dependency package;
474 434
475 UnlockPackage(this.package); 435 UnlockPackage(this.package);
476 436
477 Future process(VersionSolver solver) { 437 Future process(VersionSolver solver) {
478 log.fine("Unlocking ${package.name}."); 438 log.fine("Unlocking ${package.name}.");
479 439
480 solver.lockFile.packages.remove(package.name); 440 solver.lockFile.packages.remove(package.name);
481 return solver.getBestVersion(package).transform((best) { 441 return solver.getBestVersion(package).transform((best) {
482 if (best == null) return null; 442 if (best == null) return null;
483 solver.enqueue(new ChangeVersion( 443 solver.enqueue(new ChangeVersion(
484 package.name, package.source, package.description, best)); 444 package.name, package.source, package.description, best));
485 }); 445 });
486 } 446 }
487 } 447 }
488 448
489 // TODO(rnystrom): Instead of always pulling from the source (which will mean 449 // TODO(rnystrom): Instead of always pulling from the source (which will mean
490 // hitting a server), we should consider caching pubspecs of uninstalled 450 // hitting a server), we should consider caching pubspecs of uninstalled
491 // packages in the system cache. 451 // packages in the system cache.
492 /** 452 /// Maintains a cache of previously-loaded pubspecs. Used to avoid requesting
493 * Maintains a cache of previously-loaded pubspecs. Used to avoid requesting 453 /// the same pubspec from the server repeatedly.
494 * the same pubspec from the server repeatedly.
495 */
496 class PubspecCache { 454 class PubspecCache {
497 final SourceRegistry _sources; 455 final SourceRegistry _sources;
498 final Map<PackageId, Pubspec> _pubspecs; 456 final Map<PackageId, Pubspec> _pubspecs;
499 457
500 PubspecCache(this._sources) 458 PubspecCache(this._sources)
501 : _pubspecs = new Map<PackageId, Pubspec>(); 459 : _pubspecs = new Map<PackageId, Pubspec>();
502 460
503 /** 461 /// Caches [pubspec] as the [Pubspec] for the package identified by [id].
504 * Caches [pubspec] as the [Pubspec] for the package identified by [id].
505 */
506 void cache(PackageId id, Pubspec pubspec) { 462 void cache(PackageId id, Pubspec pubspec) {
507 _pubspecs[id] = pubspec; 463 _pubspecs[id] = pubspec;
508 } 464 }
509 465
510 /** 466 /// Loads the pubspec for the package identified by [id].
511 * Loads the pubspec for the package identified by [id].
512 */
513 Future<Pubspec> load(PackageId id) { 467 Future<Pubspec> load(PackageId id) {
514 // Complete immediately if it's already cached. 468 // Complete immediately if it's already cached.
515 if (_pubspecs.containsKey(id)) { 469 if (_pubspecs.containsKey(id)) {
516 return new Future<Pubspec>.immediate(_pubspecs[id]); 470 return new Future<Pubspec>.immediate(_pubspecs[id]);
517 } 471 }
518 472
519 return id.describe().transform((pubspec) { 473 return id.describe().transform((pubspec) {
520 // Cache it. 474 // Cache it.
521 _pubspecs[id] = pubspec; 475 _pubspecs[id] = pubspec;
522 return pubspec; 476 return pubspec;
523 }); 477 });
524 } 478 }
525 } 479 }
526 480
527 /** 481 /// Describes one [Package] in the [DependencyGraph] and keeps track of which
528 * Describes one [Package] in the [DependencyGraph] and keeps track of which 482 /// packages depend on it and what constraints they place on it.
529 * packages depend on it and what constraints they place on it.
530 */
531 class Dependency { 483 class Dependency {
532 /** 484 /// The name of the this dependency's package.
533 * The name of the this dependency's package.
534 */
535 final String name; 485 final String name;
536 486
537 /** 487 /// The [PackageRefs] that represent constraints that depending packages have
538 * The [PackageRefs] that represent constraints that depending packages have 488 /// placed on this one.
539 * placed on this one.
540 */
541 final Map<String, PackageRef> _refs; 489 final Map<String, PackageRef> _refs;
542 490
543 /** 491 /// The currently-selected best version for this dependency.
544 * The currently-selected best version for this dependency.
545 */
546 Version version; 492 Version version;
547 493
548 /** 494 /// Whether this dependency should always select the latest version.
549 * Whether this dependency should always select the latest version.
550 */
551 bool useLatestVersion = false; 495 bool useLatestVersion = false;
552 496
553 /** 497 /// Gets whether or not any other packages are currently depending on this
554 * Gets whether or not any other packages are currently depending on this 498 /// one. If `false`, then it means this package is not part of the dependency
555 * one. If `false`, then it means this package is not part of the dependency 499 /// graph and should be omitted.
556 * graph and should be omitted.
557 */
558 bool get isDependedOn => !_refs.isEmpty; 500 bool get isDependedOn => !_refs.isEmpty;
559 501
560 /** The names of all the packages that depend on this dependency. */ 502 /// The names of all the packages that depend on this dependency.
561 Collection<String> get dependers => _refs.keys; 503 Collection<String> get dependers => _refs.keys;
562 504
563 /** 505 /// Gets the overall constraint that all packages are placing on this one.
564 * Gets the overall constraint that all packages are placing on this one. 506 /// If no packages have a constraint on this one (which can happen when this
565 * If no packages have a constraint on this one (which can happen when this 507 /// package is in the process of being added to the graph), returns `null`.
566 * package is in the process of being added to the graph), returns `null`.
567 */
568 VersionConstraint get constraint { 508 VersionConstraint get constraint {
569 if (_refs.isEmpty) return null; 509 if (_refs.isEmpty) return null;
570 return new VersionConstraint.intersection( 510 return new VersionConstraint.intersection(
571 _refs.values.map((ref) => ref.constraint)); 511 _refs.values.map((ref) => ref.constraint));
572 } 512 }
573 513
574 /// The source of this dependency's package. 514 /// The source of this dependency's package.
575 Source get source { 515 Source get source {
576 var canonical = _canonicalRef(); 516 var canonical = _canonicalRef();
577 if (canonical == null) return null; 517 if (canonical == null) return null;
(...skipping 21 matching lines...) Expand all
599 } 539 }
600 540
601 Dependency(this.name) 541 Dependency(this.name)
602 : _refs = <String, PackageRef>{}; 542 : _refs = <String, PackageRef>{};
603 543
604 Dependency._clone(Dependency other) 544 Dependency._clone(Dependency other)
605 : name = other.name, 545 : name = other.name,
606 version = other.version, 546 version = other.version,
607 _refs = new Map<String, PackageRef>.from(other._refs); 547 _refs = new Map<String, PackageRef>.from(other._refs);
608 548
609 /** Creates a copy of this dependency. */ 549 /// Creates a copy of this dependency.
610 Dependency clone() => new Dependency._clone(this); 550 Dependency clone() => new Dependency._clone(this);
611 551
612 /// Return a list of available versions for this dependency. 552 /// Return a list of available versions for this dependency.
613 Future<List<Version>> getVersions() => source.getVersions(name, description); 553 Future<List<Version>> getVersions() => source.getVersions(name, description);
614 554
615 /** 555 /// Places [ref] as a constraint from [package] onto this.
616 * Places [ref] as a constraint from [package] onto this.
617 */
618 void placeConstraint(String package, PackageRef ref) { 556 void placeConstraint(String package, PackageRef ref) {
619 var requiredDepender = _requiredDepender(); 557 var requiredDepender = _requiredDepender();
620 if (requiredDepender != null) { 558 if (requiredDepender != null) {
621 var required = _refs[requiredDepender]; 559 var required = _refs[requiredDepender];
622 if (required.source.name != ref.source.name) { 560 if (required.source.name != ref.source.name) {
623 throw new SourceMismatchException(name, 561 throw new SourceMismatchException(name,
624 requiredDepender, required.source, package, ref.source); 562 requiredDepender, required.source, package, ref.source);
625 } else if (!required.source.descriptionsEqual( 563 } else if (!required.source.descriptionsEqual(
626 required.description, ref.description)) { 564 required.description, ref.description)) {
627 throw new DescriptionMismatchException(name, 565 throw new DescriptionMismatchException(name,
(...skipping 13 matching lines...) Expand all
641 var dependers = _refs.keys; 579 var dependers = _refs.keys;
642 if (dependers.length == 1) { 580 if (dependers.length == 1) {
643 var depender = dependers[0]; 581 var depender = dependers[0];
644 if (_refs[depender].source is RootSource) return null; 582 if (_refs[depender].source is RootSource) return null;
645 return depender; 583 return depender;
646 } 584 }
647 585
648 return dependers[1]; 586 return dependers[1];
649 } 587 }
650 588
651 /** 589 /// Removes the constraint from [package] onto this.
652 * Removes the constraint from [package] onto this.
653 */
654 PackageRef removeConstraint(String package) => _refs.remove(package); 590 PackageRef removeConstraint(String package) => _refs.remove(package);
655 } 591 }
656 592
657 /** 593 /// Exception thrown when the [VersionConstraint] used to match a package is
658 * Exception thrown when the [VersionConstraint] used to match a package is 594 /// valid (i.e. non-empty), but there are no released versions of the package
659 * valid (i.e. non-empty), but there are no released versions of the package 595 /// that fit that constraint.
660 * that fit that constraint.
661 */
662 class NoVersionException implements Exception { 596 class NoVersionException implements Exception {
663 final String package; 597 final String package;
664 final VersionConstraint constraint; 598 final VersionConstraint constraint;
665 final Map<String, PackageRef> _dependencies; 599 final Map<String, PackageRef> _dependencies;
666 600
667 NoVersionException(this.package, this.constraint, this._dependencies); 601 NoVersionException(this.package, this.constraint, this._dependencies);
668 602
669 String toString() { 603 String toString() {
670 var buffer = new StringBuffer(); 604 var buffer = new StringBuffer();
671 buffer.add("Package '$package' has no versions that match $constraint " 605 buffer.add("Package '$package' has no versions that match $constraint "
672 "derived from:\n"); 606 "derived from:\n");
673 607
674 var keys = new List.from(_dependencies.keys); 608 var keys = new List.from(_dependencies.keys);
675 keys.sort(); 609 keys.sort();
676 610
677 for (var key in keys) { 611 for (var key in keys) {
678 buffer.add("- '$key' depends on version " 612 buffer.add("- '$key' depends on version "
679 "${_dependencies[key].constraint}\n"); 613 "${_dependencies[key].constraint}\n");
680 } 614 }
681 615
682 return buffer.toString(); 616 return buffer.toString();
683 } 617 }
684 } 618 }
685 619
686 // TODO(rnystrom): Report the list of depending packages and their constraints. 620 // TODO(rnystrom): Report the list of depending packages and their constraints.
687 /** 621 /// Exception thrown when the most recent version of [package] must be selected,
688 * Exception thrown when the most recent version of [package] must be selected, 622 /// but doesn't match the [VersionConstraint] imposed on the package.
689 * but doesn't match the [VersionConstraint] imposed on the package.
690 */
691 class CouldNotUpdateException implements Exception { 623 class CouldNotUpdateException implements Exception {
692 final String package; 624 final String package;
693 final VersionConstraint constraint; 625 final VersionConstraint constraint;
694 final Version best; 626 final Version best;
695 627
696 CouldNotUpdateException(this.package, this.constraint, this.best); 628 CouldNotUpdateException(this.package, this.constraint, this.best);
697 629
698 String toString() => 630 String toString() =>
699 "The latest version of '$package', $best, does not match $constraint."; 631 "The latest version of '$package', $best, does not match $constraint.";
700 } 632 }
701 633
702 /** 634 /// Exception thrown when the [VersionConstraint] used to match a package is
703 * Exception thrown when the [VersionConstraint] used to match a package is 635 /// the empty set: in other words, multiple packages depend on it and have
704 * the empty set: in other words, multiple packages depend on it and have 636 /// conflicting constraints that have no overlap.
705 * conflicting constraints that have no overlap.
706 */
707 class DisjointConstraintException implements Exception { 637 class DisjointConstraintException implements Exception {
708 final String package; 638 final String package;
709 final Map<String, PackageRef> _dependencies; 639 final Map<String, PackageRef> _dependencies;
710 640
711 DisjointConstraintException(this.package, this._dependencies); 641 DisjointConstraintException(this.package, this._dependencies);
712 642
713 String toString() { 643 String toString() {
714 var buffer = new StringBuffer(); 644 var buffer = new StringBuffer();
715 buffer.add("Incompatible version constraints on '$package':\n"); 645 buffer.add("Incompatible version constraints on '$package':\n");
716 646
717 var keys = new List.from(_dependencies.keys); 647 var keys = new List.from(_dependencies.keys);
718 keys.sort(); 648 keys.sort();
719 649
720 for (var key in keys) { 650 for (var key in keys) {
721 buffer.add("- '$key' depends on version " 651 buffer.add("- '$key' depends on version "
722 "${_dependencies[key].constraint}\n"); 652 "${_dependencies[key].constraint}\n");
723 } 653 }
724 654
725 return buffer.toString(); 655 return buffer.toString();
726 } 656 }
727 } 657 }
728 658
729 /** 659 /// Exception thrown when the [VersionSolver] fails to find a solution after a
730 * Exception thrown when the [VersionSolver] fails to find a solution after a 660 /// certain number of iterations.
731 * certain number of iterations.
732 */
733 class CouldNotSolveException implements Exception { 661 class CouldNotSolveException implements Exception {
734 CouldNotSolveException(); 662 CouldNotSolveException();
735 663
736 String toString() => 664 String toString() =>
737 "Could not find a solution that met all version constraints."; 665 "Could not find a solution that met all version constraints.";
738 } 666 }
739 667
740 /** 668 /// Exception thrown when two packages with the same name but different sources
741 * Exception thrown when two packages with the same name but different sources 669 /// are depended upon.
742 * are depended upon.
743 */
744 class SourceMismatchException implements Exception { 670 class SourceMismatchException implements Exception {
745 final String package; 671 final String package;
746 final String depender1; 672 final String depender1;
747 final Source source1; 673 final Source source1;
748 final String depender2; 674 final String depender2;
749 final Source source2; 675 final Source source2;
750 676
751 SourceMismatchException(this.package, this.depender1, this.source1, 677 SourceMismatchException(this.package, this.depender1, this.source1,
752 this.depender2, this.source2); 678 this.depender2, this.source2);
753 679
754 String toString() { 680 String toString() {
755 return "Incompatible dependencies on '$package':\n" 681 return "Incompatible dependencies on '$package':\n"
756 "- '$depender1' depends on it from source '$source1'\n" 682 "- '$depender1' depends on it from source '$source1'\n"
757 "- '$depender2' depends on it from source '$source2'"; 683 "- '$depender2' depends on it from source '$source2'";
758 } 684 }
759 } 685 }
760 686
761 /** 687 /// Exception thrown when two packages with the same name and source but
762 * Exception thrown when two packages with the same name and source but 688 /// different descriptions are depended upon.
763 * different descriptions are depended upon.
764 */
765 class DescriptionMismatchException implements Exception { 689 class DescriptionMismatchException implements Exception {
766 final String package; 690 final String package;
767 final String depender1; 691 final String depender1;
768 final description1; 692 final description1;
769 final String depender2; 693 final String depender2;
770 final description2; 694 final description2;
771 695
772 DescriptionMismatchException(this.package, this.depender1, this.description1, 696 DescriptionMismatchException(this.package, this.depender1, this.description1,
773 this.depender2, this.description2); 697 this.depender2, this.description2);
774 698
775 String toString() { 699 String toString() {
776 // TODO(nweiz): Dump descriptions to YAML when that's supported. 700 // TODO(nweiz): Dump descriptions to YAML when that's supported.
777 return "Incompatible dependencies on '$package':\n" 701 return "Incompatible dependencies on '$package':\n"
778 "- '$depender1' depends on it with description " 702 "- '$depender1' depends on it with description "
779 "${JSON.stringify(description1)}\n" 703 "${JSON.stringify(description1)}\n"
780 "- '$depender2' depends on it with description " 704 "- '$depender2' depends on it with description "
781 "${JSON.stringify(description2)}"; 705 "${JSON.stringify(description2)}";
782 } 706 }
783 } 707 }
OLDNEW
« no previous file with comments | « utils/pub/version.dart ('k') | utils/pub/yaml/composer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698