| 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 /** | 5 /** |
| 6 * 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 |
| 7 * 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 |
| 8 * dependent packages. It works iteratively and tries to reach a stable | 8 * dependent packages. It works iteratively and tries to reach a stable |
| 9 * 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 |
| 10 * 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 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 final Map<String, Dependency> _packages; | 69 final Map<String, Dependency> _packages; |
| 70 final Queue<WorkItem> _work; | 70 final Queue<WorkItem> _work; |
| 71 int _numIterations = 0; | 71 int _numIterations = 0; |
| 72 | 72 |
| 73 VersionSolver(SourceRegistry sources, this._root, this.lockFile) | 73 VersionSolver(SourceRegistry sources, this._root, this.lockFile) |
| 74 : _sources = sources, | 74 : _sources = sources, |
| 75 _pubspecs = new PubspecCache(sources), | 75 _pubspecs = new PubspecCache(sources), |
| 76 _packages = <String, Dependency>{}, | 76 _packages = <String, Dependency>{}, |
| 77 _work = new Queue<WorkItem>(); | 77 _work = new Queue<WorkItem>(); |
| 78 | 78 |
| 79 /** |
| 80 * Tell the version solver to use the most recent version of [package] that |
| 81 * exists in whatever source it's installed from. If that version violates |
| 82 * constraints imposed by other dependencies, an error will be raised when |
| 83 * solving the versions, even if an earlier compatible version exists. |
| 84 */ |
| 85 void useLatestVersion(String package) { |
| 86 // TODO(nweiz): How do we want to detect and handle unknown dependencies |
| 87 // here? |
| 88 getDependency(package).useLatestVersion = true; |
| 89 lockFile.packages.remove(package); |
| 90 } |
| 91 |
| 79 Future<List<PackageId>> solve() { | 92 Future<List<PackageId>> solve() { |
| 80 // Kick off the work by adding the root package at its concrete version to | 93 // Kick off the work by adding the root package at its concrete version to |
| 81 // the dependency graph. | 94 // the dependency graph. |
| 82 var ref = new PackageRef(new RootSource(_root), _root.version, _root.name); | 95 var ref = new PackageRef(new RootSource(_root), _root.version, _root.name); |
| 83 enqueue(new AddConstraint('(entrypoint)', ref)); | 96 enqueue(new AddConstraint('(entrypoint)', ref)); |
| 84 _pubspecs.cache(ref.atVersion(_root.version), _root.pubspec); | 97 _pubspecs.cache(ref.atVersion(_root.version), _root.pubspec); |
| 85 | 98 |
| 86 Future processNextWorkItem(_) { | 99 Future processNextWorkItem(_) { |
| 87 while (true) { | 100 while (true) { |
| 88 // Stop if we are done. | 101 // Stop if we are done. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 return _packages[package]; | 137 return _packages[package]; |
| 125 } | 138 } |
| 126 | 139 |
| 127 /** | 140 /** |
| 128 * Sets the best selected version of [package] to [version]. | 141 * Sets the best selected version of [package] to [version]. |
| 129 */ | 142 */ |
| 130 void setVersion(String package, Version version) { | 143 void setVersion(String package, Version version) { |
| 131 _packages[package].version = version; | 144 _packages[package].version = version; |
| 132 } | 145 } |
| 133 | 146 |
| 147 /** |
| 148 * Returns the most recent version of [dependency] that satisfies all of its |
| 149 * version constraints. |
| 150 */ |
| 151 Future<Version> getBestVersion(Dependency dependency) { |
| 152 return dependency.source.getVersions(dependency.description) |
| 153 .transform((versions) { |
| 154 var best = null; |
| 155 for (var version in versions) { |
| 156 if (dependency.useLatestVersion || |
| 157 dependency.constraint.allows(version)) { |
| 158 if (best == null || version > best) best = version; |
| 159 } |
| 160 } |
| 161 |
| 162 // TODO(rnystrom): Better exception. |
| 163 if (best == null) { |
| 164 if (tryUnlockDepender(dependency)) return null; |
| 165 throw new NoVersionException(dependency.name, dependency.constraint); |
| 166 } else if (!dependency.constraint.allows(best)) { |
| 167 if (tryUnlockDepender(dependency)) return null; |
| 168 throw new CouldNotUpdateException( |
| 169 dependency.name, dependency.constraint, best); |
| 170 } |
| 171 |
| 172 return best; |
| 173 }); |
| 174 } |
| 175 |
| 176 /** |
| 177 * Looks for a package that depends (transitively) on [dependency] and has its |
| 178 * version locked in the lockfile. If one is found, enqueues an |
| 179 * [UnlockPackage] work item for it and returns true. Otherwise, returns |
| 180 * false. |
| 181 * |
| 182 * This does a breadth-first search; immediate dependers will be unlocked |
| 183 * first, followed by transitive dependers. |
| 184 */ |
| 185 bool tryUnlockDepender(Dependency dependency) { |
| 186 for (var dependerName in dependency.dependers) { |
| 187 var depender = getDependency(dependerName); |
| 188 var locked = lockFile.packages[dependerName]; |
| 189 if (locked != null && depender.version == locked.version) { |
| 190 enqueue(new UnlockPackage(depender)); |
| 191 return true; |
| 192 } |
| 193 } |
| 194 return dependency.dependers.map(getDependency).some(tryUnlockDepender); |
| 195 } |
| 196 |
| 134 List<PackageId> buildResults() { | 197 List<PackageId> buildResults() { |
| 135 return _packages.getValues().filter((dep) => dep.isDependedOn).map((dep) { | 198 return _packages.getValues().filter((dep) => dep.isDependedOn).map((dep) { |
| 136 var description = dep.description; | 199 var description = dep.description; |
| 137 | 200 |
| 138 // If the lockfile contains a fully-resolved description for the package, | 201 // If the lockfile contains a fully-resolved description for the package, |
| 139 // use that. This allows e.g. Git to ensure that the same commit is used. | 202 // use that. This allows e.g. Git to ensure that the same commit is used. |
| 140 var lockedPackage = lockFile.packages[dep.name]; | 203 var lockedPackage = lockFile.packages[dep.name]; |
| 141 if (lockedPackage != null && lockedPackage.version == dep.version && | 204 if (lockedPackage != null && lockedPackage.version == dep.version && |
| 142 lockedPackage.source.name == dep.source.name && | 205 lockedPackage.source.name == dep.source.name && |
| 143 dep.source.descriptionsEqual( | 206 dep.source.descriptionsEqual( |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 * A constraint that a depending package places on a dependent package has | 318 * A constraint that a depending package places on a dependent package has |
| 256 * changed. | 319 * changed. |
| 257 * | 320 * |
| 258 * This is an abstract class that contains logic for updating the dependency | 321 * This is an abstract class that contains logic for updating the dependency |
| 259 * graph once a dependency has changed. Changing the dependency is the | 322 * graph once a dependency has changed. Changing the dependency is the |
| 260 * responsibility of subclasses. | 323 * responsibility of subclasses. |
| 261 */ | 324 */ |
| 262 class ChangeConstraint implements WorkItem { | 325 class ChangeConstraint implements WorkItem { |
| 263 abstract Future process(VersionSolver solver); | 326 abstract Future process(VersionSolver solver); |
| 264 | 327 |
| 265 Future _processChange(VersionSolver solver, Source source, description, | 328 abstract void undo(VersionSolver solver); |
| 266 Dependency dependency, VersionConstraint oldConstraint, | 329 |
| 267 VersionConstraint newConstraint) { | 330 Future _processChange(VersionSolver solver, Dependency oldDependency, |
| 268 var name = dependency.name; | 331 Dependency newDependency) { |
| 332 var name = newDependency.name; |
| 333 var source = oldDependency.source != null ? |
| 334 oldDependency.source : newDependency.source; |
| 335 var description = oldDependency.description != null ? |
| 336 oldDependency.description : newDependency.description; |
| 337 var oldConstraint = oldDependency.constraint; |
| 338 var newConstraint = newDependency.constraint; |
| 269 | 339 |
| 270 // If the package is over-constrained, i.e. the packages depending have | 340 // If the package is over-constrained, i.e. the packages depending have |
| 271 // disjoint constraints, then stop. | 341 // disjoint constraints, then try unlocking a depender that's locked by the |
| 342 // lockfile. If there are no remaining locked dependencies, throw an error. |
| 272 if (newConstraint != null && newConstraint.isEmpty) { | 343 if (newConstraint != null && newConstraint.isEmpty) { |
| 344 if (solver.tryUnlockDepender(newDependency)) { |
| 345 undo(solver); |
| 346 return null; |
| 347 } |
| 348 |
| 273 throw new DisjointConstraintException(name); | 349 throw new DisjointConstraintException(name); |
| 274 } | 350 } |
| 275 | 351 |
| 276 // If this constraint change didn't cause the overall constraint on the | 352 // If this constraint change didn't cause the overall constraint on the |
| 277 // package to change, then we don't need to do any further work. | 353 // package to change, then we don't need to do any further work. |
| 278 if (oldConstraint == newConstraint) return null; | 354 if (oldConstraint == newConstraint) return null; |
| 279 | 355 |
| 280 // If the dependency has been cut free from the graph, just remove it. | 356 // If the dependency has been cut free from the graph, just remove it. |
| 281 if (!dependency.isDependedOn) { | 357 if (!newDependency.isDependedOn) { |
| 282 solver.enqueue(new ChangeVersion(source, description, null)); | 358 solver.enqueue(new ChangeVersion(source, description, null)); |
| 283 return null; | 359 return null; |
| 284 } | 360 } |
| 285 | 361 |
| 286 // If the dependency is on the root package, then we don't need to do | 362 // If the dependency is on the root package, then we don't need to do |
| 287 // anything since it's already at the best version. | 363 // anything since it's already at the best version. |
| 288 if (name == solver._root.name) { | 364 if (name == solver._root.name) { |
| 289 solver.enqueue(new ChangeVersion( | 365 solver.enqueue(new ChangeVersion( |
| 290 source, description, solver._root.version)); | 366 source, description, solver._root.version)); |
| 291 return null; | 367 return null; |
| 292 } | 368 } |
| 293 | 369 |
| 294 // If the dependency is on a package in the lockfile, use the lockfile's | 370 // If the dependency is on a package in the lockfile, use the lockfile's |
| 295 // version for that package if it's valid given the other constraints. | 371 // version for that package if it's valid given the other constraints. |
| 296 var lockedPackage = solver.lockFile.packages[name]; | 372 var lockedPackage = solver.lockFile.packages[name]; |
| 297 if (lockedPackage != null) { | 373 if (lockedPackage != null) { |
| 298 var lockedVersion = lockedPackage.version; | 374 var lockedVersion = lockedPackage.version; |
| 299 if (newConstraint.allows(lockedVersion)) { | 375 if (newConstraint.allows(lockedVersion)) { |
| 300 solver.enqueue(new ChangeVersion(source, description, lockedVersion)); | 376 solver.enqueue(new ChangeVersion(source, description, lockedVersion)); |
| 301 return null; | 377 return null; |
| 302 } | 378 } |
| 303 } | 379 } |
| 304 | 380 |
| 305 // The constraint has changed, so see what the best version of the package | 381 // The constraint has changed, so see what the best version of the package |
| 306 // that meets the new constraint is. | 382 // that meets the new constraint is. |
| 307 return source.getVersions(description).transform((versions) { | 383 return solver.getBestVersion(newDependency).transform((best) { |
| 308 var best = null; | 384 if (best == null) { |
| 309 for (var version in versions) { | 385 undo(solver); |
| 310 if (newConstraint.allows(version)) { | 386 } else if (newDependency.version != best) { |
| 311 if (best == null || version > best) best = version; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 // TODO(rnystrom): Better exception. | |
| 316 if (best == null) throw new NoVersionException(name, newConstraint); | |
| 317 | |
| 318 if (dependency.version != best) { | |
| 319 solver.enqueue(new ChangeVersion(source, description, best)); | 387 solver.enqueue(new ChangeVersion(source, description, best)); |
| 320 } | 388 } |
| 321 }); | 389 }); |
| 322 } | 390 } |
| 323 } | 391 } |
| 324 | 392 |
| 325 /** | 393 /** |
| 326 * The constraint given by [ref] is being placed by [depender]. | 394 * The constraint given by [ref] is being placed by [depender]. |
| 327 */ | 395 */ |
| 328 class AddConstraint extends ChangeConstraint { | 396 class AddConstraint extends ChangeConstraint { |
| 329 /** | 397 /** |
| 330 * The package that has the dependency. | 398 * The package that has the dependency. |
| 331 */ | 399 */ |
| 332 final String depender; | 400 final String depender; |
| 333 | 401 |
| 334 /** | 402 /** |
| 335 * The package being depended on and the constraints being placed on it. The | 403 * The package being depended on and the constraints being placed on it. The |
| 336 * source, version, and description in this ref are all considered constraints | 404 * source, version, and description in this ref are all considered constraints |
| 337 * on the dependent package. | 405 * on the dependent package. |
| 338 */ | 406 */ |
| 339 final PackageRef ref; | 407 final PackageRef ref; |
| 340 | 408 |
| 341 AddConstraint(this.depender, this.ref); | 409 AddConstraint(this.depender, this.ref); |
| 342 | 410 |
| 343 Future process(VersionSolver solver) { | 411 Future process(VersionSolver solver) { |
| 344 var dependency = solver.getDependency(ref.name); | 412 var dependency = solver.getDependency(ref.name); |
| 345 var oldConstraint = dependency.constraint; | 413 var oldDependency = dependency.clone(); |
| 346 dependency.placeConstraint(depender, ref); | 414 dependency.placeConstraint(depender, ref); |
| 347 var newConstraint = dependency.constraint; | 415 return _processChange(solver, oldDependency, dependency); |
| 348 return _processChange(solver, ref.source, ref.description, dependency, | 416 } |
| 349 oldConstraint, newConstraint); | 417 |
| 418 void undo(VersionSolver solver) { |
| 419 solver.getDependency(ref.name).removeConstraint(depender); |
| 350 } | 420 } |
| 351 } | 421 } |
| 352 | 422 |
| 353 /** | 423 /** |
| 354 * [depender] is no longer placing a constraint on [dependent]. | 424 * [depender] is no longer placing a constraint on [dependent]. |
| 355 */ | 425 */ |
| 356 class RemoveConstraint extends ChangeConstraint { | 426 class RemoveConstraint extends ChangeConstraint { |
| 357 /** | 427 /** |
| 358 * The package that was placing a constraint on [dependent]. | 428 * The package that was placing a constraint on [dependent]. |
| 359 */ | 429 */ |
| 360 String depender; | 430 String depender; |
| 361 | 431 |
| 362 /** | 432 /** |
| 363 * The package that was being depended on. | 433 * The package that was being depended on. |
| 364 */ | 434 */ |
| 365 String dependent; | 435 String dependent; |
| 366 | 436 |
| 437 /** The constraint that was removed. */ |
| 438 PackageRef _removed; |
| 439 |
| 367 RemoveConstraint(this.depender, this.dependent); | 440 RemoveConstraint(this.depender, this.dependent); |
| 368 | 441 |
| 369 Future process(VersionSolver solver) { | 442 Future process(VersionSolver solver) { |
| 370 var dependency = solver.getDependency(dependent); | 443 var dependency = solver.getDependency(dependent); |
| 371 var oldConstraint = dependency.constraint; | 444 var oldDependency = dependency.clone(); |
| 372 var source = dependency.source; | 445 _removed = dependency.removeConstraint(depender); |
| 373 var description = dependency.description; | 446 return _processChange(solver, oldDependency, dependency); |
| 374 dependency.removeConstraint(depender); | 447 } |
| 375 var newConstraint = dependency.constraint; | 448 |
| 376 return _processChange(solver, source, description, dependency, | 449 void undo() { |
| 377 oldConstraint, newConstraint); | 450 solver.getDependency(dependent).placeConstraint(depender, _removed); |
| 378 } | 451 } |
| 379 } | 452 } |
| 380 | 453 |
| 454 /** [package]'s version is no longer constrained by the lockfile. */ |
| 455 class UnlockPackage implements WorkItem { |
| 456 /** The package being unlocked. */ |
| 457 Dependency package; |
| 458 |
| 459 UnlockPackage(this.package); |
| 460 |
| 461 Future process(VersionSolver solver) { |
| 462 solver.lockFile.packages.remove(package.name); |
| 463 return solver.getBestVersion(package).transform((best) { |
| 464 if (best == null) return null; |
| 465 solver.enqueue(new ChangeVersion( |
| 466 package.source, package.description, best)); |
| 467 }); |
| 468 } |
| 469 } |
| 470 |
| 381 // TODO(rnystrom): Instead of always pulling from the source (which will mean | 471 // TODO(rnystrom): Instead of always pulling from the source (which will mean |
| 382 // hitting a server), we should consider caching pubspecs of uninstalled | 472 // hitting a server), we should consider caching pubspecs of uninstalled |
| 383 // packages in the system cache. | 473 // packages in the system cache. |
| 384 /** | 474 /** |
| 385 * Maintains a cache of previously-loaded pubspecs. Used to avoid requesting | 475 * Maintains a cache of previously-loaded pubspecs. Used to avoid requesting |
| 386 * the same pubspec from the server repeatedly. | 476 * the same pubspec from the server repeatedly. |
| 387 */ | 477 */ |
| 388 class PubspecCache { | 478 class PubspecCache { |
| 389 final SourceRegistry _sources; | 479 final SourceRegistry _sources; |
| 390 final Map<PackageId, Pubspec> _pubspecs; | 480 final Map<PackageId, Pubspec> _pubspecs; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 446 * according to [source]. | 536 * according to [source]. |
| 447 */ | 537 */ |
| 448 var description; | 538 var description; |
| 449 | 539 |
| 450 /** | 540 /** |
| 451 * The currently-selected best version for this dependency. | 541 * The currently-selected best version for this dependency. |
| 452 */ | 542 */ |
| 453 Version version; | 543 Version version; |
| 454 | 544 |
| 455 /** | 545 /** |
| 546 * Whether this dependency should always select the latest version. |
| 547 */ |
| 548 bool useLatestVersion = false; |
| 549 |
| 550 /** |
| 456 * Gets whether or not any other packages are currently depending on this | 551 * Gets whether or not any other packages are currently depending on this |
| 457 * one. If `false`, then it means this package is not part of the dependency | 552 * one. If `false`, then it means this package is not part of the dependency |
| 458 * graph and should be omitted. | 553 * graph and should be omitted. |
| 459 */ | 554 */ |
| 460 bool get isDependedOn() => !_refs.isEmpty(); | 555 bool get isDependedOn() => !_refs.isEmpty(); |
| 461 | 556 |
| 557 /** The names of all the packages that depend on this dependency. */ |
| 558 Collection<String> get dependers() => _refs.getKeys(); |
| 559 |
| 462 /** | 560 /** |
| 463 * Gets the overall constraint that all packages are placing on this one. | 561 * Gets the overall constraint that all packages are placing on this one. |
| 464 * If no packages have a constraint on this one (which can happen when this | 562 * If no packages have a constraint on this one (which can happen when this |
| 465 * package is in the process of being added to the graph), returns `null`. | 563 * package is in the process of being added to the graph), returns `null`. |
| 466 */ | 564 */ |
| 467 VersionConstraint get constraint() { | 565 VersionConstraint get constraint() { |
| 468 if (_refs.isEmpty()) return null; | 566 if (_refs.isEmpty()) return null; |
| 469 return new VersionConstraint.intersect( | 567 return new VersionConstraint.intersect( |
| 470 _refs.getValues().map((ref) => ref.constraint)); | 568 _refs.getValues().map((ref) => ref.constraint)); |
| 471 } | 569 } |
| 472 | 570 |
| 473 Dependency(this.name) | 571 Dependency(this.name) |
| 474 : _refs = <String, PackageRef>{}; | 572 : _refs = <String, PackageRef>{}; |
| 475 | 573 |
| 574 Dependency._clone(Dependency other) |
| 575 : name = other.name, |
| 576 source = other.source, |
| 577 description = other.description, |
| 578 version = other.version, |
| 579 _refs = new Map<String, PackageRef>.from(other._refs); |
| 580 |
| 581 /** Creates a copy of this dependency. */ |
| 582 Dependency clone() => new Dependency._clone(this); |
| 583 |
| 476 /** | 584 /** |
| 477 * Places [ref] as a constraint from [package] onto this. | 585 * Places [ref] as a constraint from [package] onto this. |
| 478 */ | 586 */ |
| 479 void placeConstraint(String package, PackageRef ref) { | 587 void placeConstraint(String package, PackageRef ref) { |
| 480 // If this isn't the first constraint placed on this package, make sure it | 588 // If this isn't the first constraint placed on this package, make sure it |
| 481 // matches the source and description of past constraints. | 589 // matches the source and description of past constraints. |
| 482 if (_refs.isEmpty()) { | 590 if (_refs.isEmpty()) { |
| 483 source = ref.source; | 591 source = ref.source; |
| 484 description = ref.description; | 592 description = ref.description; |
| 485 } else if (source.name != ref.source.name) { | 593 } else if (source.name != ref.source.name) { |
| 486 throw new SourceMismatchException(name, source, ref.source); | 594 throw new SourceMismatchException(name, source, ref.source); |
| 487 } else if (!source.descriptionsEqual(description, ref.description)) { | 595 } else if (!source.descriptionsEqual(description, ref.description)) { |
| 488 throw new DescriptionMismatchException( | 596 throw new DescriptionMismatchException( |
| 489 name, description, ref.description); | 597 name, description, ref.description); |
| 490 } | 598 } |
| 491 | 599 |
| 492 _refs[package] = ref; | 600 _refs[package] = ref; |
| 493 } | 601 } |
| 494 | 602 |
| 495 /** | 603 /** |
| 496 * Removes the constraint from [package] onto this. | 604 * Removes the constraint from [package] onto this. |
| 497 */ | 605 */ |
| 498 void removeConstraint(String package) { | 606 PackageRef removeConstraint(String package) { |
| 499 _refs.remove(package); | 607 var removed = _refs.remove(package); |
| 500 | 608 |
| 501 if (_refs.isEmpty()) { | 609 if (_refs.isEmpty()) { |
| 502 source = null; | 610 source = null; |
| 503 description = null; | 611 description = null; |
| 504 } | 612 } |
| 613 |
| 614 return removed; |
| 505 } | 615 } |
| 506 } | 616 } |
| 507 | 617 |
| 508 // TODO(rnystrom): Report the last of depending packages and their constraints. | 618 // TODO(rnystrom): Report the last of depending packages and their constraints. |
| 509 /** | 619 /** |
| 510 * Exception thrown when the [VersionConstraint] used to match a package is | 620 * Exception thrown when the [VersionConstraint] used to match a package is |
| 511 * valid (i.e. non-empty), but there are no released versions of the package | 621 * valid (i.e. non-empty), but there are no released versions of the package |
| 512 * that fit that constraint. | 622 * that fit that constraint. |
| 513 */ | 623 */ |
| 514 class NoVersionException implements Exception { | 624 class NoVersionException implements Exception { |
| 515 final String package; | 625 final String package; |
| 516 final VersionConstraint constraint; | 626 final VersionConstraint constraint; |
| 517 | 627 |
| 518 NoVersionException(this.package, this.constraint); | 628 NoVersionException(this.package, this.constraint); |
| 519 | 629 |
| 520 String toString() => | 630 String toString() => |
| 521 "Package '$package' has no versions that match $constraint."; | 631 "Package '$package' has no versions that match $constraint."; |
| 522 } | 632 } |
| 523 | 633 |
| 634 // TODO(rnystrom): Report the list of depending packages and their constraints. |
| 635 /** |
| 636 * Exception thrown when the most recent version of [package] must be selected, |
| 637 * but doesn't match the [VersionConstraint] imposed on the package. |
| 638 */ |
| 639 class CouldNotUpdateException implements Exception { |
| 640 final String package; |
| 641 final VersionConstraint constraint; |
| 642 final Version best; |
| 643 |
| 644 CouldNotUpdateException(this.package, this.constraint, this.best); |
| 645 |
| 646 String toString() => |
| 647 "The latest version of '$package', $best, does not match $constraint."; |
| 648 } |
| 649 |
| 524 // TODO(rnystrom): Report the last of depending packages and their constraints. | 650 // TODO(rnystrom): Report the last of depending packages and their constraints. |
| 525 /** | 651 /** |
| 526 * Exception thrown when the [VersionConstraint] used to match a package is | 652 * Exception thrown when the [VersionConstraint] used to match a package is |
| 527 * the empty set: in other words, multiple packages depend on it and have | 653 * the empty set: in other words, multiple packages depend on it and have |
| 528 * conflicting constraints that have no overlap. | 654 * conflicting constraints that have no overlap. |
| 529 */ | 655 */ |
| 530 class DisjointConstraintException implements Exception { | 656 class DisjointConstraintException implements Exception { |
| 531 final String package; | 657 final String package; |
| 532 | 658 |
| 533 DisjointConstraintException(this.package); | 659 DisjointConstraintException(this.package); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 573 final description1; | 699 final description1; |
| 574 final description2; | 700 final description2; |
| 575 | 701 |
| 576 DescriptionMismatchException(this.package, this.description1, | 702 DescriptionMismatchException(this.package, this.description1, |
| 577 this.description2); | 703 this.description2); |
| 578 | 704 |
| 579 // TODO(nweiz): Dump to YAML when that's supported | 705 // TODO(nweiz): Dump to YAML when that's supported |
| 580 String toString() => "Package '$package' has conflicting descriptions " | 706 String toString() => "Package '$package' has conflicting descriptions " |
| 581 "'${JSON.stringify(description1)}' and '${JSON.stringify(description2)}'"; | 707 "'${JSON.stringify(description1)}' and '${JSON.stringify(description2)}'"; |
| 582 } | 708 } |
| OLD | NEW |