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 source; | 5 library source; |
6 | 6 |
7 import 'io.dart'; | 7 import 'io.dart'; |
8 import 'package.dart'; | 8 import 'package.dart'; |
9 import 'pubspec.dart'; | 9 import 'pubspec.dart'; |
10 import 'system_cache.dart'; | 10 import 'system_cache.dart'; |
11 import 'version.dart'; | 11 import 'version.dart'; |
12 | 12 |
13 /** | 13 /// A source from which to install packages. |
14 * A source from which to install packages. | 14 /// |
15 * | 15 /// Each source has many packages that it looks up using [PackageId]s. The |
16 * Each source has many packages that it looks up using [PackageId]s. The source | 16 /// source is responsible for installing these packages to the package cache. |
17 * is responsible for installing these packages to the package cache. | |
18 */ | |
19 abstract class Source { | 17 abstract class Source { |
20 /** | 18 /// The name of the source. Should be lower-case, suitable for use in a |
21 * The name of the source. Should be lower-case, suitable for use in a | 19 /// filename, and unique accross all sources. |
22 * filename, and unique accross all sources. | |
23 */ | |
24 String get name; | 20 String get name; |
25 | 21 |
26 /// Whether or not this source is the default source. | 22 /// Whether or not this source is the default source. |
27 bool get isDefault => systemCache.sources.defaultSource == this; | 23 bool get isDefault => systemCache.sources.defaultSource == this; |
28 | 24 |
29 /** | 25 /// Whether this source's packages should be cached in Pub's global cache |
30 * Whether this source's packages should be cached in Pub's global cache | 26 /// directory. |
31 * directory. | 27 /// |
32 * | 28 /// A source should be cached if it requires network access to retrieve |
33 * A source should be cached if it requires network access to retrieve | 29 /// packages. It doesn't need to be cached if all packages are available |
34 * packages. It doesn't need to be cached if all packages are available | 30 /// locally. |
35 * locally. | |
36 */ | |
37 bool get shouldCache; | 31 bool get shouldCache; |
38 | 32 |
39 /** | 33 /// The system cache with which this source is registered. |
40 * The system cache with which this source is registered. | |
41 */ | |
42 SystemCache get systemCache { | 34 SystemCache get systemCache { |
43 assert(_systemCache != null); | 35 assert(_systemCache != null); |
44 return _systemCache; | 36 return _systemCache; |
45 } | 37 } |
46 | 38 |
47 /** | 39 /// The system cache variable. Set by [_bind]. |
48 * The system cache variable. Set by [_bind]. | |
49 */ | |
50 SystemCache _systemCache; | 40 SystemCache _systemCache; |
51 | 41 |
52 /** | 42 /// The root directory of this source's cache within the system cache. |
53 * The root directory of this source's cache within the system cache. | 43 /// |
54 * | 44 /// This shouldn't be overridden by subclasses. |
55 * This shouldn't be overridden by subclasses. | |
56 */ | |
57 String get systemCacheRoot => join(systemCache.rootDir, name); | 45 String get systemCacheRoot => join(systemCache.rootDir, name); |
58 | 46 |
59 /** | 47 /// Records the system cache to which this source belongs. |
60 * Records the system cache to which this source belongs. | 48 /// |
61 * | 49 /// This should only be called once for each source, by |
62 * This should only be called once for each source, by [SystemCache.register]. | 50 /// [SystemCache.register]. It should not be overridden by base classes. |
63 * It should not be overridden by base classes. | |
64 */ | |
65 void bind(SystemCache systemCache) { | 51 void bind(SystemCache systemCache) { |
66 assert(_systemCache == null); | 52 assert(_systemCache == null); |
67 this._systemCache = systemCache; | 53 this._systemCache = systemCache; |
68 } | 54 } |
69 | 55 |
70 /** | 56 /// Get the list of all versions that exist for the package described by |
71 * Get the list of all versions that exist for the package described by | 57 /// [description]. [name] is the expected name of the package. |
72 * [description]. [name] is the expected name of the package. | 58 /// |
73 * | 59 /// Note that this does *not* require the packages to be installed, which is |
74 * Note that this does *not* require the packages to be installed, which is | 60 /// the point. This is used during version resolution to determine which |
75 * the point. This is used during version resolution to determine which | 61 /// package versions are available to be installed (or already installed). |
76 * package versions are available to be installed (or already installed). | 62 /// |
77 * | 63 /// By default, this assumes that each description has a single version and |
78 * By default, this assumes that each description has a single version and | 64 /// uses [describe] to get that version. |
79 * uses [describe] to get that version. | |
80 */ | |
81 Future<List<Version>> getVersions(String name, description) { | 65 Future<List<Version>> getVersions(String name, description) { |
82 return describe(new PackageId(name, this, Version.none, description)) | 66 return describe(new PackageId(name, this, Version.none, description)) |
83 .transform((pubspec) => [pubspec.version]); | 67 .transform((pubspec) => [pubspec.version]); |
84 } | 68 } |
85 | 69 |
86 /** | 70 /// Loads the (possibly remote) pubspec for the package version identified by |
87 * Loads the (possibly remote) pubspec for the package version identified by | 71 /// [id]. This may be called for packages that have not yet been installed |
88 * [id]. This may be called for packages that have not yet been installed | 72 /// during the version resolution process. |
89 * during the version resolution process. | 73 /// |
90 * | 74 /// For cached sources, by default this uses [installToSystemCache] to get the |
91 * For cached sources, by default this uses [installToSystemCache] to get the | 75 /// pubspec. There is no default implementation for non-cached sources; they |
92 * pubspec. There is no default implementation for non-cached sources; they | 76 /// must implement it manually. |
93 * must implement it manually. | |
94 */ | |
95 Future<Pubspec> describe(PackageId id) { | 77 Future<Pubspec> describe(PackageId id) { |
96 if (!shouldCache) throw "Source $name must implement describe(id)."; | 78 if (!shouldCache) throw "Source $name must implement describe(id)."; |
97 return installToSystemCache(id).transform((package) => package.pubspec); | 79 return installToSystemCache(id).transform((package) => package.pubspec); |
98 } | 80 } |
99 | 81 |
100 /** | 82 /// Installs the package identified by [id] to [path]. Returns a [Future] that |
101 * Installs the package identified by [id] to [path]. Returns a [Future] that | 83 /// completes when the installation was finished. The [Future] should resolve |
102 * completes when the installation was finished. The [Future] should resolve | 84 /// to true if the package was found in the source and false if it wasn't. For |
103 * to true if the package was found in the source and false if it wasn't. For | 85 /// all other error conditions, it should complete with an exception. |
104 * all other error conditions, it should complete with an exception. | 86 /// |
105 * | 87 /// [path] is guaranteed not to exist, and its parent directory is guaranteed |
106 * [path] is guaranteed not to exist, and its parent directory is guaranteed | 88 /// to exist. |
107 * to exist. | 89 /// |
108 * | 90 /// Note that [path] may be deleted. If re-installing a package that has |
109 * Note that [path] may be deleted. If re-installing a package that has | 91 /// already been installed would be costly or impossible, |
110 * already been installed would be costly or impossible, | 92 /// [installToSystemCache] should be implemented instead of [install]. |
111 * [installToSystemCache] should be implemented instead of [install]. | 93 /// |
112 * | 94 /// This doesn't need to be implemented if [installToSystemCache] is |
113 * This doesn't need to be implemented if [installToSystemCache] is | 95 /// implemented. |
114 * implemented. | |
115 */ | |
116 Future<bool> install(PackageId id, String path) { | 96 Future<bool> install(PackageId id, String path) { |
117 throw "Either install or installToSystemCache must be implemented for " | 97 throw "Either install or installToSystemCache must be implemented for " |
118 "source $name."; | 98 "source $name."; |
119 } | 99 } |
120 | 100 |
121 /** | 101 /// Installs the package identified by [id] to the system cache. This is only |
122 * Installs the package identified by [id] to the system cache. This is only | 102 /// called for sources with [shouldCache] set to true. |
123 * called for sources with [shouldCache] set to true. | 103 /// |
124 * | 104 /// By default, this uses [systemCacheDirectory] and [install]. |
125 * By default, this uses [systemCacheDirectory] and [install]. | |
126 */ | |
127 Future<Package> installToSystemCache(PackageId id) { | 105 Future<Package> installToSystemCache(PackageId id) { |
128 var path = systemCacheDirectory(id); | 106 var path = systemCacheDirectory(id); |
129 return exists(path).chain((exists) { | 107 return exists(path).chain((exists) { |
130 if (exists) return new Future<bool>.immediate(true); | 108 if (exists) return new Future<bool>.immediate(true); |
131 return ensureDir(dirname(path)).chain((_) => install(id, path)); | 109 return ensureDir(dirname(path)).chain((_) => install(id, path)); |
132 }).chain((found) { | 110 }).chain((found) { |
133 if (!found) throw 'Package $id not found.'; | 111 if (!found) throw 'Package $id not found.'; |
134 return Package.load(id.name, path, systemCache.sources); | 112 return Package.load(id.name, path, systemCache.sources); |
135 }); | 113 }); |
136 } | 114 } |
137 | 115 |
138 /** | 116 /// Returns the directory in the system cache that the package identified by |
139 * Returns the directory in the system cache that the package identified by | 117 /// [id] should be installed to. This should return a path to a subdirectory |
140 * [id] should be installed to. This should return a path to a subdirectory of | 118 /// of [systemCacheRoot]. |
141 * [systemCacheRoot]. | 119 /// |
142 * | 120 /// This doesn't need to be implemented if [shouldCache] is false, or if |
143 * This doesn't need to be implemented if [shouldCache] is false, or if | 121 /// [installToSystemCache] is implemented. |
144 * [installToSystemCache] is implemented. | |
145 */ | |
146 String systemCacheDirectory(PackageId id) => | 122 String systemCacheDirectory(PackageId id) => |
147 join(systemCacheRoot, packageName(id.description)); | 123 join(systemCacheRoot, packageName(id.description)); |
148 | 124 |
149 /** | 125 /// When a [Pubspec] or [LockFile] is parsed, it reads in the description for |
150 * When a [Pubspec] or [LockFile] is parsed, it reads in the description for | 126 /// each dependency. It is up to the dependency's [Source] to determine how |
151 * each dependency. It is up to the dependency's [Source] to determine how | 127 /// that should be interpreted. This will be called during parsing to validate |
152 * that should be interpreted. This will be called during parsing to validate | 128 /// that the given [description] is well-formed according to this source. It |
153 * that the given [description] is well-formed according to this source. It | 129 /// should return if the description is valid, or throw a [FormatException] if |
154 * should return if the description is valid, or throw a [FormatException] if | 130 /// not. |
155 * not. | 131 /// |
156 * | 132 /// [fromLockFile] is true when the description comes from a [LockFile], to |
157 * [fromLockFile] is true when the description comes from a [LockFile], to | 133 /// allow the source to use lockfile-specific descriptions via [resolveId]. |
158 * allow the source to use lockfile-specific descriptions via [resolveId]. | |
159 */ | |
160 void validateDescription(description, {bool fromLockFile: false}) {} | 134 void validateDescription(description, {bool fromLockFile: false}) {} |
161 | 135 |
162 /** | 136 /// Returns whether or not [description1] describes the same package as |
163 * Returns whether or not [description1] describes the same package as | 137 /// [description2] for this source. This method should be light-weight. It |
164 * [description2] for this source. This method should be light-weight. It | 138 /// doesn't need to validate that either package exists. |
165 * doesn't need to validate that either package exists. | 139 /// |
166 * | 140 /// By default, just uses regular equality. |
167 * By default, just uses regular equality. | |
168 */ | |
169 bool descriptionsEqual(description1, description2) => | 141 bool descriptionsEqual(description1, description2) => |
170 description1 == description2; | 142 description1 == description2; |
171 | 143 |
172 /** | 144 /// For some sources, [PackageId]s can point to different chunks of code at |
173 * For some sources, [PackageId]s can point to different chunks of code at | 145 /// different times. This takes such an [id] and returns a future that |
174 * different times. This takes such an [id] and returns a future that | 146 /// completes to a [PackageId] that will uniquely specify a single chunk of |
175 * completes to a [PackageId] that will uniquely specify a single chunk of | 147 /// code forever. |
176 * code forever. | 148 /// |
177 * | 149 /// For example, [GitSource] might take an [id] with description |
178 * For example, [GitSource] might take an [id] with description | 150 /// `http://github.com/dart-lang/some-lib.git` and return an id with a |
179 * `http://github.com/dart-lang/some-lib.git` and return an id with a | 151 /// description that includes the current commit of the Git repository. |
180 * description that includes the current commit of the Git repository. | 152 /// |
181 * | 153 /// This will be called after the package identified by [id] is installed, so |
182 * This will be called after the package identified by [id] is installed, so | 154 /// the source can use the installed package to determine information about |
183 * the source can use the installed package to determine information about the | 155 /// the resolved id. |
184 * resolved id. | 156 /// |
185 * | 157 /// The returned [PackageId] may have a description field that's invalid |
186 * The returned [PackageId] may have a description field that's invalid | 158 /// according to [validateDescription], although it must still be serializable |
187 * according to [validateDescription], although it must still be serializable | 159 /// to JSON and YAML. It must also be equal to [id] according to |
188 * to JSON and YAML. It must also be equal to [id] according to | 160 /// [descriptionsEqual]. |
189 * [descriptionsEqual]. | 161 /// |
190 * | 162 /// By default, this just returns [id]. |
191 * By default, this just returns [id]. | |
192 */ | |
193 Future<PackageId> resolveId(PackageId id) => new Future.immediate(id); | 163 Future<PackageId> resolveId(PackageId id) => new Future.immediate(id); |
194 | 164 |
195 /// Returns the source's name. | 165 /// Returns the source's name. |
196 String toString() => name; | 166 String toString() => name; |
197 } | 167 } |
OLD | NEW |