| Index: utils/pub/version_solver1.dart | 
| diff --git a/utils/pub/version_solver.dart b/utils/pub/version_solver1.dart | 
| similarity index 70% | 
| copy from utils/pub/version_solver.dart | 
| copy to utils/pub/version_solver1.dart | 
| index 5b055d87e86b92871a42ebfbe91de1249bec52c1..d73d81807194fe3142b5142b222f4b1e530348b9 100644 | 
| --- a/utils/pub/version_solver.dart | 
| +++ b/utils/pub/version_solver1.dart | 
| @@ -33,7 +33,7 @@ | 
| /// that happens, we find the new best version that meets the updated constraint | 
| /// and then the change the package to use that version. That cycles back up to | 
| /// the beginning again. | 
| -library version_solver; | 
| +library version_solver1; | 
|  | 
| import 'dart:async'; | 
| import 'dart:collection' show Queue; | 
| @@ -47,52 +47,29 @@ import 'source.dart'; | 
| import 'source_registry.dart'; | 
| import 'utils.dart'; | 
| import 'version.dart'; | 
| +import 'version_solver.dart'; | 
|  | 
| -/// Attempts to select the best concrete versions for all of the transitive | 
| -/// dependencies of [root] taking into account all of the [VersionConstraint]s | 
| -/// that those dependencies place on each other and the requirements imposed by | 
| -/// [lockFile]. If successful, completes to a [Map] that maps package names to | 
| -/// the selected version for that package. If it fails, the future will complete | 
| -/// with a [NoVersionException], [DisjointConstraintException], or | 
| -/// [CouldNotSolveException]. | 
| -Future<List<PackageId>> resolveVersions(SourceRegistry sources, Package root, | 
| -    LockFile lockFile) { | 
| -  log.message('Resolving dependencies...'); | 
| -  return new VersionSolver(sources, root, lockFile).solve(); | 
| -} | 
| - | 
| -class VersionSolver { | 
| -  final SourceRegistry _sources; | 
| -  final Package _root; | 
| -  final LockFile lockFile; | 
| -  final PubspecCache _pubspecs; | 
| -  final Map<String, Dependency> _packages; | 
| -  final Queue<WorkItem> _work; | 
| +class GreedyVersionSolver extends VersionSolver { | 
| +  final _packages = <String, DependencyNode>{}; | 
| +  final _work = new Queue<WorkItem>(); | 
| int _numIterations = 0; | 
|  | 
| -  VersionSolver(SourceRegistry sources, this._root, this.lockFile) | 
| -      : _sources = sources, | 
| -        _pubspecs = new PubspecCache(sources), | 
| -        _packages = <String, Dependency>{}, | 
| -        _work = new Queue<WorkItem>(); | 
| - | 
| -  /// Tell the version solver to use the most recent version of [package] that | 
| -  /// exists in whatever source it's installed from. If that version violates | 
| -  /// constraints imposed by other dependencies, an error will be raised when | 
| -  /// solving the versions, even if an earlier compatible version exists. | 
| -  void useLatestVersion(String package) { | 
| +  GreedyVersionSolver(SourceRegistry sources, Package root, LockFile lockFile, | 
| +                      List<String> useLatest) | 
| +      : super(sources, root, lockFile, useLatest); | 
| + | 
| +  void forceLatestVersion(String package) { | 
| // TODO(nweiz): How do we want to detect and handle unknown dependencies | 
| // here? | 
| getDependency(package).useLatestVersion = true; | 
| -    lockFile.packages.remove(package); | 
| } | 
|  | 
| -  Future<List<PackageId>> solve() { | 
| +  Future<List<PackageId>> runSolver() { | 
| // Kick off the work by adding the root package at its concrete version to | 
| // the dependency graph. | 
| -    var ref = new PackageRef.root(_root); | 
| +    var ref = new PackageRef.root(root); | 
| enqueue(new AddConstraint('(entrypoint)', ref)); | 
| -    _pubspecs.cache(ref.atVersion(_root.version), _root.pubspec); | 
| +    cache.cache(ref.atVersion(root.version), root.pubspec); | 
|  | 
| Future processNextWorkItem(_) { | 
| while (true) { | 
| @@ -128,10 +105,10 @@ class VersionSolver { | 
| _work.add(work); | 
| } | 
|  | 
| -  Dependency getDependency(String package) { | 
| +  DependencyNode getDependency(String package) { | 
| // There can be unused dependencies in the graph, so just create an empty | 
| // one if needed. | 
| -    _packages.putIfAbsent(package, () => new Dependency(package)); | 
| +    _packages.putIfAbsent(package, () => new DependencyNode(package)); | 
| return _packages[package]; | 
| } | 
|  | 
| @@ -142,13 +119,14 @@ class VersionSolver { | 
|  | 
| /// Returns the most recent version of [dependency] that satisfies all of its | 
| /// version constraints. | 
| -  Future<Version> getBestVersion(Dependency dependency) { | 
| -    return dependency.getVersions().then((versions) { | 
| +  Future<Version> getBestVersion(DependencyNode dependency) { | 
| +    return cache.getVersions(dependency.name, | 
| +        dependency.source, dependency.description).then((versions) { | 
| var best = null; | 
| -      for (var version in versions) { | 
| +      for (var ref in versions) { | 
| if (dependency.useLatestVersion || | 
| -            dependency.constraint.allows(version)) { | 
| -          if (best == null || version > best) best = version; | 
| +            dependency.constraint.allows(ref.version)) { | 
| +          if (best == null || ref.version > best) best = ref.version; | 
| } | 
| } | 
|  | 
| @@ -156,7 +134,7 @@ class VersionSolver { | 
| if (best == null) { | 
| if (tryUnlockDepender(dependency)) return null; | 
| throw new NoVersionException(dependency.name, dependency.constraint, | 
| -            dependency._refs); | 
| +            dependency.toList()); | 
| } else if (!dependency.constraint.allows(best)) { | 
| if (tryUnlockDepender(dependency)) return null; | 
| throw new CouldNotUpdateException( | 
| @@ -174,7 +152,7 @@ class VersionSolver { | 
| /// | 
| /// This does a breadth-first search; immediate dependers will be unlocked | 
| /// first, followed by transitive dependers. | 
| -  bool tryUnlockDepender(Dependency dependency, [Set<String> seen]) { | 
| +  bool tryUnlockDepender(DependencyNode dependency, [Set<String> seen]) { | 
| if (seen == null) seen = new Set(); | 
| // Avoid an infinite loop if there are circular dependencies. | 
| if (seen.contains(dependency.name)) return false; | 
| @@ -222,7 +200,7 @@ abstract class WorkItem { | 
| /// Processes this work item. Returns a future that completes when the work is | 
| /// done. If `null` is returned, that means the work has completed | 
| /// synchronously and the next item can be started immediately. | 
| -  Future process(VersionSolver solver); | 
| +  Future process(GreedyVersionSolver solver); | 
| } | 
|  | 
| /// The best selected version for a package has changed to [version]. If the | 
| @@ -243,7 +221,7 @@ class ChangeVersion implements WorkItem { | 
|  | 
| ChangeVersion(this.package, this.source, this.description, this.version); | 
|  | 
| -  Future process(VersionSolver solver) { | 
| +  Future process(GreedyVersionSolver solver) { | 
| log.fine("Changing $package to version $version."); | 
|  | 
| var dependency = solver.getDependency(package); | 
| @@ -289,7 +267,7 @@ class ChangeVersion implements WorkItem { | 
| } | 
|  | 
| var id = new PackageId(package, source, version, description); | 
| -    return solver._pubspecs.load(id).then((pubspec) { | 
| +    return solver.cache.getPubspec(id).then((pubspec) { | 
| var dependencies = <String, PackageRef>{}; | 
| for (var dependency in pubspec.dependencies) { | 
| dependencies[dependency.name] = dependency; | 
| @@ -314,12 +292,13 @@ class ChangeVersion implements WorkItem { | 
| /// graph once a dependency has changed. Changing the dependency is the | 
| /// responsibility of subclasses. | 
| abstract class ChangeConstraint implements WorkItem { | 
| -  Future process(VersionSolver solver); | 
| +  Future process(GreedyVersionSolver solver); | 
|  | 
| -  void undo(VersionSolver solver); | 
| +  void undo(GreedyVersionSolver solver); | 
|  | 
| -  Future _processChange(VersionSolver solver, Dependency oldDependency, | 
| -      Dependency newDependency) { | 
| +  Future _processChange(GreedyVersionSolver solver, | 
| +                        DependencyNode oldDependency, | 
| +                        DependencyNode newDependency) { | 
| var name = newDependency.name; | 
| var source = oldDependency.source != null ? | 
| oldDependency.source : newDependency.source; | 
| @@ -337,7 +316,7 @@ abstract class ChangeConstraint implements WorkItem { | 
| return null; | 
| } | 
|  | 
| -      throw new DisjointConstraintException(name, newDependency._refs); | 
| +      throw new DisjointConstraintException(name, newDependency.toList()); | 
| } | 
|  | 
| // If this constraint change didn't cause the overall constraint on the | 
| @@ -352,9 +331,9 @@ abstract class ChangeConstraint implements WorkItem { | 
|  | 
| // If the dependency is on the root package, then we don't need to do | 
| // anything since it's already at the best version. | 
| -    if (name == solver._root.name) { | 
| +    if (name == solver.root.name) { | 
| solver.enqueue(new ChangeVersion( | 
| -          name, source, description, solver._root.version)); | 
| +          name, source, description, solver.root.version)); | 
| return null; | 
| } | 
|  | 
| @@ -394,7 +373,7 @@ class AddConstraint extends ChangeConstraint { | 
|  | 
| AddConstraint(this.depender, this.ref); | 
|  | 
| -  Future process(VersionSolver solver) { | 
| +  Future process(GreedyVersionSolver solver) { | 
| log.fine("Adding $depender's constraint $ref."); | 
|  | 
| var dependency = solver.getDependency(ref.name); | 
| @@ -403,7 +382,7 @@ class AddConstraint extends ChangeConstraint { | 
| return _processChange(solver, oldDependency, dependency); | 
| } | 
|  | 
| -  void undo(VersionSolver solver) { | 
| +  void undo(GreedyVersionSolver solver) { | 
| solver.getDependency(ref.name).removeConstraint(depender); | 
| } | 
| } | 
| @@ -421,7 +400,7 @@ class RemoveConstraint extends ChangeConstraint { | 
|  | 
| RemoveConstraint(this.depender, this.dependent); | 
|  | 
| -  Future process(VersionSolver solver) { | 
| +  Future process(GreedyVersionSolver solver) { | 
| log.fine("Removing $depender's constraint ($_removed) on $dependent."); | 
|  | 
| var dependency = solver.getDependency(dependent); | 
| @@ -430,7 +409,7 @@ class RemoveConstraint extends ChangeConstraint { | 
| return _processChange(solver, oldDependency, dependency); | 
| } | 
|  | 
| -  void undo(VersionSolver solver) { | 
| +  void undo(GreedyVersionSolver solver) { | 
| solver.getDependency(dependent).placeConstraint(depender, _removed); | 
| } | 
| } | 
| @@ -438,11 +417,11 @@ class RemoveConstraint extends ChangeConstraint { | 
| /// [package]'s version is no longer constrained by the lockfile. | 
| class UnlockPackage implements WorkItem { | 
| /// The package being unlocked. | 
| -  Dependency package; | 
| +  DependencyNode package; | 
|  | 
| UnlockPackage(this.package); | 
|  | 
| -  Future process(VersionSolver solver) { | 
| +  Future process(GreedyVersionSolver solver) { | 
| log.fine("Unlocking ${package.name}."); | 
|  | 
| solver.lockFile.packages.remove(package.name); | 
| @@ -454,41 +433,9 @@ class UnlockPackage implements WorkItem { | 
| } | 
| } | 
|  | 
| -// TODO(rnystrom): Instead of always pulling from the source (which will mean | 
| -// hitting a server), we should consider caching pubspecs of uninstalled | 
| -// packages in the system cache. | 
| -/// Maintains a cache of previously-loaded pubspecs. Used to avoid requesting | 
| -/// the same pubspec from the server repeatedly. | 
| -class PubspecCache { | 
| -  final SourceRegistry _sources; | 
| -  final Map<PackageId, Pubspec> _pubspecs; | 
| - | 
| -  PubspecCache(this._sources) | 
| -      : _pubspecs = new Map<PackageId, Pubspec>(); | 
| - | 
| -  /// Caches [pubspec] as the [Pubspec] for the package identified by [id]. | 
| -  void cache(PackageId id, Pubspec pubspec) { | 
| -    _pubspecs[id] = pubspec; | 
| -  } | 
| - | 
| -  /// Loads the pubspec for the package identified by [id]. | 
| -  Future<Pubspec> load(PackageId id) { | 
| -    // Complete immediately if it's already cached. | 
| -    if (_pubspecs.containsKey(id)) { | 
| -      return new Future<Pubspec>.immediate(_pubspecs[id]); | 
| -    } | 
| - | 
| -    return id.describe().then((pubspec) { | 
| -      // Cache it. | 
| -      _pubspecs[id] = pubspec; | 
| -      return pubspec; | 
| -    }); | 
| -  } | 
| -} | 
| - | 
| /// Describes one [Package] in the [DependencyGraph] and keeps track of which | 
| /// packages depend on it and what constraints they place on it. | 
| -class Dependency { | 
| +class DependencyNode { | 
| /// The name of the this dependency's package. | 
| final String name; | 
|  | 
| @@ -546,19 +493,16 @@ class Dependency { | 
| return refs.first; | 
| } | 
|  | 
| -  Dependency(this.name) | 
| +  DependencyNode(this.name) | 
| : _refs = <String, PackageRef>{}; | 
|  | 
| -  Dependency._clone(Dependency other) | 
| +  DependencyNode._clone(DependencyNode other) | 
| : name = other.name, | 
| version = other.version, | 
| _refs = new Map<String, PackageRef>.from(other._refs); | 
|  | 
| /// Creates a copy of this dependency. | 
| -  Dependency clone() => new Dependency._clone(this); | 
| - | 
| -  /// Return a list of available versions for this dependency. | 
| -  Future<List<Version>> getVersions() => source.getVersions(name, description); | 
| +  DependencyNode clone() => new DependencyNode._clone(this); | 
|  | 
| /// Places [ref] as a constraint from [package] onto this. | 
| void placeConstraint(String package, PackageRef ref) { | 
| @@ -596,120 +540,14 @@ class Dependency { | 
|  | 
| /// Removes the constraint from [package] onto this. | 
| PackageRef removeConstraint(String package) => _refs.remove(package); | 
| -} | 
| - | 
| -/// Exception thrown when the [VersionConstraint] used to match a package is | 
| -/// valid (i.e. non-empty), but there are no released versions of the package | 
| -/// that fit that constraint. | 
| -class NoVersionException implements Exception { | 
| -  final String package; | 
| -  final VersionConstraint constraint; | 
| -  final Map<String, PackageRef> _dependencies; | 
| - | 
| -  NoVersionException(this.package, this.constraint, this._dependencies); | 
| - | 
| -  String toString() { | 
| -    var buffer = new StringBuffer(); | 
| -    buffer.write("Package '$package' has no versions that match $constraint " | 
| -        "derived from:\n"); | 
| - | 
| -    var keys = new List.from(_dependencies.keys); | 
| -    keys.sort(); | 
|  | 
| -    for (var key in keys) { | 
| -      buffer.write("- '$key' depends on version " | 
| -          "${_dependencies[key].constraint}\n"); | 
| -    } | 
| - | 
| -    return buffer.toString(); | 
| -  } | 
| -} | 
| - | 
| -// TODO(rnystrom): Report the list of depending packages and their constraints. | 
| -/// Exception thrown when the most recent version of [package] must be selected, | 
| -/// but doesn't match the [VersionConstraint] imposed on the package. | 
| -class CouldNotUpdateException implements Exception { | 
| -  final String package; | 
| -  final VersionConstraint constraint; | 
| -  final Version best; | 
| - | 
| -  CouldNotUpdateException(this.package, this.constraint, this.best); | 
| - | 
| -  String toString() => | 
| -      "The latest version of '$package', $best, does not match $constraint."; | 
| -} | 
| - | 
| -/// Exception thrown when the [VersionConstraint] used to match a package is | 
| -/// the empty set: in other words, multiple packages depend on it and have | 
| -/// conflicting constraints that have no overlap. | 
| -class DisjointConstraintException implements Exception { | 
| -  final String package; | 
| -  final Map<String, PackageRef> _dependencies; | 
| - | 
| -  DisjointConstraintException(this.package, this._dependencies); | 
| - | 
| -  String toString() { | 
| -    var buffer = new StringBuffer(); | 
| -    buffer.write("Incompatible version constraints on '$package':\n"); | 
| - | 
| -    var keys = new List.from(_dependencies.keys); | 
| -    keys.sort(); | 
| - | 
| -    for (var key in keys) { | 
| -      buffer.write("- '$key' depends on version " | 
| -          "${_dependencies[key].constraint}\n"); | 
| -    } | 
| - | 
| -    return buffer.toString(); | 
| -  } | 
| -} | 
| - | 
| -/// Exception thrown when the [VersionSolver] fails to find a solution after a | 
| -/// certain number of iterations. | 
| -class CouldNotSolveException implements Exception { | 
| -  CouldNotSolveException(); | 
| - | 
| -  String toString() => | 
| -      "Could not find a solution that met all version constraints."; | 
| -} | 
| - | 
| -/// Exception thrown when two packages with the same name but different sources | 
| -/// are depended upon. | 
| -class SourceMismatchException implements Exception { | 
| -  final String package; | 
| -  final String depender1; | 
| -  final Source source1; | 
| -  final String depender2; | 
| -  final Source source2; | 
| - | 
| -  SourceMismatchException(this.package, this.depender1, this.source1, | 
| -      this.depender2, this.source2); | 
| - | 
| -  String toString() { | 
| -    return "Incompatible dependencies on '$package':\n" | 
| -        "- '$depender1' depends on it from source '$source1'\n" | 
| -        "- '$depender2' depends on it from source '$source2'"; | 
| -  } | 
| -} | 
| - | 
| -/// Exception thrown when two packages with the same name and source but | 
| -/// different descriptions are depended upon. | 
| -class DescriptionMismatchException implements Exception { | 
| -  final String package; | 
| -  final String depender1; | 
| -  final description1; | 
| -  final String depender2; | 
| -  final description2; | 
| - | 
| -  DescriptionMismatchException(this.package, this.depender1, this.description1, | 
| -      this.depender2, this.description2); | 
| - | 
| -  String toString() { | 
| -    // TODO(nweiz): Dump descriptions to YAML when that's supported. | 
| -    return "Incompatible dependencies on '$package':\n" | 
| -        "- '$depender1' depends on it with description " | 
| -        "${json.stringify(description1)}\n" | 
| -        "- '$depender2' depends on it with description " | 
| -        "${json.stringify(description2)}"; | 
| +  /// Converts this to a list of [Dependency] objects like the error types | 
| +  /// expect. | 
| +  List<Dependency> toList() { | 
| +    var result = <Dependency>[]; | 
| +    _refs.forEach((name, ref) { | 
| +      result.add(new Dependency(name, ref)); | 
| +    }); | 
| +    return result; | 
| } | 
| } | 
|  |