OLD | NEW |
| (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 package; | |
6 | |
7 import 'dart:async'; | |
8 | |
9 import 'package:pathos/path.dart' as path; | |
10 | |
11 import 'io.dart'; | |
12 import 'pubspec.dart'; | |
13 import 'source.dart'; | |
14 import 'source_registry.dart'; | |
15 import 'version.dart'; | |
16 | |
17 final _README_REGEXP = new RegExp(r"^README($|\.)", caseSensitive: false); | |
18 | |
19 /// A named, versioned, unit of code and resource reuse. | |
20 class Package { | |
21 /// The path to the directory containing the package. | |
22 final String dir; | |
23 | |
24 /// The name of the package. | |
25 String get name { | |
26 if (pubspec.name != null) return pubspec.name; | |
27 if (dir != null) return path.basename(dir); | |
28 return null; | |
29 } | |
30 | |
31 /// The package's version. | |
32 Version get version => pubspec.version; | |
33 | |
34 /// The parsed pubspec associated with this package. | |
35 final Pubspec pubspec; | |
36 | |
37 /// The ids of the packages that this package depends on. This is what is | |
38 /// specified in the pubspec when this package depends on another. | |
39 List<PackageRef> get dependencies => pubspec.dependencies; | |
40 | |
41 /// Returns the path to the README file at the root of the entrypoint, or null | |
42 /// if no README file is found. If multiple READMEs are found, this uses the | |
43 /// same conventions as pub.dartlang.org for choosing the primary one: the | |
44 /// README with the fewest extensions that is lexically ordered first is | |
45 /// chosen. | |
46 String get readmePath { | |
47 var readmes = listDir(dir).map(path.basename). | |
48 where((entry) => entry.contains(_README_REGEXP)); | |
49 if (readmes.isEmpty) return null; | |
50 | |
51 return path.join(dir, readmes.reduce((readme1, readme2) { | |
52 var extensions1 = ".".allMatches(readme1).length; | |
53 var extensions2 = ".".allMatches(readme2).length; | |
54 var comparison = extensions1.compareTo(extensions2); | |
55 if (comparison == 0) comparison = readme1.compareTo(readme2); | |
56 return (comparison <= 0) ? readme1 : readme2; | |
57 })); | |
58 } | |
59 | |
60 /// Loads the package whose root directory is [packageDir]. [name] is the | |
61 /// expected name of that package (e.g. the name given in the dependency), or | |
62 /// `null` if the package being loaded is the entrypoint package. | |
63 Package.load(String name, String packageDir, SourceRegistry sources) | |
64 : dir = packageDir, | |
65 pubspec = new Pubspec.load(name, packageDir, sources); | |
66 | |
67 /// Constructs a package with the given pubspec. The package will have no | |
68 /// directory associated with it. | |
69 Package.inMemory(this.pubspec) | |
70 : dir = null; | |
71 | |
72 /// Constructs a package. This should not be called directly. Instead, acquire | |
73 /// packages from [load()]. | |
74 Package._(this.dir, this.pubspec); | |
75 | |
76 /// Returns a debug string for the package. | |
77 String toString() => '$name $version ($dir)'; | |
78 } | |
79 | |
80 /// An unambiguous resolved reference to a package. A package ID contains enough | |
81 /// information to correctly install the package. | |
82 /// | |
83 /// Note that it's possible for multiple distinct package IDs to point to | |
84 /// different directories that happen to contain identical packages. For | |
85 /// example, the same package may be available from multiple sources. As far as | |
86 /// Pub is concerned, those packages are different. | |
87 class PackageId implements Comparable<PackageId> { | |
88 /// The name of the package being identified. | |
89 final String name; | |
90 | |
91 /// The [Source] used to look up this package given its [description]. If | |
92 /// this is a root package ID, this will be `null`. | |
93 final Source source; | |
94 | |
95 /// The package's version. | |
96 final Version version; | |
97 | |
98 /// The metadata used by the package's [source] to identify and locate it. It | |
99 /// contains whatever [Source]-specific data it needs to be able to install | |
100 /// the package. For example, the description of a git sourced package might | |
101 /// by the URL "git://github.com/dart/uilib.git". | |
102 final description; | |
103 | |
104 PackageId(this.name, this.source, this.version, this.description); | |
105 | |
106 /// Creates an ID for the given root package. | |
107 PackageId.root(Package package) | |
108 : name = package.name, | |
109 source = null, | |
110 version = package.version, | |
111 description = package.name; | |
112 | |
113 /// Whether this ID identifies the root package. | |
114 bool get isRoot => source == null; | |
115 | |
116 int get hashCode => name.hashCode ^ source.hashCode ^ version.hashCode; | |
117 | |
118 /// Gets the directory where this package is or would be found in the | |
119 /// [SystemCache]. | |
120 Future<String> get systemCacheDirectory => source.systemCacheDirectory(this); | |
121 | |
122 bool operator ==(other) { | |
123 if (other is! PackageId) return false; | |
124 // TODO(rnystrom): We're assuming here the name/version/source tuple is | |
125 // enough to uniquely identify the package and that we don't need to delve | |
126 // into the description. | |
127 return other.name == name && | |
128 other.source == source && | |
129 other.version == version; | |
130 } | |
131 | |
132 String toString() { | |
133 if (isRoot) return "$name $version (root)"; | |
134 if (source.isDefault) return "$name $version"; | |
135 return "$name $version from $source"; | |
136 } | |
137 | |
138 int compareTo(PackageId other) { | |
139 var sourceComp = source.name.compareTo(other.source.name); | |
140 if (sourceComp != 0) return sourceComp; | |
141 | |
142 var nameComp = name.compareTo(other.name); | |
143 if (nameComp != 0) return nameComp; | |
144 | |
145 return version.compareTo(other.version); | |
146 } | |
147 | |
148 /// Returns the pubspec for this package. | |
149 Future<Pubspec> describe() => source.systemCache.describe(this); | |
150 | |
151 /// Returns a future that completes to the resovled [PackageId] for this id. | |
152 Future<PackageId> get resolved => source.resolveId(this); | |
153 | |
154 /// Returns a [PackageRef] that references this package and constrains its | |
155 /// version to exactly match [version]. | |
156 PackageRef toRef() { | |
157 return new PackageRef(name, source, version, description); | |
158 } | |
159 | |
160 /// Returns `true` if this id's description matches [other]'s. | |
161 bool descriptionEquals(PackageRef other) { | |
162 return source.descriptionsEqual(description, other.description); | |
163 } | |
164 } | |
165 | |
166 /// A reference to a package. Unlike a [PackageId], a PackageRef may not | |
167 /// unambiguously refer to a single package. It may describe a range of allowed | |
168 /// packages. | |
169 class PackageRef { | |
170 /// The name of the package being identified. | |
171 final String name; | |
172 | |
173 /// The [Source] used to look up the package. If this refers to a root | |
174 /// package, this will be `null`. | |
175 final Source source; | |
176 | |
177 /// The allowed package versions. | |
178 final VersionConstraint constraint; | |
179 | |
180 /// The metadata used to identify the package being referenced. The | |
181 /// interpretation of this will vary based on the [source]. | |
182 final description; | |
183 | |
184 PackageRef(this.name, this.source, this.constraint, this.description); | |
185 | |
186 // TODO(rnystrom): Remove this if the old version solver is removed. | |
187 /// Creates a reference to the given root package. | |
188 PackageRef.root(Package package) | |
189 : name = package.name, | |
190 source = null, | |
191 constraint = package.version, | |
192 description = package.name; | |
193 | |
194 /// Whether this refers to the root package. | |
195 bool get isRoot => source == null; | |
196 | |
197 String toString() { | |
198 if (isRoot) return "$name $constraint (root)"; | |
199 return "$name $constraint from $source ($description)"; | |
200 } | |
201 | |
202 /// Returns a [PackageId] generated from this [PackageRef] with the given | |
203 /// concrete version. | |
204 PackageId atVersion(Version version) => | |
205 new PackageId(name, source, version, description); | |
206 | |
207 /// Returns `true` if this reference's description matches [other]'s. | |
208 bool descriptionEquals(PackageRef other) { | |
209 return source.descriptionsEqual(description, other.description); | |
210 } | |
211 } | |
212 | |
213 class PubspecNotFoundException implements Exception { | |
214 final String name; | |
215 | |
216 PubspecNotFoundException(this.name); | |
217 | |
218 String toString() => 'Package "$name" doesn\'t have a pubspec.yaml file.'; | |
219 } | |
220 | |
221 class PubspecHasNoNameException implements Exception { | |
222 final String name; | |
223 | |
224 PubspecHasNoNameException(this.name); | |
225 | |
226 String toString() => 'Package "$name"\'s pubspec.yaml file is missing the ' | |
227 'required "name" field (e.g. "name: $name").'; | |
228 } | |
229 | |
230 class PubspecNameMismatchException implements Exception { | |
231 final String expectedName; | |
232 final String actualName; | |
233 | |
234 PubspecNameMismatchException(this.expectedName, this.actualName); | |
235 | |
236 String toString() => 'The name you specified for your dependency, ' | |
237 '"$expectedName", doesn\'t match the name "$actualName" in its pubspec.'; | |
238 } | |
OLD | NEW |