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. Attempts to find the best solution for | 5 /// A back-tracking depth-first solver. Attempts to find the best solution for |
6 /// a root package's transitive dependency graph, where a "solution" is a set | 6 /// a root package's transitive dependency graph, where a "solution" is a set |
7 /// of concrete package versions. A valid solution will select concrete | 7 /// of concrete package versions. A valid solution will select concrete |
8 /// versions for every package reached from the root package's dependency graph, | 8 /// versions for every package reached from the root package's dependency graph, |
9 /// and each of those packages will fit the version constraints placed on it. | 9 /// and each of those packages will fit the version constraints placed on it. |
10 /// | 10 /// |
(...skipping 13 matching lines...) Expand all Loading... |
24 /// package. | 24 /// package. |
25 /// | 25 /// |
26 /// - etc. | 26 /// - etc. |
27 /// | 27 /// |
28 /// then the current solution is invalid. It will then backtrack to the most | 28 /// then the current solution is invalid. It will then backtrack to the most |
29 /// recent speculative version choice and try the next one. That becomes the | 29 /// recent speculative version choice and try the next one. That becomes the |
30 /// new in-progress solution and it tries to proceed from there. It will keep | 30 /// new in-progress solution and it tries to proceed from there. It will keep |
31 /// doing this, traversing and then backtracking when it meets a failure until | 31 /// doing this, traversing and then backtracking when it meets a failure until |
32 /// a valid solution has been found or until all possible options for all | 32 /// a valid solution has been found or until all possible options for all |
33 /// speculative choices have been exhausted. | 33 /// speculative choices have been exhausted. |
34 library version_solver2; | 34 library solver.backtracking_solver; |
35 | 35 |
36 import 'dart:async'; | 36 import 'dart:async'; |
37 import 'dart:collection' show Queue; | 37 import 'dart:collection' show Queue; |
38 | 38 |
39 import '../lock_file.dart'; | 39 import '../lock_file.dart'; |
40 import '../log.dart' as log; | 40 import '../log.dart' as log; |
41 import '../package.dart'; | 41 import '../package.dart'; |
42 import '../pubspec.dart'; | 42 import '../pubspec.dart'; |
43 import '../sdk.dart' as sdk; | 43 import '../sdk.dart' as sdk; |
44 import '../source.dart'; | 44 import '../source.dart'; |
45 import '../source_registry.dart'; | 45 import '../source_registry.dart'; |
46 import '../utils.dart'; | 46 import '../utils.dart'; |
47 import '../version.dart'; | 47 import '../version.dart'; |
48 import 'version_solver.dart'; | 48 import 'version_solver.dart'; |
49 | 49 |
50 /// The top-level solver. Keeps track of the current potential solution, and | 50 /// The top-level solver. Keeps track of the current potential solution, and |
51 /// the other possible versions for speculative package selections. Backtracks | 51 /// the other possible versions for speculative package selections. Backtracks |
52 /// and advances to the next potential solution in the case of a failure. | 52 /// and advances to the next potential solution in the case of a failure. |
53 class BacktrackingVersionSolver extends VersionSolver { | 53 class BacktrackingSolver { |
| 54 final SourceRegistry sources; |
| 55 final Package root; |
| 56 final LockFile lockFile; |
| 57 final PubspecCache cache; |
| 58 |
54 /// The set of packages that are being explicitly updated. The solver will | 59 /// The set of packages that are being explicitly updated. The solver will |
55 /// only allow the very latest version for each of these packages. | 60 /// only allow the very latest version for each of these packages. |
56 final _forceLatest = new Set<String>(); | 61 final _forceLatest = new Set<String>(); |
57 | 62 |
58 /// Every time a package is encountered when traversing the dependency graph, | 63 /// Every time a package is encountered when traversing the dependency graph, |
59 /// the solver must select a version for it, sometimes when multiple versions | 64 /// the solver must select a version for it, sometimes when multiple versions |
60 /// are valid. This keeps track of which versions have been selected so far | 65 /// are valid. This keeps track of which versions have been selected so far |
61 /// and which remain to be tried. | 66 /// and which remain to be tried. |
62 /// | 67 /// |
63 /// Each entry in the list is an ordered [Queue] of versions to try for a | 68 /// Each entry in the list is an ordered [Queue] of versions to try for a |
64 /// single package. The first item in the queue is the currently selected | 69 /// single package. The first item in the queue is the currently selected |
65 /// version for that package. When a new dependency is encountered, a queue | 70 /// version for that package. When a new dependency is encountered, a queue |
66 /// of versions of that dependency is pushed onto the end of the list. A | 71 /// of versions of that dependency is pushed onto the end of the list. A |
67 /// queue is removed from the list once it's empty, indicating that none of | 72 /// queue is removed from the list once it's empty, indicating that none of |
68 /// the versions provided a solution. | 73 /// the versions provided a solution. |
69 /// | 74 /// |
70 /// The solver tries versions in depth-first order, so only the last queue in | 75 /// The solver tries versions in depth-first order, so only the last queue in |
71 /// the list will have items removed from it. When a new constraint is placed | 76 /// the list will have items removed from it. When a new constraint is placed |
72 /// on an already-selected package, and that constraint doesn't match the | 77 /// on an already-selected package, and that constraint doesn't match the |
73 /// selected version, that will cause the current solution to fail and | 78 /// selected version, that will cause the current solution to fail and |
74 /// trigger backtracking. | 79 /// trigger backtracking. |
75 final _selected = <Queue<PackageId>>[]; | 80 final _selected = <Queue<PackageId>>[]; |
76 | 81 |
77 /// The number of possible solutions that have been attempted. | 82 /// The number of solutions the solver has tried so far. |
78 int get attemptedSolutions => _attemptedSolutions; | 83 int get attemptedSolutions => _attemptedSolutions; |
79 var _attemptedSolutions = 1; | 84 var _attemptedSolutions = 1; |
80 | 85 |
81 BacktrackingVersionSolver(SourceRegistry sources, Package root, | 86 BacktrackingSolver(SourceRegistry sources, this.root, this.lockFile, |
82 LockFile lockFile, List<String> useLatest) | 87 List<String> useLatest) |
83 : super(sources, root, lockFile, useLatest); | 88 : sources = sources, |
| 89 cache = new PubspecCache(sources) { |
| 90 for (var package in useLatest) { |
| 91 forceLatestVersion(package); |
| 92 lockFile.packages.remove(package); |
| 93 } |
| 94 } |
| 95 |
| 96 /// Run the solver. Completes with a list of specific package versions if |
| 97 /// successful or an error if it failed to find a solution. |
| 98 Future<SolveResult> solve() { |
| 99 var stopwatch = new Stopwatch(); |
| 100 |
| 101 return new Future(() { |
| 102 stopwatch.start(); |
| 103 |
| 104 // Pre-cache the root package's known pubspec. |
| 105 cache.cache(new PackageId.root(root), root.pubspec); |
| 106 |
| 107 _validateSdkConstraint(root.pubspec); |
| 108 return _traverseSolution(); |
| 109 }).then((packages) { |
| 110 return new SolveResult(packages, null, attemptedSolutions); |
| 111 }).catchError((error) { |
| 112 if (error is! SolveFailure) throw error; |
| 113 |
| 114 // Wrap a failure in a result so we can attach some other data. |
| 115 return new SolveResult(null, error, attemptedSolutions); |
| 116 }).whenComplete(() { |
| 117 // Gather some solving metrics. |
| 118 var buffer = new StringBuffer(); |
| 119 buffer.writeln('${runtimeType} took ${stopwatch.elapsed} seconds.'); |
| 120 buffer.writeln( |
| 121 '- Requested ${cache.versionCacheMisses} version lists'); |
| 122 buffer.writeln( |
| 123 '- Looked up ${cache.versionCacheHits} cached version lists'); |
| 124 buffer.writeln( |
| 125 '- Requested ${cache.pubspecCacheMisses} pubspecs'); |
| 126 buffer.writeln( |
| 127 '- Looked up ${cache.pubspecCacheHits} cached pubspecs'); |
| 128 log.solver(buffer); |
| 129 }); |
| 130 } |
84 | 131 |
85 void forceLatestVersion(String package) { | 132 void forceLatestVersion(String package) { |
86 _forceLatest.add(package); | 133 _forceLatest.add(package); |
87 } | 134 } |
88 | 135 |
89 Future<List<PackageId>> runSolver() { | |
90 return new Future.sync(() { | |
91 _validateSdkConstraint(root.pubspec); | |
92 return _traverseSolution(); | |
93 }); | |
94 } | |
95 | |
96 /// Adds [versions], which is the list of all allowed versions of a given | 136 /// Adds [versions], which is the list of all allowed versions of a given |
97 /// package, to the set of versions to consider for solutions. The first item | 137 /// package, to the set of versions to consider for solutions. The first item |
98 /// in the list will be the currently selected version of that package. | 138 /// in the list will be the currently selected version of that package. |
99 /// Subsequent items will be tried if it the current selection fails. Returns | 139 /// Subsequent items will be tried if it the current selection fails. Returns |
100 /// the first selected version. | 140 /// the first selected version. |
101 PackageId select(Iterable<PackageId> versions) { | 141 PackageId select(Iterable<PackageId> versions) { |
102 _selected.add(new Queue<PackageId>.from(versions)); | 142 _selected.add(new Queue<PackageId>.from(versions)); |
103 logSolve(); | 143 logSolve(); |
104 return versions.first; | 144 return versions.first; |
105 } | 145 } |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 } | 291 } |
252 } | 292 } |
253 | 293 |
254 /// Given the solver's current set of selected package versions, this tries to | 294 /// Given the solver's current set of selected package versions, this tries to |
255 /// traverse the dependency graph and see if a complete set of valid versions | 295 /// traverse the dependency graph and see if a complete set of valid versions |
256 /// has been chosen. If it reaches a conflict, it will fail and stop | 296 /// has been chosen. If it reaches a conflict, it will fail and stop |
257 /// traversing. If it reaches a package that isn't selected it will refine the | 297 /// traversing. If it reaches a package that isn't selected it will refine the |
258 /// solution by adding that package's set of allowed versions to the solver and | 298 /// solution by adding that package's set of allowed versions to the solver and |
259 /// then select the best one and continue. | 299 /// then select the best one and continue. |
260 class Traverser { | 300 class Traverser { |
261 final BacktrackingVersionSolver _solver; | 301 final BacktrackingSolver _solver; |
262 | 302 |
263 /// The queue of packages left to traverse. We do a breadth-first traversal | 303 /// The queue of packages left to traverse. We do a breadth-first traversal |
264 /// using an explicit queue just to avoid the code complexity of a recursive | 304 /// using an explicit queue just to avoid the code complexity of a recursive |
265 /// asynchronous traversal. | 305 /// asynchronous traversal. |
266 final _packages = new Queue<PackageId>(); | 306 final _packages = new Queue<PackageId>(); |
267 | 307 |
268 /// The packages we have already traversed. Used to avoid traversing the same | 308 /// The packages we have already traversed. Used to avoid traversing the same |
269 /// package multiple times, and to build the complete solution results. | 309 /// package multiple times, and to build the complete solution results. |
270 final _visited = new Set<PackageId>(); | 310 final _visited = new Set<PackageId>(); |
271 | 311 |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
548 /// Ensures that if [pubspec] has an SDK constraint, then it is compatible | 588 /// Ensures that if [pubspec] has an SDK constraint, then it is compatible |
549 /// with the current SDK. Throws a [SolverFailure] if not. | 589 /// with the current SDK. Throws a [SolverFailure] if not. |
550 void _validateSdkConstraint(Pubspec pubspec) { | 590 void _validateSdkConstraint(Pubspec pubspec) { |
551 if (pubspec.environment.sdkVersion.allows(sdk.version)) return; | 591 if (pubspec.environment.sdkVersion.allows(sdk.version)) return; |
552 | 592 |
553 throw new CouldNotSolveException( | 593 throw new CouldNotSolveException( |
554 'Package ${pubspec.name} requires SDK version ' | 594 'Package ${pubspec.name} requires SDK version ' |
555 '${pubspec.environment.sdkVersion} but the current SDK is ' | 595 '${pubspec.environment.sdkVersion} but the current SDK is ' |
556 '${sdk.version}.'); | 596 '${sdk.version}.'); |
557 } | 597 } |
OLD | NEW |