Chromium Code Reviews| Index: utils/pub/entrypoint.dart |
| diff --git a/utils/pub/entrypoint.dart b/utils/pub/entrypoint.dart |
| index e33b9f91d7cc6e224a33086ffa47d6aed526e623..a8202ae6aa45263fbb79688f9361ca8f27bca944 100644 |
| --- a/utils/pub/entrypoint.dart |
| +++ b/utils/pub/entrypoint.dart |
| @@ -5,6 +5,7 @@ |
| #library('entrypoint'); |
| #import('io.dart'); |
| +#import('lock_file.dart'); |
| #import('package.dart'); |
| #import('root_source.dart'); |
| #import('system_cache.dart'); |
| @@ -42,19 +43,13 @@ class Entrypoint { |
| final SystemCache cache; |
| /** |
| - * Packages which have already been loaded into memory. |
| + * Packages which are either currently being asynchronously installed to the |
| + * directory, or have already been installed. |
| */ |
| - final Map<PackageId, Package> _loadedPackages; |
| - |
| - /** |
| - * Packages which are currently being asynchronously installed to the |
| - * directory. |
| - */ |
| - final Map<PackageId, Future<Package>> _pendingInstalls; |
| + final Map<PackageId, Future<PackageId>> _installs; |
| Entrypoint(this.root, this.cache) |
| - : _loadedPackages = new Map<PackageId, Package>(), |
| - _pendingInstalls = new Map<PackageId, Future<Package>>(); |
| + : _installs = new Map<PackageId, Future<PackageId>>(); |
| /** |
| * The path to this "packages" directory. |
| @@ -63,8 +58,8 @@ class Entrypoint { |
| String get path() => join(root.dir, 'packages'); |
| /** |
| - * Ensures that the package identified by [id] is installed to the directory, |
| - * loads it, and returns it. |
| + * Ensures that the package identified by [id] is installed to the directory. |
| + * Returns the resolved [PackageId]. |
| * |
| * If this completes successfully, the package is guaranteed to be importable |
| * using the `package:` scheme. |
| @@ -73,22 +68,21 @@ class Entrypoint { |
| * well if it requires network access to retrieve (specifically, if |
| * `id.source.shouldCache` is true). |
| * |
| - * See also [installTransitively]. |
| + * See also [installDependencies]. |
| */ |
| - Future<Package> install(PackageId id) { |
| - var package = _loadedPackages[id]; |
| - if (package != null) return new Future<Package>.immediate(package); |
| - |
| - var pending = _pendingInstalls[id]; |
| - if (pending != null) return new Future<Package>.immediate(package); |
| + Future<PackageId> install(PackageId id) { |
| + var pendingOrCompleted = _installs[id]; |
| + if (pendingOrCompleted != null) return pendingOrCompleted; |
| var packageDir = join(path, id.name); |
| var future = ensureDir(dirname(packageDir)).chain((_) { |
| return exists(packageDir); |
| }).chain((exists) { |
| - // If the package already exists in the directory, no need to re-install. |
| - if (exists) return new Future.immediate(null); |
| - |
| + if (!exists) return new Future.immediate(null); |
| + // TODO(nweiz): figure out when to actually delete the directory, and when |
| + // we can just re-use the existing symlink. |
| + return deleteDir(packageDir); |
|
Jennifer Messerly
2012/07/19 18:45:34
the idea here is to try and delete if if it alread
nweiz
2012/07/19 18:56:54
That's right. We want to get rid of any already-in
|
| + }).chain((_) { |
| if (id.source.shouldCache) { |
| return cache.install(id).chain( |
| (pkg) => createSymlink(pkg.dir, packageDir)); |
| @@ -99,11 +93,9 @@ class Entrypoint { |
| throw 'Package ${id.name} not found in source "${id.source.name}".'; |
| }); |
| } |
| - }).chain((_) => Package.load(packageDir, cache.sources)); |
| + }).chain((_) => id.resolved); |
| - future.then((pkg) => _loadedPackages[id] = pkg); |
| - always(future, () => _pendingInstalls.remove(id)); |
| - _pendingInstalls[id] = future; |
| + _installs[id] = future; |
| return future; |
| } |
| @@ -114,12 +106,63 @@ class Entrypoint { |
| * installed. |
| */ |
| Future installDependencies() { |
| - return resolveVersions(cache.sources, root).chain((packageVersions) { |
| - // TODO(nweiz): persist packageVersions to a lockfile. |
| + return _getResolvedDependencies().chain((packageVersions) { |
| return Futures.wait(packageVersions.map((id) { |
| - if (id.source is RootSource) return new Future.immediate(null); |
| + if (id.source is RootSource) return new Future.immediate(id); |
| return install(id); |
| })); |
| + }).chain(_saveLockFile); |
| + } |
| + |
| + /** |
| + * Gets a list of all the [PackageId]s that this entrypoint transitively |
| + * depends on. The concrete versions of these ids are given by the |
| + * [VersionSolver] and the `pubspec.lock` file, if it exists. |
| + */ |
| + Future<List<PackageId>> _getResolvedDependencies() { |
| + return _loadLockFile().chain((lockFile) => |
| + resolveVersions(cache.sources, root, lockFile)); |
| + } |
| + |
| + /** |
| + * Loads the list of concrete package versions from the `pubspec.lock`, if it |
| + * exists. If it doesn't, this completes to an empty [LockFile]. |
| + * |
| + * If there's an error reading the `pubspec.lock` file, this will print a |
| + * warning message and act as though the file doesn't exist. |
| + */ |
| + Future<LockFile> _loadLockFile() { |
| + var completer = new Completer<LockFile>(); |
| + var lockFilePath = join(root.dir, 'pubspec.lock'); |
| + var future = readTextFile(lockFilePath); |
| + |
| + future.handleException((_) { |
| + completer.complete(new LockFile.empty()); |
| + |
| + // If we failed to load the lockfile but it does exist, something's |
| + // probably wrong and we should notify the user. |
| + fileExists(lockFilePath).then((exists) { |
| + if (!exists) return; |
| + printError("Error reading pubspec.lock: ${future.exception}"); |
| + }); |
| + |
| + return true; |
| }); |
| + |
| + future.then((text) => |
| + completer.complete(new LockFile.parse(text, cache.sources))); |
| + return completer.future; |
| + } |
| + |
| + /** |
| + * Saves a list of concrete package versions to the `pubspec.lock` file. |
| + */ |
| + Future _saveLockFile(List<PackageId> packageIds) { |
| + var lockFile = new LockFile.empty(); |
| + for (var id in packageIds) { |
| + if (id.source is! RootSource) lockFile.packages[id.name] = id; |
| + } |
| + |
| + return writeTextFile(join(root.dir, 'pubspec.lock'), lockFile.serialize()); |
| } |
| } |