Index: sdk/lib/_internal/pub/bin/pub.dart |
diff --git a/sdk/lib/_internal/pub/bin/pub.dart b/sdk/lib/_internal/pub/bin/pub.dart |
index a589dcc8403a9ac31593a593ad2afb4545c1f627..ecc8318a9c686ec9342af187b5ab2d87dceb503a 100644 |
--- a/sdk/lib/_internal/pub/bin/pub.dart |
+++ b/sdk/lib/_internal/pub/bin/pub.dart |
@@ -7,9 +7,11 @@ import 'dart:io'; |
import 'package:args/args.dart'; |
import 'package:path/path.dart' as path; |
+import 'package:stack_trace/stack_trace.dart'; |
import '../lib/src/command.dart'; |
import '../lib/src/exit_codes.dart' as exit_codes; |
+import '../lib/src/http.dart'; |
import '../lib/src/io.dart'; |
import '../lib/src/log.dart' as log; |
import '../lib/src/sdk.dart' as sdk; |
@@ -38,18 +40,6 @@ void main(List<String> arguments) { |
return; |
} |
- if (options.command == null) { |
- if (options.rest.isEmpty) { |
- // No command was chosen. |
- PubCommand.printGlobalUsage(); |
- } else { |
- log.error('Could not find a command named "${options.rest[0]}".'); |
- log.error('Run "pub help" to see available commands.'); |
- flushThenExit(exit_codes.USAGE); |
- } |
- return; |
- } |
- |
if (options['trace']) { |
log.recordTranscript(); |
} |
@@ -81,8 +71,125 @@ void main(List<String> arguments) { |
cacheDir = '${Platform.environment['HOME']}/.pub-cache'; |
} |
- validatePlatform().then((_) { |
- PubCommand.commands[options.command.name].run(cacheDir, options, arguments); |
+ validatePlatform().then((_) => runPub(cacheDir, options, arguments)); |
+} |
+ |
+/// Runs the appropriate pub command whose [arguments] have been parsed to |
+/// [options] using the system cache in [cacheDir]. |
+/// |
+/// Handles and correctly reports any errors that occur while running. |
+void runPub(String cacheDir, ArgResults options, List<String> arguments) { |
+ var captureStackChains = |
+ options['trace'] || |
+ options['verbose'] || |
+ options['verbosity'] == 'all'; |
+ |
+ captureErrors(() => invokeCommand(cacheDir, options), |
+ captureStackChains: captureStackChains).catchError((error, Chain chain) { |
+ // This is basically the top-level exception handler so that we don't |
+ // spew a stack trace on our users. |
+ var message; |
+ |
+ log.error(getErrorMessage(error)); |
+ log.fine("Exception type: ${error.runtimeType}"); |
+ |
+ if (options['trace'] || !isUserFacingException(error)) { |
+ log.error(chain.terse); |
+ } else { |
+ log.fine(chain.terse); |
+ } |
+ |
+ if (error is ApplicationException && error.innerError != null) { |
+ var message = "Wrapped exception: ${error.innerError}"; |
+ if (error.innerTrace != null) message = "$message\n${error.innerTrace}"; |
+ log.fine(message); |
+ } |
+ |
+ if (options['trace']) { |
+ log.dumpTranscript(); |
+ } else if (!isUserFacingException(error)) { |
+ log.error(""" |
+This is an unexpected error. Please run |
+ |
+ pub --trace ${arguments.map((arg) => "'$arg'").join(' ')} |
+ |
+and include the results in a bug report on http://dartbug.com/new. |
+"""); |
+ } |
+ |
+ return flushThenExit(chooseExitCode(error)); |
+ }).then((_) { |
+ // Explicitly exit on success to ensure that any dangling dart:io handles |
+ // don't cause the process to never terminate. |
+ return flushThenExit(exit_codes.SUCCESS); |
+ }); |
+} |
+ |
+/// Returns the appropriate exit code for [exception], falling back on 1 if no |
+/// appropriate exit code could be found. |
+int chooseExitCode(exception) { |
+ if (exception is HttpException || exception is HttpException || |
+ exception is SocketException || exception is PubHttpException) { |
+ return exit_codes.UNAVAILABLE; |
+ } else if (exception is FormatException) { |
+ return exit_codes.DATA; |
+ } else if (exception is UsageException) { |
+ return exit_codes.USAGE; |
+ } else { |
+ return 1; |
+ } |
+} |
+ |
+/// Walks the command tree and runs the selected pub command. |
+Future invokeCommand(String cacheDir, ArgResults mainOptions) { |
+ var commands = PubCommand.mainCommands; |
+ var command; |
+ var commandString = "pub"; |
+ var options = mainOptions; |
+ |
+ while (commands.isNotEmpty) { |
+ if (options.command == null) { |
+ if (options.rest.isEmpty) { |
+ if (command == null) { |
+ // No top-level command was chosen. |
+ PubCommand.printGlobalUsage(); |
+ return new Future.value(); |
+ } |
+ |
+ command.usageError('Missing subcommand for "$commandString".'); |
+ } else { |
+ if (command == null) { |
+ PubCommand.usageErrorWithCommands(commands, |
+ 'Could not find a command named "${options.rest[0]}".'); |
+ } |
+ |
+ command.usageError('Could not find a subcommand named ' |
+ '"${options.rest[0]}" for "$commandString".'); |
+ } |
+ } |
+ |
+ // Step into the command. |
+ options = options.command; |
+ command = commands[options.name]; |
+ commands = command.subcommands; |
+ commandString += " ${options.name}"; |
+ |
+ if (options['help']) { |
+ command.printUsage(); |
+ return new Future.value(); |
+ } |
+ } |
+ |
+ // Make sure there aren't unexpected arguments. |
+ if (!command.takesArguments && options.rest.isNotEmpty) { |
+ command.usageError( |
+ 'Command "${options.name}" does not take any arguments.'); |
+ } |
+ |
+ return syncFuture(() { |
+ return command.run(cacheDir, options); |
+ }).whenComplete(() { |
+ command.cache.deleteTempDir(); |
}); |
} |