| 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 library pub.solver.version_solver; | 5 library pub.solver.version_solver; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import "dart:convert"; | 8 import "dart:convert"; |
| 9 | 9 |
| 10 import 'package:pub_semver/pub_semver.dart'; | 10 import 'package:pub_semver/pub_semver.dart'; |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 if (!succeeded) { | 126 if (!succeeded) { |
| 127 return 'Failed to solve after $attemptedSolutions attempts:\n' | 127 return 'Failed to solve after $attemptedSolutions attempts:\n' |
| 128 '$error'; | 128 '$error'; |
| 129 } | 129 } |
| 130 | 130 |
| 131 return 'Took $attemptedSolutions tries to resolve to\n' | 131 return 'Took $attemptedSolutions tries to resolve to\n' |
| 132 '- ${packages.join("\n- ")}'; | 132 '- ${packages.join("\n- ")}'; |
| 133 } | 133 } |
| 134 } | 134 } |
| 135 | 135 |
| 136 /// Maintains a cache of previously-requested data: pubspecs and version lists. | 136 /// Maintains a cache of previously-requested version lists. |
| 137 /// | 137 class SolverCache { |
| 138 /// Used to avoid requesting the same pubspec from the server repeatedly. | |
| 139 class PubspecCache { | |
| 140 final SourceRegistry _sources; | 138 final SourceRegistry _sources; |
| 141 | 139 |
| 142 /// The already-requested cached pubspec lists. | 140 /// The already-requested cached version lists. |
| 143 final _versions = new Map<PackageRef, List<PackageId>>(); | 141 final _versions = new Map<PackageRef, List<PackageId>>(); |
| 144 | 142 |
| 145 /// The errors from failed version list requests. | 143 /// The errors from failed version list requests. |
| 146 final _versionErrors = new Map<PackageRef, Pair<Object, Chain>>(); | 144 final _versionErrors = new Map<PackageRef, Pair<Object, Chain>>(); |
| 147 | 145 |
| 148 /// The already-requested cached pubspecs. | |
| 149 final _pubspecs = new Map<PackageId, Pubspec>(); | |
| 150 | |
| 151 // TODO(nweiz): Currently, if [getCachedPubspec] returns pubspecs cached via | |
| 152 // [getVersions], the "complex backtrack" test case in version_solver_test | |
| 153 // fails. Fix that. See also [BacktrackingSolver._getTransitiveDependers]. | |
| 154 /// The set of package ids for which [getPubspec] has been explicitly called. | |
| 155 final _explicitlyCached = new Set<PackageId>(); | |
| 156 | |
| 157 /// The type of version resolution that was run. | 146 /// The type of version resolution that was run. |
| 158 final SolveType _type; | 147 final SolveType _type; |
| 159 | 148 |
| 160 /// The number of times a version list was requested and it wasn't cached and | 149 /// The number of times a version list was requested and it wasn't cached and |
| 161 /// had to be requested from the source. | 150 /// had to be requested from the source. |
| 162 int _versionCacheMisses = 0; | 151 int _versionCacheMisses = 0; |
| 163 | 152 |
| 164 /// The number of times a version list was requested and the cached version | 153 /// The number of times a version list was requested and the cached version |
| 165 /// was returned. | 154 /// was returned. |
| 166 int _versionCacheHits = 0; | 155 int _versionCacheHits = 0; |
| 167 | 156 |
| 168 /// The number of times a pubspec was requested and it wasn't cached and had | 157 SolverCache(this._type, this._sources); |
| 169 /// to be requested from the source. | |
| 170 int _pubspecCacheMisses = 0; | |
| 171 | |
| 172 /// The number of times a pubspec was requested and the cached version was | |
| 173 /// returned. | |
| 174 int _pubspecCacheHits = 0; | |
| 175 | |
| 176 PubspecCache(this._type, this._sources); | |
| 177 | |
| 178 /// Caches [pubspec] as the [Pubspec] for the package identified by [id]. | |
| 179 void cache(PackageId id, Pubspec pubspec) { | |
| 180 _pubspecs[id] = pubspec; | |
| 181 } | |
| 182 | |
| 183 /// Loads the pubspec for the package identified by [id]. | |
| 184 Future<Pubspec> getPubspec(PackageId id) async { | |
| 185 _explicitlyCached.add(id); | |
| 186 | |
| 187 // Complete immediately if it's already cached. | |
| 188 if (_pubspecs.containsKey(id)) { | |
| 189 _pubspecCacheHits++; | |
| 190 return _pubspecs[id]; | |
| 191 } | |
| 192 | |
| 193 _pubspecCacheMisses++; | |
| 194 | |
| 195 var source = _sources[id.source]; | |
| 196 var pubspec = await source.describe(id); | |
| 197 _pubspecs[id] = pubspec; | |
| 198 return pubspec; | |
| 199 } | |
| 200 | |
| 201 /// Returns the previously cached pubspec for the package identified by [id] | |
| 202 /// or returns `null` if not in the cache. | |
| 203 Pubspec getCachedPubspec(PackageId id) => | |
| 204 _explicitlyCached.contains(id) ? _pubspecs[id] : null; | |
| 205 | 158 |
| 206 /// Gets the list of versions for [package]. | 159 /// Gets the list of versions for [package]. |
| 207 /// | 160 /// |
| 208 /// Packages are sorted in descending version order with all "stable" | 161 /// Packages are sorted in descending version order with all "stable" |
| 209 /// versions (i.e. ones without a prerelease suffix) before pre-release | 162 /// versions (i.e. ones without a prerelease suffix) before pre-release |
| 210 /// versions. This ensures that the solver prefers stable packages over | 163 /// versions. This ensures that the solver prefers stable packages over |
| 211 /// unstable ones. | 164 /// unstable ones. |
| 212 Future<List<PackageId>> getVersions(PackageRef package) async { | 165 Future<List<PackageId>> getVersions(PackageRef package) async { |
| 213 if (package.isRoot) { | 166 if (package.isRoot) { |
| 214 throw new StateError("Cannot get versions for root package $package."); | 167 throw new StateError("Cannot get versions for root package $package."); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 226 // See if we cached a failure. | 179 // See if we cached a failure. |
| 227 var error = _versionErrors[package]; | 180 var error = _versionErrors[package]; |
| 228 if (error != null) { | 181 if (error != null) { |
| 229 _versionCacheHits++; | 182 _versionCacheHits++; |
| 230 await new Future.error(error.first, error.last); | 183 await new Future.error(error.first, error.last); |
| 231 } | 184 } |
| 232 | 185 |
| 233 _versionCacheMisses++; | 186 _versionCacheMisses++; |
| 234 | 187 |
| 235 var source = _sources[package.source]; | 188 var source = _sources[package.source]; |
| 236 var pubspecs; | 189 var ids; |
| 237 try { | 190 try { |
| 238 pubspecs = await source.getVersions(package.name, package.description); | 191 ids = await source.getVersions(package); |
| 239 } catch (error, stackTrace) { | 192 } catch (error, stackTrace) { |
| 240 // If an error occurs, cache that too. We only want to do one request | 193 // If an error occurs, cache that too. We only want to do one request |
| 241 // for any given package, successful or not. | 194 // for any given package, successful or not. |
| 242 var chain = new Chain.forTrace(stackTrace); | 195 var chain = new Chain.forTrace(stackTrace); |
| 243 log.solver("Could not get versions for $package:\n$error\n\n" + | 196 log.solver("Could not get versions for $package:\n$error\n\n" + |
| 244 chain.terse.toString()); | 197 chain.terse.toString()); |
| 245 _versionErrors[package] = new Pair(error, chain); | 198 _versionErrors[package] = new Pair(error, chain); |
| 246 throw error; | 199 throw error; |
| 247 } | 200 } |
| 248 | 201 |
| 249 // Sort by priority so we try preferred versions first. | 202 // Sort by priority so we try preferred versions first. |
| 250 pubspecs.sort((pubspec1, pubspec2) { | 203 ids.sort((id1, id2) { |
| 204 // Reverse the IDs because we want the newest version at the front of the |
| 205 // list. |
| 251 return _type == SolveType.DOWNGRADE | 206 return _type == SolveType.DOWNGRADE |
| 252 ? Version.antiprioritize(pubspec1.version, pubspec2.version) | 207 ? Version.antiprioritize(id2.version, id1.version) |
| 253 : Version.prioritize(pubspec1.version, pubspec2.version); | 208 : Version.prioritize(id2.version, id1.version); |
| 254 }); | 209 }); |
| 255 | 210 |
| 256 var ids = pubspecs.reversed.map((pubspec) { | 211 ids = ids.toList(); |
| 257 var id = package.atVersion(pubspec.version); | |
| 258 // Eagerly cache the pubspec now since we have it. | |
| 259 _pubspecs[id] = pubspec; | |
| 260 return id; | |
| 261 }).toList(); | |
| 262 _versions[package] = ids; | 212 _versions[package] = ids; |
| 263 return ids; | 213 return ids; |
| 264 } | 214 } |
| 265 | 215 |
| 266 /// Returns the previously cached list of versions for the package identified | 216 /// Returns the previously cached list of versions for the package identified |
| 267 /// by [package] or returns `null` if not in the cache. | 217 /// by [package] or returns `null` if not in the cache. |
| 268 List<PackageId> getCachedVersions(PackageRef package) => _versions[package]; | 218 List<PackageId> getCachedVersions(PackageRef package) => _versions[package]; |
| 269 | 219 |
| 270 /// Returns a user-friendly output string describing metrics of the solve. | 220 /// Returns a user-friendly output string describing metrics of the solve. |
| 271 String describeResults() { | 221 String describeResults() { |
| 272 var results = '''- Requested $_versionCacheMisses version lists | 222 var results = '''- Requested $_versionCacheMisses version lists |
| 273 - Looked up $_versionCacheHits cached version lists | 223 - Looked up $_versionCacheHits cached version lists |
| 274 - Requested $_pubspecCacheMisses pubspecs | |
| 275 - Looked up $_pubspecCacheHits cached pubspecs | |
| 276 '''; | 224 '''; |
| 277 | 225 |
| 278 // Uncomment this to dump the visited package graph to JSON. | 226 // Uncomment this to dump the visited package graph to JSON. |
| 279 //results += _debugWritePackageGraph(); | 227 //results += _debugWritePackageGraph(); |
| 280 | 228 |
| 281 return results; | 229 return results; |
| 282 } | 230 } |
| 283 } | 231 } |
| 284 | 232 |
| 285 /// A reference from a depending package to a package that it depends on. | 233 /// A reference from a depending package to a package that it depends on. |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 478 | 426 |
| 479 DependencyNotFoundException(String package, this._innerException, | 427 DependencyNotFoundException(String package, this._innerException, |
| 480 Iterable<Dependency> dependencies) | 428 Iterable<Dependency> dependencies) |
| 481 : super(package, dependencies); | 429 : super(package, dependencies); |
| 482 | 430 |
| 483 /// The failure isn't because of the version of description of the package, | 431 /// The failure isn't because of the version of description of the package, |
| 484 /// it's the package itself that can't be found, so just show the name and no | 432 /// it's the package itself that can't be found, so just show the name and no |
| 485 /// descriptive details. | 433 /// descriptive details. |
| 486 String _describeDependency(PackageDep dep) => ""; | 434 String _describeDependency(PackageDep dep) => ""; |
| 487 } | 435 } |
| OLD | NEW |