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

Side by Side Diff: utils/pub/entrypoint.dart

Issue 12079112: Make a bunch of stuff in pub synchronous. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 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) 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 'dart:async'; 7 import 'dart:async';
8 import 'io.dart'; 8 import 'io.dart';
9 import 'lock_file.dart'; 9 import 'lock_file.dart';
10 import 'log.dart' as log; 10 import 'log.dart' as log;
(...skipping 21 matching lines...) Expand all
32 class Entrypoint { 32 class Entrypoint {
33 /// The root package this entrypoint is associated with. 33 /// The root package this entrypoint is associated with.
34 final Package root; 34 final Package root;
35 35
36 /// The system-wide cache which caches packages that need to be fetched over 36 /// The system-wide cache which caches packages that need to be fetched over
37 /// the network. 37 /// the network.
38 final SystemCache cache; 38 final SystemCache cache;
39 39
40 /// Packages which are either currently being asynchronously installed to the 40 /// Packages which are either currently being asynchronously installed to the
41 /// directory, or have already been installed. 41 /// directory, or have already been installed.
42 final Map<PackageId, Future<PackageId>> _installs; 42 final _installs = new Map<PackageId, Future<PackageId>>();
nweiz 2013/02/01 02:05:55 Can't we make this a Map literal, or are there sti
Bob Nystrom 2013/02/01 23:17:21 Map literals can only have string keys. Seems a bi
43
44 Entrypoint(this.root, this.cache)
45 : _installs = new Map<PackageId, Future<PackageId>>();
46 43
47 /// Loads the entrypoint from a package at [rootDir]. 44 /// Loads the entrypoint from a package at [rootDir].
48 static Future<Entrypoint> load(String rootDir, SystemCache cache) { 45 Entrypoint(String rootDir, SystemCache cache)
49 return Package.load(null, rootDir, cache.sources).then((package) => 46 : root = new Package(null, rootDir, cache.sources),
50 new Entrypoint(package, cache)); 47 cache = cache;
51 }
52 48
53 // TODO(rnystrom): Make this path configurable. 49 // TODO(rnystrom): Make this path configurable.
54 /// The path to this "packages" directory. 50 /// The path to this "packages" directory.
55 String get path => join(root.dir, 'packages'); 51 String get path => join(root.dir, 'packages');
56 52
57 /// Ensures that the package identified by [id] is installed to the directory. 53 /// Ensures that the package identified by [id] is installed to the directory.
58 /// Returns the resolved [PackageId]. 54 /// Returns the resolved [PackageId].
59 /// 55 ///
60 /// If this completes successfully, the package is guaranteed to be importable 56 /// If this completes successfully, the package is guaranteed to be importable
61 /// using the `package:` scheme. 57 /// using the `package:` scheme.
62 /// 58 ///
63 /// This will automatically install the package to the system-wide cache as 59 /// This will automatically install the package to the system-wide cache as
64 /// well if it requires network access to retrieve (specifically, if 60 /// well if it requires network access to retrieve (specifically, if
65 /// `id.source.shouldCache` is true). 61 /// `id.source.shouldCache` is true).
66 /// 62 ///
67 /// See also [installDependencies]. 63 /// See also [installDependencies].
68 Future<PackageId> install(PackageId id) { 64 Future<PackageId> install(PackageId id) {
69 var pendingOrCompleted = _installs[id]; 65 var pendingOrCompleted = _installs[id];
70 if (pendingOrCompleted != null) return pendingOrCompleted; 66 if (pendingOrCompleted != null) return pendingOrCompleted;
71 67
72 var packageDir = join(path, id.name); 68 var packageDir = join(path, id.name);
73 var future = ensureDir(dirname(packageDir)).then((_) { 69 var future = defer(() {
74 return exists(packageDir); 70 ensureDir(dirname(packageDir));
75 }).then((exists) { 71 if (!dirExists(packageDir)) return;
76 if (!exists) return; 72
77 // TODO(nweiz): figure out when to actually delete the directory, and when 73 // TODO(nweiz): figure out when to actually delete the directory, and when
78 // we can just re-use the existing symlink. 74 // we can just re-use the existing symlink.
79 log.fine("Deleting package directory for ${id.name} before install."); 75 log.fine("Deleting package directory for ${id.name} before install.");
80 return deleteDir(packageDir); 76 return deleteDir(packageDir);
81 }).then((_) { 77 }).then((_) {
82 if (id.source.shouldCache) { 78 if (id.source.shouldCache) {
83 return cache.install(id).then( 79 return cache.install(id).then(
84 (pkg) => createPackageSymlink(id.name, pkg.dir, packageDir)); 80 (pkg) => createPackageSymlink(id.name, pkg.dir, packageDir));
85 } else { 81 } else {
86 return id.source.install(id, packageDir).then((found) { 82 return id.source.install(id, packageDir).then((found) {
87 if (found) return null; 83 if (found) return null;
88 // TODO(nweiz): More robust error-handling. 84 // TODO(nweiz): More robust error-handling.
89 throw 'Package ${id.name} not found in source "${id.source.name}".'; 85 throw 'Package ${id.name} not found in source "${id.source.name}".';
90 }); 86 });
91 } 87 }
92 }).then((_) => id.resolved); 88 }).then((_) => id.resolved);
93 89
94 _installs[id] = future; 90 _installs[id] = future;
95 91
96 return future; 92 return future;
97 } 93 }
98 94
99 /// Installs all dependencies of the [root] package to its "packages" 95 /// Installs all dependencies of the [root] package to its "packages"
100 /// directory, respecting the [LockFile] if present. Returns a [Future] that 96 /// directory, respecting the [LockFile] if present. Returns a [Future] that
101 /// completes when all dependencies are installed. 97 /// completes when all dependencies are installed.
102 Future installDependencies() { 98 Future installDependencies() {
103 return loadLockFile() 99 // Make sure all errors propagate through future.
nweiz 2013/02/01 02:05:55 I don't want to have to leave this comment on ever
Bob Nystrom 2013/02/01 23:17:21 Done.
104 .then((lockFile) => resolveVersions(cache.sources, root, lockFile)) 100 return defer(() {
105 .then(_installDependencies); 101 return resolveVersions(cache.sources, root, loadLockFile());
102 }).then(_installDependencies);
106 } 103 }
107 104
108 /// Installs the latest available versions of all dependencies of the [root] 105 /// Installs the latest available versions of all dependencies of the [root]
109 /// package to its "package" directory, writing a new [LockFile]. Returns a 106 /// package to its "package" directory, writing a new [LockFile]. Returns a
110 /// [Future] that completes when all dependencies are installed. 107 /// [Future] that completes when all dependencies are installed.
111 Future updateAllDependencies() { 108 Future updateAllDependencies() {
112 return resolveVersions(cache.sources, root, new LockFile.empty()) 109 return resolveVersions(cache.sources, root, new LockFile.empty())
113 .then(_installDependencies); 110 .then(_installDependencies);
114 } 111 }
115 112
116 /// Installs the latest available versions of [dependencies], while leaving 113 /// Installs the latest available versions of [dependencies], while leaving
117 /// other dependencies as specified by the [LockFile] if possible. Returns a 114 /// other dependencies as specified by the [LockFile] if possible. Returns a
118 /// [Future] that completes when all dependencies are installed. 115 /// [Future] that completes when all dependencies are installed.
119 Future updateDependencies(List<String> dependencies) { 116 Future updateDependencies(List<String> dependencies) {
120 return loadLockFile().then((lockFile) { 117 // Make sure all errors propagate through future.
121 var versionSolver = new VersionSolver(cache.sources, root, lockFile); 118 return defer(() {
119 var solver = new VersionSolver(cache.sources, root, loadLockFile());
122 for (var dependency in dependencies) { 120 for (var dependency in dependencies) {
123 versionSolver.useLatestVersion(dependency); 121 solver.useLatestVersion(dependency);
124 } 122 }
125 return versionSolver.solve(); 123 return solver.solve();
126 }).then(_installDependencies); 124 }).then(_installDependencies);
127 } 125 }
128 126
129 /// Removes the old packages directory, installs all dependencies listed in 127 /// Removes the old packages directory, installs all dependencies listed in
130 /// [packageVersions], and writes a [LockFile]. 128 /// [packageVersions], and writes a [LockFile].
131 Future _installDependencies(List<PackageId> packageVersions) { 129 Future _installDependencies(List<PackageId> packageVersions) {
132 return cleanDir(path).then((_) { 130 return cleanDir(path).then((_) {
133 return Future.wait(packageVersions.map((id) { 131 return Future.wait(packageVersions.map((id) {
134 if (id.isRoot) return new Future.immediate(id); 132 if (id.isRoot) return new Future.immediate(id);
135 return install(id); 133 return install(id);
136 })); 134 }));
137 }).then(_saveLockFile) 135 }).then(_saveLockFile)
138 .then(_installSelfReference) 136 .then(_installSelfReference)
139 .then(_linkSecondaryPackageDirs); 137 .then(_linkSecondaryPackageDirs);
140 } 138 }
141 139
142 /// Loads the list of concrete package versions from the `pubspec.lock`, if it 140 /// Loads the list of concrete package versions from the `pubspec.lock`, if it
143 /// exists. If it doesn't, this completes to an empty [LockFile]. 141 /// exists. If it doesn't, this completes to an empty [LockFile].
144 Future<LockFile> loadLockFile() { 142 LockFile loadLockFile() {
145 var lockFilePath = join(root.dir, 'pubspec.lock'); 143 var lockFilePath = join(root.dir, 'pubspec.lock');
146 144 if (!fileExists(lockFilePath)) return new LockFile.empty();
147 log.fine("Loading lockfile."); 145 return new LockFile.parse(readTextFile(lockFilePath), cache.sources);
148 return fileExists(lockFilePath).then((exists) {
149 if (!exists) {
150 log.fine("No lock file at $lockFilePath, creating empty one.");
151 return new LockFile.empty();
152 }
153
154 return readTextFile(lockFilePath).then((text) =>
155 new LockFile.parse(text, cache.sources));
156 });
157 } 146 }
158 147
159 /// Saves a list of concrete package versions to the `pubspec.lock` file. 148 /// Saves a list of concrete package versions to the `pubspec.lock` file.
160 Future _saveLockFile(List<PackageId> packageIds) { 149 void _saveLockFile(List<PackageId> packageIds) {
161 var lockFile = new LockFile.empty(); 150 var lockFile = new LockFile.empty();
162 for (var id in packageIds) { 151 for (var id in packageIds) {
163 if (!id.isRoot) lockFile.packages[id.name] = id; 152 if (!id.isRoot) lockFile.packages[id.name] = id;
164 } 153 }
165 154
166 var lockFilePath = join(root.dir, 'pubspec.lock'); 155 var lockFilePath = join(root.dir, 'pubspec.lock');
167 log.fine("Saving lockfile."); 156 writeTextFile(lockFilePath, lockFile.serialize());
168 return writeTextFile(lockFilePath, lockFile.serialize());
169 } 157 }
170 158
171 /// Installs a self-referential symlink in the `packages` directory that will 159 /// Installs a self-referential symlink in the `packages` directory that will
172 /// allow a package to import its own files using `package:`. 160 /// allow a package to import its own files using `package:`.
173 Future _installSelfReference(_) { 161 Future _installSelfReference(_) {
174 var linkPath = join(path, root.name); 162 // Make sure all errors propagate through future.
175 return exists(linkPath).then((exists) { 163 return defer(() {
164 var linkPath = join(path, root.name);
176 // Create the symlink if it doesn't exist. 165 // Create the symlink if it doesn't exist.
177 if (exists) return; 166 if (entryExists(linkPath)) return;
178 return ensureDir(path).then( 167 ensureDir(path);
179 (_) => createPackageSymlink(root.name, root.dir, linkPath, 168 return createPackageSymlink(root.name, root.dir, linkPath,
180 isSelfLink: true)); 169 isSelfLink: true);
181 }); 170 });
182 } 171 }
183 172
184 /// If `bin/`, `test/`, or `example/` directories exist, symlink `packages/` 173 /// If `bin/`, `test/`, or `example/` directories exist, symlink `packages/`
185 /// into them so that their entrypoints can be run. Do the same for any 174 /// into them so that their entrypoints can be run. Do the same for any
186 /// subdirectories of `test/` and `example/`. 175 /// subdirectories of `test/` and `example/`.
187 Future _linkSecondaryPackageDirs(_) { 176 Future _linkSecondaryPackageDirs(_) {
188 var binDir = join(root.dir, 'bin'); 177 var binDir = join(root.dir, 'bin');
189 var exampleDir = join(root.dir, 'example'); 178 var exampleDir = join(root.dir, 'example');
190 var testDir = join(root.dir, 'test'); 179 var testDir = join(root.dir, 'test');
191 var toolDir = join(root.dir, 'tool'); 180 var toolDir = join(root.dir, 'tool');
192 var webDir = join(root.dir, 'web'); 181 var webDir = join(root.dir, 'web');
193 return dirExists(binDir).then((exists) { 182 // Make sure all errors propagate through future.
194 if (!exists) return; 183 return defer(() {
184 if (!dirExists(binDir)) return;
195 return _linkSecondaryPackageDir(binDir); 185 return _linkSecondaryPackageDir(binDir);
196 }).then((_) => _linkSecondaryPackageDirsRecursively(exampleDir)) 186 }).then((_) => _linkSecondaryPackageDirsRecursively(exampleDir))
197 .then((_) => _linkSecondaryPackageDirsRecursively(testDir)) 187 .then((_) => _linkSecondaryPackageDirsRecursively(testDir))
198 .then((_) => _linkSecondaryPackageDirsRecursively(toolDir)) 188 .then((_) => _linkSecondaryPackageDirsRecursively(toolDir))
199 .then((_) => _linkSecondaryPackageDirsRecursively(webDir)); 189 .then((_) => _linkSecondaryPackageDirsRecursively(webDir));
200 } 190 }
201 191
202 /// Creates a symlink to the `packages` directory in [dir] and all its 192 /// Creates a symlink to the `packages` directory in [dir] and all its
203 /// subdirectories. 193 /// subdirectories.
204 Future _linkSecondaryPackageDirsRecursively(String dir) { 194 Future _linkSecondaryPackageDirsRecursively(String dir) {
205 return dirExists(dir).then((exists) { 195 // Make sure all errors propagate through future.
206 if (!exists) return; 196 return defer(() {
197 if (!dirExists(dir)) return;
207 return _linkSecondaryPackageDir(dir) 198 return _linkSecondaryPackageDir(dir)
208 .then((_) => _listDirWithoutPackages(dir)) 199 .then((_) => _listDirWithoutPackages(dir))
209 .then((files) { 200 .then((files) {
210 return Future.wait(files.map((file) { 201 return Future.wait(files.map((file) {
211 return dirExists(file).then((isDir) { 202 // Make sure all errors propagate through future.
212 if (!isDir) return; 203 return defer(() {
204 if (!dirExists(file)) return;
213 return _linkSecondaryPackageDir(file); 205 return _linkSecondaryPackageDir(file);
214 }); 206 });
215 })); 207 }));
216 }); 208 });
217 }); 209 });
218 } 210 }
219 211
220 // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed. 212 // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed.
221 /// Recursively lists the contents of [dir], excluding hidden `.DS_Store` 213 /// Recursively lists the contents of [dir], excluding hidden `.DS_Store`
222 /// files and `package` files. 214 /// files and `package` files.
223 Future<List<String>> _listDirWithoutPackages(dir) { 215 Future<List<String>> _listDirWithoutPackages(dir) {
224 return listDir(dir).then((files) { 216 return listDir(dir).then((files) {
225 return Future.wait(files.map((file) { 217 return Future.wait(files.map((file) {
226 if (basename(file) == 'packages') return new Future.immediate([]); 218 // Make sure all errors propagate through future.
227 return dirExists(file).then((isDir) { 219 return defer(() {
228 if (!isDir) return []; 220 if (basename(file) == 'packages' || !dirExists(file)) return [];
nweiz 2013/02/01 02:05:55 Moving the "packages" check in here changes the be
Bob Nystrom 2013/02/01 23:17:21 Fixed.
229 return _listDirWithoutPackages(file); 221 return _listDirWithoutPackages(file);
230 }).then((subfiles) { 222 }).then((subfiles) {
231 var fileAndSubfiles = [file]; 223 var fileAndSubfiles = [file];
232 fileAndSubfiles.addAll(subfiles); 224 fileAndSubfiles.addAll(subfiles);
233 return fileAndSubfiles; 225 return fileAndSubfiles;
234 }); 226 });
235 })); 227 }));
236 }).then(flatten); 228 }).then(flatten);
237 } 229 }
238 230
239 /// Creates a symlink to the `packages` directory in [dir] if none exists. 231 /// Creates a symlink to the `packages` directory in [dir] if none exists.
240 Future _linkSecondaryPackageDir(String dir) { 232 Future _linkSecondaryPackageDir(String dir) {
241 var to = join(dir, 'packages'); 233 // Make sure all errors propagate through future.
242 return exists(to).then((exists) { 234 return defer(() {
243 if (exists) return; 235 var to = join(dir, 'packages');
236 if (entryExists(to)) return;
244 return createSymlink(path, to); 237 return createSymlink(path, to);
245 }); 238 });
246 } 239 }
247 } 240 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698