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

Side by Side Diff: utils/pub/solver/backtracking_solver.dart

Issue 14232023: Switch to backtracking solver. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 8 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 | « utils/pub/entrypoint.dart ('k') | utils/pub/solver/greedy_solver.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 /// 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
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
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
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 }
OLDNEW
« no previous file with comments | « utils/pub/entrypoint.dart ('k') | utils/pub/solver/greedy_solver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698