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

Side by Side Diff: sdk/lib/_internal/pub/lib/src/global_packages.dart

Issue 428313004: Support activating packages from local paths. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix deactivate. Created 6 years, 4 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
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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.global_packages; 5 library pub.global_packages;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:io'; 8 import 'dart:io';
9 9
10 import 'package:path/path.dart' as p; 10 import 'package:path/path.dart' as p;
11 11
12 import 'entrypoint.dart'; 12 import 'entrypoint.dart';
13 import 'io.dart'; 13 import 'io.dart';
14 import 'lock_file.dart'; 14 import 'lock_file.dart';
15 import 'log.dart' as log; 15 import 'log.dart' as log;
16 import 'package.dart'; 16 import 'package.dart';
17 import 'system_cache.dart'; 17 import 'system_cache.dart';
18 import 'solver/version_solver.dart'; 18 import 'solver/version_solver.dart';
19 import 'source.dart';
19 import 'source/cached.dart'; 20 import 'source/cached.dart';
21 import 'source/path.dart';
20 import 'utils.dart'; 22 import 'utils.dart';
21 import 'version.dart'; 23 import 'version.dart';
22 24
23 /// Maintains the set of packages that have been globally activated. 25 /// Maintains the set of packages that have been globally activated.
24 /// 26 ///
25 /// These have been hand-chosen by the user to make their executables in bin/ 27 /// These have been hand-chosen by the user to make their executables in bin/
26 /// available to the entire system. This lets them access them even when the 28 /// available to the entire system. This lets them access them even when the
27 /// current working directory is not inside another entrypoint package. 29 /// current working directory is not inside another entrypoint package.
28 /// 30 ///
29 /// Only one version of a given package name can be globally activated at a 31 /// Only one version of a given package name can be globally activated at a
30 /// time. Activating a different version of a package will deactivate the 32 /// time. Activating a different version of a package will deactivate the
31 /// previous one. 33 /// previous one.
34 ///
35 /// This handles packages from uncached and cached sources a little differently.
36 /// For a cached source, the package is physically in the user's pub cache and
37 /// we don't want to mess with it by putting a lockfile in there. Instead, when
38 /// we activate the package, we create a full lockfile and put it in the
39 /// "global_packages" directory. It's named "<package>.lock". Unlike a normal
40 /// lockfile, it also contains an entry for the root package itself, so that we
41 /// know the version and description that was activated.
42 ///
43 /// Uncached packages (i.e. "path" packages) are somewhere else on the user's
44 /// local file system and can have a lockfile directly in place. (And, in fact,
45 /// we want to ensure we honor the user's lockfile there.) To activate it, we
46 /// just need to know where that package directory is. For that, we create a
47 /// lockfile that *only* contains the root package's [PackageId] -- basically
48 /// just the path to the directory where the real lockfile lives.
32 class GlobalPackages { 49 class GlobalPackages {
33 /// The [SystemCache] containing the global packages. 50 /// The [SystemCache] containing the global packages.
34 final SystemCache cache; 51 final SystemCache cache;
35 52
36 /// The directory where the lockfiles for activated packages are stored. 53 /// The directory where the lockfiles for activated packages are stored.
37 String get _directory => p.join(cache.rootDir, "global_packages"); 54 String get _directory => p.join(cache.rootDir, "global_packages");
38 55
39 /// The source that global packages can be activated from.
40 // TODO(rnystrom): Allow activating packages from other sources.
41 CachedSource get _source => cache.sources["hosted"] as CachedSource;
42
43 /// Creates a new global package registry backed by the given directory on 56 /// Creates a new global package registry backed by the given directory on
44 /// the user's file system. 57 /// the user's file system.
45 /// 58 ///
46 /// The directory may not physically exist yet. If not, this will create it 59 /// The directory may not physically exist yet. If not, this will create it
47 /// when needed. 60 /// when needed.
48 GlobalPackages(this.cache); 61 GlobalPackages(this.cache);
49 62
50 /// Finds the latest version of the hosted package with [name] that matches 63 /// Finds the latest version of the hosted package with [name] that matches
51 /// [constraint] and makes it the active global version. 64 /// [constraint] and makes it the active global version.
52 Future activate(String name, VersionConstraint constraint) { 65 Future activateHosted(String name, VersionConstraint constraint) {
53 // See if we already have it activated. 66 // See if we already have it activated.
54 var lockFile; 67 var lockFile = _describeActive(name);
55 var currentVersion; 68 var currentVersion;
56 try { 69 if (lockFile != null) {
57 lockFile = new LockFile.load(_getLockFilePath(name), cache.sources); 70 var id = lockFile.packages[name];
58 currentVersion = lockFile.packages[name].version; 71
72 // Try to preserve the current version if we've already activated the
73 // hosted package.
74 if (id.source == "hosted") currentVersion = id.version;
59 75
60 // Pull the root package out of the lock file so the solver doesn't see 76 // Pull the root package out of the lock file so the solver doesn't see
61 // it. 77 // it.
62 lockFile.packages.remove(name); 78 lockFile.packages.remove(name);
63 79 } else {
64 log.message("Package ${log.bold(name)} is already active at "
65 "version ${log.bold(currentVersion)}.");
66 } on IOException catch (error) {
67 // If we couldn't read the lock file, it's not activated.
68 lockFile = new LockFile.empty(); 80 lockFile = new LockFile.empty();
69 } 81 }
70 82
71 var package;
72 var id;
73 return _selectVersion(name, currentVersion, constraint).then((version) { 83 return _selectVersion(name, currentVersion, constraint).then((version) {
74 // Make sure it's in the cache. 84 // Make sure it's in the cache.
75 id = new PackageId(name, _source.name, version, name); 85 var id = new PackageId(name, "hosted", version, name);
76 return _source.downloadToSystemCache(id); 86 return _installInCache(id, lockFile);
77 }).then((p) { 87 });
78 package = p; 88 }
79 // Resolve it and download its dependencies. 89
80 return resolveVersions(SolveType.GET, cache.sources, package, 90 /// Makes the local package at [path] globally active.
81 lockFile: lockFile); 91 Future activatePath(String path) {
92 return syncFuture(() {
93 var entrypoint = new Entrypoint(path, cache);
94
95 var name = entrypoint.root.name;
96
97 // Call this just to log what the current active package is, if any.
98 _describeActive(name);
99
100 // Write a lockfile that points to the local package.
101 var fullPath = canonicalize(entrypoint.root.dir);
102 var id = new PackageId(name, "path", entrypoint.root.version,
103 PathSource.describePath(fullPath));
104 _writeLockFile(id, new LockFile.empty());
105 });
106 }
107
108 /// Installs the package [id] with [lockFile] into the system cache along
109 /// with its dependencies.
110 Future _installInCache(PackageId id, LockFile lockFile) {
111 var source = cache.sources[id.source];
112
113 // Put the main package in the cache.
114 return source.downloadToSystemCache(id).then((package) {
115 // If we didn't know the version for the ID (which is true for Git
116 // packages), look it up now that we have it.
117 if (id.version == Version.none) {
118 id = id.atVersion(package.version);
119 }
120
121 return source.resolveId(id).then((id_) {
122 id = id_;
123
124 // Resolve it and download its dependencies.
125 return resolveVersions(SolveType.GET, cache.sources, package,
126 lockFile: lockFile);
127 });
82 }).then((result) { 128 }).then((result) {
83 if (!result.succeeded) throw result.error; 129 if (!result.succeeded) throw result.error;
84 result.showReport(SolveType.GET); 130 result.showReport(SolveType.GET);
85 131
86 // Make sure all of the dependencies are locally installed. 132 // Make sure all of the dependencies are locally installed.
87 return Future.wait(result.packages.map((id) { 133 return Future.wait(result.packages.map(_cacheDependency));
88 var source = cache.sources[id.source];
89 if (source is! CachedSource) return new Future.value();
90 return source.downloadToSystemCache(id)
91 .then((_) => source.resolveId(id));
92 }));
93 }).then((ids) { 134 }).then((ids) {
94 var lockFile = new LockFile(ids); 135 _writeLockFile(id, new LockFile(ids));
95 136 });
96 // Add the root package itself to the lockfile. 137 }
97 lockFile.packages[name] = id; 138
98 139 /// Downloads [id] into the system cache if it's a cached package.
99 ensureDir(_directory); 140 ///
100 writeTextFile(_getLockFilePath(name), 141 /// Returns the resolved [PackageId] for [id].
101 lockFile.serialize(cache.rootDir, cache.sources)); 142 Future<PackageId> _cacheDependency(PackageId id) {
102 143 var source = cache.sources[id.source];
103 log.message("Activated ${log.bold(package.name)} ${package.version}."); 144
104 // TODO(rnystrom): Look in "bin" and display list of binaries that 145 return syncFuture(() {
105 // user can run. 146 if (id.isRoot) return null;
106 }); 147 if (source is! CachedSource) return null;
107 } 148
108 149 return source.downloadToSystemCache(id);
109 /// Deactivates a previously-activated package named [name] or fails with 150 }).then((_) => source.resolveId(id));
110 /// an error if [name] is not an active package. 151 }
111 void deactivate(String name) { 152
112 // See if we already have it activated. 153 /// Finishes activating package [id] by saving [lockFile] in the cache.
154 void _writeLockFile(PackageId id, LockFile lockFile) {
155 // Add the root package to the lockfile.
156 lockFile.packages[id.name] = id;
157
158 ensureDir(_directory);
159 writeTextFile(_getLockFilePath(id.name),
160 lockFile.serialize(cache.rootDir, cache.sources));
161
162 if (id.source == "path") {
163 var path = PathSource.pathFromDescription(id.description);
164 log.message('Activated ${log.bold(id.name)} ${id.version} at path '
165 '"$path".');
166 } else {
167 log.message("Activated ${log.bold(id.name)} ${id.version}.");
168 }
169
170 // TODO(rnystrom): Look in "bin" and display list of binaries that
171 // user can run.
172 }
173
174 /// Gets the lock file for the currently active package with [name].
175 ///
176 /// Displays a message to the user about the current package, if any. Returns
177 /// the [LockFile] for the active package or `null` otherwise.
178 LockFile _describeActive(String package) {
113 try { 179 try {
114 var lockFilePath = p.join(_directory, "$name.lock"); 180 var lockFile = new LockFile.load(_getLockFilePath(package),
115 var lockFile = new LockFile.load(lockFilePath, cache.sources); 181 cache.sources);
116 var version = lockFile.packages[name].version; 182 var id = lockFile.packages[package];
117 183
118 deleteEntry(lockFilePath); 184 if (id.source == "path") {
119 log.message("Deactivated package ${log.bold(name)} $version."); 185 var path = PathSource.pathFromDescription(id.description);
186 log.message('Package ${log.bold(package)} is currently active at '
187 'path "$path".');
188 } else {
189 log.message("Package ${log.bold(package)} is currently active at "
190 "version ${log.bold(id.version)}.");
191 }
192
193 return lockFile;
120 } on IOException catch (error) { 194 } on IOException catch (error) {
121 dataError("No active package ${log.bold(name)}."); 195 // If we couldn't read the lock file, it's not activated.
122 } 196 return null;
123 } 197 }
124 198 }
125 /// Finds the active packge with [name]. 199
200 /// Deactivates a previously-activated package named [name].
201 ///
202 /// If [logDeletion] is true, displays to the user when a package is
203 /// deactivated. Otherwise, deactivates silently.
204 ///
205 /// Returns `false` if no package with [name] was currently active.
206 bool deactivate(String name, {bool logDeactivate: false}) {
207 var lockFilePath = _getLockFilePath(name);
208 if (!fileExists(lockFilePath)) return false;
209
210 var lockFile = new LockFile.load(lockFilePath, cache.sources);
211 var id = lockFile.packages[name];
212
213 deleteEntry(lockFilePath);
214
215 if (logDeactivate) {
216 if (id.source == "path") {
217 var path = PathSource.pathFromDescription(id.description);
218 log.message('Deactivated package ${log.bold(name)} at path "$path".');
219 } else {
220 log.message("Deactivated package ${log.bold(name)} ${id.version}.");
221 }
222 }
223
224 return true;
225 }
226
227 /// Finds the active package with [name].
126 /// 228 ///
127 /// Returns an [Entrypoint] loaded with the active package if found. 229 /// Returns an [Entrypoint] loaded with the active package if found.
128 Future<Entrypoint> find(String name) { 230 Future<Entrypoint> find(String name) {
129 var lockFile;
130 var version;
131 return syncFuture(() { 231 return syncFuture(() {
232 var lockFile;
132 try { 233 try {
133 lockFile = new LockFile.load(_getLockFilePath(name), cache.sources); 234 lockFile = new LockFile.load(_getLockFilePath(name), cache.sources);
134 version = lockFile.packages[name].version;
135 } on IOException catch (error) { 235 } on IOException catch (error) {
136 // If we couldn't read the lock file, it's not activated. 236 // If we couldn't read the lock file, it's not activated.
137 dataError("No active package ${log.bold(name)}."); 237 dataError("No active package ${log.bold(name)}.");
138 } 238 }
139 }).then((_) { 239
140 // Load the package from the cache. 240 // Load the package from the cache.
141 var id = new PackageId(name, _source.name, version, name); 241 var id = lockFile.packages[name];
142 return _source.getDirectory(id);
143 }).then((dir) {
144 return new Package.load(name, dir, cache.sources);
145 }).then((package) {
146 // Pull the root package out of the lock file so the solver doesn't see
147 // it.
148 lockFile.packages.remove(name); 242 lockFile.packages.remove(name);
149 243
150 return new Entrypoint.inMemory(package, lockFile, cache); 244 var source = cache.sources[id.source];
151 }); 245 if (source is CachedSource) {
152 } 246 // For cached sources, the package itself is in the cache and the
153 247 // lockfile is the one we just loaded.
154 /// Picks the best version of [package] to activate that meets [constraint]. 248 return cache.sources[id.source].getDirectory(id)
249 .then((dir) => new Package.load(name, dir, cache.sources))
250 .then((package) {
251 return new Entrypoint.inMemory(package, lockFile, cache);
252 });
253 }
254
255 // For uncached sources (i.e. path), the ID just points to the real
256 // directory for the package.
257 assert(id.source == "path");
258 return new Entrypoint(PathSource.pathFromDescription(id.description),
259 cache);
260 });
261 }
262
263 /// Picks the best hosted version of [package] to activate that meets
264 /// [constraint].
155 /// 265 ///
156 /// If [version] is not `null`, this tries to maintain that version if 266 /// If [version] is not `null`, this tries to maintain that version if
157 /// possible. 267 /// possible.
158 Future<Version> _selectVersion(String package, Version version, 268 Future<Version> _selectVersion(String package, Version version,
159 VersionConstraint constraint) { 269 VersionConstraint constraint) {
160 // If we already have a valid active version, just use it. 270 // If we already have a valid active version, just use it.
161 if (version != null && constraint.allows(version)) { 271 if (version != null && constraint.allows(version)) {
162 return new Future.value(version); 272 return new Future.value(version);
163 } 273 }
164 274
165 // Otherwise, select the best version the matches the constraint. 275 // Otherwise, select the best version the matches the constraint.
166 return _source.getVersions(package, package).then((versions) { 276 var source = cache.sources["hosted"];
277 return source.getVersions(package, package).then((versions) {
167 versions = versions.where(constraint.allows).toList(); 278 versions = versions.where(constraint.allows).toList();
168 279
169 if (versions.isEmpty) { 280 if (versions.isEmpty) {
170 // TODO(rnystrom): Show most recent unmatching version? 281 // TODO(rnystrom): Show most recent unmatching version?
171 dataError("Package ${log.bold(package)} has no versions that match " 282 dataError("Package ${log.bold(package)} has no versions that match "
172 "$constraint."); 283 "$constraint.");
173 } 284 }
174 285
175 // Pick the best matching version. 286 // Pick the best matching version.
176 versions.sort(Version.prioritize); 287 versions.sort(Version.prioritize);
177 return versions.last; 288 return versions.last;
178 }); 289 });
179 } 290 }
180 291
181 /// Gets the path to the lock file for an activated package with [name]. 292 /// Gets the path to the lock file for an activated cached package with
293 /// [name].
182 String _getLockFilePath(name) => p.join(_directory, name + ".lock"); 294 String _getLockFilePath(name) => p.join(_directory, name + ".lock");
183 } 295 }
OLDNEW
« no previous file with comments | « sdk/lib/_internal/pub/lib/src/command/global_deactivate.dart ('k') | sdk/lib/_internal/pub/lib/src/source/path.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698