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

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: Fix after merge. 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
« no previous file with comments | « utils/pub/command_uploader.dart ('k') | utils/pub/error_group.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 22 matching lines...) Expand all
33 class Entrypoint { 33 class Entrypoint {
34 /// The root package this entrypoint is associated with. 34 /// The root package this entrypoint is associated with.
35 final Package root; 35 final Package root;
36 36
37 /// The system-wide cache which caches packages that need to be fetched over 37 /// The system-wide cache which caches packages that need to be fetched over
38 /// the network. 38 /// the network.
39 final SystemCache cache; 39 final SystemCache cache;
40 40
41 /// Packages which are either currently being asynchronously installed to the 41 /// Packages which are either currently being asynchronously installed to the
42 /// directory, or have already been installed. 42 /// directory, or have already been installed.
43 final Map<PackageId, Future<PackageId>> _installs; 43 final _installs = new Map<PackageId, Future<PackageId>>();
44
45 Entrypoint(this.root, this.cache)
46 : _installs = new Map<PackageId, Future<PackageId>>();
47 44
48 /// Loads the entrypoint from a package at [rootDir]. 45 /// Loads the entrypoint from a package at [rootDir].
49 static Future<Entrypoint> load(String rootDir, SystemCache cache) { 46 Entrypoint(String rootDir, SystemCache cache)
50 return Package.load(null, rootDir, cache.sources).then((package) => 47 : root = new Package(null, rootDir, cache.sources),
51 new Entrypoint(package, cache)); 48 cache = cache;
52 }
53 49
54 // TODO(rnystrom): Make this path configurable. 50 // TODO(rnystrom): Make this path configurable.
55 /// The path to this "packages" directory. 51 /// The path to this "packages" directory.
56 String get path => join(root.dir, 'packages'); 52 String get path => join(root.dir, 'packages');
57 53
58 /// Ensures that the package identified by [id] is installed to the directory. 54 /// Ensures that the package identified by [id] is installed to the directory.
59 /// Returns the resolved [PackageId]. 55 /// Returns the resolved [PackageId].
60 /// 56 ///
61 /// If this completes successfully, the package is guaranteed to be importable 57 /// If this completes successfully, the package is guaranteed to be importable
62 /// using the `package:` scheme. 58 /// using the `package:` scheme.
63 /// 59 ///
64 /// This will automatically install the package to the system-wide cache as 60 /// This will automatically install the package to the system-wide cache as
65 /// well if it requires network access to retrieve (specifically, if 61 /// well if it requires network access to retrieve (specifically, if
66 /// `id.source.shouldCache` is true). 62 /// `id.source.shouldCache` is true).
67 /// 63 ///
68 /// See also [installDependencies]. 64 /// See also [installDependencies].
69 Future<PackageId> install(PackageId id) { 65 Future<PackageId> install(PackageId id) {
70 var pendingOrCompleted = _installs[id]; 66 var pendingOrCompleted = _installs[id];
71 if (pendingOrCompleted != null) return pendingOrCompleted; 67 if (pendingOrCompleted != null) return pendingOrCompleted;
72 68
73 var packageDir = join(path, id.name); 69 var packageDir = join(path, id.name);
74 var future = ensureDir(dirname(packageDir)).then((_) { 70 var future = defer(() {
75 return exists(packageDir); 71 ensureDir(dirname(packageDir));
76 }).then((exists) { 72 if (!dirExists(packageDir)) return;
77 if (!exists) return; 73
78 // TODO(nweiz): figure out when to actually delete the directory, and when 74 // TODO(nweiz): figure out when to actually delete the directory, and when
79 // we can just re-use the existing symlink. 75 // we can just re-use the existing symlink.
80 log.fine("Deleting package directory for ${id.name} before install."); 76 log.fine("Deleting package directory for ${id.name} before install.");
81 return deleteDir(packageDir); 77 return deleteDir(packageDir);
82 }).then((_) { 78 }).then((_) {
83 if (id.source.shouldCache) { 79 if (id.source.shouldCache) {
84 return cache.install(id).then( 80 return cache.install(id).then(
85 (pkg) => createPackageSymlink(id.name, pkg.dir, packageDir)); 81 (pkg) => createPackageSymlink(id.name, pkg.dir, packageDir));
86 } else { 82 } else {
87 return id.source.install(id, packageDir).then((found) { 83 return id.source.install(id, packageDir).then((found) {
88 if (found) return null; 84 if (found) return null;
89 // TODO(nweiz): More robust error-handling. 85 // TODO(nweiz): More robust error-handling.
90 throw 'Package ${id.name} not found in source "${id.source.name}".'; 86 throw 'Package ${id.name} not found in source "${id.source.name}".';
91 }); 87 });
92 } 88 }
93 }).then((_) => id.resolved); 89 }).then((_) => id.resolved);
94 90
95 _installs[id] = future; 91 _installs[id] = future;
96 92
97 return future; 93 return future;
98 } 94 }
99 95
100 /// Installs all dependencies of the [root] package to its "packages" 96 /// Installs all dependencies of the [root] package to its "packages"
101 /// directory, respecting the [LockFile] if present. Returns a [Future] that 97 /// directory, respecting the [LockFile] if present. Returns a [Future] that
102 /// completes when all dependencies are installed. 98 /// completes when all dependencies are installed.
103 Future installDependencies() { 99 Future installDependencies() {
104 return loadLockFile() 100 return defer(() {
105 .then((lockFile) => resolveVersions(cache.sources, root, lockFile)) 101 return resolveVersions(cache.sources, root, loadLockFile());
106 .then(_installDependencies); 102 }).then(_installDependencies);
107 } 103 }
108 104
109 /// Installs the latest available versions of all dependencies of the [root] 105 /// Installs the latest available versions of all dependencies of the [root]
110 /// package to its "package" directory, writing a new [LockFile]. Returns a 106 /// package to its "package" directory, writing a new [LockFile]. Returns a
111 /// [Future] that completes when all dependencies are installed. 107 /// [Future] that completes when all dependencies are installed.
112 Future updateAllDependencies() { 108 Future updateAllDependencies() {
113 return resolveVersions(cache.sources, root, new LockFile.empty()) 109 return resolveVersions(cache.sources, root, new LockFile.empty())
114 .then(_installDependencies); 110 .then(_installDependencies);
115 } 111 }
116 112
117 /// Installs the latest available versions of [dependencies], while leaving 113 /// Installs the latest available versions of [dependencies], while leaving
118 /// other dependencies as specified by the [LockFile] if possible. Returns a 114 /// other dependencies as specified by the [LockFile] if possible. Returns a
119 /// [Future] that completes when all dependencies are installed. 115 /// [Future] that completes when all dependencies are installed.
120 Future updateDependencies(List<String> dependencies) { 116 Future updateDependencies(List<String> dependencies) {
121 return loadLockFile().then((lockFile) { 117 return defer(() {
122 var versionSolver = new VersionSolver(cache.sources, root, lockFile); 118 var solver = new VersionSolver(cache.sources, root, loadLockFile());
123 for (var dependency in dependencies) { 119 for (var dependency in dependencies) {
124 versionSolver.useLatestVersion(dependency); 120 solver.useLatestVersion(dependency);
125 } 121 }
126 return versionSolver.solve(); 122 return solver.solve();
127 }).then(_installDependencies); 123 }).then(_installDependencies);
128 } 124 }
129 125
130 /// Removes the old packages directory, installs all dependencies listed in 126 /// Removes the old packages directory, installs all dependencies listed in
131 /// [packageVersions], and writes a [LockFile]. 127 /// [packageVersions], and writes a [LockFile].
132 Future _installDependencies(List<PackageId> packageVersions) { 128 Future _installDependencies(List<PackageId> packageVersions) {
133 return cleanDir(path).then((_) { 129 return cleanDir(path).then((_) {
134 return Future.wait(packageVersions.map((id) { 130 return Future.wait(packageVersions.map((id) {
135 if (id.isRoot) return new Future.immediate(id); 131 if (id.isRoot) return new Future.immediate(id);
136 return install(id); 132 return install(id);
137 })); 133 }));
138 }).then(_saveLockFile) 134 }).then(_saveLockFile)
139 .then(_installSelfReference) 135 .then(_installSelfReference)
140 .then(_linkSecondaryPackageDirs); 136 .then(_linkSecondaryPackageDirs);
141 } 137 }
142 138
143 /// Traverses the root's package dependency graph and loads each of the 139 /// Traverses the root's package dependency graph and loads each of the
144 /// reached packages. This should only be called after the lockfile has been 140 /// reached packages. This should only be called after the lockfile has been
145 /// successfully generated. 141 /// successfully generated.
146 Future<List<Package>> walkDependencies() { 142 Future<List<Package>> walkDependencies() {
147 return loadLockFile().then((lockFile) { 143 return defer(() {
144 var lockFile = loadLockFile();
148 var group = new FutureGroup<Package>(); 145 var group = new FutureGroup<Package>();
149 var visited = new Set<String>(); 146 var visited = new Set<String>();
150 147
151 // Include the root package in the results. 148 // Include the root package in the results.
152 group.add(new Future.immediate(root)); 149 group.add(new Future.immediate(root));
153 150
154 visitPackage(Package package) { 151 visitPackage(Package package) {
155 for (var ref in package.dependencies) { 152 for (var ref in package.dependencies) {
156 if (visited.contains(ref.name)) continue; 153 if (visited.contains(ref.name)) continue;
157 154
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 "You may be able to resolve this by upgrading to the latest Dart " 189 "You may be able to resolve this by upgrading to the latest Dart "
193 "SDK\n" 190 "SDK\n"
194 "or adding a version constraint to use an older version of a " 191 "or adding a version constraint to use an older version of a "
195 "package."); 192 "package.");
196 } 193 }
197 }); 194 });
198 } 195 }
199 196
200 /// Loads the list of concrete package versions from the `pubspec.lock`, if it 197 /// Loads the list of concrete package versions from the `pubspec.lock`, if it
201 /// exists. If it doesn't, this completes to an empty [LockFile]. 198 /// exists. If it doesn't, this completes to an empty [LockFile].
202 Future<LockFile> loadLockFile() { 199 LockFile loadLockFile() {
203 var lockFilePath = join(root.dir, 'pubspec.lock'); 200 var lockFilePath = join(root.dir, 'pubspec.lock');
204 201 if (!fileExists(lockFilePath)) return new LockFile.empty();
205 log.fine("Loading lockfile."); 202 return new LockFile.parse(readTextFile(lockFilePath), cache.sources);
206 return fileExists(lockFilePath).then((exists) {
207 if (!exists) {
208 log.fine("No lock file at $lockFilePath, creating empty one.");
209 return new LockFile.empty();
210 }
211
212 return readTextFile(lockFilePath).then((text) =>
213 new LockFile.parse(text, cache.sources));
214 });
215 } 203 }
216 204
217 /// Saves a list of concrete package versions to the `pubspec.lock` file. 205 /// Saves a list of concrete package versions to the `pubspec.lock` file.
218 Future _saveLockFile(List<PackageId> packageIds) { 206 void _saveLockFile(List<PackageId> packageIds) {
219 var lockFile = new LockFile.empty(); 207 var lockFile = new LockFile.empty();
220 for (var id in packageIds) { 208 for (var id in packageIds) {
221 if (!id.isRoot) lockFile.packages[id.name] = id; 209 if (!id.isRoot) lockFile.packages[id.name] = id;
222 } 210 }
223 211
224 var lockFilePath = join(root.dir, 'pubspec.lock'); 212 var lockFilePath = join(root.dir, 'pubspec.lock');
225 log.fine("Saving lockfile."); 213 writeTextFile(lockFilePath, lockFile.serialize());
226 return writeTextFile(lockFilePath, lockFile.serialize());
227 } 214 }
228 215
229 /// Installs a self-referential symlink in the `packages` directory that will 216 /// Installs a self-referential symlink in the `packages` directory that will
230 /// allow a package to import its own files using `package:`. 217 /// allow a package to import its own files using `package:`.
231 Future _installSelfReference(_) { 218 Future _installSelfReference(_) {
232 var linkPath = join(path, root.name); 219 return defer(() {
233 return exists(linkPath).then((exists) { 220 var linkPath = join(path, root.name);
234 // Create the symlink if it doesn't exist. 221 // Create the symlink if it doesn't exist.
235 if (exists) return; 222 if (entryExists(linkPath)) return;
236 return ensureDir(path).then( 223 ensureDir(path);
237 (_) => createPackageSymlink(root.name, root.dir, linkPath, 224 return createPackageSymlink(root.name, root.dir, linkPath,
238 isSelfLink: true)); 225 isSelfLink: true);
239 }); 226 });
240 } 227 }
241 228
242 /// If `bin/`, `test/`, or `example/` directories exist, symlink `packages/` 229 /// If `bin/`, `test/`, or `example/` directories exist, symlink `packages/`
243 /// into them so that their entrypoints can be run. Do the same for any 230 /// into them so that their entrypoints can be run. Do the same for any
244 /// subdirectories of `test/` and `example/`. 231 /// subdirectories of `test/` and `example/`.
245 Future _linkSecondaryPackageDirs(_) { 232 Future _linkSecondaryPackageDirs(_) {
246 var binDir = join(root.dir, 'bin'); 233 var binDir = join(root.dir, 'bin');
247 var exampleDir = join(root.dir, 'example'); 234 var exampleDir = join(root.dir, 'example');
248 var testDir = join(root.dir, 'test'); 235 var testDir = join(root.dir, 'test');
249 var toolDir = join(root.dir, 'tool'); 236 var toolDir = join(root.dir, 'tool');
250 var webDir = join(root.dir, 'web'); 237 var webDir = join(root.dir, 'web');
251 return dirExists(binDir).then((exists) { 238 return defer(() {
252 if (!exists) return; 239 if (!dirExists(binDir)) return;
253 return _linkSecondaryPackageDir(binDir); 240 return _linkSecondaryPackageDir(binDir);
254 }).then((_) => _linkSecondaryPackageDirsRecursively(exampleDir)) 241 }).then((_) => _linkSecondaryPackageDirsRecursively(exampleDir))
255 .then((_) => _linkSecondaryPackageDirsRecursively(testDir)) 242 .then((_) => _linkSecondaryPackageDirsRecursively(testDir))
256 .then((_) => _linkSecondaryPackageDirsRecursively(toolDir)) 243 .then((_) => _linkSecondaryPackageDirsRecursively(toolDir))
257 .then((_) => _linkSecondaryPackageDirsRecursively(webDir)); 244 .then((_) => _linkSecondaryPackageDirsRecursively(webDir));
258 } 245 }
259 246
260 /// Creates a symlink to the `packages` directory in [dir] and all its 247 /// Creates a symlink to the `packages` directory in [dir] and all its
261 /// subdirectories. 248 /// subdirectories.
262 Future _linkSecondaryPackageDirsRecursively(String dir) { 249 Future _linkSecondaryPackageDirsRecursively(String dir) {
263 return dirExists(dir).then((exists) { 250 return defer(() {
264 if (!exists) return; 251 if (!dirExists(dir)) return;
265 return _linkSecondaryPackageDir(dir) 252 return _linkSecondaryPackageDir(dir)
266 .then((_) => _listDirWithoutPackages(dir)) 253 .then((_) => _listDirWithoutPackages(dir))
267 .then((files) { 254 .then((files) {
268 return Future.wait(files.map((file) { 255 return Future.wait(files.map((file) {
269 return dirExists(file).then((isDir) { 256 return defer(() {
270 if (!isDir) return; 257 if (!dirExists(file)) return;
271 return _linkSecondaryPackageDir(file); 258 return _linkSecondaryPackageDir(file);
272 }); 259 });
273 })); 260 }));
274 }); 261 });
275 }); 262 });
276 } 263 }
277 264
278 // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed. 265 // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed.
279 /// Recursively lists the contents of [dir], excluding hidden `.DS_Store` 266 /// Recursively lists the contents of [dir], excluding hidden `.DS_Store`
280 /// files and `package` files. 267 /// files and `package` files.
281 Future<List<String>> _listDirWithoutPackages(dir) { 268 Future<List<String>> _listDirWithoutPackages(dir) {
282 return listDir(dir).then((files) { 269 return listDir(dir).then((files) {
283 return Future.wait(files.map((file) { 270 return Future.wait(files.map((file) {
284 if (basename(file) == 'packages') return new Future.immediate([]); 271 if (basename(file) == 'packages') return new Future.immediate([]);
285 return dirExists(file).then((isDir) { 272 return defer(() {
286 if (!isDir) return []; 273 if (!dirExists(file)) return [];
287 return _listDirWithoutPackages(file); 274 return _listDirWithoutPackages(file);
288 }).then((subfiles) { 275 }).then((subfiles) {
289 var fileAndSubfiles = [file]; 276 var fileAndSubfiles = [file];
290 fileAndSubfiles.addAll(subfiles); 277 fileAndSubfiles.addAll(subfiles);
291 return fileAndSubfiles; 278 return fileAndSubfiles;
292 }); 279 });
293 })); 280 }));
294 }).then(flatten); 281 }).then(flatten);
295 } 282 }
296 283
297 /// Creates a symlink to the `packages` directory in [dir] if none exists. 284 /// Creates a symlink to the `packages` directory in [dir] if none exists.
298 Future _linkSecondaryPackageDir(String dir) { 285 Future _linkSecondaryPackageDir(String dir) {
299 var to = join(dir, 'packages'); 286 return defer(() {
300 return exists(to).then((exists) { 287 var to = join(dir, 'packages');
301 if (exists) return; 288 if (entryExists(to)) return;
302 return createSymlink(path, to); 289 return createSymlink(path, to);
303 }); 290 });
304 } 291 }
305 } 292 }
OLDNEW
« no previous file with comments | « utils/pub/command_uploader.dart ('k') | utils/pub/error_group.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698