 Chromium Code Reviews
 Chromium Code Reviews Issue 138723005:
  Support subcommands in pub and pub help.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
    
  
    Issue 138723005:
  Support subcommands in pub and pub help.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart| 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..d0bfcba3e2f758e9535303f86cce05ac580d14bb 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,130 @@ 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 parsed to [options] from [arguments] using | 
| 
nweiz
2014/02/04 01:24:29
I'm not sure what "parsed to [options] from [argum
 
Bob Nystrom
2014/02/06 00:06:31
Reworded.
 | 
| +/// the system cache in [cacheDir]. | 
| +/// | 
| +/// Handles and correctly reports any errors that occur while running. | 
| +void runPub(String cacheDir, ArgResults options, List<String> arguments) { | 
| + handleError(error, Chain chain) { | 
| 
nweiz
2014/02/04 01:24:29
I don't like how this is a separate named function
 
Bob Nystrom
2014/02/06 00:06:31
Done.
 | 
| + // 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)); | 
| + } | 
| + | 
| + var captureStackChains = | 
| + options['trace'] || | 
| + options['verbose'] || | 
| + options['verbosity'] == 'all'; | 
| + | 
| + captureErrors(() { | 
| + return invokeCommand(cacheDir, options); | 
| 
nweiz
2014/02/04 01:24:29
Nit: =>
 
Bob Nystrom
2014/02/06 00:06:31
Done.
 | 
| + }, captureStackChains: captureStackChains).catchError(handleError) | 
| + .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 null; | 
| 
nweiz
2014/02/04 01:24:29
I know it works in this case, but I don't like ret
 
Bob Nystrom
2014/02/06 00:06:31
Done.
 | 
| + } | 
| + | 
| + command.usageError('Missing subcommand for "$commandString".'); | 
| + } else { | 
| + if (command == null) { | 
| + PubCommand.commandUsageError(commands, | 
| + 'Could not find a command named "${options.rest[0]}".'); | 
| + } | 
| + | 
| + command.usageError( | 
| + 'Could not find a subcommand named "${options.rest[0]}" ' | 
| + 'for "$commandString".'); | 
| 
nweiz
2014/02/04 01:24:29
Nit: when writing multiline strings like this, I l
 
Bob Nystrom
2014/02/06 00:06:31
Moved the first line up. I think that makes it con
 | 
| + } | 
| + } | 
| + | 
| + // Step into the command. | 
| + options = options.command; | 
| + command = commands[options.name]; | 
| + commands = command.subcommands; | 
| + commandString += " ${options.name}"; | 
| + | 
| + if (options['help']) { | 
| + command.printUsage(); | 
| + return null; | 
| + } | 
| + } | 
| + | 
| + // 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(); | 
| }); | 
| } |