| 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('entrypoint'); | 5 #library('entrypoint'); | 
| 6 | 6 | 
| 7 #import('io.dart'); | 7 #import('io.dart'); | 
| 8 #import('lock_file.dart'); |  | 
| 9 #import('package.dart'); | 8 #import('package.dart'); | 
| 10 #import('root_source.dart'); | 9 #import('root_source.dart'); | 
| 11 #import('system_cache.dart'); | 10 #import('system_cache.dart'); | 
| 12 #import('version.dart'); | 11 #import('version.dart'); | 
| 13 #import('version_solver.dart'); | 12 #import('version_solver.dart'); | 
| 14 #import('utils.dart'); | 13 #import('utils.dart'); | 
| 15 | 14 | 
| 16 /** | 15 /** | 
| 17  * Pub operates over a directed graph of dependencies that starts at a root | 16  * Pub operates over a directed graph of dependencies that starts at a root | 
| 18  * "entrypoint" package. This is typically the package where the current | 17  * "entrypoint" package. This is typically the package where the current | 
| (...skipping 17 matching lines...) Expand all  Loading... | 
| 36    */ | 35    */ | 
| 37   final Package root; | 36   final Package root; | 
| 38 | 37 | 
| 39   /** | 38   /** | 
| 40    * The system-wide cache which caches packages that need to be fetched over | 39    * The system-wide cache which caches packages that need to be fetched over | 
| 41    * the network. | 40    * the network. | 
| 42    */ | 41    */ | 
| 43   final SystemCache cache; | 42   final SystemCache cache; | 
| 44 | 43 | 
| 45   /** | 44   /** | 
| 46    * Packages which are either currently being asynchronously installed to the | 45    * Packages which have already been loaded into memory. | 
| 47    * directory, or have already been installed. |  | 
| 48    */ | 46    */ | 
| 49   final Map<PackageId, Future<PackageId>> _installs; | 47   final Map<PackageId, Package> _loadedPackages; | 
|  | 48 | 
|  | 49   /** | 
|  | 50    * Packages which are currently being asynchronously installed to the | 
|  | 51    * directory. | 
|  | 52    */ | 
|  | 53   final Map<PackageId, Future<Package>> _pendingInstalls; | 
| 50 | 54 | 
| 51   Entrypoint(this.root, this.cache) | 55   Entrypoint(this.root, this.cache) | 
| 52   : _installs = new Map<PackageId, Future<PackageId>>(); | 56   : _loadedPackages  = new Map<PackageId, Package>(), | 
|  | 57     _pendingInstalls = new Map<PackageId, Future<Package>>(); | 
| 53 | 58 | 
| 54   /** | 59   /** | 
| 55    * The path to this "packages" directory. | 60    * The path to this "packages" directory. | 
| 56    */ | 61    */ | 
| 57   // TODO(rnystrom): Make this path configurable. | 62   // TODO(rnystrom): Make this path configurable. | 
| 58   String get path() => join(root.dir, 'packages'); | 63   String get path() => join(root.dir, 'packages'); | 
| 59 | 64 | 
| 60   /** | 65   /** | 
| 61    * Ensures that the package identified by [id] is installed to the directory. | 66    * Ensures that the package identified by [id] is installed to the directory, | 
| 62    * Returns the resolved [PackageId]. | 67    * loads it, and returns it. | 
| 63    * | 68    * | 
| 64    * If this completes successfully, the package is guaranteed to be importable | 69    * If this completes successfully, the package is guaranteed to be importable | 
| 65    * using the `package:` scheme. | 70    * using the `package:` scheme. | 
| 66    * | 71    * | 
| 67    * This will automatically install the package to the system-wide cache as | 72    * This will automatically install the package to the system-wide cache as | 
| 68    * well if it requires network access to retrieve (specifically, if | 73    * well if it requires network access to retrieve (specifically, if | 
| 69    * `id.source.shouldCache` is true). | 74    * `id.source.shouldCache` is true). | 
| 70    * | 75    * | 
| 71    * See also [installDependencies]. | 76    * See also [installTransitively]. | 
| 72    */ | 77    */ | 
| 73   Future<PackageId> install(PackageId id) { | 78   Future<Package> install(PackageId id) { | 
| 74     var pendingOrCompleted = _installs[id]; | 79     var package = _loadedPackages[id]; | 
| 75     if (pendingOrCompleted != null) return pendingOrCompleted; | 80     if (package != null) return new Future<Package>.immediate(package); | 
|  | 81 | 
|  | 82     var pending = _pendingInstalls[id]; | 
|  | 83     if (pending != null) return new Future<Package>.immediate(package); | 
| 76 | 84 | 
| 77     var packageDir = join(path, id.name); | 85     var packageDir = join(path, id.name); | 
| 78     var future = ensureDir(dirname(packageDir)).chain((_) { | 86     var future = ensureDir(dirname(packageDir)).chain((_) { | 
| 79       return exists(packageDir); | 87       return exists(packageDir); | 
| 80     }).chain((exists) { | 88     }).chain((exists) { | 
| 81       if (!exists) return new Future.immediate(null); | 89       // If the package already exists in the directory, no need to re-install. | 
| 82       // TODO(nweiz): figure out when to actually delete the directory, and when | 90       if (exists) return new Future.immediate(null); | 
| 83       // we can just re-use the existing symlink. | 91 | 
| 84       return deleteDir(packageDir); |  | 
| 85     }).chain((_) { |  | 
| 86       if (id.source.shouldCache) { | 92       if (id.source.shouldCache) { | 
| 87         return cache.install(id).chain( | 93         return cache.install(id).chain( | 
| 88             (pkg) => createSymlink(pkg.dir, packageDir)); | 94             (pkg) => createSymlink(pkg.dir, packageDir)); | 
| 89       } else { | 95       } else { | 
| 90         return id.source.install(id, packageDir).transform((found) { | 96         return id.source.install(id, packageDir).transform((found) { | 
| 91           if (found) return null; | 97           if (found) return null; | 
| 92           // TODO(nweiz): More robust error-handling. | 98           // TODO(nweiz): More robust error-handling. | 
| 93           throw 'Package ${id.name} not found in source "${id.source.name}".'; | 99           throw 'Package ${id.name} not found in source "${id.source.name}".'; | 
| 94         }); | 100         }); | 
| 95       } | 101       } | 
| 96     }).chain((_) => id.resolved); | 102     }).chain((_) => Package.load(packageDir, cache.sources)); | 
| 97 | 103 | 
| 98     _installs[id] = future; | 104     future.then((pkg) => _loadedPackages[id] = pkg); | 
|  | 105     always(future, () => _pendingInstalls.remove(id)); | 
|  | 106     _pendingInstalls[id] = future; | 
| 99 | 107 | 
| 100     return future; | 108     return future; | 
| 101   } | 109   } | 
| 102 | 110 | 
| 103   /** | 111   /** | 
| 104    * Installs all dependencies of the [root] package to its "packages" | 112    * Installs all dependencies of the [root] package to its "packages" | 
| 105    * directory. Returns a [Future] that completes when all dependencies are | 113    * directory. Returns a [Future] that completes when all dependencies are | 
| 106    * installed. | 114    * installed. | 
| 107    */ | 115    */ | 
| 108   Future installDependencies() { | 116   Future installDependencies() { | 
| 109     return _getResolvedDependencies().chain((packageVersions) { | 117     return resolveVersions(cache.sources, root).chain((packageVersions) { | 
|  | 118       // TODO(nweiz): persist packageVersions to a lockfile. | 
| 110       return Futures.wait(packageVersions.map((id) { | 119       return Futures.wait(packageVersions.map((id) { | 
| 111         if (id.source is RootSource) return new Future.immediate(id); | 120         if (id.source is RootSource) return new Future.immediate(null); | 
| 112         return install(id); | 121         return install(id); | 
| 113       })); | 122       })); | 
| 114     }).chain(_saveLockFile); |  | 
| 115   } |  | 
| 116 |  | 
| 117   /** |  | 
| 118    * Gets a list of all the [PackageId]s that this entrypoint transitively |  | 
| 119    * depends on. The concrete versions of these ids are given by the |  | 
| 120    * [VersionSolver] and the `pubspec.lock` file, if it exists. |  | 
| 121    */ |  | 
| 122   Future<List<PackageId>> _getResolvedDependencies() { |  | 
| 123     return _loadLockFile().chain((lockFile) => |  | 
| 124         resolveVersions(cache.sources, root, lockFile)); |  | 
| 125   } |  | 
| 126 |  | 
| 127   /** |  | 
| 128    * Loads the list of concrete package versions from the `pubspec.lock`, if it |  | 
| 129    * exists. If it doesn't, this completes to an empty [LockFile]. |  | 
| 130    * |  | 
| 131    * If there's an error reading the `pubspec.lock` file, this will print a |  | 
| 132    * warning message and act as though the file doesn't exist. |  | 
| 133    */ |  | 
| 134   Future<LockFile> _loadLockFile() { |  | 
| 135     var completer = new Completer<LockFile>(); |  | 
| 136     var lockFilePath = join(root.dir, 'pubspec.lock'); |  | 
| 137     var future = readTextFile(lockFilePath); |  | 
| 138 |  | 
| 139     future.handleException((_) { |  | 
| 140       completer.complete(new LockFile.empty()); |  | 
| 141 |  | 
| 142       // If we failed to load the lockfile but it does exist, something's |  | 
| 143       // probably wrong and we should notify the user. |  | 
| 144       fileExists(lockFilePath).then((exists) { |  | 
| 145         if (!exists) return; |  | 
| 146         printError("Error reading pubspec.lock: ${future.exception}"); |  | 
| 147       }); |  | 
| 148 |  | 
| 149       return true; |  | 
| 150     }); | 123     }); | 
| 151 |  | 
| 152     future.then((text) => |  | 
| 153         completer.complete(new LockFile.parse(text, cache.sources))); |  | 
| 154     return completer.future; |  | 
| 155   } |  | 
| 156 |  | 
| 157   /** |  | 
| 158    * Saves a list of concrete package versions to the `pubspec.lock` file. |  | 
| 159    */ |  | 
| 160   Future _saveLockFile(List<PackageId> packageIds) { |  | 
| 161     var lockFile = new LockFile.empty(); |  | 
| 162     for (var id in packageIds) { |  | 
| 163       if (id.source is! RootSource) lockFile.packages[id.name] = id; |  | 
| 164     } |  | 
| 165 |  | 
| 166     return writeTextFile(join(root.dir, 'pubspec.lock'), lockFile.serialize()); |  | 
| 167   } | 124   } | 
| 168 } | 125 } | 
| OLD | NEW | 
|---|