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

Side by Side Diff: utils/pub/pubspec.dart

Issue 14297021: Move pub into sdk/lib/_internal. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Disallow package: imports of pub. Created 7 years, 8 months 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 | Annotate | Revision Log
« no previous file with comments | « utils/pub/pub.dart ('k') | utils/pub/resource/certs/cert9.db » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library pubspec;
6
7 import 'package:yaml/yaml.dart';
8 import 'package:pathos/path.dart' as path;
9
10 import 'io.dart';
11 import 'package.dart';
12 import 'source.dart';
13 import 'source_registry.dart';
14 import 'utils.dart';
15 import 'version.dart';
16
17 /// The parsed and validated contents of a pubspec file.
18 class Pubspec {
19 /// This package's name.
20 final String name;
21
22 /// This package's version.
23 final Version version;
24
25 /// The packages this package depends on.
26 final List<PackageRef> dependencies;
27
28 /// The packages this package depends on when it is the root package.
29 final List<PackageRef> devDependencies;
30
31 /// The environment-related metadata.
32 final PubspecEnvironment environment;
33
34 /// All pubspec fields. This includes the fields from which other properties
35 /// are derived.
36 final Map<String, Object> fields;
37
38 /// Loads the pubspec for a package [name] located in [packageDir].
39 factory Pubspec.load(String name, String packageDir, SourceRegistry sources) {
40 var pubspecPath = path.join(packageDir, 'pubspec.yaml');
41 if (!fileExists(pubspecPath)) throw new PubspecNotFoundException(name);
42
43 try {
44 var pubspec = new Pubspec.parse(pubspecPath, readTextFile(pubspecPath),
45 sources);
46
47 if (pubspec.name == null) {
48 throw new PubspecHasNoNameException(name);
49 }
50
51 if (name != null && pubspec.name != name) {
52 throw new PubspecNameMismatchException(name, pubspec.name);
53 }
54
55 return pubspec;
56 } on FormatException catch (ex) {
57 fail('Could not parse $pubspecPath:\n${ex.message}');
58 }
59 }
60
61 Pubspec(this.name, this.version, this.dependencies, this.devDependencies,
62 this.environment, [Map<String, Object> fields])
63 : this.fields = fields == null ? {} : fields;
64
65 Pubspec.empty()
66 : name = null,
67 version = Version.none,
68 dependencies = <PackageRef>[],
69 devDependencies = <PackageRef>[],
70 environment = new PubspecEnvironment(),
71 fields = {};
72
73 /// Whether or not the pubspec has no contents.
74 bool get isEmpty =>
75 name == null && version == Version.none && dependencies.isEmpty;
76
77 // TODO(rnystrom): Instead of allowing a null argument here, split this up
78 // into load(), parse(), and _parse() like LockFile does.
79 /// Parses the pubspec stored at [filePath] whose text is [contents]. If the
80 /// pubspec doesn't define version for itself, it defaults to [Version.none].
81 /// [filePath] may be `null` if the pubspec is not on the user's local
82 /// file system.
83 factory Pubspec.parse(String filePath, String contents,
84 SourceRegistry sources) {
85 var name = null;
86 var version = Version.none;
87
88 if (contents.trim() == '') return new Pubspec.empty();
89
90 var parsedPubspec = loadYaml(contents);
91 if (parsedPubspec == null) return new Pubspec.empty();
92
93 if (parsedPubspec is! Map) {
94 throw new FormatException('The pubspec must be a YAML mapping.');
95 }
96
97 if (parsedPubspec.containsKey('name')) {
98 name = parsedPubspec['name'];
99 if (name is! String) {
100 throw new FormatException(
101 'The pubspec "name" field should be a string, but was "$name".');
102 }
103 }
104
105 if (parsedPubspec.containsKey('version')) {
106 version = new Version.parse(parsedPubspec['version']);
107 }
108
109 var dependencies = _parseDependencies(filePath, sources,
110 parsedPubspec['dependencies']);
111
112 var devDependencies = _parseDependencies(filePath, sources,
113 parsedPubspec['dev_dependencies']);
114
115 // Make sure the same package doesn't appear as both a regular and dev
116 // dependency.
117 var dependencyNames = dependencies.map((dep) => dep.name).toSet();
118 var collisions = dependencyNames.intersection(
119 devDependencies.map((dep) => dep.name).toSet());
120
121 if (!collisions.isEmpty) {
122 var packageNames;
123 if (collisions.length == 1) {
124 packageNames = 'Package "${collisions.first}"';
125 } else {
126 var names = collisions.toList();
127 names.sort();
128 var buffer = new StringBuffer();
129 buffer.write("Packages ");
130 for (var i = 0; i < names.length; i++) {
131 buffer.write('"');
132 buffer.write(names[i]);
133 buffer.write('"');
134 if (i == names.length - 2) {
135 buffer.write(", ");
136 } else if (i == names.length - 1) {
137 buffer.write(", and ");
138 }
139 }
140
141 packageNames = buffer.toString();
142 }
143 throw new FormatException(
144 '$packageNames cannot appear in both "dependencies" and '
145 '"dev_dependencies".');
146 }
147
148 var environmentYaml = parsedPubspec['environment'];
149 var sdkConstraint = VersionConstraint.any;
150 if (environmentYaml != null) {
151 if (environmentYaml is! Map) {
152 throw new FormatException(
153 'The pubspec "environment" field should be a map, but was '
154 '"$environmentYaml".');
155 }
156
157 var sdkYaml = environmentYaml['sdk'];
158 if (sdkYaml is! String) {
159 throw new FormatException(
160 'The "sdk" field of "environment" should be a string, but was '
161 '"$sdkYaml".');
162 }
163
164 sdkConstraint = new VersionConstraint.parse(sdkYaml);
165 }
166 var environment = new PubspecEnvironment(sdkConstraint);
167
168 // Even though the pub app itself doesn't use these fields, we validate
169 // them here so that users find errors early before they try to upload to
170 // the server:
171 // TODO(rnystrom): We should split this validation into separate layers:
172 // 1. Stuff that is required in any pubspec to perform any command. Things
173 // like "must have a name". That should go here.
174 // 2. Stuff that is required to upload a package. Things like "homepage
175 // must use a valid scheme". That should go elsewhere. pub upload should
176 // call it, and we should provide a separate command to show the user,
177 // and also expose it to the editor in some way.
178
179 if (parsedPubspec.containsKey('homepage')) {
180 _validateFieldUrl(parsedPubspec['homepage'], 'homepage');
181 }
182 if (parsedPubspec.containsKey('documentation')) {
183 _validateFieldUrl(parsedPubspec['documentation'], 'documentation');
184 }
185
186 if (parsedPubspec.containsKey('author') &&
187 parsedPubspec['author'] is! String) {
188 throw new FormatException(
189 'The "author" field should be a string, but was '
190 '${parsedPubspec["author"]}.');
191 }
192
193 if (parsedPubspec.containsKey('authors')) {
194 var authors = parsedPubspec['authors'];
195 if (authors is List) {
196 // All of the elements must be strings.
197 if (!authors.every((author) => author is String)) {
198 throw new FormatException('The "authors" field should be a string '
199 'or a list of strings, but was "$authors".');
200 }
201 } else if (authors is! String) {
202 throw new FormatException('The pubspec "authors" field should be a '
203 'string or a list of strings, but was "$authors".');
204 }
205
206 if (parsedPubspec.containsKey('author')) {
207 throw new FormatException('A pubspec should not have both an "author" '
208 'and an "authors" field.');
209 }
210 }
211
212 return new Pubspec(name, version, dependencies, devDependencies,
213 environment, parsedPubspec);
214 }
215 }
216
217 /**
218 * Evaluates whether the given [url] for [field] is valid.
219 *
220 * Throws [FormatException] on an invalid url.
221 */
222 void _validateFieldUrl(url, String field) {
223 if (url is! String) {
224 throw new FormatException(
225 'The "$field" field should be a string, but was "$url".');
226 }
227
228 var goodScheme = new RegExp(r'^https?:');
229 if (!goodScheme.hasMatch(url)) {
230 throw new FormatException(
231 'The "$field" field should be an "http:" or "https:" URL, but '
232 'was "$url".');
233 }
234 }
235
236 List<PackageRef> _parseDependencies(String pubspecPath, SourceRegistry sources,
237 yaml) {
238 var dependencies = <PackageRef>[];
239
240 // Allow an empty dependencies key.
241 if (yaml == null) return dependencies;
242
243 if (yaml is! Map || yaml.keys.any((e) => e is! String)) {
244 throw new FormatException(
245 'The pubspec dependencies should be a map of package names, but '
246 'was ${yaml}.');
247 }
248
249 yaml.forEach((name, spec) {
250 var description, source;
251 var versionConstraint = new VersionRange();
252 if (spec == null) {
253 description = name;
254 source = sources.defaultSource;
255 } else if (spec is String) {
256 description = name;
257 source = sources.defaultSource;
258 versionConstraint = new VersionConstraint.parse(spec);
259 } else if (spec is Map) {
260 if (spec.containsKey('version')) {
261 versionConstraint = new VersionConstraint.parse(spec.remove('version'));
262 }
263
264 var sourceNames = spec.keys.toList();
265 if (sourceNames.length > 1) {
266 throw new FormatException(
267 'Dependency $name may only have one source: $sourceNames.');
268 }
269
270 var sourceName = only(sourceNames);
271 if (sourceName is! String) {
272 throw new FormatException(
273 'Source name $sourceName should be a string.');
274 }
275
276 source = sources[sourceName];
277 description = spec[sourceName];
278 } else {
279 throw new FormatException(
280 'Dependency specification $spec should be a string or a mapping.');
281 }
282
283 description = source.parseDescription(pubspecPath, description,
284 fromLockFile: false);
285
286 dependencies.add(new PackageRef(
287 name, source, versionConstraint, description));
288 });
289
290 return dependencies;
291 }
292
293 /// The environment-related metadata in the pubspec. Corresponds to the data
294 /// under the "environment:" key in the pubspec.
295 class PubspecEnvironment {
296 /// The version constraint specifying which SDK versions this package works
297 /// with.
298 final VersionConstraint sdkVersion;
299
300 PubspecEnvironment([VersionConstraint sdk])
301 : sdkVersion = sdk != null ? sdk : VersionConstraint.any;
302 }
OLDNEW
« no previous file with comments | « utils/pub/pub.dart ('k') | utils/pub/resource/certs/cert9.db » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698