Chromium Code Reviews| Index: utils/pub/app_cache.dart |
| diff --git a/utils/pub/app_cache.dart b/utils/pub/app_cache.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f1d2db89010360363069c3870ae2646c17fe1259 |
| --- /dev/null |
| +++ b/utils/pub/app_cache.dart |
| @@ -0,0 +1,113 @@ |
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +/** |
| + * The application-specific cache of installed packages. |
| + * |
| + * This cache contains symlinks to all packages used by an app. These links |
| + * point either to the [SystemCache] or to some other location on the local |
| + * filesystem. |
| + * |
| + * This corresponds to the application's packages directory. |
| + */ |
| +class AppCache { |
|
Bob Nystrom
2012/05/03 00:23:02
"EntrypointCache"
|
| + /** |
| + * The root directory of the application containing this cache. |
| + */ |
| + final String appDir; |
| + |
| + /** |
| + * The system-wide cache which caches packages that need to be fetched over |
| + * the network. |
| + */ |
| + final SystemCache systemCache; |
| + |
| + /** |
| + * Packages which have already been loaded into memory. |
| + */ |
| + final Map<PackageId, Package> _loadedPackages; |
| + |
| + /** |
| + * Packages which are currently being asynchronously installed to the cache. |
| + */ |
| + final Map<PackageId, Future<Package>> _pendingInstalls; |
| + |
| + /** |
| + * Creates a new package cache for the given application directory. It's |
| + * backed by the packages subdirectory of [appDir]. |
| + */ |
| + AppCache(this.appDir, this.systemCache) |
| + : _loadedPackages = new Map<PackageId, Package>(), |
| + _pendingInstalls = new Map<PackageId, Future<Package>>(); |
| + |
| + /** |
| + * Returns the directory containing the packages. |
| + */ |
| + // TODO(rnystrom): Make this path configurable. |
| + String get packagesDir() => join(this.appDir, 'packages'); |
| + |
| + /** |
| + * Ensures that the package identified by [id] is installed to the cache, |
| + * loads it, and returns it. |
| + * |
| + * If this completes successfully, the package is guaranteed to be importable |
| + * using the `package:` scheme. |
| + * |
| + * This will automatically install the package to the system-wide cache as |
| + * well if it requires network access to retrieve (specifically, if |
| + * `id.source.shouldCache` is true). |
| + * |
| + * See also [installTransitively]. |
| + */ |
| + Future<Package> install(PackageId id) { |
| + var package = _loadedPackages[id]; |
| + if (package != null) return package; |
| + |
| + var pending = _pendingInstalls[id]; |
| + if (pending != null) return pending; |
| + |
| + var packageDir = join(packagesDir, id.name); |
| + var future = ensureNestedDir(dirname(packageDir)).chain((_) { |
| + return exists(packageDir); |
| + }).chain((exists) { |
| + // If the package already exists in the cache, no need to re-install. |
| + if (exists) return new Future.immediate(null); |
| + |
| + if (id.source.shouldCache) { |
| + return systemCache.install(id). |
| + chain((pkg) => createSymlink(pkg.dir, packageDir)); |
| + } else { |
| + return id.source.install(id, packageDir).transform((found) { |
| + if (found) return null; |
| + throw 'Package ${id.name} not found in source "${id.source.name}".'; |
| + }); |
| + } |
| + }).chain((_) => Package.load(packageDir)); |
| + |
| + future.then((pkg) => _loadedPackages[id] = pkg); |
| + always(future, () => _pendingInstalls.remove(id)); |
| + _pendingInstalls[id] = future; |
| + |
| + return future; |
| + } |
| + |
| + /** |
| + * Installs the package identified by [id] and all its transitive |
| + * dependencies. |
| + */ |
| + Future<Package> installTransitively(PackageId id) { |
| + var seen = new Set<PackageId>(); |
| + Future<Package> helper(id) { |
| + if (seen.contains(id)) return new Future.immediate(null); |
| + seen.add(id); |
| + |
| + return install(id).chain((package) { |
| + return Futures.wait(package.dependencies.map(helper)). |
| + transform((_) => package); |
| + }); |
| + } |
| + |
| + return helper(id); |
| + } |
| +} |