| 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 import 'dart:collection'; | 5 import 'dart:collection'; |
| 6 | 6 |
| 7 import 'package:path/path.dart' as p; | 7 import 'package:path/path.dart' as p; |
| 8 import 'package:package_config/packages_file.dart' as packages_file; | 8 import 'package:package_config/packages_file.dart' as packages_file; |
| 9 import 'package:pub_semver/pub_semver.dart'; | 9 import 'package:pub_semver/pub_semver.dart'; |
| 10 import 'package:source_span/source_span.dart'; | 10 import 'package:source_span/source_span.dart'; |
| 11 import 'package:yaml/yaml.dart'; | 11 import 'package:yaml/yaml.dart'; |
| 12 | 12 |
| 13 import 'io.dart'; | 13 import 'io.dart'; |
| 14 import 'package.dart'; | 14 import 'package.dart'; |
| 15 import 'source_registry.dart'; | 15 import 'source_registry.dart'; |
| 16 import 'system_cache.dart'; | 16 import 'system_cache.dart'; |
| 17 import 'utils.dart'; | 17 import 'utils.dart'; |
| 18 | 18 |
| 19 /// A parsed and validated `pubspec.lock` file. | 19 /// A parsed and validated `pubspec.lock` file. |
| 20 class LockFile { | 20 class LockFile { |
| 21 /// The packages this lockfile pins. | 21 /// The packages this lockfile pins. |
| 22 final Map<String, PackageId> packages; | 22 final Map<String, PackageId> packages; |
| 23 | 23 |
| 24 /// The intersection of all SDK constraints for all locked packages. | 24 /// The intersection of all Dart SDK constraints for all locked packages. |
| 25 final VersionConstraint sdkConstraint; | 25 final VersionConstraint dartSdkConstraint; |
| 26 |
| 27 /// The intersection of all Flutter SDK constraints for all locked packages, |
| 28 /// or `null` if no packages require the Flutter SDK. |
| 29 final VersionConstraint flutterSdkConstraint; |
| 26 | 30 |
| 27 /// Creates a new lockfile containing [ids]. | 31 /// Creates a new lockfile containing [ids]. |
| 28 /// | 32 /// |
| 29 /// If passed, [sdkConstraint] represents the intersection of all SKD | 33 /// If passed, [dartSdkConstraint] represents the intersection of all Dart SDK |
| 30 /// constraints for all locked packages. It defaults to | 34 /// constraints for all locked packages. It defaults to |
| 31 /// [VersionConstraint.any]. | 35 /// [VersionConstraint.any]. Similarly, [flutterSdkConstraint] represents the |
| 32 LockFile(Iterable<PackageId> ids, {VersionConstraint sdkConstraint}) | 36 /// intersection of all Flutter SDK constraints; however, it defaults to |
| 37 /// `null`. |
| 38 LockFile(Iterable<PackageId> ids, {VersionConstraint dartSdkConstraint, |
| 39 VersionConstraint flutterSdkConstraint}) |
| 33 : this._( | 40 : this._( |
| 34 new Map.fromIterable( | 41 new Map.fromIterable( |
| 35 ids.where((id) => !id.isRoot), | 42 ids.where((id) => !id.isRoot), |
| 36 key: (id) => id.name), | 43 key: (id) => id.name), |
| 37 sdkConstraint ?? VersionConstraint.any); | 44 dartSdkConstraint ?? VersionConstraint.any, |
| 45 flutterSdkConstraint); |
| 38 | 46 |
| 39 LockFile._(Map<String, PackageId> packages, this.sdkConstraint) | 47 LockFile._(Map<String, PackageId> packages, this.dartSdkConstraint, |
| 48 this.flutterSdkConstraint) |
| 40 : packages = new UnmodifiableMapView(packages); | 49 : packages = new UnmodifiableMapView(packages); |
| 41 | 50 |
| 42 LockFile.empty() | 51 LockFile.empty() |
| 43 : packages = const {}, | 52 : packages = const {}, |
| 44 sdkConstraint = VersionConstraint.any; | 53 dartSdkConstraint = VersionConstraint.any, |
| 54 flutterSdkConstraint = null; |
| 45 | 55 |
| 46 /// Loads a lockfile from [filePath]. | 56 /// Loads a lockfile from [filePath]. |
| 47 factory LockFile.load(String filePath, SourceRegistry sources) { | 57 factory LockFile.load(String filePath, SourceRegistry sources) { |
| 48 return LockFile._parse(filePath, readTextFile(filePath), sources); | 58 return LockFile._parse(filePath, readTextFile(filePath), sources); |
| 49 } | 59 } |
| 50 | 60 |
| 51 /// Parses a lockfile whose text is [contents]. | 61 /// Parses a lockfile whose text is [contents]. |
| 52 factory LockFile.parse(String contents, SourceRegistry sources) { | 62 factory LockFile.parse(String contents, SourceRegistry sources) { |
| 53 return LockFile._parse(null, contents, sources); | 63 return LockFile._parse(null, contents, sources); |
| 54 } | 64 } |
| 55 | 65 |
| 56 /// Parses the lockfile whose text is [contents]. | 66 /// Parses the lockfile whose text is [contents]. |
| 57 /// | 67 /// |
| 58 /// [filePath] is the system-native path to the lockfile on disc. It may be | 68 /// [filePath] is the system-native path to the lockfile on disc. It may be |
| 59 /// `null`. | 69 /// `null`. |
| 60 static LockFile _parse(String filePath, String contents, | 70 static LockFile _parse(String filePath, String contents, |
| 61 SourceRegistry sources) { | 71 SourceRegistry sources) { |
| 62 if (contents.trim() == '') return new LockFile.empty(); | 72 if (contents.trim() == '') return new LockFile.empty(); |
| 63 | 73 |
| 64 var sourceUrl; | 74 var sourceUrl; |
| 65 if (filePath != null) sourceUrl = p.toUri(filePath); | 75 if (filePath != null) sourceUrl = p.toUri(filePath); |
| 66 var parsed = loadYamlNode(contents, sourceUrl: sourceUrl); | 76 var parsed = loadYamlNode(contents, sourceUrl: sourceUrl); |
| 67 | 77 |
| 68 _validate(parsed is Map, 'The lockfile must be a YAML mapping.', parsed); | 78 _validate(parsed is Map, 'The lockfile must be a YAML mapping.', parsed); |
| 69 | 79 |
| 70 var sdkConstraint = VersionConstraint.any; | 80 var dartSdkConstraint = VersionConstraint.any; |
| 71 var sdkConstraintText = parsed['sdk']; | 81 VersionConstraint flutterSdkConstraint; |
| 72 if (sdkConstraintText != null) { | 82 var sdkNode = parsed.nodes['sdk']; |
| 83 if (sdkNode != null) { |
| 84 // Lockfiles produced by pub versions from 1.14.0 through 1.18.0 included |
| 85 // a top-level "sdk" field which encoded the unified constraint on the |
| 86 // Dart SDK. They had no way of specifying constraints on other SDKs. |
| 87 dartSdkConstraint = _parseVersionConstraint(sdkNode); |
| 88 } else if ((parsed as Map).containsKey('sdks')) { |
| 89 var sdksField = parsed['sdks']; |
| 73 _validate( | 90 _validate( |
| 74 sdkConstraintText is String, | 91 sdksField is Map, |
| 75 'The "sdk" field must be a string.', | 92 'The "sdks" field must be a mapping.', |
| 76 parsed.nodes['sdk']); | 93 parsed.nodes['sdks']); |
| 77 | 94 |
| 78 sdkConstraint = _wrapFormatException( | 95 dartSdkConstraint = _parseVersionConstraint(sdksField.nodes['dart']); |
| 79 'version constraint', | 96 flutterSdkConstraint = |
| 80 parsed.nodes['sdk'].span, | 97 _parseVersionConstraint(sdksField.nodes['flutter']); |
| 81 () => new VersionConstraint.parse(sdkConstraintText)); | |
| 82 } | 98 } |
| 83 | 99 |
| 84 var packages = {}; | 100 var packages = {}; |
| 85 var packageEntries = parsed['packages']; | 101 var packageEntries = parsed['packages']; |
| 86 if (packageEntries != null) { | 102 if (packageEntries != null) { |
| 87 _validate(packageEntries is Map, 'The "packages" field must be a map.', | 103 _validate(packageEntries is Map, 'The "packages" field must be a map.', |
| 88 parsed.nodes['packages']); | 104 parsed.nodes['packages']); |
| 89 | 105 |
| 90 packageEntries.forEach((name, spec) { | 106 packageEntries.forEach((name, spec) { |
| 91 // Parse the version. | 107 // Parse the version. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 113 } | 129 } |
| 114 | 130 |
| 115 // Validate the name. | 131 // Validate the name. |
| 116 _validate(name == id.name, | 132 _validate(name == id.name, |
| 117 "Package name $name doesn't match ${id.name}.", spec); | 133 "Package name $name doesn't match ${id.name}.", spec); |
| 118 | 134 |
| 119 packages[name] = id; | 135 packages[name] = id; |
| 120 }); | 136 }); |
| 121 } | 137 } |
| 122 | 138 |
| 123 return new LockFile._(packages, sdkConstraint); | 139 return new LockFile._(packages, dartSdkConstraint, flutterSdkConstraint); |
| 140 } |
| 141 |
| 142 /// Asserts that [node] is a version constraint, and parses it. |
| 143 static VersionConstraint _parseVersionConstraint(YamlNode node) { |
| 144 if (node == null) return null; |
| 145 |
| 146 _validate( |
| 147 node.value is String, |
| 148 'Invalid version constraint: must be a string.', |
| 149 node); |
| 150 |
| 151 return _wrapFormatException( |
| 152 'version constraint', |
| 153 node.span, |
| 154 () => new VersionConstraint.parse(node.value)); |
| 124 } | 155 } |
| 125 | 156 |
| 126 /// Runs [fn] and wraps any [FormatException] it throws in a | 157 /// Runs [fn] and wraps any [FormatException] it throws in a |
| 127 /// [SourceSpanFormatException]. | 158 /// [SourceSpanFormatException]. |
| 128 /// | 159 /// |
| 129 /// [description] should be a noun phrase that describes whatever's being | 160 /// [description] should be a noun phrase that describes whatever's being |
| 130 /// parsed or processed by [fn]. [span] should be the location of whatever's | 161 /// parsed or processed by [fn]. [span] should be the location of whatever's |
| 131 /// being processed within the pubspec. | 162 /// being processed within the pubspec. |
| 132 static _wrapFormatException(String description, SourceSpan span, fn()) { | 163 static _wrapFormatException(String description, SourceSpan span, fn()) { |
| 133 try { | 164 try { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 146 | 177 |
| 147 /// Returns a copy of this LockFile with [id] added. | 178 /// Returns a copy of this LockFile with [id] added. |
| 148 /// | 179 /// |
| 149 /// If there's already an ID with the same name as [id] in the LockFile, it's | 180 /// If there's already an ID with the same name as [id] in the LockFile, it's |
| 150 /// overwritten. | 181 /// overwritten. |
| 151 LockFile setPackage(PackageId id) { | 182 LockFile setPackage(PackageId id) { |
| 152 if (id.isRoot) return this; | 183 if (id.isRoot) return this; |
| 153 | 184 |
| 154 var packages = new Map.from(this.packages); | 185 var packages = new Map.from(this.packages); |
| 155 packages[id.name] = id; | 186 packages[id.name] = id; |
| 156 return new LockFile._(packages, sdkConstraint); | 187 return new LockFile._(packages, dartSdkConstraint, flutterSdkConstraint); |
| 157 } | 188 } |
| 158 | 189 |
| 159 /// Returns a copy of this LockFile with a package named [name] removed. | 190 /// Returns a copy of this LockFile with a package named [name] removed. |
| 160 /// | 191 /// |
| 161 /// Returns an identical [LockFile] if there's no package named [name]. | 192 /// Returns an identical [LockFile] if there's no package named [name]. |
| 162 LockFile removePackage(String name) { | 193 LockFile removePackage(String name) { |
| 163 if (!this.packages.containsKey(name)) return this; | 194 if (!this.packages.containsKey(name)) return this; |
| 164 | 195 |
| 165 var packages = new Map.from(this.packages); | 196 var packages = new Map.from(this.packages); |
| 166 packages.remove(name); | 197 packages.remove(name); |
| 167 return new LockFile._(packages, sdkConstraint); | 198 return new LockFile._(packages, dartSdkConstraint, flutterSdkConstraint); |
| 168 } | 199 } |
| 169 | 200 |
| 170 /// Returns the contents of the `.packages` file generated from this lockfile. | 201 /// Returns the contents of the `.packages` file generated from this lockfile. |
| 171 /// | 202 /// |
| 172 /// If [entrypoint] is passed, a relative entry is added for its "lib/" | 203 /// If [entrypoint] is passed, a relative entry is added for its "lib/" |
| 173 /// directory. | 204 /// directory. |
| 174 String packagesFile(SystemCache cache, [String entrypoint]) { | 205 String packagesFile(SystemCache cache, [String entrypoint]) { |
| 175 var header = "Generated by pub on ${new DateTime.now()}."; | 206 var header = "Generated by pub on ${new DateTime.now()}."; |
| 176 | 207 |
| 177 var map = new Map.fromIterable(ordered(packages.keys), value: (name) { | 208 var map = new Map.fromIterable(ordered(packages.keys), value: (name) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 198 var description = package.source | 229 var description = package.source |
| 199 .serializeDescription(packageDir, package.description); | 230 .serializeDescription(packageDir, package.description); |
| 200 | 231 |
| 201 packageMap[name] = { | 232 packageMap[name] = { |
| 202 'version': package.version.toString(), | 233 'version': package.version.toString(), |
| 203 'source': package.source.name, | 234 'source': package.source.name, |
| 204 'description': description | 235 'description': description |
| 205 }; | 236 }; |
| 206 }); | 237 }); |
| 207 | 238 |
| 208 var data = {'sdk': sdkConstraint.toString(), 'packages': packageMap}; | 239 var sdks = { |
| 240 'dart': dartSdkConstraint.toString() |
| 241 }; |
| 242 if (flutterSdkConstraint != null) { |
| 243 sdks['flutter'] = flutterSdkConstraint.toString(); |
| 244 } |
| 245 |
| 246 var data = {'sdks': sdks, 'packages': packageMap}; |
| 209 return """ | 247 return """ |
| 210 # Generated by pub | 248 # Generated by pub |
| 211 # See http://pub.dartlang.org/doc/glossary.html#lockfile | 249 # See http://pub.dartlang.org/doc/glossary.html#lockfile |
| 212 ${yamlToString(data)} | 250 ${yamlToString(data)} |
| 213 """; | 251 """; |
| 214 } | 252 } |
| 215 } | 253 } |
| OLD | NEW |