diff --git a/bin/async_compile.dart b/bin/async_compile.dart index a6f27c0fc6795230196037da8f7959ae68ed29cf..c8d56813a7cebb806ec3e0389745c85d1b377a9c 100644 --- a/bin/async_compile.dart +++ b/bin/async_compile.dart @@ -13,7 +13,7 @@ import 'package:path/path.dart' as p; /// Increment this whenever a meaningful change in the async/await compiler /// itself is landed. Bumping this will force all previously compiled files /// that were compiled against an older compiler to be recompiled. -const COMPILER_VERSION = "1"; +const COMPILER_VERSION = "2"; /// The path to pub's root directory (sdk/lib/_internal/pub) in the Dart repo. /// @@ -172,4 +172,4 @@ void _writeFile(String path, String contents) { } on IOException catch (ex) { // Do nothing. } -} \ No newline at end of file +} diff --git a/bin/pub.dart b/bin/pub.dart index 8f7fa4e32ddb192e82476c190c4953617b49973d..67cefeb189a5d5967ce539e8d71974987a9478d5 100644 --- a/bin/pub.dart +++ b/bin/pub.dart @@ -129,7 +129,7 @@ int chooseExitCode(exception) { } /// Walks the command tree and runs the selected pub command. -Future invokeCommand(String cacheDir, ArgResults mainOptions) { +Future invokeCommand(String cacheDir, ArgResults mainOptions) async { var commands = PubCommand.mainCommands; var command; var commandString = "pub"; @@ -174,26 +174,25 @@ Future invokeCommand(String cacheDir, ArgResults mainOptions) { 'Command "${options.name}" does not take any arguments.'); } - return syncFuture(() { + try { + // TODO(rnystrom): Use await here when this is fixed: + // https://github.com/dart-lang/async_await/issues/40. return command.run(cacheDir, mainOptions, options); - }).whenComplete(() { + } finally { command.cache.deleteTempDir(); - }); + } } /// Checks that pub is running on a supported platform. /// /// If it isn't, it prints an error message and exits. Completes when the /// validation is done. -Future validatePlatform() { - return syncFuture(() { - if (Platform.operatingSystem != 'windows') return null; - - return runProcess('ver', []).then((result) { - if (result.stdout.join('\n').contains('XP')) { - log.error('Sorry, but pub is not supported on Windows XP.'); - return flushThenExit(exit_codes.USAGE); - } - }); - }); +Future validatePlatform() async { + if (Platform.operatingSystem != 'windows') return; + + var result = await runProcess('ver', []); + if (result.stdout.join('\n').contains('XP')) { + log.error('Sorry, but pub is not supported on Windows XP.'); + await flushThenExit(exit_codes.USAGE); + } } diff --git a/lib/src/command/cache_repair.dart b/lib/src/command/cache_repair.dart index b16efe2f77bae181b151a9b8818fc534a14874b8..dfeab5eaa55f085668d51f093b5ed140db28f3ef 100644 --- a/lib/src/command/cache_repair.dart +++ b/lib/src/command/cache_repair.dart @@ -19,33 +19,33 @@ class CacheRepairCommand extends PubCommand { String get usage => "pub cache repair"; String get docUrl => "http://dartlang.org/tools/pub/cmd/pub-cache.html"; - Future onRun() { + Future onRun() async { var successes = 0; var failures = 0; // Repair every cached source. - return Future.forEach(cache.sources.where( - (source) => source is CachedSource), (source) { - return source.repairCachedPackages().then((results) { - successes += results.first; - failures += results.last; - }); - }).then((_) { - if (successes > 0) { - var packages = pluralize("package", successes); - log.message("Reinstalled ${log.green(successes)} $packages."); - } - - if (failures > 0) { - var packages = pluralize("package", failures); - log.message("Failed to reinstall ${log.red(failures)} $packages."); - } - - if (successes == 0 && failures == 0) { - log.message("No packages in cache, so nothing to repair."); - } - - if (failures > 0) return flushThenExit(exit_codes.UNAVAILABLE); - }); + for (var source in cache.sources) { + if (source is! CachedSource) continue; + + var results = await source.repairCachedPackages(); + successes += results.first; + failures += results.last; + } + + if (successes > 0) { + var packages = pluralize("package", successes); + log.message("Reinstalled ${log.green(successes)} $packages."); + } + + if (failures > 0) { + var packages = pluralize("package", failures); + log.message("Failed to reinstall ${log.red(failures)} $packages."); + } + + if (successes == 0 && failures == 0) { + log.message("No packages in cache, so nothing to repair."); + } + + if (failures > 0) await flushThenExit(exit_codes.UNAVAILABLE); } } diff --git a/lib/src/command/downgrade.dart b/lib/src/command/downgrade.dart index 94e8fc6c056d3442c6937f47857e9d648ae559a8..d3313a49b104dfedef48800d70473cad762811f7 100644 --- a/lib/src/command/downgrade.dart +++ b/lib/src/command/downgrade.dart @@ -28,14 +28,13 @@ class DowngradeCommand extends PubCommand { help: "Report what dependencies would change but don't change any."); } - Future onRun() { + Future onRun() async { var dryRun = commandOptions['dry-run']; - return entrypoint.acquireDependencies(SolveType.DOWNGRADE, - useLatest: commandOptions.rest, dryRun: dryRun).then((_) { - if (isOffline) { - log.warning("Warning: Downgrading when offline may not update you to " - "the oldest versions of your dependencies."); - } - }); + await entrypoint.acquireDependencies(SolveType.DOWNGRADE, + useLatest: commandOptions.rest, dryRun: dryRun); + if (isOffline) { + log.warning("Warning: Downgrading when offline may not update you to " + "the oldest versions of your dependencies."); + } } } diff --git a/lib/src/command/serve.dart b/lib/src/command/serve.dart index 0d6a84d24a802ba82dcaab95d74ae5c166b6353e..1d7db1562df5e226494c9c45b9920f8d0eec1719 100644 --- a/lib/src/command/serve.dart +++ b/lib/src/command/serve.dart @@ -79,7 +79,7 @@ class ServeCommand extends BarbackCommand { help: 'Force the use of a polling filesystem watcher.'); } - Future onRunTransformerCommand() { + Future onRunTransformerCommand() async { var port = parseInt(commandOptions['port'], 'port'); var adminPort = commandOptions['admin-port'] == null ? null : parseInt(commandOptions['admin-port'], 'admin port'); @@ -87,91 +87,87 @@ class ServeCommand extends BarbackCommand { var watcherType = commandOptions['force-poll'] ? WatcherType.POLLING : WatcherType.AUTO; - return AssetEnvironment.create(entrypoint, mode, watcherType: watcherType, - hostname: hostname, basePort: port, useDart2JS: useDart2JS) - .then((environment) { - - var directoryLength = sourceDirectories.map((dir) => dir.length) - .reduce(math.max); - - return environment.startAdminServer(adminPort).then((server) { - server.results.listen((_) { - // The admin server produces no result values. - assert(false); - }, onError: _fatalError); - - if (logAdminUrl) { - log.message("Running admin server on " - "${log.bold('http://$hostname:${server.port}')}"); - } - - // Start up the servers. We pause updates while this is happening so - // that we don't log spurious build results in the middle of listing - // out the bound servers. - environment.pauseUpdates(); - return Future.forEach(sourceDirectories, (directory) { - return _startServer(environment, directory, directoryLength); - }); - }).then((_) { - // Now that the servers are up and logged, send them to barback. - environment.barback.errors.listen((error) { - log.error(log.red("Build error:\n$error")); - }); - - environment.barback.results.listen((result) { - if (result.succeeded) { - // TODO(rnystrom): Report using growl/inotify-send where available. - log.message("Build completed ${log.green('successfully')}"); - } else { - log.message("Build completed with " - "${log.red(result.errors.length)} errors."); - } - }, onError: _fatalError); - - environment.resumeUpdates(); - return _completer.future; - }); + var environment = await AssetEnvironment.create(entrypoint, mode, + watcherType: watcherType, hostname: hostname, basePort: port, + useDart2JS: useDart2JS); + var directoryLength = sourceDirectories.map((dir) => dir.length) + .reduce(math.max); + + var server = await environment.startAdminServer(adminPort); + server.results.listen((_) { + // The admin server produces no result values. + assert(false); + }, onError: _fatalError); + + if (logAdminUrl) { + log.message("Running admin server on " + "${log.bold('http://$hostname:${server.port}')}"); + } + + // Start up the servers. We pause updates while this is happening so + // that we don't log spurious build results in the middle of listing + // out the bound servers. + environment.pauseUpdates(); + for (var directory in sourceDirectories) { + await _startServer(environment, directory, directoryLength); + } + + // Now that the servers are up and logged, send them to barback. + environment.barback.errors.listen((error) { + log.error(log.red("Build error:\n$error")); }); - } - Future _startServer(AssetEnvironment environment, String rootDirectory, - int directoryLength) { - return environment.serveDirectory(rootDirectory).then((server) { - // In release mode, strip out .dart files since all relevant ones have - // been compiled to JavaScript already. - if (mode == BarbackMode.RELEASE) { - server.allowAsset = (url) => !url.path.endsWith(".dart"); + environment.barback.results.listen((result) { + if (result.succeeded) { + // TODO(rnystrom): Report using growl/inotify-send where available. + log.message("Build completed ${log.green('successfully')}"); + } else { + log.message("Build completed with " + "${log.red(result.errors.length)} errors."); } + }, onError: _fatalError); - // Add two characters to account for "[" and "]". - var prefix = log.gray( - padRight("[${server.rootDirectory}]", directoryLength + 2)); - - server.results.listen((result) { - var buffer = new StringBuffer(); - buffer.write("$prefix "); + environment.resumeUpdates(); + await _completer.future; + } - if (result.isSuccess) { - buffer.write( - "${log.green('GET')} ${result.url.path} $_arrow ${result.id}"); + Future _startServer(AssetEnvironment environment, String rootDirectory, + int directoryLength) async { + var server = await environment.serveDirectory(rootDirectory); + // In release mode, strip out .dart files since all relevant ones have + // been compiled to JavaScript already. + if (mode == BarbackMode.RELEASE) { + server.allowAsset = (url) => !url.path.endsWith(".dart"); + } + + // Add two characters to account for "[" and "]". + var prefix = log.gray( + padRight("[${server.rootDirectory}]", directoryLength + 2)); + + server.results.listen((result) { + var buffer = new StringBuffer(); + buffer.write("$prefix "); + + if (result.isSuccess) { + buffer.write( + "${log.green('GET')} ${result.url.path} $_arrow ${result.id}"); + } else { + buffer.write("${log.red('GET')} ${result.url.path} $_arrow"); + + var error = result.error.toString(); + if (error.contains("\n")) { + buffer.write("\n${prefixLines(error)}"); } else { - buffer.write("${log.red('GET')} ${result.url.path} $_arrow"); - - var error = result.error.toString(); - if (error.contains("\n")) { - buffer.write("\n${prefixLines(error)}"); - } else { - buffer.write(" $error"); - } + buffer.write(" $error"); } + } - log.message(buffer); - }, onError: _fatalError); + log.message(buffer); + }, onError: _fatalError); - log.message("Serving ${entrypoint.root.name} " - "${padRight(server.rootDirectory, directoryLength)} " - "on ${log.bold('http://$hostname:${server.port}')}"); - }); + log.message("Serving ${entrypoint.root.name} " + "${padRight(server.rootDirectory, directoryLength)} " + "on ${log.bold('http://$hostname:${server.port}')}"); } /// Reports [error] and exits the server. diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index c42c33b385b4df610dad2dd9fd01999158681274..841a3ef6d79894c16bac2e49f1f07a61b83adeca 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart @@ -29,14 +29,13 @@ class UpgradeCommand extends PubCommand { help: "Report what dependencies would change but don't change any."); } - Future onRun() { + Future onRun() async { var dryRun = commandOptions['dry-run']; - return entrypoint.acquireDependencies(SolveType.UPGRADE, - useLatest: commandOptions.rest, dryRun: dryRun).then((_) { - if (isOffline) { - log.warning("Warning: Upgrading when offline may not update you to the " - "latest versions of your dependencies."); - } - }); + await entrypoint.acquireDependencies(SolveType.UPGRADE, + useLatest: commandOptions.rest, dryRun: dryRun); + if (isOffline) { + log.warning("Warning: Upgrading when offline may not update you to the " + "latest versions of your dependencies."); + } } } diff --git a/lib/src/executable.dart b/lib/src/executable.dart index 795139230b5f2ce36b8df38e57599edd38b57bea..3976d0d5256a561c7831bd9d50a3e7c389c3506a 100644 --- a/lib/src/executable.dart +++ b/lib/src/executable.dart @@ -30,7 +30,7 @@ import 'utils.dart'; /// /// Returns the exit code of the spawned app. Future<int> runExecutable(Entrypoint entrypoint, String package, - String executable, Iterable<String> args, {bool isGlobal: false}) { + String executable, Iterable<String> args, {bool isGlobal: false}) async { // Unless the user overrides the verbosity, we want to filter out the // normal pub output shown while loading the environment. if (log.verbosity == log.Verbosity.NORMAL) { @@ -55,24 +55,21 @@ Future<int> runExecutable(Entrypoint entrypoint, String package, executable = p.join("bin", executable); } - var environment; // TODO(nweiz): Use [packages] to only load assets from packages that the // executable might load. - return AssetEnvironment.create(entrypoint, BarbackMode.RELEASE, - useDart2JS: false).then((_environment) { - environment = _environment; - - environment.barback.errors.listen((error) { - log.error(log.red("Build error:\n$error")); - }); - - if (package == entrypoint.root.name) { - // Serve the entire root-most directory containing the entrypoint. That - // ensures that, for example, things like `import '../../utils.dart';` - // will work from within some deeply nested script. - return environment.serveDirectory(rootDir); - } + var environment = await AssetEnvironment.create(entrypoint, + BarbackMode.RELEASE, useDart2JS: false); + environment.barback.errors.listen((error) { + log.error(log.red("Build error:\n$error")); + }); + var server; + if (package == entrypoint.root.name) { + // Serve the entire root-most directory containing the entrypoint. That + // ensures that, for example, things like `import '../../utils.dart';` + // will work from within some deeply nested script. + server = await environment.serveDirectory(rootDir); + } else { // Make sure the dependency exists. var dep = entrypoint.root.immediateDependencies.firstWhere( (dep) => dep.name == package, orElse: () => null); @@ -87,46 +84,47 @@ Future<int> runExecutable(Entrypoint entrypoint, String package, } // For other packages, always use the "bin" directory. - return environment.servePackageBinDirectory(package); - }).then((server) { - // Try to make sure the entrypoint script exists (or is generated) before - // we spawn the process to run it. - var assetPath = "${p.url.joinAll(p.split(executable))}.dart"; - var id = new AssetId(server.package, assetPath); - return environment.barback.getAssetById(id).then((_) { - var vmArgs = []; - - // Run in checked mode. - // TODO(rnystrom): Make this configurable. - vmArgs.add("--checked"); - - // Get the URL of the executable, relative to the server's root directory. - var relativePath = p.url.relative(assetPath, - from: p.url.joinAll(p.split(server.rootDirectory))); - vmArgs.add(server.url.resolve(relativePath).toString()); - vmArgs.addAll(args); - - return Process.start(Platform.executable, vmArgs).then((process) { - // Note: we're not using process.std___.pipe(std___) here because - // that prevents pub from also writing to the output streams. - process.stderr.listen(stderr.add); - process.stdout.listen(stdout.add); - stdin.listen(process.stdin.add); - - return process.exitCode; - }); - }).catchError((error, stackTrace) { - if (error is! AssetNotFoundException) throw error; - - var message = "Could not find ${log.bold(executable + ".dart")}"; - if (package != entrypoint.root.name) { - message += " in package ${log.bold(server.package)}"; - } + server = await environment.servePackageBinDirectory(package); + } + + // Try to make sure the entrypoint script exists (or is generated) before + // we spawn the process to run it. + var assetPath = "${p.url.joinAll(p.split(executable))}.dart"; + var id = new AssetId(server.package, assetPath); + // TODO(rnystrom): Use try/catch here when + // https://github.com/dart-lang/async_await/issues/4 is fixed. + return environment.barback.getAssetById(id).then((_) async { + var vmArgs = []; + + // Run in checked mode. + // TODO(rnystrom): Make this configurable. + vmArgs.add("--checked"); + + // Get the URL of the executable, relative to the server's root directory. + var relativePath = p.url.relative(assetPath, + from: p.url.joinAll(p.split(server.rootDirectory))); + vmArgs.add(server.url.resolve(relativePath).toString()); + vmArgs.addAll(args); + + var process = await Process.start(Platform.executable, vmArgs); + // Note: we're not using process.std___.pipe(std___) here because + // that prevents pub from also writing to the output streams. + process.stderr.listen(stderr.add); + process.stdout.listen(stdout.add); + stdin.listen(process.stdin.add); + + return process.exitCode; + }).catchError((error, stackTrace) { + if (error is! AssetNotFoundException) throw error; + + var message = "Could not find ${log.bold(executable + ".dart")}"; + if (package != entrypoint.root.name) { + message += " in package ${log.bold(server.package)}"; + } - log.error("$message."); - log.fine(new Chain.forTrace(stackTrace)); - return exit_codes.NO_INPUT; - }); + log.error("$message."); + log.fine(new Chain.forTrace(stackTrace)); + return exit_codes.NO_INPUT; }); } @@ -136,45 +134,42 @@ Future<int> runExecutable(Entrypoint entrypoint, String package, /// Returns the snapshot's exit code. /// /// This doesn't do any validation of the snapshot's SDK version. -Future<int> runSnapshot(String path, Iterable<String> args) { +Future<int> runSnapshot(String path, Iterable<String> args) async { var vmArgs = [path]..addAll(args); - return Process.start(Platform.executable, vmArgs).then((process) { - // Note: we're not using process.std___.pipe(std___) here because - // that prevents pub from also writing to the output streams. - process.stderr.listen(stderr.add); - process.stdout.listen(stdout.add); - stdin.listen(process.stdin.add); + var process = await Process.start(Platform.executable, vmArgs); + // Note: we're not using process.std___.pipe(std___) here because + // that prevents pub from also writing to the output streams. + process.stderr.listen(stderr.add); + process.stdout.listen(stdout.add); + stdin.listen(process.stdin.add); - return process.exitCode; - }); + return process.exitCode; } /// Runs the executable snapshot at [snapshotPath]. -Future _runCachedExecutable(Entrypoint entrypoint, String snapshotPath, - List<String> args) { - return syncFuture(() { - // If the snapshot was compiled with a different SDK version, we need to - // recompile it. - var sdkVersionPath = p.join(".pub", "bin", "sdk-version"); - if (fileExists(sdkVersionPath) && - readTextFile(sdkVersionPath) == "${sdk.version}\n") { - return null; - } - +Future<int> _runCachedExecutable(Entrypoint entrypoint, String snapshotPath, + List<String> args) async { + // If the snapshot was compiled with a different SDK version, we need to + // recompile it. + var sdkVersionPath = p.join(".pub", "bin", "sdk-version"); + if (!fileExists(sdkVersionPath) || + readTextFile(sdkVersionPath) != "${sdk.version}\n") { log.fine("Precompiled executables are out of date."); - return entrypoint.precompileExecutables(); - }).then((_) { - var vmArgs = ["--checked", snapshotPath]..addAll(args); - - return Process.start(Platform.executable, vmArgs).then((process) { - // Note: we're not using process.std___.pipe(std___) here because - // that prevents pub from also writing to the output streams. - process.stderr.listen(stderr.add); - process.stdout.listen(stdout.add); - stdin.listen(process.stdin.add); - - return process.exitCode; - }); - }); + await entrypoint.precompileExecutables(); + } + + // TODO(rnystrom): Use cascade here when async_await compiler supports it. + // See: https://github.com/dart-lang/async_await/issues/26. + var vmArgs = ["--checked", snapshotPath]; + vmArgs.addAll(args); + + var process = await Process.start(Platform.executable, vmArgs); + // Note: we're not using process.std___.pipe(std___) here because + // that prevents pub from also writing to the output streams. + process.stderr.listen(stderr.add); + process.stdout.listen(stdout.add); + stdin.listen(process.stdin.add); + + return process.exitCode; } diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index 08f9fb1a544e57b55396aa90669b740c8950313b..be8614801fb980f1959bc1c54ecccaeb934b0381 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -68,19 +68,18 @@ class GlobalPackages { /// Caches the package located in the Git repository [repo] and makes it the /// active global version. - Future activateGit(String repo) { + Future activateGit(String repo) async { var source = cache.sources["git"] as GitSource; - return source.getPackageNameFromRepo(repo).then((name) { - // Call this just to log what the current active package is, if any. - _describeActive(name); - - // TODO(nweiz): Add some special handling for git repos that contain path - // dependencies. Their executables shouldn't be cached, and there should - // be a mechanism for redoing dependency resolution if a path pubspec has - // changed (see also issue 20499). - return _installInCache( - new PackageDep(name, "git", VersionConstraint.any, repo)); - }); + var name = await source.getPackageNameFromRepo(repo); + // Call this just to log what the current active package is, if any. + _describeActive(name); + + // TODO(nweiz): Add some special handling for git repos that contain path + // dependencies. Their executables shouldn't be cached, and there should + // be a mechanism for redoing dependency resolution if a path pubspec has + // changed (see also issue 20499). + await _installInCache( + new PackageDep(name, "git", VersionConstraint.any, repo)); } /// Finds the latest version of the hosted package with [name] that matches @@ -91,32 +90,31 @@ class GlobalPackages { } /// Makes the local package at [path] globally active. - Future activatePath(String path) { + Future activatePath(String path) async { var entrypoint = new Entrypoint(path, cache); // Get the package's dependencies. - return entrypoint.ensureLockFileIsUpToDate().then((_) { - var name = entrypoint.root.name; + await entrypoint.ensureLockFileIsUpToDate(); + var name = entrypoint.root.name; - // Call this just to log what the current active package is, if any. - _describeActive(name); + // Call this just to log what the current active package is, if any. + _describeActive(name); - // Write a lockfile that points to the local package. - var fullPath = canonicalize(entrypoint.root.dir); - var id = new PackageId(name, "path", entrypoint.root.version, - PathSource.describePath(fullPath)); + // Write a lockfile that points to the local package. + var fullPath = canonicalize(entrypoint.root.dir); + var id = new PackageId(name, "path", entrypoint.root.version, + PathSource.describePath(fullPath)); - // TODO(rnystrom): Look in "bin" and display list of binaries that - // user can run. - _writeLockFile(name, new LockFile([id])); + // TODO(rnystrom): Look in "bin" and display list of binaries that + // user can run. + _writeLockFile(name, new LockFile([id])); - var binDir = p.join(_directory, name, 'bin'); - if (dirExists(binDir)) deleteEntry(binDir); - }); + var binDir = p.join(_directory, name, 'bin'); + if (dirExists(binDir)) deleteEntry(binDir); } /// Installs the package [dep] and its dependencies into the system cache. - Future _installInCache(PackageDep dep) { + Future _installInCache(PackageDep dep) async { var source = cache.sources[dep.source]; // Create a dummy package with just [dep] so we can do resolution on it. @@ -124,28 +122,26 @@ class GlobalPackages { dependencies: [dep], sources: cache.sources)); // Resolve it and download its dependencies. - return resolveVersions(SolveType.GET, cache.sources, root).then((result) { - if (!result.succeeded) { - // If the package specified by the user doesn't exist, we want to - // surface that as a [DataError] with the associated exit code. - if (result.error.package != dep.name) throw result.error; - if (result.error is NoVersionException) dataError(result.error.message); - throw result.error; - } - result.showReport(SolveType.GET); - - // Make sure all of the dependencies are locally installed. - return Future.wait(result.packages.map(_cacheDependency)).then((ids) { - var lockFile = new LockFile(ids); - - // Load the package graph from [result] so we don't need to re-parse all - // the pubspecs. - return new Entrypoint.inMemory(root, lockFile, cache) - .loadPackageGraph(result) - .then((graph) => _precompileExecutables(graph.entrypoint, dep.name)) - .then((_) => _writeLockFile(dep.name, lockFile)); - }); - }); + var result = await resolveVersions(SolveType.GET, cache.sources, root); + if (!result.succeeded) { + // If the package specified by the user doesn't exist, we want to + // surface that as a [DataError] with the associated exit code. + if (result.error.package != dep.name) throw result.error; + if (result.error is NoVersionException) dataError(result.error.message); + throw result.error; + } + result.showReport(SolveType.GET); + + // Make sure all of the dependencies are locally installed. + var ids = await Future.wait(result.packages.map(_cacheDependency)); + var lockFile = new LockFile(ids); + + // Load the package graph from [result] so we don't need to re-parse all + // the pubspecs. + var graph = await new Entrypoint.inMemory(root, lockFile, cache) + .loadPackageGraph(result); + await _precompileExecutables(graph.entrypoint, dep.name); + _writeLockFile(dep.name, lockFile); } /// Precompiles the executables for [package] and saves them in the global @@ -171,15 +167,14 @@ class GlobalPackages { /// Downloads [id] into the system cache if it's a cached package. /// /// Returns the resolved [PackageId] for [id]. - Future<PackageId> _cacheDependency(PackageId id) { + Future<PackageId> _cacheDependency(PackageId id) async { var source = cache.sources[id.source]; - return syncFuture(() { - if (id.isRoot) return null; - if (source is! CachedSource) return null; + if (!id.isRoot && source is CachedSource) { + await source.downloadToSystemCache(id); + } - return source.downloadToSystemCache(id); - }).then((_) => source.resolveId(id)); + return source.resolveId(id); } /// Finishes activating package [package] by saving [lockFile] in the cache. @@ -249,6 +244,8 @@ class GlobalPackages { /// /// Returns an [Entrypoint] loaded with the active package if found. Future<Entrypoint> find(String name) { + // TODO(rnystrom): Use async/await here when on __ catch is supported. + // See: https://github.com/dart-lang/async_await/issues/27 return syncFuture(() { var lockFilePath = _getLockFilePath(name); var lockFile;