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 /// 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 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 /// solution to try. If possible, it will backjump based on the cause of the | 186 /// solution to try. If possible, it will backjump based on the cause of the |
| 187 /// [failure] to minize backtracking. Otherwise, it will simply backtrack to | 187 /// [failure] to minize backtracking. Otherwise, it will simply backtrack to |
| 188 /// the next possible solution. | 188 /// the next possible solution. |
| 189 /// | 189 /// |
| 190 /// Returns `true` if there is a new solution to try. | 190 /// Returns `true` if there is a new solution to try. |
| 191 bool _backtrack(SolveFailure failure) { | 191 bool _backtrack(SolveFailure failure) { |
| 192 var dependers = failure.dependencies.map((dep) => dep.depender).toSet(); | 192 var dependers = failure.dependencies.map((dep) => dep.depender).toSet(); |
| 193 | 193 |
| 194 while (!_selected.isEmpty) { | 194 while (!_selected.isEmpty) { |
| 195 // Look for a relevant selection to jump back to. | 195 // Look for a relevant selection to jump back to. |
| 196 for (var i = _selected.length - 1; i >= 0; i--) { | 196 if (failure.mayHaveTransitiveCause) { |
|
nweiz
2013/04/30 22:00:29
I don't understand this distinction. Why can't sou
Bob Nystrom
2013/05/01 01:06:15
After putting on my thinking cap, I've got a clean
| |
| 197 // Can't jump to a package that has no more alternatives. | 197 _backjumpToDependency(failure.package); |
| 198 if (_selected[i].length == 1) continue; | 198 } else { |
| 199 | 199 _backjumpToPackage(failure.dependencies); |
| 200 var selected = _selected[i].first; | |
| 201 | |
| 202 // If we find the package itself that failed, jump to it. | |
| 203 if (selected.name == failure.package) { | |
| 204 logSolve('jump to selected package ${failure.package}'); | |
| 205 _selected.removeRange(i + 1, _selected.length); | |
| 206 break; | |
| 207 } | |
| 208 | |
| 209 // See if this package directly or indirectly depends on [package]. | |
| 210 var path = _getDependencyPath(selected, failure.package); | |
| 211 if (path != null) { | |
| 212 logSolve('backjump to ${selected.name} because it depends on ' | |
| 213 '${failure.package} by $path'); | |
| 214 _selected.removeRange(i + 1, _selected.length); | |
| 215 break; | |
| 216 } | |
| 217 } | 200 } |
| 218 | 201 |
| 219 // Advance past the current version of the leaf-most package. | 202 // Advance past the current version of the leaf-most package. |
| 220 var previous = _selected.last.removeFirst(); | 203 var previous = _selected.last.removeFirst(); |
| 221 if (!_selected.last.isEmpty) { | 204 if (!_selected.last.isEmpty) { |
| 222 logSolve(); | 205 logSolve(); |
| 223 return true; | 206 return true; |
| 224 } | 207 } |
| 225 | 208 |
| 226 logSolve('$previous is last version, backtracking'); | 209 logSolve('$previous is last version, backtracking'); |
| 227 | 210 |
| 228 // That package has no more versions, so pop it and try the next one. | 211 // That package has no more versions, so pop it and try the next one. |
| 229 _selected.removeLast(); | 212 _selected.removeLast(); |
| 230 } | 213 } |
| 231 | 214 |
| 232 return false; | 215 return false; |
| 233 } | 216 } |
| 234 | 217 |
| 218 /// Backjumps to the most recently selected package in [dependencies]. Does | |
| 219 /// not consider dependent or related packages. This is used for failures | |
| 220 /// like [SourceMismatchException] where one of the packages explicitly in | |
| 221 /// that list is known to be a direct cause of the failure. | |
| 222 void _backjumpToPackage(Iterable<Dependency> dependencies) { | |
| 223 for (var i = _selected.length - 1; i >= 0; i--) { | |
| 224 var selected = _selected[i].first; | |
| 225 | |
| 226 for (var dep in dependencies) { | |
| 227 if (selected.name == dep.dep.name) { | |
| 228 logSolve('backjump to ${selected.name} because it caused conflict'); | |
| 229 _selected.removeRange(i + 1, _selected.length); | |
| 230 return; | |
| 231 } | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 /// Backjumps to the most recently selected package that directly or | |
| 237 /// indirectly depends on [package]. This is used for failures like | |
| 238 /// [NoVersionException] where we failed to find a version for [package] and | |
| 239 /// any package along the dependency graph leading to it could have caused | |
| 240 /// that failure. | |
| 241 void _backjumpToDependency(String package) { | |
| 242 for (var i = _selected.length - 1; i >= 0; i--) { | |
| 243 // Can't jump to a package that has no more alternatives. | |
| 244 if (_selected[i].length == 1) continue; | |
| 245 | |
| 246 var selected = _selected[i].first; | |
| 247 | |
| 248 // See if this package directly or indirectly depends on [package]. | |
| 249 var path = _getDependencyPath(selected, package); | |
| 250 if (path != null) { | |
| 251 logSolve('backjump to ${selected.name} because it depends on ' | |
| 252 '${package} by $path'); | |
|
nweiz
2013/04/30 22:00:29
indentation
Bob Nystrom
2013/05/01 01:06:15
Done.
| |
| 253 _selected.removeRange(i + 1, _selected.length); | |
| 254 return; | |
| 255 } | |
| 256 } | |
| 257 } | |
| 258 | |
| 235 /// Determines if [depender] has a direct or indirect dependency on | 259 /// Determines if [depender] has a direct or indirect dependency on |
| 236 /// [dependent] based on the currently selected versions of all packages. | 260 /// [dependent] based on the currently selected versions of all packages. |
| 237 /// Returns a string describing the dependency chain if it does, or `null` if | 261 /// Returns a string describing the dependency chain if it does, or `null` if |
| 238 /// there is no dependency. | 262 /// there is no dependency. |
| 239 String _getDependencyPath(PackageId depender, String dependent) { | 263 String _getDependencyPath(PackageId depender, String dependent) { |
| 240 // TODO(rnystrom): This is O(n^2) where n is the number of selected | 264 // TODO(rnystrom): This is O(n^2) where n is the number of selected |
| 241 // packages. Could store the reverse dependency graph to address that. If | 265 // packages. Could store the reverse dependency graph to address that. If |
| 242 // we do that, we need to make sure it gets correctly rolled back when | 266 // we do that, we need to make sure it gets correctly rolled back when |
| 243 // backtracking occurs. | 267 // backtracking occurs. |
| 244 var visited = new Set<String>(); | 268 var visited = new Set<String>(); |
| (...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 592 // know what they're doing. | 616 // know what they're doing. |
| 593 if (sdk.isBleedingEdge) return; | 617 if (sdk.isBleedingEdge) return; |
| 594 | 618 |
| 595 if (pubspec.environment.sdkVersion.allows(sdk.version)) return; | 619 if (pubspec.environment.sdkVersion.allows(sdk.version)) return; |
| 596 | 620 |
| 597 throw new CouldNotSolveException( | 621 throw new CouldNotSolveException( |
| 598 'Package ${pubspec.name} requires SDK version ' | 622 'Package ${pubspec.name} requires SDK version ' |
| 599 '${pubspec.environment.sdkVersion} but the current SDK is ' | 623 '${pubspec.environment.sdkVersion} but the current SDK is ' |
| 600 '${sdk.version}.'); | 624 '${sdk.version}.'); |
| 601 } | 625 } |
| OLD | NEW |