Chromium Code Reviews| 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 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |