Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: lib/src/lock_file.dart

Issue 1534093002: Improve the detection lockfile freshness. (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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.lock_file; 5 library pub.lock_file;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 8
9 import 'package:path/path.dart' as p; 9 import 'package:path/path.dart' as p;
10 import 'package:package_config/packages_file.dart' as packages_file; 10 import 'package:package_config/packages_file.dart' as packages_file;
11 import 'package:pub_semver/pub_semver.dart'; 11 import 'package:pub_semver/pub_semver.dart';
12 import 'package:source_span/source_span.dart'; 12 import 'package:source_span/source_span.dart';
13 import 'package:yaml/yaml.dart'; 13 import 'package:yaml/yaml.dart';
14 14
15 import 'io.dart'; 15 import 'io.dart';
16 import 'package.dart'; 16 import 'package.dart';
17 import 'source_registry.dart'; 17 import 'source_registry.dart';
18 import 'utils.dart'; 18 import 'utils.dart';
19 19
20 /// A parsed and validated `pubspec.lock` file. 20 /// A parsed and validated `pubspec.lock` file.
21 class LockFile { 21 class LockFile {
22 /// The source registry with which the lock file's IDs are interpreted. 22 /// The source registry with which the lock file's IDs are interpreted.
23 final SourceRegistry _sources; 23 final SourceRegistry _sources;
24 24
25 /// The packages this lockfile pins. 25 /// The packages this lockfile pins.
26 final Map<String, PackageId> packages; 26 final Map<String, PackageId> packages;
27 27
28 /// The intersection of all SDK constraints for all locked packages.
29 final VersionConstraint sdkConstraint;
30
28 /// Creates a new lockfile containing [ids]. 31 /// Creates a new lockfile containing [ids].
29 factory LockFile(Iterable<PackageId> ids, SourceRegistry sources) { 32 ///
30 var packages = new Map.fromIterable( 33 /// If passed, [sdkConstraint] represents the intersection of all SKD
31 ids.where((id) => !id.isRoot), 34 /// constraints for all locked packages. It defaults to
32 key: (id) => id.name); 35 /// [VersionConstraint.any].
33 return new LockFile._(packages, sources); 36 LockFile(Iterable<PackageId> ids, SourceRegistry sources,
34 } 37 {VersionConstraint sdkConstraint})
38 : this._(
39 new Map.fromIterable(
40 ids.where((id) => !id.isRoot),
41 key: (id) => id.name),
42 sdkConstraint ?? VersionConstraint.any,
43 sources);
35 44
36 LockFile._(Map<String, PackageId> packages, this._sources) 45 LockFile._(Map<String, PackageId> packages, this.sdkConstraint, this._sources)
37 : packages = new UnmodifiableMapView(packages); 46 : packages = new UnmodifiableMapView(packages);
38 47
39 LockFile.empty(this._sources) 48 LockFile.empty(this._sources)
40 : packages = const {}; 49 : packages = const {},
50 sdkConstraint = VersionConstraint.any;
41 51
42 /// Loads a lockfile from [filePath]. 52 /// Loads a lockfile from [filePath].
43 factory LockFile.load(String filePath, SourceRegistry sources) { 53 factory LockFile.load(String filePath, SourceRegistry sources) {
44 return LockFile._parse(filePath, readTextFile(filePath), sources); 54 return LockFile._parse(filePath, readTextFile(filePath), sources);
45 } 55 }
46 56
47 /// Parses a lockfile whose text is [contents]. 57 /// Parses a lockfile whose text is [contents].
48 factory LockFile.parse(String contents, SourceRegistry sources) { 58 factory LockFile.parse(String contents, SourceRegistry sources) {
49 return LockFile._parse(null, contents, sources); 59 return LockFile._parse(null, contents, sources);
50 } 60 }
51 61
52 /// Parses the lockfile whose text is [contents]. 62 /// Parses the lockfile whose text is [contents].
53 /// 63 ///
54 /// [filePath] is the system-native path to the lockfile on disc. It may be 64 /// [filePath] is the system-native path to the lockfile on disc. It may be
55 /// `null`. 65 /// `null`.
56 static LockFile _parse(String filePath, String contents, 66 static LockFile _parse(String filePath, String contents,
57 SourceRegistry sources) { 67 SourceRegistry sources) {
58 var packages = {};
59
60 if (contents.trim() == '') return new LockFile.empty(sources); 68 if (contents.trim() == '') return new LockFile.empty(sources);
61 69
62 var sourceUrl; 70 var sourceUrl;
63 if (filePath != null) sourceUrl = p.toUri(filePath); 71 if (filePath != null) sourceUrl = p.toUri(filePath);
64 var parsed = loadYamlNode(contents, sourceUrl: sourceUrl); 72 var parsed = loadYamlNode(contents, sourceUrl: sourceUrl);
65 73
66 _validate(parsed is Map, 'The lockfile must be a YAML mapping.', parsed); 74 _validate(parsed is Map, 'The lockfile must be a YAML mapping.', parsed);
67 75
76 var sdkConstraint = VersionConstraint.any;
77 var sdkConstraintText = parsed['sdk'];
78 if (sdkConstraintText != null) {
79 _validate(sdkConstraintText is String, 'The "sdk" field must be a string.' ,
Bob Nystrom 2015/12/21 20:55:03 Long line.
nweiz 2016/01/04 23:27:29 Done.
80 parsed.nodes['sdk']);
81
82 sdkConstraint = _wrapFormatException(
83 'version constraint',
84 parsed.nodes['sdk'],
85 () => new VersionConstraint.parse(sdkConstraintText));
86 }
87
88 var packages = {};
68 var packageEntries = parsed['packages']; 89 var packageEntries = parsed['packages'];
69 if (packageEntries != null) { 90 if (packageEntries != null) {
70 _validate(packageEntries is Map, 'The "packages" field must be a map.', 91 _validate(packageEntries is Map, 'The "packages" field must be a map.',
71 parsed.nodes['packages']); 92 parsed.nodes['packages']);
72 93
73 packageEntries.forEach((name, spec) { 94 packageEntries.forEach((name, spec) {
74 // Parse the version. 95 // Parse the version.
75 _validate(spec.containsKey('version'), 96 _validate(spec.containsKey('version'),
76 'Package $name is missing a version.', spec); 97 'Package $name is missing a version.', spec);
77 var version = new Version.parse(spec['version']); 98 var version = new Version.parse(spec['version']);
(...skipping 18 matching lines...) Expand all
96 } 117 }
97 118
98 // Validate the name. 119 // Validate the name.
99 _validate(name == id.name, 120 _validate(name == id.name,
100 "Package name $name doesn't match ${id.name}.", spec); 121 "Package name $name doesn't match ${id.name}.", spec);
101 122
102 packages[name] = id; 123 packages[name] = id;
103 }); 124 });
104 } 125 }
105 126
106 return new LockFile._(packages, sources); 127 return new LockFile._(packages, sdkConstraint, sources);
128 }
129
130 /// Runs [fn] and wraps any [FormatException] it throws in a
131 /// [SourceSpanFormatException].
132 ///
133 /// [description] should be a noun phrase that describes whatever's being
134 /// parsed or processed by [fn]. [span] should be the location of whatever's
135 /// being processed within the pubspec.
136 static _wrapFormatException(String description, SourceSpan span, fn()) {
137 try {
138 return fn();
139 } on FormatException catch (e) {
140 throw new SourceSpanFormatException(
141 'Invalid $description: ${e.message}', span);
142 }
107 } 143 }
Bob Nystrom 2015/12/21 20:55:03 Do we do this elsewhere? Maybe move it into utils?
nweiz 2016/01/04 23:27:29 We do something similar in pubspec.dart, but it th
108 144
109 /// If [condition] is `false` throws a format error with [message] for [node]. 145 /// If [condition] is `false` throws a format error with [message] for [node].
110 static void _validate(bool condition, String message, YamlNode node) { 146 static void _validate(bool condition, String message, YamlNode node) {
111 if (condition) return; 147 if (condition) return;
112 throw new SourceSpanFormatException(message, node.span); 148 throw new SourceSpanFormatException(message, node.span);
113 } 149 }
114 150
115 /// Returns a copy of this LockFile with [id] added. 151 /// Returns a copy of this LockFile with [id] added.
116 /// 152 ///
117 /// If there's already an ID with the same name as [id] in the LockFile, it's 153 /// If there's already an ID with the same name as [id] in the LockFile, it's
118 /// overwritten. 154 /// overwritten.
119 LockFile setPackage(PackageId id) { 155 LockFile setPackage(PackageId id) {
120 if (id.isRoot) return this; 156 if (id.isRoot) return this;
121 157
122 var packages = new Map.from(this.packages); 158 var packages = new Map.from(this.packages);
123 packages[id.name] = id; 159 packages[id.name] = id;
124 return new LockFile._(packages, _sources); 160 return new LockFile._(packages, sdkConstraint, _sources);
125 } 161 }
126 162
127 /// Returns a copy of this LockFile with a package named [name] removed. 163 /// Returns a copy of this LockFile with a package named [name] removed.
128 /// 164 ///
129 /// Returns an identical [LockFile] if there's no package named [name]. 165 /// Returns an identical [LockFile] if there's no package named [name].
130 LockFile removePackage(String name) { 166 LockFile removePackage(String name) {
131 if (!this.packages.containsKey(name)) return this; 167 if (!this.packages.containsKey(name)) return this;
132 168
133 var packages = new Map.from(this.packages); 169 var packages = new Map.from(this.packages);
134 packages.remove(name); 170 packages.remove(name);
135 return new LockFile._(packages, _sources); 171 return new LockFile._(packages, sdkConstraint, _sources);
136 } 172 }
137 173
138 /// Returns the contents of the `.packages` file generated from this lockfile. 174 /// Returns the contents of the `.packages` file generated from this lockfile.
139 /// 175 ///
140 /// If [entrypoint] is passed, a relative entry is added for its "lib/" 176 /// If [entrypoint] is passed, a relative entry is added for its "lib/"
141 /// directory. 177 /// directory.
142 String packagesFile([String entrypoint]) { 178 String packagesFile([String entrypoint]) {
143 var header = "Generated by pub on ${new DateTime.now()}."; 179 var header = "Generated by pub on ${new DateTime.now()}.";
144 180
145 var map = new Map.fromIterable(ordered(packages.keys), value: (name) { 181 var map = new Map.fromIterable(ordered(packages.keys), value: (name) {
146 var id = packages[name]; 182 var id = packages[name];
147 var source = _sources[id.source]; 183 var source = _sources[id.source];
148 return p.toUri(p.join(source.getDirectory(id), "lib")); 184 return p.toUri(p.join(source.getDirectory(id), "lib"));
149 }); 185 });
150 186
151 if (entrypoint != null) map[entrypoint] = Uri.parse("lib/"); 187 if (entrypoint != null) map[entrypoint] = Uri.parse("lib/");
152 188
153 var text = new StringBuffer(); 189 var text = new StringBuffer();
154 packages_file.write(text, map, comment: header); 190 packages_file.write(text, map, comment: header);
155 return text.toString(); 191 return text.toString();
156 } 192 }
157 193
158 /// Returns the serialized YAML text of the lock file. 194 /// Returns the serialized YAML text of the lock file.
159 /// 195 ///
160 /// [packageDir] is the containing directory of the root package, used to 196 /// [packageDir] is the containing directory of the root package, used to
161 /// properly serialize package descriptions. 197 /// properly serialize package descriptions.
162 String serialize(String packageDir) { 198 String serialize(String packageDir) {
163 // Convert the dependencies to a simple object. 199 // Convert the dependencies to a simple object.
164 var data = {}; 200 var packageMap = {};
165 packages.forEach((name, package) { 201 packages.forEach((name, package) {
166 var description = _sources[package.source] 202 var description = _sources[package.source]
167 .serializeDescription(packageDir, package.description); 203 .serializeDescription(packageDir, package.description);
168 204
169 data[name] = { 205 packageMap[name] = {
170 'version': package.version.toString(), 206 'version': package.version.toString(),
171 'source': package.source, 207 'source': package.source,
172 'description': description 208 'description': description
173 }; 209 };
174 }); 210 });
175 211
212 var data = {'sdk': sdkConstraint.toString(), 'packages': packageMap};
176 return """ 213 return """
177 # Generated by pub 214 # Generated by pub
178 # See http://pub.dartlang.org/doc/glossary.html#lockfile 215 # See http://pub.dartlang.org/doc/glossary.html#lockfile
179 ${yamlToString({'packages': data})} 216 ${yamlToString(data)}
180 """; 217 """;
181 } 218 }
182 } 219 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698