| 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 version_solver; | 5 library version_solver; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:json' as json; | 8 import 'dart:json' as json; |
| 9 | 9 |
| 10 import '../lock_file.dart'; | 10 import '../lock_file.dart'; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 63 | 63 |
| 64 return 'Took $attemptedSolutions tries to resolve to\n' | 64 return 'Took $attemptedSolutions tries to resolve to\n' |
| 65 '- ${packages.join("\n- ")}'; | 65 '- ${packages.join("\n- ")}'; |
| 66 } | 66 } |
| 67 } | 67 } |
| 68 | 68 |
| 69 /// Maintains a cache of previously-requested data: pubspecs and version lists. | 69 /// Maintains a cache of previously-requested data: pubspecs and version lists. |
| 70 /// Used to avoid requesting the same pubspec from the server repeatedly. | 70 /// Used to avoid requesting the same pubspec from the server repeatedly. |
| 71 class PubspecCache { | 71 class PubspecCache { |
| 72 final SourceRegistry _sources; | 72 final SourceRegistry _sources; |
| 73 final _versions = new Map<PackageId, List<PackageId>>(); | 73 final _versions = new Map<PackageRef, List<PackageId>>(); |
| 74 final _pubspecs = new Map<PackageId, Pubspec>(); | 74 final _pubspecs = new Map<PackageId, Pubspec>(); |
| 75 | 75 |
| 76 /// The number of times a version list was requested and it wasn't cached and | 76 /// The number of times a version list was requested and it wasn't cached and |
| 77 /// had to be requested from the source. | 77 /// had to be requested from the source. |
| 78 int versionCacheMisses = 0; | 78 int versionCacheMisses = 0; |
| 79 | 79 |
| 80 /// The number of times a version list was requested and the cached version | 80 /// The number of times a version list was requested and the cached version |
| 81 /// was returned. | 81 /// was returned. |
| 82 int versionCacheHits = 0; | 82 int versionCacheHits = 0; |
| 83 | 83 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 112 _pubspecs[id] = pubspec; | 112 _pubspecs[id] = pubspec; |
| 113 return pubspec; | 113 return pubspec; |
| 114 }); | 114 }); |
| 115 } | 115 } |
| 116 | 116 |
| 117 /// Returns the previously cached pubspec for the package identified by [id] | 117 /// Returns the previously cached pubspec for the package identified by [id] |
| 118 /// or returns `null` if not in the cache. | 118 /// or returns `null` if not in the cache. |
| 119 Pubspec getCachedPubspec(PackageId id) => _pubspecs[id]; | 119 Pubspec getCachedPubspec(PackageId id) => _pubspecs[id]; |
| 120 | 120 |
| 121 /// Gets the list of versions for [package] in descending order. | 121 /// Gets the list of versions for [package] in descending order. |
| 122 Future<List<PackageId>> getVersions(String package, Source source, | 122 Future<List<PackageId>> getVersions(PackageRef package) { |
| 123 description) { | |
| 124 // Create a fake ID to use as a key. | |
| 125 // TODO(rnystrom): Create a separate type for (name, source, description) | |
| 126 // without a version. | |
| 127 var id = new PackageId(package, source, Version.none, description); | |
| 128 | |
| 129 // See if we have it cached. | 123 // See if we have it cached. |
| 130 var versions = _versions[id]; | 124 var versions = _versions[package]; |
| 131 if (versions != null) { | 125 if (versions != null) { |
| 132 versionCacheHits++; | 126 versionCacheHits++; |
| 133 return new Future.value(versions); | 127 return new Future.value(versions); |
| 134 } | 128 } |
| 135 | 129 |
| 136 versionCacheMisses++; | 130 versionCacheMisses++; |
| 137 return source.getVersions(package, description).then((versions) { | 131 return package.getVersions().then((ids) { |
| 138 var ids = versions | |
| 139 .map((version) => new PackageId(package, source, version, | |
| 140 description)) | |
| 141 .toList(); | |
| 142 | |
| 143 // Sort by descending version so we try newer versions first. | 132 // Sort by descending version so we try newer versions first. |
| 144 ids.sort((a, b) => b.version.compareTo(a.version)); | 133 ids.sort((a, b) => b.version.compareTo(a.version)); |
| 145 | 134 |
| 146 log.solver('requested $package version list'); | 135 log.solver('requested $package version list'); |
| 147 _versions[id] = ids; | 136 _versions[package] = ids; |
| 148 return ids; | 137 return ids; |
| 149 }); | 138 }); |
| 150 } | 139 } |
| 151 } | 140 } |
| 152 | 141 |
| 153 /// A reference from a depending package to a package that it depends on. | 142 /// A reference from a depending package to a package that it depends on. |
| 154 class Dependency { | 143 class Dependency { |
| 155 /// The name of the package that has this dependency. | 144 /// The name of the package that has this dependency. |
| 156 final String depender; | 145 final String depender; |
| 157 | 146 |
| 158 /// The referenced dependent package. | 147 /// The package being depended on. |
| 159 final PackageRef ref; | 148 final PackageDep dep; |
| 160 | 149 |
| 161 Dependency(this.depender, this.ref); | 150 Dependency(this.depender, this.dep); |
| 162 | 151 |
| 163 String toString() => '$depender -> $ref'; | 152 String toString() => '$depender -> $dep'; |
| 164 } | 153 } |
| 165 | 154 |
| 166 /// Base class for all failures that can occur while trying to resolve versions. | 155 /// Base class for all failures that can occur while trying to resolve versions. |
| 167 class SolveFailure implements Exception { | 156 class SolveFailure implements Exception { |
| 168 /// The name of the package whose version could not be solved. Will be `null` | 157 /// The name of the package whose version could not be solved. Will be `null` |
| 169 /// if the failure is not specific to one package. | 158 /// if the failure is not specific to one package. |
| 170 final String package; | 159 final String package; |
| 171 | 160 |
| 172 /// The known dependencies on [package] at the time of the failure. Will be | 161 /// The known dependencies on [package] at the time of the failure. Will be |
| 173 /// an empty collection if the failure is not specific to one package. | 162 /// an empty collection if the failure is not specific to one package. |
| 174 final Iterable<Dependency> dependencies; | 163 final Iterable<Dependency> dependencies; |
| 175 | 164 |
| 176 SolveFailure(this.package, Iterable<Dependency> dependencies) | 165 SolveFailure(this.package, Iterable<Dependency> dependencies) |
| 177 : dependencies = dependencies != null ? dependencies : <Dependency>[]; | 166 : dependencies = dependencies != null ? dependencies : <Dependency>[]; |
| 178 | 167 |
| 179 /// Writes [dependencies] to [buffer] as a bullet list. If [describe] is | 168 /// Writes [dependencies] to [buffer] as a bullet list. If [describe] is |
| 180 /// passed, it will be called for each dependency and the result will be | 169 /// passed, it will be called for each dependency and the result will be |
| 181 /// written next to the dependency. | 170 /// written next to the dependency. |
| 182 void writeDependencies(StringBuffer buffer, | 171 void writeDependencies(StringBuffer buffer, |
| 183 [String describe(PackageRef ref)]) { | 172 [String describe(PackageDep dep)]) { |
| 184 var map = {}; | 173 var map = {}; |
| 185 for (var dep in dependencies) { | 174 for (var dep in dependencies) { |
| 186 map[dep.depender] = dep.ref; | 175 map[dep.depender] = dep.dep; |
| 187 } | 176 } |
| 188 | 177 |
| 189 var names = map.keys.toList(); | 178 var names = map.keys.toList(); |
| 190 names.sort(); | 179 names.sort(); |
| 191 | 180 |
| 192 for (var name in names) { | 181 for (var name in names) { |
| 193 buffer.writeln("- '$name' "); | 182 buffer.writeln("- '$name' "); |
| 194 if (describe != null) { | 183 if (describe != null) { |
| 195 buffer.writeln(describe(map[name])); | 184 buffer.writeln(describe(map[name])); |
| 196 } else { | 185 } else { |
| 197 buffer.writeln("depends on version ${map[name].constraint}"); | 186 buffer.writeln("depends on version ${map[name].constraint}"); |
| 198 } | 187 } |
| 199 } | 188 } |
| 200 } | 189 } |
| 201 | 190 |
| 202 String toString() { | 191 String toString() { |
| 203 if (dependencies.isEmpty) return _message; | 192 if (dependencies.isEmpty) return _message; |
| 204 | 193 |
| 205 var buffer = new StringBuffer(); | 194 var buffer = new StringBuffer(); |
| 206 buffer.writeln("$_message:"); | 195 buffer.writeln("$_message:"); |
| 207 | 196 |
| 208 var map = {}; | 197 var map = {}; |
| 209 for (var dep in dependencies) { | 198 for (var dep in dependencies) { |
| 210 map[dep.depender] = dep.ref; | 199 map[dep.depender] = dep.dep; |
| 211 } | 200 } |
| 212 | 201 |
| 213 var names = map.keys.toList(); | 202 var names = map.keys.toList(); |
| 214 names.sort(); | 203 names.sort(); |
| 215 | 204 |
| 216 for (var name in names) { | 205 for (var name in names) { |
| 217 buffer.writeln("- '$name' ${_describeDependency(map[name])}"); | 206 buffer.writeln("- '$name' ${_describeDependency(map[name])}"); |
| 218 } | 207 } |
| 219 | 208 |
| 220 return buffer.toString(); | 209 return buffer.toString(); |
| 221 } | 210 } |
| 222 | 211 |
| 223 /// A message describing the specific kind of solve failure. | 212 /// A message describing the specific kind of solve failure. |
| 224 String get _message; | 213 String get _message; |
| 225 | 214 |
| 226 /// Describes a dependencie's reference in the output message. Override this | 215 /// Describes a dependencie's reference in the output message. Override this |
| 227 /// to highlight which aspect of [ref] led to the failure. | 216 /// to highlight which aspect of [dep] led to the failure. |
| 228 String _describeDependency(PackageRef ref) => | 217 String _describeDependency(PackageDep dep) => |
| 229 "depends on version ${ref.constraint}"; | 218 "depends on version ${dep.constraint}"; |
| 230 } | 219 } |
| 231 | 220 |
| 232 /// Exception thrown when the [VersionSolver] fails to find a solution after a | 221 /// Exception thrown when the [VersionSolver] fails to find a solution after a |
| 233 /// certain number of iterations. | 222 /// certain number of iterations. |
| 234 class CouldNotSolveException extends SolveFailure { | 223 class CouldNotSolveException extends SolveFailure { |
| 235 CouldNotSolveException([String message]) | 224 CouldNotSolveException([String message]) |
| 236 : super(null, null), | 225 : super(null, null), |
| 237 _message = (message != null) ? message : | 226 _message = (message != null) ? message : |
| 238 "Could not find a solution that met all constraints."; | 227 "Could not find a solution that met all constraints."; |
| 239 | 228 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 | 270 |
| 282 /// Exception thrown when two packages with the same name but different sources | 271 /// Exception thrown when two packages with the same name but different sources |
| 283 /// are depended upon. | 272 /// are depended upon. |
| 284 class SourceMismatchException extends SolveFailure { | 273 class SourceMismatchException extends SolveFailure { |
| 285 | 274 |
| 286 SourceMismatchException(String package, Iterable<Dependency> dependencies) | 275 SourceMismatchException(String package, Iterable<Dependency> dependencies) |
| 287 : super(package, dependencies); | 276 : super(package, dependencies); |
| 288 | 277 |
| 289 String get _message => "Incompatible dependencies on '$package'"; | 278 String get _message => "Incompatible dependencies on '$package'"; |
| 290 | 279 |
| 291 String _describeDependency(PackageRef ref) => | 280 String _describeDependency(PackageDep dep) => |
| 292 "depends on it from source ${ref.source}"; | 281 "depends on it from source ${dep.source}"; |
| 293 } | 282 } |
| 294 | 283 |
| 295 /// Exception thrown when two packages with the same name and source but | 284 /// Exception thrown when two packages with the same name and source but |
| 296 /// different descriptions are depended upon. | 285 /// different descriptions are depended upon. |
| 297 class DescriptionMismatchException extends SolveFailure { | 286 class DescriptionMismatchException extends SolveFailure { |
| 298 DescriptionMismatchException(String package, | 287 DescriptionMismatchException(String package, |
| 299 Iterable<Dependency> dependencies) | 288 Iterable<Dependency> dependencies) |
| 300 : super(package, dependencies); | 289 : super(package, dependencies); |
| 301 | 290 |
| 302 String get _message => "Incompatible dependencies on '$package'"; | 291 String get _message => "Incompatible dependencies on '$package'"; |
| 303 | 292 |
| 304 String _describeDependency(PackageRef ref) { | 293 String _describeDependency(PackageDep dep) { |
| 305 // TODO(nweiz): Dump descriptions to YAML when that's supported. | 294 // TODO(nweiz): Dump descriptions to YAML when that's supported. |
| 306 return "depends on it with description ${json.stringify(ref.description)}"; | 295 return "depends on it with description ${json.stringify(dep.description)}"; |
| 307 } | 296 } |
| 308 } | 297 } |
| OLD | NEW |