Chromium Code Reviews

Unified 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.
Jump to:
View side-by-side diff with in-line comments
Index: sdk/lib/_internal/pub/lib/src/global_packages.dart
diff --git a/sdk/lib/_internal/pub/lib/src/global_packages.dart b/sdk/lib/_internal/pub/lib/src/global_packages.dart
index b9bb54881fc29e904daf9b3afdc69add61c7c97e..45ceeb6526850b66bd514d4ca312523c6af51447 100644
--- a/sdk/lib/_internal/pub/lib/src/global_packages.dart
+++ b/sdk/lib/_internal/pub/lib/src/global_packages.dart
@@ -16,7 +16,9 @@ import 'log.dart' as log;
import 'package.dart';
import 'system_cache.dart';
import 'solver/version_solver.dart';
+import 'source.dart';
import 'source/cached.dart';
+import 'source/path.dart';
import 'utils.dart';
import 'version.dart';
@@ -29,6 +31,21 @@ import 'version.dart';
/// Only one version of a given package name can be globally activated at a
/// time. Activating a different version of a package will deactivate the
/// previous one.
+///
+/// This handles packages from uncached and cached sources a little differently.
+/// For a cached source, the package is physically in the user's pub cache and
+/// we don't want to mess with it by putting a lockfile in there. Instead, when
+/// we activate the package, we create a full lockfile and put it in the
+/// "global_packages" directory. It's named "<package>.lock". Unlike a normal
+/// lockfile, it also contains an entry for the root package itself, so that we
+/// know the version and description that was activated.
+///
+/// Uncached packages (i.e. "path" packages) are somewhere else on the user's
+/// local file system and can have a lockfile directly in place. (And, in fact,
+/// we want to ensure we honor the user's lockfile there.) To activate it, we
+/// just need to know where that package directory is. For that, we create a
+/// lockfile that *only* contains the root package's [PackageId] -- basically
+/// just the path to the directory where the real lockfile lives.
class GlobalPackages {
/// The [SystemCache] containing the global packages.
final SystemCache cache;
@@ -36,10 +53,6 @@ class GlobalPackages {
/// The directory where the lockfiles for activated packages are stored.
String get _directory => p.join(cache.rootDir, "global_packages");
- /// The source that global packages can be activated from.
- // TODO(rnystrom): Allow activating packages from other sources.
- CachedSource get _source => cache.sources["hosted"] as CachedSource;
-
/// Creates a new global package registry backed by the given directory on
/// the user's file system.
///
@@ -49,109 +62,206 @@ class GlobalPackages {
/// Finds the latest version of the hosted package with [name] that matches
/// [constraint] and makes it the active global version.
- Future activate(String name, VersionConstraint constraint) {
+ Future activateHosted(String name, VersionConstraint constraint) {
// See if we already have it activated.
- var lockFile;
+ var lockFile = _describeActive(name);
var currentVersion;
- try {
- lockFile = new LockFile.load(_getLockFilePath(name), cache.sources);
- currentVersion = lockFile.packages[name].version;
+ if (lockFile != null) {
+ var id = lockFile.packages[name];
+
+ // Try to preserve the current version if we've already activated the
+ // hosted package.
+ if (id.source == "hosted") currentVersion = id.version;
// Pull the root package out of the lock file so the solver doesn't see
// it.
lockFile.packages.remove(name);
-
- log.message("Package ${log.bold(name)} is already active at "
- "version ${log.bold(currentVersion)}.");
- } on IOException catch (error) {
- // If we couldn't read the lock file, it's not activated.
+ } else {
lockFile = new LockFile.empty();
}
- var package;
- var id;
return _selectVersion(name, currentVersion, constraint).then((version) {
// Make sure it's in the cache.
- id = new PackageId(name, _source.name, version, name);
- return _source.downloadToSystemCache(id);
- }).then((p) {
- package = p;
- // Resolve it and download its dependencies.
- return resolveVersions(SolveType.GET, cache.sources, package,
- lockFile: lockFile);
+ var id = new PackageId(name, "hosted", version, name);
+ return _installInCache(id, lockFile);
+ });
+ }
+
+ /// Makes the local package at [path] globally active.
+ Future activatePath(String path) {
+ return syncFuture(() {
+ var entrypoint = new Entrypoint(path, cache);
+
+ var name = entrypoint.root.name;
+
+ // Call this just to log what the current active package is, if any.
+ _describeActive(name);
+
+ // Write a lockfile that points to the local package.
+ var fullPath = canonicalize(entrypoint.root.dir);
+ var id = new PackageId(name, "path", entrypoint.root.version,
+ PathSource.describePath(fullPath));
+ _writeLockFile(id, new LockFile.empty());
+ });
+ }
+
+ /// Installs the package [id] with [lockFile] into the system cache along
+ /// with its dependencies.
+ Future _installInCache(PackageId id, LockFile lockFile) {
+ var source = cache.sources[id.source];
+
+ // Put the main package in the cache.
+ return source.downloadToSystemCache(id).then((package) {
+ // If we didn't know the version for the ID (which is true for Git
+ // packages), look it up now that we have it.
+ if (id.version == Version.none) {
+ id = id.atVersion(package.version);
+ }
+
+ return source.resolveId(id).then((id_) {
+ id = id_;
+
+ // Resolve it and download its dependencies.
+ return resolveVersions(SolveType.GET, cache.sources, package,
+ lockFile: lockFile);
+ });
}).then((result) {
if (!result.succeeded) throw result.error;
result.showReport(SolveType.GET);
// Make sure all of the dependencies are locally installed.
- return Future.wait(result.packages.map((id) {
- var source = cache.sources[id.source];
- if (source is! CachedSource) return new Future.value();
- return source.downloadToSystemCache(id)
- .then((_) => source.resolveId(id));
- }));
+ return Future.wait(result.packages.map(_cacheDependency));
}).then((ids) {
- var lockFile = new LockFile(ids);
+ _writeLockFile(id, new LockFile(ids));
+ });
+ }
- // Add the root package itself to the lockfile.
- lockFile.packages[name] = id;
+ /// Downloads [id] into the system cache if it's a cached package.
+ ///
+ /// Returns the resolved [PackageId] for [id].
+ Future<PackageId> _cacheDependency(PackageId id) {
+ var source = cache.sources[id.source];
- ensureDir(_directory);
- writeTextFile(_getLockFilePath(name),
- lockFile.serialize(cache.rootDir, cache.sources));
+ return syncFuture(() {
+ if (id.isRoot) return null;
+ if (source is! CachedSource) return null;
- log.message("Activated ${log.bold(package.name)} ${package.version}.");
- // TODO(rnystrom): Look in "bin" and display list of binaries that
- // user can run.
- });
+ return source.downloadToSystemCache(id);
+ }).then((_) => source.resolveId(id));
}
- /// Deactivates a previously-activated package named [name] or fails with
- /// an error if [name] is not an active package.
- void deactivate(String name) {
- // See if we already have it activated.
+ /// Finishes activating package [id] by saving [lockFile] in the cache.
+ void _writeLockFile(PackageId id, LockFile lockFile) {
+ // Add the root package to the lockfile.
+ lockFile.packages[id.name] = id;
+
+ ensureDir(_directory);
+ writeTextFile(_getLockFilePath(id.name),
+ lockFile.serialize(cache.rootDir, cache.sources));
+
+ if (id.source == "path") {
+ var path = PathSource.pathFromDescription(id.description);
+ log.message('Activated ${log.bold(id.name)} ${id.version} at path '
+ '"$path".');
+ } else {
+ log.message("Activated ${log.bold(id.name)} ${id.version}.");
+ }
+
+ // TODO(rnystrom): Look in "bin" and display list of binaries that
+ // user can run.
+ }
+
+ /// Gets the lock file for the currently active package with [name].
+ ///
+ /// Displays a message to the user about the current package, if any. Returns
+ /// the [LockFile] for the active package or `null` otherwise.
+ LockFile _describeActive(String package) {
try {
- var lockFilePath = p.join(_directory, "$name.lock");
- var lockFile = new LockFile.load(lockFilePath, cache.sources);
- var version = lockFile.packages[name].version;
+ var lockFile = new LockFile.load(_getLockFilePath(package),
+ cache.sources);
+ var id = lockFile.packages[package];
+
+ if (id.source == "path") {
+ var path = PathSource.pathFromDescription(id.description);
+ log.message('Package ${log.bold(package)} is currently active at '
+ 'path "$path".');
+ } else {
+ log.message("Package ${log.bold(package)} is currently active at "
+ "version ${log.bold(id.version)}.");
+ }
- deleteEntry(lockFilePath);
- log.message("Deactivated package ${log.bold(name)} $version.");
+ return lockFile;
} on IOException catch (error) {
- dataError("No active package ${log.bold(name)}.");
+ // If we couldn't read the lock file, it's not activated.
+ return null;
+ }
+ }
+
+ /// Deactivates a previously-activated package named [name].
+ ///
+ /// If [logDeletion] is true, displays to the user when a package is
+ /// deactivated. Otherwise, deactivates silently.
+ ///
+ /// Returns `false` if no package with [name] was currently active.
+ bool deactivate(String name, {bool logDeactivate: false}) {
+ var lockFilePath = _getLockFilePath(name);
+ if (!fileExists(lockFilePath)) return false;
+
+ var lockFile = new LockFile.load(lockFilePath, cache.sources);
+ var id = lockFile.packages[name];
+
+ deleteEntry(lockFilePath);
+
+ if (logDeactivate) {
+ if (id.source == "path") {
+ var path = PathSource.pathFromDescription(id.description);
+ log.message('Deactivated package ${log.bold(name)} at path "$path".');
+ } else {
+ log.message("Deactivated package ${log.bold(name)} ${id.version}.");
+ }
}
+
+ return true;
}
- /// Finds the active packge with [name].
+ /// Finds the active package with [name].
///
/// Returns an [Entrypoint] loaded with the active package if found.
Future<Entrypoint> find(String name) {
- var lockFile;
- var version;
return syncFuture(() {
+ var lockFile;
try {
lockFile = new LockFile.load(_getLockFilePath(name), cache.sources);
- version = lockFile.packages[name].version;
} on IOException catch (error) {
// If we couldn't read the lock file, it's not activated.
dataError("No active package ${log.bold(name)}.");
}
- }).then((_) {
+
// Load the package from the cache.
- var id = new PackageId(name, _source.name, version, name);
- return _source.getDirectory(id);
- }).then((dir) {
- return new Package.load(name, dir, cache.sources);
- }).then((package) {
- // Pull the root package out of the lock file so the solver doesn't see
- // it.
+ var id = lockFile.packages[name];
lockFile.packages.remove(name);
- return new Entrypoint.inMemory(package, lockFile, cache);
+ var source = cache.sources[id.source];
+ if (source is CachedSource) {
+ // For cached sources, the package itself is in the cache and the
+ // lockfile is the one we just loaded.
+ return cache.sources[id.source].getDirectory(id)
+ .then((dir) => new Package.load(name, dir, cache.sources))
+ .then((package) {
+ return new Entrypoint.inMemory(package, lockFile, cache);
+ });
+ }
+
+ // For uncached sources (i.e. path), the ID just points to the real
+ // directory for the package.
+ assert(id.source == "path");
+ return new Entrypoint(PathSource.pathFromDescription(id.description),
+ cache);
});
}
- /// Picks the best version of [package] to activate that meets [constraint].
+ /// Picks the best hosted version of [package] to activate that meets
+ /// [constraint].
///
/// If [version] is not `null`, this tries to maintain that version if
/// possible.
@@ -163,7 +273,8 @@ class GlobalPackages {
}
// Otherwise, select the best version the matches the constraint.
- return _source.getVersions(package, package).then((versions) {
+ var source = cache.sources["hosted"];
+ return source.getVersions(package, package).then((versions) {
versions = versions.where(constraint.allows).toList();
if (versions.isEmpty) {
@@ -178,6 +289,7 @@ class GlobalPackages {
});
}
- /// Gets the path to the lock file for an activated package with [name].
+ /// Gets the path to the lock file for an activated cached package with
+ /// [name].
String _getLockFilePath(name) => p.join(_directory, name + ".lock");
}
« 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