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 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
168 } | 168 } |
169 } | 169 } |
170 | 170 |
171 // Advance past the current version of the leaf-most package. | 171 // Advance past the current version of the leaf-most package. |
172 var previous = _selected.last.removeFirst(); | 172 var previous = _selected.last.removeFirst(); |
173 if (!_selected.last.isEmpty) { | 173 if (!_selected.last.isEmpty) { |
174 logSolve(); | 174 logSolve(); |
175 return true; | 175 return true; |
176 } | 176 } |
177 | 177 |
178 logSolve('${previous} is last version, backtracking'); | 178 logSolve('$previous is last version, backtracking'); |
179 | 179 |
180 // That package has no more versions, so pop it and try the next one. | 180 // That package has no more versions, so pop it and try the next one. |
181 _selected.removeLast(); | 181 _selected.removeLast(); |
182 } | 182 } |
183 | 183 |
184 return false; | 184 return false; |
185 } | 185 } |
186 | 186 |
187 /// Determines if [depender] has a direct or indirect dependency on | 187 /// Determines if [depender] has a direct or indirect dependency on |
188 /// [dependent] based on the currently selected versions of all packages. | 188 /// [dependent] based on the currently selected versions of all packages. |
189 /// Returns a string describing the dependency chain if it does, or `null` if | 189 /// Returns a string describing the dependency chain if it does, or `null` if |
190 /// there is no dependency. | 190 /// there is no dependency. |
191 String _getDependencyPath(PackageId depender, String dependent) { | 191 String _getDependencyPath(PackageId depender, String dependent) { |
192 // TODO(rnystrom): This is O(n^2) where n is the number of selected | |
193 // packages. Could store the reverse dependency graph to address that. If | |
194 // we do that, we need to make sure it gets correctly rolled back when | |
195 // backtracking occurs. | |
192 var visited = new Set<String>(); | 196 var visited = new Set<String>(); |
193 var resultPath; | 197 var currentPath = depender.name; |
nweiz
2013/04/17 00:59:55
I think my comment on this may have been swallowed
Bob Nystrom
2013/04/17 17:11:03
Oh, now I see. I read that earlier comment as just
| |
194 var currentPath = '${depender.name}'; | |
195 | 198 |
196 walkDeps(PackageId package) { | 199 walkDeps(PackageId package) { |
197 if (visited.contains(package.name)) return false; | 200 if (visited.contains(package.name)) return null; |
198 visited.add(package.name); | 201 visited.add(package.name); |
199 | 202 |
200 var pubspec = cache.getCachedPubspec(package); | 203 var pubspec = cache.getCachedPubspec(package); |
201 if (pubspec == null) return false; | 204 if (pubspec == null) return null; |
202 | 205 |
203 for (var dep in pubspec.dependencies) { | 206 for (var dep in pubspec.dependencies) { |
204 var previousPath = currentPath; | 207 var previousPath = currentPath; |
205 currentPath = '$currentPath -> ${dep.name}'; | 208 currentPath = '$currentPath -> ${dep.name}'; |
206 | 209 |
207 if (dep.name == dependent) { | 210 if (dep.name == dependent) return currentPath; |
208 resultPath = currentPath; | |
209 return true; | |
210 } | |
211 | 211 |
212 var selected = getSelected(dep.name); | 212 var selected = getSelected(dep.name); |
213 // Ignore unselected dependencies. We haven't traversed into them yet, | 213 // Ignore unselected dependencies. We haven't traversed into them yet, |
214 // so they can't affect backjumping. | 214 // so they can't affect backjumping. |
215 if (selected == null) continue; | 215 if (selected == null) continue; |
216 | 216 |
217 if (walkDeps(selected)) return true; | 217 var depPath = walkDeps(selected); |
218 if (depPath != null) return depPath; | |
218 | 219 |
219 currentPath = previousPath; | 220 currentPath = previousPath; |
220 } | 221 } |
221 | 222 |
222 return false; | 223 return null; |
223 } | 224 } |
224 | 225 |
225 return walkDeps(depender) ? resultPath : null; | 226 return walkDeps(depender); |
226 } | 227 } |
227 | 228 |
228 /// Logs [message] in the context of the current selected packages. If | 229 /// Logs [message] in the context of the current selected packages. If |
229 /// [message] is omitted, just logs a description of leaf-most selection. | 230 /// [message] is omitted, just logs a description of leaf-most selection. |
230 void logSolve([String message]) { | 231 void logSolve([String message]) { |
231 if (message == null) { | 232 if (message == null) { |
232 if (_selected.isEmpty) { | 233 if (_selected.isEmpty) { |
233 message = "* start at root"; | 234 message = "* start at root"; |
234 } else { | 235 } else { |
235 var versions = _selected.last.map((id) => id.version).toList(); | 236 var count = _selected.last.length; |
236 if (versions.length > 5) { | 237 message = "* select ${_selected.last.first} ($count versions)"; |
237 versions = versions.take(5).join(', ') + '...'; | |
238 } else { | |
239 versions = versions.join(', '); | |
240 } | |
241 message = "* select ${_selected.last.first} (from $versions)"; | |
242 } | 238 } |
243 } else { | 239 } else { |
244 // Otherwise, indent it under the current selected package. | 240 // Otherwise, indent it under the current selected package. |
245 message = "| $message"; | 241 message = "| $message"; |
246 } | 242 } |
247 | 243 |
248 // Indent for the previous selections. | 244 // Indent for the previous selections. |
249 var buffer = new StringBuffer(); | 245 var buffer = new StringBuffer(); |
250 buffer.writeAll(_selected.skip(1).map((_) => '| ')); | 246 buffer.writeAll(_selected.skip(1).map((_) => '| ')); |
251 buffer.write(message); | 247 buffer.write(message); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
320 return new Future.value(new Pair<PackageRef, int>(ref, 1)); | 316 return new Future.value(new Pair<PackageRef, int>(ref, 1)); |
321 } | 317 } |
322 | 318 |
323 return _solver.cache.getVersions(ref.name, ref.source, ref.description) | 319 return _solver.cache.getVersions(ref.name, ref.source, ref.description) |
324 .then((versions) { | 320 .then((versions) { |
325 return new Pair<PackageRef, int>(ref, versions.length); | 321 return new Pair<PackageRef, int>(ref, versions.length); |
326 }).catchError((error) { | 322 }).catchError((error) { |
327 // If it fails for any reason, just treat that as no versions. This | 323 // If it fails for any reason, just treat that as no versions. This |
328 // will sort this reference higher so that we can traverse into it | 324 // will sort this reference higher so that we can traverse into it |
329 // and report the error more properly. | 325 // and report the error more properly. |
326 log.solver("Could not get versions for $ref:\n$error\n\n" | |
327 "${getAttachedStackTrace(error)}"); | |
330 return new Pair<PackageRef, int>(ref, 0); | 328 return new Pair<PackageRef, int>(ref, 0); |
331 }); | 329 }); |
332 } | 330 } |
333 | 331 |
334 return Future.wait(refs.map(getNumVersions)).then((pairs) { | 332 return Future.wait(refs.map(getNumVersions)).then((pairs) { |
335 // Future.wait() returns an immutable list, so make a copy. | 333 // Future.wait() returns an immutable list, so make a copy. |
336 pairs = pairs.toList(); | 334 pairs = pairs.toList(); |
337 | 335 |
338 // Sort in best-first order to minimize backtracking. | 336 // Sort in best-first order to minimize backtracking. |
339 pairs.sort((a, b) { | 337 pairs.sort((a, b) { |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
533 | 531 |
534 var required = _getRequired(name); | 532 var required = _getRequired(name); |
535 if (required != null) { | 533 if (required != null) { |
536 if (package.source.name != required.ref.source.name) return null; | 534 if (package.source.name != required.ref.source.name) return null; |
537 if (!package.descriptionEquals(required.ref)) return null; | 535 if (!package.descriptionEquals(required.ref)) return null; |
538 } | 536 } |
539 | 537 |
540 return package; | 538 return package; |
541 } | 539 } |
542 } | 540 } |
OLD | NEW |