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 /// A back-tracking depth-first solver. | 5 /// A back-tracking depth-first solver. |
6 /// | 6 /// |
7 /// Attempts to find the best solution for a root package's transitive | 7 /// Attempts to find the best solution for a root package's transitive |
8 /// dependency graph, where a "solution" is a set of concrete package versions. | 8 /// dependency graph, where a "solution" is a set of concrete package versions. |
9 /// A valid solution will select concrete versions for every package reached | 9 /// A valid solution will select concrete versions for every package reached |
10 /// from the root package's dependency graph, and each of those packages will | 10 /// from the root package's dependency graph, and each of those packages will |
(...skipping 516 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
527 /// Desctructively modifies [deps]. Completes to a list of packages if the | 527 /// Desctructively modifies [deps]. Completes to a list of packages if the |
528 /// traversal is complete. Completes it to an error if a failure occurred. | 528 /// traversal is complete. Completes it to an error if a failure occurred. |
529 /// Otherwise, recurses. | 529 /// Otherwise, recurses. |
530 Future<List<PackageId>> _traverseDeps(PackageId depender, | 530 Future<List<PackageId>> _traverseDeps(PackageId depender, |
531 DependencyQueue deps) { | 531 DependencyQueue deps) { |
532 // Move onto the next package if we've traversed all of these references. | 532 // Move onto the next package if we've traversed all of these references. |
533 if (deps.isEmpty) return _traversePackage(); | 533 if (deps.isEmpty) return _traversePackage(); |
534 | 534 |
535 return resetStack(() { | 535 return resetStack(() { |
536 return deps.advance().then((dep) { | 536 return deps.advance().then((dep) { |
537 _validateDependency(dep, depender); | 537 var dependency = new Dependency(depender.name, depender.version, dep); |
538 | 538 return _registerDependency(dependency).then((_) { |
539 // Add the dependency. | 539 if (dep.name == "barback") return _addImplicitDependencies(); |
540 var dependencies = _getDependencies(dep.name); | |
541 dependencies.add(new Dependency(depender.name, depender.version, dep)); | |
542 | |
543 // If the package is barback, pub has an implicit version constraint on | |
544 // it since pub itself uses barback too. Note that we don't check for | |
545 // the hosted source here because we still want to do this even when | |
546 // people on the Dart team are on the bleeding edge and have a path | |
547 // dependency on the tip version of barback in the Dart repo. | |
548 // | |
549 // The length check here is to ensure we only add the barback | |
550 // dependency once. | |
551 if (dep.name == "barback" && dependencies.length == 1) { | |
552 _solver.logSolve('add implicit ${barback.supportedVersions} pub ' | |
553 'dependency on barback'); | |
554 | |
555 // Use the same source and description as the explicit dependency. | |
556 // That way, this doesn't fail with a source/desc conflict if users | |
557 // (like Dart team members) use things like a path dependency to | |
558 // find barback. | |
559 var barbackDep = new PackageDep(dep.name, dep.source, | |
560 barback.supportedVersions, dep.description); | |
561 dependencies.add(new Dependency("pub itself", null, barbackDep)); | |
562 } | |
563 | |
564 var constraint = _getConstraint(dep.name); | |
565 | |
566 // See if it's possible for a package to match that constraint. | |
567 if (constraint.isEmpty) { | |
568 var constraints = _getDependencies(dep.name) | |
569 .map((dep) => " ${dep.dep.constraint} from ${dep.depender}") | |
570 .join('\n'); | |
571 _solver.logSolve( | |
572 'disjoint constraints on ${dep.name}:\n$constraints'); | |
573 throw new DisjointConstraintException(dep.name, dependencies); | |
574 } | |
575 | |
576 var selected = _validateSelected(dep, constraint); | |
577 if (selected != null) { | |
578 // The selected package version is good, so enqueue it to traverse | |
579 // into it. | |
580 _packages.add(selected); | |
581 return _traverseDeps(depender, deps); | |
582 } | |
583 | |
584 // We haven't selected a version. Try all of the versions that match | |
585 // the constraints we currently have for this package. | |
586 var locked = _getValidLocked(dep.name); | |
587 | |
588 return VersionQueue.create(locked, | |
589 () => _getAllowedVersions(dep)).then((versions) { | |
590 _packages.add(_solver.select(versions)); | |
591 }); | 540 }); |
592 }).then((_) => _traverseDeps(depender, deps)); | 541 }).then((_) => _traverseDeps(depender, deps)); |
593 }); | 542 }); |
594 } | 543 } |
595 | 544 |
545 /// Register [dependency]'s constraints on the package it depends on and | |
546 /// enqueues the package for processing if necessary. | |
547 Future _registerDependency(Dependency dependency) { | |
548 return syncFuture(() { | |
549 _validateDependency(dependency); | |
550 | |
551 var dep = dependency.dep; | |
552 var dependencies = _getDependencies(dep.name); | |
553 dependencies.add(dependency); | |
554 | |
555 var constraint = _getConstraint(dep.name); | |
556 | |
557 // See if it's possible for a package to match that constraint. | |
558 if (constraint.isEmpty) { | |
559 var constraints = dependencies | |
560 .map((dep) => " ${dep.dep.constraint} from ${dep.depender}") | |
561 .join('\n'); | |
562 _solver.logSolve( | |
563 'disjoint constraints on ${dep.name}:\n$constraints'); | |
564 throw new DisjointConstraintException(dep.name, dependencies); | |
565 } | |
566 | |
567 var selected = _validateSelected(dep, constraint); | |
568 if (selected != null) { | |
569 // The selected package version is good, so enqueue it to traverse | |
570 // into it. | |
571 _packages.add(selected); | |
572 return null; | |
573 } | |
574 | |
575 // We haven't selected a version. Try all of the versions that match | |
576 // the constraints we currently have for this package. | |
577 var locked = _getValidLocked(dep.name); | |
578 | |
579 return VersionQueue.create(locked, () { | |
580 return _getAllowedVersions(dep); | |
581 }).then((versions) => _packages.add(_solver.select(versions))); | |
582 }); | |
583 } | |
584 | |
596 /// Gets all versions of [dep] that match the current constraints placed on | 585 /// Gets all versions of [dep] that match the current constraints placed on |
597 /// it. | 586 /// it. |
598 Future<Iterable<PackageId>> _getAllowedVersions(PackageDep dep) { | 587 Future<Iterable<PackageId>> _getAllowedVersions(PackageDep dep) { |
599 var constraint = _getConstraint(dep.name); | 588 var constraint = _getConstraint(dep.name); |
600 return _solver.cache.getVersions(dep.toRef()).then((versions) { | 589 return _solver.cache.getVersions(dep.toRef()).then((versions) { |
601 var allowed = versions.where((id) => constraint.allows(id.version)); | 590 var allowed = versions.where((id) => constraint.allows(id.version)); |
602 | 591 |
603 if (allowed.isEmpty) { | 592 if (allowed.isEmpty) { |
604 _solver.logSolve('no versions for ${dep.name} match $constraint'); | 593 _solver.logSolve('no versions for ${dep.name} match $constraint'); |
605 throw new NoVersionException(dep.name, null, constraint, | 594 throw new NoVersionException(dep.name, null, constraint, |
(...skipping 20 matching lines...) Expand all Loading... | |
626 | 615 |
627 throw error; | 616 throw error; |
628 }); | 617 }); |
629 } | 618 } |
630 | 619 |
631 /// Ensures that dependency [dep] from [depender] is consistent with the | 620 /// Ensures that dependency [dep] from [depender] is consistent with the |
632 /// other dependencies on the same package. | 621 /// other dependencies on the same package. |
633 /// | 622 /// |
634 /// Throws a [SolveFailure] exception if not. Only validates sources and | 623 /// Throws a [SolveFailure] exception if not. Only validates sources and |
635 /// descriptions, not the version. | 624 /// descriptions, not the version. |
636 void _validateDependency(PackageDep dep, PackageId depender) { | 625 void _validateDependency(Dependency dependency) { |
626 var dep = dependency.dep; | |
627 | |
637 // Make sure the dependencies agree on source and description. | 628 // Make sure the dependencies agree on source and description. |
638 var required = _getRequired(dep.name); | 629 var required = _getRequired(dep.name); |
639 if (required == null) return; | 630 if (required == null) return; |
640 | 631 |
641 // Make sure all of the existing sources match the new reference. | 632 // Make sure all of the existing sources match the new reference. |
642 if (required.dep.source != dep.source) { | 633 if (required.dep.source != dep.source) { |
643 _solver.logSolve('source mismatch on ${dep.name}: ${required.dep.source} ' | 634 _solver.logSolve('source mismatch on ${dep.name}: ${required.dep.source} ' |
644 '!= ${dep.source}'); | 635 '!= ${dep.source}'); |
645 throw new SourceMismatchException(dep.name, | 636 throw new SourceMismatchException(dep.name, [required, dependency]); |
646 [required, new Dependency(depender.name, depender.version, dep)]); | |
647 } | 637 } |
648 | 638 |
649 // Make sure all of the existing descriptions match the new reference. | 639 // Make sure all of the existing descriptions match the new reference. |
650 var source = _solver.sources[dep.source]; | 640 var source = _solver.sources[dep.source]; |
651 if (!source.descriptionsEqual(dep.description, required.dep.description)) { | 641 if (!source.descriptionsEqual(dep.description, required.dep.description)) { |
652 _solver.logSolve('description mismatch on ${dep.name}: ' | 642 _solver.logSolve('description mismatch on ${dep.name}: ' |
653 '${required.dep.description} != ${dep.description}'); | 643 '${required.dep.description} != ${dep.description}'); |
654 throw new DescriptionMismatchException(dep.name, | 644 throw new DescriptionMismatchException(dep.name, [required, dependency]); |
655 [required, new Dependency(depender.name, depender.version, dep)]); | |
656 } | 645 } |
657 } | 646 } |
658 | 647 |
659 /// Validates the currently selected package against the new dependency that | 648 /// Validates the currently selected package against the new dependency that |
660 /// [dep] and [constraint] place on it. | 649 /// [dep] and [constraint] place on it. |
661 /// | 650 /// |
662 /// Returns `null` if there is no currently selected package, throws a | 651 /// Returns `null` if there is no currently selected package, throws a |
663 /// [SolveFailure] if the new reference it not does not allow the previously | 652 /// [SolveFailure] if the new reference it not does not allow the previously |
664 /// selected version, or returns the selected package if successful. | 653 /// selected version, or returns the selected package if successful. |
665 PackageId _validateSelected(PackageDep dep, VersionConstraint constraint) { | 654 PackageId _validateSelected(PackageDep dep, VersionConstraint constraint) { |
666 var selected = _solver.getSelected(dep.name); | 655 var selected = _solver.getSelected(dep.name); |
667 if (selected == null) return null; | 656 if (selected == null) return null; |
668 | 657 |
669 // Make sure it meets the constraint. | 658 // Make sure it meets the constraint. |
670 if (!dep.constraint.allows(selected.version)) { | 659 if (!dep.constraint.allows(selected.version)) { |
671 _solver.logSolve('selection $selected does not match $constraint'); | 660 _solver.logSolve('selection $selected does not match $constraint'); |
672 throw new NoVersionException(dep.name, selected.version, constraint, | 661 throw new NoVersionException(dep.name, selected.version, constraint, |
673 _getDependencies(dep.name)); | 662 _getDependencies(dep.name)); |
674 } | 663 } |
675 | 664 |
676 return selected; | 665 return selected; |
677 } | 666 } |
678 | 667 |
668 /// Register pub's implicit dependencies. | |
669 /// | |
670 /// Pub has an implicit version constraint on barback and various other | |
671 /// packages used in barback's plugin isolate. | |
672 Future _addImplicitDependencies() { | |
673 /// Ensure we only add the barback dependency once. | |
674 if (_getDependencies("barback").length != 1) return new Future.value(); | |
675 | |
676 return Future.wait(barback.pubConstraints.keys.map((depName) { | |
677 var constraint = barback.pubConstraints[depName]; | |
678 _solver.logSolve('add implicit $constraint pub dependency on ' | |
679 '$depName'); | |
680 | |
681 var override = _solver._overrides[depName]; | |
Bob Nystrom
2014/06/26 16:37:33
Nice. I didn't remember that the solver already ha
| |
682 | |
683 // Use the same source and description as the dependency override if | |
684 // one exists. | |
Bob Nystrom
2014/06/26 16:37:33
Document that this is mainly for the bots.
nweiz
2014/06/26 20:03:48
Done.
| |
685 var pubDep = override == null ? | |
686 new PackageDep(depName, "hosted", constraint, depName) : | |
687 override.withConstraint(constraint); | |
688 return _registerDependency(new Dependency("pub itself", null, pubDep)); | |
689 })); | |
690 } | |
691 | |
679 /// Gets the list of dependencies for package [name]. | 692 /// Gets the list of dependencies for package [name]. |
680 /// | 693 /// |
681 /// Creates an empty list if needed. | 694 /// Creates an empty list if needed. |
682 List<Dependency> _getDependencies(String name) { | 695 List<Dependency> _getDependencies(String name) { |
683 return _dependencies.putIfAbsent(name, () => <Dependency>[]); | 696 return _dependencies.putIfAbsent(name, () => <Dependency>[]); |
684 } | 697 } |
685 | 698 |
686 /// Gets a "required" reference to the package [name]. | 699 /// Gets a "required" reference to the package [name]. |
687 /// | 700 /// |
688 /// This is the first non-root dependency on that package. All dependencies | 701 /// This is the first non-root dependency on that package. All dependencies |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
745 /// | 758 /// |
746 /// Throws a [SolveFailure] if not. | 759 /// Throws a [SolveFailure] if not. |
747 void _validateSdkConstraint(Pubspec pubspec) { | 760 void _validateSdkConstraint(Pubspec pubspec) { |
748 if (pubspec.environment.sdkVersion.allows(sdk.version)) return; | 761 if (pubspec.environment.sdkVersion.allows(sdk.version)) return; |
749 | 762 |
750 throw new BadSdkVersionException(pubspec.name, | 763 throw new BadSdkVersionException(pubspec.name, |
751 'Package ${pubspec.name} requires SDK version ' | 764 'Package ${pubspec.name} requires SDK version ' |
752 '${pubspec.environment.sdkVersion} but the current SDK is ' | 765 '${pubspec.environment.sdkVersion} but the current SDK is ' |
753 '${sdk.version}.'); | 766 '${sdk.version}.'); |
754 } | 767 } |
OLD | NEW |