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

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

Issue 10949026: Allow circular dependencies on the root source in Pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 3 months 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 | « no previous file | utils/tests/pub/version_solver_test.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 /**
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 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 176
177 /** 177 /**
178 * Looks for a package that depends (transitively) on [dependency] and has its 178 * Looks for a package that depends (transitively) on [dependency] and has its
179 * version locked in the lockfile. If one is found, enqueues an 179 * version locked in the lockfile. If one is found, enqueues an
180 * [UnlockPackage] work item for it and returns true. Otherwise, returns 180 * [UnlockPackage] work item for it and returns true. Otherwise, returns
181 * false. 181 * false.
182 * 182 *
183 * This does a breadth-first search; immediate dependers will be unlocked 183 * This does a breadth-first search; immediate dependers will be unlocked
184 * first, followed by transitive dependers. 184 * first, followed by transitive dependers.
185 */ 185 */
186 bool tryUnlockDepender(Dependency dependency) { 186 bool tryUnlockDepender(Dependency dependency, [Set<String> seen]) {
187 if (seen == null) seen = new Set();
188 if (seen.contains(dependency.name)) return false;
Bob Nystrom 2012/09/20 01:19:54 How about a comment: // Avoid infinite loop if the
nweiz 2012/09/24 22:31:16 Done.
189 seen.add(dependency.name);
190
187 for (var dependerName in dependency.dependers) { 191 for (var dependerName in dependency.dependers) {
188 var depender = getDependency(dependerName); 192 var depender = getDependency(dependerName);
189 var locked = lockFile.packages[dependerName]; 193 var locked = lockFile.packages[dependerName];
190 if (locked != null && depender.version == locked.version) { 194 if (locked != null && depender.version == locked.version) {
191 enqueue(new UnlockPackage(depender)); 195 enqueue(new UnlockPackage(depender));
192 return true; 196 return true;
193 } 197 }
194 } 198 }
195 return dependency.dependers.map(getDependency).some(tryUnlockDepender); 199
200 return dependency.dependers.map(getDependency).some((subdependency) =>
201 tryUnlockDepender(subdependency, seen));
196 } 202 }
197 203
198 List<PackageId> buildResults() { 204 List<PackageId> buildResults() {
199 return _packages.getValues().filter((dep) => dep.isDependedOn).map((dep) { 205 return _packages.getValues().filter((dep) => dep.isDependedOn).map((dep) {
200 var description = dep.description; 206 var description = dep.description;
201 207
202 // If the lockfile contains a fully-resolved description for the package, 208 // If the lockfile contains a fully-resolved description for the package,
203 // use that. This allows e.g. Git to ensure that the same commit is used. 209 // use that. This allows e.g. Git to ensure that the same commit is used.
204 var lockedPackage = lockFile.packages[dep.name]; 210 var lockedPackage = lockFile.packages[dep.name];
205 if (lockedPackage != null && lockedPackage.version == dep.version && 211 if (lockedPackage != null && lockedPackage.version == dep.version &&
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
516 */ 522 */
517 final String name; 523 final String name;
518 524
519 /** 525 /**
520 * The [PackageRefs] that represent constraints that depending packages have 526 * The [PackageRefs] that represent constraints that depending packages have
521 * placed on this one. 527 * placed on this one.
522 */ 528 */
523 final Map<String, PackageRef> _refs; 529 final Map<String, PackageRef> _refs;
524 530
525 /** 531 /**
526 * The source of this dependency's package.
527 *
528 * All constraints in [_refs] must have this as their source.
529 */
530 Source source;
531
532 /**
533 * The description of this dependency's package.
534 *
535 * All constraints in [_refs] must have a description equivalent to this one
536 * according to [source].
537 */
538 var description;
539
540 /**
541 * The currently-selected best version for this dependency. 532 * The currently-selected best version for this dependency.
542 */ 533 */
543 Version version; 534 Version version;
544 535
545 /** 536 /**
546 * Whether this dependency should always select the latest version. 537 * Whether this dependency should always select the latest version.
547 */ 538 */
548 bool useLatestVersion = false; 539 bool useLatestVersion = false;
549 540
550 /** 541 /**
(...skipping 10 matching lines...) Expand all
561 * Gets the overall constraint that all packages are placing on this one. 552 * Gets the overall constraint that all packages are placing on this one.
562 * If no packages have a constraint on this one (which can happen when this 553 * If no packages have a constraint on this one (which can happen when this
563 * package is in the process of being added to the graph), returns `null`. 554 * package is in the process of being added to the graph), returns `null`.
564 */ 555 */
565 VersionConstraint get constraint { 556 VersionConstraint get constraint {
566 if (_refs.isEmpty()) return null; 557 if (_refs.isEmpty()) return null;
567 return new VersionConstraint.intersect( 558 return new VersionConstraint.intersect(
568 _refs.getValues().map((ref) => ref.constraint)); 559 _refs.getValues().map((ref) => ref.constraint));
569 } 560 }
570 561
562 /// The source of this dependency's package.
563 Source get source {
564 var canonical = _canonicalRef();
565 if (canonical == null) return null;
566 return canonical.source;
567 }
568
569 /// The description of this dependency's package.
570 get description {
571 var canonical = _canonicalRef();
572 if (canonical == null) return null;
573 return canonical.description;
574 }
575
576 /// Return the PackageRef that has the canonical source and description for
577 /// this package. If any dependency requires that this package come from a
578 /// [RootSource], that will be used; otherwise, it will be the source and
579 /// description that all dependencies agree upon.
580 PackageRef _canonicalRef() {
581 if (_refs.isEmpty()) return null;
582 var refs = _refs.getValues();
583 for (var ref in refs) {
584 if (ref is RootSource) return ref;
585 }
586 return refs[0];
587 }
588
571 Dependency(this.name) 589 Dependency(this.name)
572 : _refs = <String, PackageRef>{}; 590 : _refs = <String, PackageRef>{};
573 591
574 Dependency._clone(Dependency other) 592 Dependency._clone(Dependency other)
575 : name = other.name, 593 : name = other.name,
576 source = other.source,
577 description = other.description,
578 version = other.version, 594 version = other.version,
579 _refs = new Map<String, PackageRef>.from(other._refs); 595 _refs = new Map<String, PackageRef>.from(other._refs);
580 596
581 /** Creates a copy of this dependency. */ 597 /** Creates a copy of this dependency. */
582 Dependency clone() => new Dependency._clone(this); 598 Dependency clone() => new Dependency._clone(this);
583 599
584 /// Return a list of available versions for this dependency. 600 /// Return a list of available versions for this dependency.
585 Future<List<Version>> getVersions() => source.getVersions(name, description); 601 Future<List<Version>> getVersions() => source.getVersions(name, description);
586 602
587 /** 603 /**
588 * Places [ref] as a constraint from [package] onto this. 604 * Places [ref] as a constraint from [package] onto this.
589 */ 605 */
590 void placeConstraint(String package, PackageRef ref) { 606 void placeConstraint(String package, PackageRef ref) {
591 // If this isn't the first constraint placed on this package, make sure it 607 var required = _requiredRef();
592 // matches the source and description of past constraints. 608 if (required != null) {
593 if (_refs.isEmpty()) { 609 if (required.source.name != ref.source.name) {
594 source = ref.source; 610 throw new SourceMismatchException(name, required.source, ref.source);
595 description = ref.description; 611 } else if (!required.source.descriptionsEqual(
596 } else if (source.name != ref.source.name) { 612 required.description, ref.description)) {
597 throw new SourceMismatchException(name, source, ref.source); 613 throw new DescriptionMismatchException(
598 } else if (!source.descriptionsEqual(description, ref.description)) { 614 name, required.description, ref.description);
599 throw new DescriptionMismatchException( 615 }
600 name, description, ref.description);
601 } 616 }
602 617
603 _refs[package] = ref; 618 _refs[package] = ref;
604 } 619 }
605 620
621 /// Returns a PackageRef whose source and description any new constraints are
622 /// required to match. Returns null if there are no requirements on new
623 /// constraints.
624 PackageRef _requiredRef() {
625 if (_refs.isEmpty()) return null;
626 var refs = _refs.getValues();
627 var first = refs[0];
628 if (refs.length == 1) {
629 if (first.source is RootSource) return null;
630 return first;
631 }
632 return refs[1];
633 }
634
606 /** 635 /**
607 * Removes the constraint from [package] onto this. 636 * Removes the constraint from [package] onto this.
608 */ 637 */
609 PackageRef removeConstraint(String package) { 638 PackageRef removeConstraint(String package) => _refs.remove(package);
610 var removed = _refs.remove(package);
611
612 if (_refs.isEmpty()) {
613 source = null;
614 description = null;
615 }
616
617 return removed;
618 }
619 } 639 }
620 640
621 // TODO(rnystrom): Report the last of depending packages and their constraints. 641 // TODO(rnystrom): Report the last of depending packages and their constraints.
622 /** 642 /**
623 * Exception thrown when the [VersionConstraint] used to match a package is 643 * Exception thrown when the [VersionConstraint] used to match a package is
624 * valid (i.e. non-empty), but there are no released versions of the package 644 * valid (i.e. non-empty), but there are no released versions of the package
625 * that fit that constraint. 645 * that fit that constraint.
626 */ 646 */
627 class NoVersionException implements Exception { 647 class NoVersionException implements Exception {
628 final String package; 648 final String package;
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
702 final description1; 722 final description1;
703 final description2; 723 final description2;
704 724
705 DescriptionMismatchException(this.package, this.description1, 725 DescriptionMismatchException(this.package, this.description1,
706 this.description2); 726 this.description2);
707 727
708 // TODO(nweiz): Dump to YAML when that's supported 728 // TODO(nweiz): Dump to YAML when that's supported
709 String toString() => "Package '$package' has conflicting descriptions " 729 String toString() => "Package '$package' has conflicting descriptions "
710 "'${JSON.stringify(description1)}' and '${JSON.stringify(description2)}'"; 730 "'${JSON.stringify(description1)}' and '${JSON.stringify(description2)}'";
711 } 731 }
OLDNEW
« no previous file with comments | « no previous file | utils/tests/pub/version_solver_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698