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

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

Issue 11445034: Better error messages for dependency conflicts. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: 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
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 /**
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 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 for (var version in versions) { 157 for (var version in versions) {
158 if (dependency.useLatestVersion || 158 if (dependency.useLatestVersion ||
159 dependency.constraint.allows(version)) { 159 dependency.constraint.allows(version)) {
160 if (best == null || version > best) best = version; 160 if (best == null || version > best) best = version;
161 } 161 }
162 } 162 }
163 163
164 // TODO(rnystrom): Better exception. 164 // TODO(rnystrom): Better exception.
165 if (best == null) { 165 if (best == null) {
166 if (tryUnlockDepender(dependency)) return null; 166 if (tryUnlockDepender(dependency)) return null;
167 throw new NoVersionException(dependency.name, dependency.constraint); 167 throw new NoVersionException(dependency.name, dependency.constraint,
168 dependency._refs);
168 } else if (!dependency.constraint.allows(best)) { 169 } else if (!dependency.constraint.allows(best)) {
169 if (tryUnlockDepender(dependency)) return null; 170 if (tryUnlockDepender(dependency)) return null;
170 throw new CouldNotUpdateException( 171 throw new CouldNotUpdateException(
171 dependency.name, dependency.constraint, best); 172 dependency.name, dependency.constraint, best);
172 } 173 }
173 174
174 return best; 175 return best;
175 }); 176 });
176 } 177 }
177 178
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 347
347 // If the package is over-constrained, i.e. the packages depending have 348 // If the package is over-constrained, i.e. the packages depending have
348 // disjoint constraints, then try unlocking a depender that's locked by the 349 // disjoint constraints, then try unlocking a depender that's locked by the
349 // lockfile. If there are no remaining locked dependencies, throw an error. 350 // lockfile. If there are no remaining locked dependencies, throw an error.
350 if (newConstraint != null && newConstraint.isEmpty) { 351 if (newConstraint != null && newConstraint.isEmpty) {
351 if (solver.tryUnlockDepender(newDependency)) { 352 if (solver.tryUnlockDepender(newDependency)) {
352 undo(solver); 353 undo(solver);
353 return null; 354 return null;
354 } 355 }
355 356
356 throw new DisjointConstraintException(name); 357 throw new DisjointConstraintException(name, newDependency._refs);
357 } 358 }
358 359
359 // If this constraint change didn't cause the overall constraint on the 360 // If this constraint change didn't cause the overall constraint on the
360 // package to change, then we don't need to do any further work. 361 // package to change, then we don't need to do any further work.
361 if (oldConstraint == newConstraint) return null; 362 if (oldConstraint == newConstraint) return null;
362 363
363 // If the dependency has been cut free from the graph, just remove it. 364 // If the dependency has been cut free from the graph, just remove it.
364 if (!newDependency.isDependedOn) { 365 if (!newDependency.isDependedOn) {
365 solver.enqueue(new ChangeVersion(name, source, description, null)); 366 solver.enqueue(new ChangeVersion(name, source, description, null));
366 return null; 367 return null;
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after
599 /** Creates a copy of this dependency. */ 600 /** Creates a copy of this dependency. */
600 Dependency clone() => new Dependency._clone(this); 601 Dependency clone() => new Dependency._clone(this);
601 602
602 /// Return a list of available versions for this dependency. 603 /// Return a list of available versions for this dependency.
603 Future<List<Version>> getVersions() => source.getVersions(name, description); 604 Future<List<Version>> getVersions() => source.getVersions(name, description);
604 605
605 /** 606 /**
606 * Places [ref] as a constraint from [package] onto this. 607 * Places [ref] as a constraint from [package] onto this.
607 */ 608 */
608 void placeConstraint(String package, PackageRef ref) { 609 void placeConstraint(String package, PackageRef ref) {
609 var required = _requiredRef(); 610 var requiredDepender = _requiredDepender();
610 if (required != null) { 611 if (requiredDepender != null) {
612 var required = _refs[requiredDepender];
611 if (required.source.name != ref.source.name) { 613 if (required.source.name != ref.source.name) {
612 throw new SourceMismatchException(name, required.source, ref.source); 614 throw new SourceMismatchException(name,
615 requiredDepender, required.source, package, ref.source);
613 } else if (!required.source.descriptionsEqual( 616 } else if (!required.source.descriptionsEqual(
614 required.description, ref.description)) { 617 required.description, ref.description)) {
615 throw new DescriptionMismatchException( 618 throw new DescriptionMismatchException(name,
616 name, required.description, ref.description); 619 requiredDepender, required.description, package, ref.description);
617 } 620 }
618 } 621 }
619 622
620 _refs[package] = ref; 623 _refs[package] = ref;
621 } 624 }
622 625
623 /// Returns a PackageRef whose source and description any new constraints are 626 /// Returns the name of a package whose constraint source and description
nweiz 2012/12/06 00:58:55 "constraint" -> ""?
Bob Nystrom 2012/12/06 17:44:41 It returns the name of the depender, and it's not
nweiz 2012/12/06 19:43:10 Then maybe "constraint's"?
624 /// required to match. Returns null if there are no requirements on new 627 /// all other constraints must match. Returns null if there are no
625 /// constraints. 628 /// requirements on new constraints.
626 PackageRef _requiredRef() { 629 String _requiredDepender() {
627 if (_refs.isEmpty) return null; 630 if (_refs.isEmpty) return null;
628 var refs = _refs.values; 631
629 var first = refs[0]; 632 var dependers = _refs.keys;
630 if (refs.length == 1) { 633 if (dependers.length == 1) {
631 if (first.source is RootSource) return null; 634 var depender = dependers[0];
632 return first; 635 if (_refs[depender].source is RootSource) return null;
636 return depender;
633 } 637 }
634 return refs[1]; 638
639 return dependers[1];
635 } 640 }
636 641
637 /** 642 /**
638 * Removes the constraint from [package] onto this. 643 * Removes the constraint from [package] onto this.
639 */ 644 */
640 PackageRef removeConstraint(String package) => _refs.remove(package); 645 PackageRef removeConstraint(String package) => _refs.remove(package);
641 } 646 }
642 647
643 // TODO(rnystrom): Report the last of depending packages and their constraints.
644 /** 648 /**
645 * Exception thrown when the [VersionConstraint] used to match a package is 649 * Exception thrown when the [VersionConstraint] used to match a package is
646 * valid (i.e. non-empty), but there are no released versions of the package 650 * valid (i.e. non-empty), but there are no released versions of the package
647 * that fit that constraint. 651 * that fit that constraint.
648 */ 652 */
649 class NoVersionException implements Exception { 653 class NoVersionException implements Exception {
650 final String package; 654 final String package;
651 final VersionConstraint constraint; 655 final VersionConstraint constraint;
656 final Map<String, PackageRef> _dependencies;
652 657
653 NoVersionException(this.package, this.constraint); 658 NoVersionException(this.package, this.constraint, this._dependencies);
654 659
655 String toString() => 660 String toString() {
656 "Package '$package' has no versions that match $constraint."; 661 var buffer = new StringBuffer();
662 buffer.add("Package '$package' has no versions that match $constraint derive d from:\n");
nweiz 2012/12/06 00:58:55 Long line
Bob Nystrom 2012/12/06 17:44:41 Done.
663
664 var keys = new List.from(_dependencies.keys);
665 keys.sort();
666
667 for (var key in keys) {
668 buffer.add("- '$key' depends on version "
669 "${_dependencies[key].constraint}\n");
670 }
671
672 return buffer.toString();
673 }
657 } 674 }
658 675
659 // TODO(rnystrom): Report the list of depending packages and their constraints. 676 // TODO(rnystrom): Report the list of depending packages and their constraints.
660 /** 677 /**
661 * Exception thrown when the most recent version of [package] must be selected, 678 * Exception thrown when the most recent version of [package] must be selected,
662 * but doesn't match the [VersionConstraint] imposed on the package. 679 * but doesn't match the [VersionConstraint] imposed on the package.
663 */ 680 */
664 class CouldNotUpdateException implements Exception { 681 class CouldNotUpdateException implements Exception {
665 final String package; 682 final String package;
666 final VersionConstraint constraint; 683 final VersionConstraint constraint;
667 final Version best; 684 final Version best;
668 685
669 CouldNotUpdateException(this.package, this.constraint, this.best); 686 CouldNotUpdateException(this.package, this.constraint, this.best);
670 687
671 String toString() => 688 String toString() =>
672 "The latest version of '$package', $best, does not match $constraint."; 689 "The latest version of '$package', $best, does not match $constraint.";
673 } 690 }
674 691
675 // TODO(rnystrom): Report the last of depending packages and their constraints.
676 /** 692 /**
677 * Exception thrown when the [VersionConstraint] used to match a package is 693 * Exception thrown when the [VersionConstraint] used to match a package is
678 * the empty set: in other words, multiple packages depend on it and have 694 * the empty set: in other words, multiple packages depend on it and have
679 * conflicting constraints that have no overlap. 695 * conflicting constraints that have no overlap.
680 */ 696 */
681 class DisjointConstraintException implements Exception { 697 class DisjointConstraintException implements Exception {
682 final String package; 698 final String package;
699 final Map<String, PackageRef> _dependencies;
683 700
684 DisjointConstraintException(this.package); 701 DisjointConstraintException(this.package, this._dependencies);
685 702
686 String toString() => 703 String toString() {
687 "Package '$package' has disjoint constraints."; 704 var buffer = new StringBuffer();
705 buffer.add("Shared dependency version conflict on '$package':\n");
nweiz 2012/12/06 00:58:55 I don't think "shared dependency version conflict"
Bob Nystrom 2012/12/06 17:44:41 Done.
706
707 var keys = new List.from(_dependencies.keys);
708 keys.sort();
709
710 for (var key in keys) {
711 buffer.add("- '$key' depends on version "
712 "${_dependencies[key].constraint}\n");
713 }
714
715 return buffer.toString();
716 }
688 } 717 }
689 718
690 /** 719 /**
691 * Exception thrown when the [VersionSolver] fails to find a solution after a 720 * Exception thrown when the [VersionSolver] fails to find a solution after a
692 * certain number of iterations. 721 * certain number of iterations.
693 */ 722 */
694 class CouldNotSolveException implements Exception { 723 class CouldNotSolveException implements Exception {
695 CouldNotSolveException(); 724 CouldNotSolveException();
696 725
697 String toString() => 726 String toString() =>
698 "Could not find a solution that met all version constraints."; 727 "Could not find a solution that met all version constraints.";
699 } 728 }
700 729
701 /** 730 /**
702 * Exception thrown when two packages with the same name but different sources 731 * Exception thrown when two packages with the same name but different sources
703 * are depended upon. 732 * are depended upon.
704 */ 733 */
705 class SourceMismatchException implements Exception { 734 class SourceMismatchException implements Exception {
706 final String package; 735 final String package;
736 final String depender1;
707 final Source source1; 737 final Source source1;
738 final String depender2;
708 final Source source2; 739 final Source source2;
709 740
710 SourceMismatchException(this.package, this.source1, this.source2); 741 SourceMismatchException(this.package, this.depender1, this.source1,
742 this.depender2, this.source2);
711 743
712 String toString() { 744 String toString() {
713 return "Package '$package' is depended on from both sources " 745 return "Shared dependency conflict on '$package':\n"
714 "'${source1.name}' and '${source2.name}'."; 746 "- '$depender1' depends on it from source '$source1'\n"
747 "- '$depender2' depends on it from source '$source2'";
715 } 748 }
716 } 749 }
717 750
718 /** 751 /**
719 * Exception thrown when two packages with the same name and source but 752 * Exception thrown when two packages with the same name and source but
720 * different descriptions are depended upon. 753 * different descriptions are depended upon.
721 */ 754 */
722 class DescriptionMismatchException implements Exception { 755 class DescriptionMismatchException implements Exception {
723 final String package; 756 final String package;
757 final String depender1;
724 final description1; 758 final description1;
759 final String depender2;
725 final description2; 760 final description2;
726 761
727 DescriptionMismatchException(this.package, this.description1, 762 DescriptionMismatchException(this.package, this.depender1, this.description1,
728 this.description2); 763 this.depender2, this.description2);
729 764
730 // TODO(nweiz): Dump to YAML when that's supported 765 String toString() {
731 String toString() => "Package '$package' has conflicting descriptions " 766 // TODO(nweiz): Dump descriptions to YAML when that's supported.
732 "'${JSON.stringify(description1)}' and '${JSON.stringify(description2)}'"; 767 return "Shared dependency conflict on '$package':\n"
768 "- '$depender1' depends on it with description "
769 "${JSON.stringify(description1)}\n"
770 "- '$depender2' depends on it with description "
771 "${JSON.stringify(description2)}";
772 }
733 } 773 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698