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 20 matching lines...) Expand all Loading... | |
31 * of the constraints that its depending packages place on it. If that overall | 31 * of the constraints that its depending packages place on it. If that overall |
32 * constraint changes (say from "<3.0.0" to "<2.5.0"), then the currently | 32 * constraint changes (say from "<3.0.0" to "<2.5.0"), then the currently |
33 * picked version for that package may fall outside of the new constraint. If | 33 * picked version for that package may fall outside of the new constraint. If |
34 * that happens, we find the new best version that meets the updated constraint | 34 * that happens, we find the new best version that meets the updated constraint |
35 * and then the change the package to use that version. That cycles back up to | 35 * and then the change the package to use that version. That cycles back up to |
36 * the beginning again. | 36 * the beginning again. |
37 */ | 37 */ |
38 #library('version_solver'); | 38 #library('version_solver'); |
39 | 39 |
40 #import('dart:json'); | 40 #import('dart:json'); |
41 #import('lock_file.dart'); | |
41 #import('package.dart'); | 42 #import('package.dart'); |
42 #import('pubspec.dart'); | 43 #import('pubspec.dart'); |
43 #import('root_source.dart'); | 44 #import('root_source.dart'); |
44 #import('source.dart'); | 45 #import('source.dart'); |
45 #import('source_registry.dart'); | 46 #import('source_registry.dart'); |
46 #import('utils.dart'); | 47 #import('utils.dart'); |
47 #import('version.dart'); | 48 #import('version.dart'); |
48 | 49 |
49 /** | 50 /** |
50 * Attempts to select the best concrete versions for all of the transitive | 51 * Attempts to select the best concrete versions for all of the transitive |
51 * dependencies of [root] taking into account all of the [VersionConstraint]s | 52 * dependencies of [root] taking into account all of the [VersionConstraint]s |
52 * that those dependencies place on each other. If successful, completes to a | 53 * that those dependencies place on each other and the requirements imposed by |
53 * [Map] that maps package names to the selected version for that package. If | 54 * [lockFile]. If successful, completes to a [Map] that maps package names to |
54 * it fails, the future will complete with a [NoVersionException], | 55 * the selected version for that package. If it fails, the future will complete |
55 * [DisjointConstraintException], or [CouldNotSolveException]. | 56 * with a [NoVersionException], [DisjointConstraintException], or |
57 * [CouldNotSolveException]. | |
56 */ | 58 */ |
57 Future<List<PackageId>> resolveVersions(SourceRegistry sources, Package root) { | 59 Future<List<PackageId>> resolveVersions(SourceRegistry sources, Package root, |
58 return new VersionSolver(sources, root).solve(); | 60 LockFile lockFile) { |
61 return new VersionSolver(sources, root, lockFile).solve(); | |
59 } | 62 } |
60 | 63 |
61 class VersionSolver { | 64 class VersionSolver { |
62 final SourceRegistry _sources; | 65 final SourceRegistry _sources; |
63 final Package _root; | 66 final Package _root; |
67 final LockFile lockFile; | |
64 final PubspecCache _pubspecs; | 68 final PubspecCache _pubspecs; |
65 final Map<String, Dependency> _packages; | 69 final Map<String, Dependency> _packages; |
66 final Queue<WorkItem> _work; | 70 final Queue<WorkItem> _work; |
67 int _numIterations = 0; | 71 int _numIterations = 0; |
68 | 72 |
69 VersionSolver(SourceRegistry sources, Package root) | 73 VersionSolver(SourceRegistry sources, this._root, this.lockFile) |
70 : _sources = sources, | 74 : _sources = sources, |
71 _root = root, | |
72 _pubspecs = new PubspecCache(sources), | 75 _pubspecs = new PubspecCache(sources), |
73 _packages = <Dependency>{}, | 76 _packages = <Dependency>{}, |
74 _work = new Queue<WorkItem>(); | 77 _work = new Queue<WorkItem>(); |
75 | 78 |
76 Future<List<PackageId>> solve() { | 79 Future<List<PackageId>> solve() { |
77 // Kick off the work by adding the root package at its concrete version to | 80 // Kick off the work by adding the root package at its concrete version to |
78 // the dependency graph. | 81 // the dependency graph. |
79 var ref = new PackageRef(new RootSource(_root), _root.version, _root.name); | 82 var ref = new PackageRef(new RootSource(_root), _root.version, _root.name); |
80 enqueue(new AddConstraint('(entrypoint)', ref)); | 83 enqueue(new AddConstraint('(entrypoint)', ref)); |
81 _pubspecs.cache(ref.atVersion(_root.version), _root.pubspec); | 84 _pubspecs.cache(ref.atVersion(_root.version), _root.pubspec); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
122 } | 125 } |
123 | 126 |
124 /** | 127 /** |
125 * Sets the best selected version of [package] to [version]. | 128 * Sets the best selected version of [package] to [version]. |
126 */ | 129 */ |
127 void setVersion(String package, Version version) { | 130 void setVersion(String package, Version version) { |
128 _packages[package].version = version; | 131 _packages[package].version = version; |
129 } | 132 } |
130 | 133 |
131 List<PackageId> buildResults() { | 134 List<PackageId> buildResults() { |
132 return _packages.getValues() | 135 return _packages.getValues().filter((dep) => dep.isDependedOn).map((dep) { |
133 .filter((dep) => dep.isDependedOn) | 136 var description = dep.description; |
134 .map((dep) => new PackageId(dep.source, dep.version, dep.description)); | 137 |
138 // If the lockfile contains a fully-resolved description for the package, | |
139 // use that. This allows e.g. Git to ensure that the same commit is used. | |
140 var lockedPackage = lockFile.packages[dep.name]; | |
141 if (lockedPackage != null && lockedPackage.version == dep.version && | |
142 lockedPackage.source.name == dep.source.name && | |
143 dep.source.descriptionsEqual(description, lockedPackage.description)) { | |
Jennifer Messerly
2012/07/19 18:45:34
long line
nweiz
2012/07/19 18:56:54
Done.
| |
144 description = lockedPackage.description; | |
145 } | |
146 | |
147 return new PackageId(dep.source, dep.version, description); | |
148 }); | |
135 } | 149 } |
136 } | 150 } |
137 | 151 |
138 /** | 152 /** |
139 * The constraint solver works by iteratively processing a queue of work items. | 153 * The constraint solver works by iteratively processing a queue of work items. |
140 * Each item is a single atomic change to the dependency graph. Handling them | 154 * Each item is a single atomic change to the dependency graph. Handling them |
141 * in a queue lets us handle asynchrony (resolving versions requires information | 155 * in a queue lets us handle asynchrony (resolving versions requires information |
142 * from servers) as well as avoid deeply nested recursion. | 156 * from servers) as well as avoid deeply nested recursion. |
143 */ | 157 */ |
144 interface WorkItem { | 158 interface WorkItem { |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
268 } | 282 } |
269 | 283 |
270 // If the dependency is on the root package, then we don't need to do | 284 // If the dependency is on the root package, then we don't need to do |
271 // anything since it's already at the best version. | 285 // anything since it's already at the best version. |
272 if (name == solver._root.name) { | 286 if (name == solver._root.name) { |
273 solver.enqueue(new ChangeVersion( | 287 solver.enqueue(new ChangeVersion( |
274 source, description, solver._root.version)); | 288 source, description, solver._root.version)); |
275 return null; | 289 return null; |
276 } | 290 } |
277 | 291 |
292 // If the dependency is on a package in the lockfile, use the lockfile's | |
293 // version for that package if it's valid given the other constraints. | |
294 if (solver.lockFile.packages.containsKey(name)) { | |
Jennifer Messerly
2012/07/19 18:45:34
nit: I probably would write this as
var lockedP
nweiz
2012/07/19 18:56:54
Done.
| |
295 var lockedVersion = solver.lockFile.packages[name].version; | |
296 if (newConstraint.allows(lockedVersion)) { | |
297 solver.enqueue(new ChangeVersion(source, description, lockedVersion)); | |
298 return null; | |
299 } | |
300 } | |
301 | |
278 // The constraint has changed, so see what the best version of the package | 302 // The constraint has changed, so see what the best version of the package |
279 // that meets the new constraint is. | 303 // that meets the new constraint is. |
280 return source.getVersions(description).transform((versions) { | 304 return source.getVersions(description).transform((versions) { |
281 var best = null; | 305 var best = null; |
282 for (var version in versions) { | 306 for (var version in versions) { |
283 if (newConstraint.allows(version)) { | 307 if (newConstraint.allows(version)) { |
284 if (best == null || version > best) best = version; | 308 if (best == null || version > best) best = version; |
285 } | 309 } |
286 } | 310 } |
287 | 311 |
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
546 final description1; | 570 final description1; |
547 final description2; | 571 final description2; |
548 | 572 |
549 DescriptionMismatchException(this.package, this.description1, | 573 DescriptionMismatchException(this.package, this.description1, |
550 this.description2); | 574 this.description2); |
551 | 575 |
552 // TODO(nweiz): Dump to YAML when that's supported | 576 // TODO(nweiz): Dump to YAML when that's supported |
553 String toString() => "Package '$package' has conflicting descriptions " | 577 String toString() => "Package '$package' has conflicting descriptions " |
554 "'${JSON.stringify(description1)}' and '${JSON.stringify(description2)}'"; | 578 "'${JSON.stringify(description1)}' and '${JSON.stringify(description2)}'"; |
555 } | 579 } |
OLD | NEW |