OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:io'; | 6 import 'dart:io'; |
7 | 7 |
8 import 'package:args/args.dart'; | 8 import 'package:args/args.dart'; |
9 import 'package:path/path.dart' as path; | 9 import 'package:path/path.dart' as path; |
| 10 import 'package:stack_trace/stack_trace.dart'; |
10 | 11 |
11 import '../lib/src/command.dart'; | 12 import '../lib/src/command.dart'; |
12 import '../lib/src/exit_codes.dart' as exit_codes; | 13 import '../lib/src/exit_codes.dart' as exit_codes; |
| 14 import '../lib/src/http.dart'; |
13 import '../lib/src/io.dart'; | 15 import '../lib/src/io.dart'; |
14 import '../lib/src/log.dart' as log; | 16 import '../lib/src/log.dart' as log; |
15 import '../lib/src/sdk.dart' as sdk; | 17 import '../lib/src/sdk.dart' as sdk; |
16 import '../lib/src/utils.dart'; | 18 import '../lib/src/utils.dart'; |
17 | 19 |
18 void main(List<String> arguments) { | 20 void main(List<String> arguments) { |
19 ArgResults options; | 21 ArgResults options; |
20 | 22 |
21 try { | 23 try { |
22 options = PubCommand.pubArgParser.parse(arguments, | 24 options = PubCommand.pubArgParser.parse(arguments, |
23 allowTrailingOptions: true); | 25 allowTrailingOptions: true); |
24 } on FormatException catch (e) { | 26 } on FormatException catch (e) { |
25 log.error(e.message); | 27 log.error(e.message); |
26 log.error('Run "pub help" to see available options.'); | 28 log.error('Run "pub help" to see available options.'); |
27 flushThenExit(exit_codes.USAGE); | 29 flushThenExit(exit_codes.USAGE); |
28 return; | 30 return; |
29 } | 31 } |
30 | 32 |
31 if (options['version']) { | 33 if (options['version']) { |
32 log.message('Pub ${sdk.version}'); | 34 log.message('Pub ${sdk.version}'); |
33 return; | 35 return; |
34 } | 36 } |
35 | 37 |
36 if (options['help']) { | 38 if (options['help']) { |
37 PubCommand.printGlobalUsage(); | 39 PubCommand.printGlobalUsage(); |
38 return; | 40 return; |
39 } | 41 } |
40 | 42 |
41 if (options.command == null) { | |
42 if (options.rest.isEmpty) { | |
43 // No command was chosen. | |
44 PubCommand.printGlobalUsage(); | |
45 } else { | |
46 log.error('Could not find a command named "${options.rest[0]}".'); | |
47 log.error('Run "pub help" to see available commands.'); | |
48 flushThenExit(exit_codes.USAGE); | |
49 } | |
50 return; | |
51 } | |
52 | |
53 if (options['trace']) { | 43 if (options['trace']) { |
54 log.recordTranscript(); | 44 log.recordTranscript(); |
55 } | 45 } |
56 | 46 |
57 switch (options['verbosity']) { | 47 switch (options['verbosity']) { |
58 case 'normal': log.showNormal(); break; | 48 case 'normal': log.showNormal(); break; |
59 case 'io': log.showIO(); break; | 49 case 'io': log.showIO(); break; |
60 case 'solver': log.showSolver(); break; | 50 case 'solver': log.showSolver(); break; |
61 case 'all': log.showAll(); break; | 51 case 'all': log.showAll(); break; |
62 default: | 52 default: |
(...skipping 11 matching lines...) Expand all Loading... |
74 var cacheDir; | 64 var cacheDir; |
75 if (Platform.environment.containsKey('PUB_CACHE')) { | 65 if (Platform.environment.containsKey('PUB_CACHE')) { |
76 cacheDir = Platform.environment['PUB_CACHE']; | 66 cacheDir = Platform.environment['PUB_CACHE']; |
77 } else if (Platform.operatingSystem == 'windows') { | 67 } else if (Platform.operatingSystem == 'windows') { |
78 var appData = Platform.environment['APPDATA']; | 68 var appData = Platform.environment['APPDATA']; |
79 cacheDir = path.join(appData, 'Pub', 'Cache'); | 69 cacheDir = path.join(appData, 'Pub', 'Cache'); |
80 } else { | 70 } else { |
81 cacheDir = '${Platform.environment['HOME']}/.pub-cache'; | 71 cacheDir = '${Platform.environment['HOME']}/.pub-cache'; |
82 } | 72 } |
83 | 73 |
84 validatePlatform().then((_) { | 74 validatePlatform().then((_) => runPub(cacheDir, options, arguments)); |
85 PubCommand.commands[options.command.name].run(cacheDir, options, arguments); | 75 } |
| 76 |
| 77 /// Runs the appropriate pub command whose [arguments] have been parsed to |
| 78 /// [options] using the system cache in [cacheDir]. |
| 79 /// |
| 80 /// Handles and correctly reports any errors that occur while running. |
| 81 void runPub(String cacheDir, ArgResults options, List<String> arguments) { |
| 82 var captureStackChains = |
| 83 options['trace'] || |
| 84 options['verbose'] || |
| 85 options['verbosity'] == 'all'; |
| 86 |
| 87 captureErrors(() => invokeCommand(cacheDir, options), |
| 88 captureStackChains: captureStackChains).catchError((error, Chain chain) { |
| 89 // This is basically the top-level exception handler so that we don't |
| 90 // spew a stack trace on our users. |
| 91 var message; |
| 92 |
| 93 log.error(getErrorMessage(error)); |
| 94 log.fine("Exception type: ${error.runtimeType}"); |
| 95 |
| 96 if (options['trace'] || !isUserFacingException(error)) { |
| 97 log.error(chain.terse); |
| 98 } else { |
| 99 log.fine(chain.terse); |
| 100 } |
| 101 |
| 102 if (error is ApplicationException && error.innerError != null) { |
| 103 var message = "Wrapped exception: ${error.innerError}"; |
| 104 if (error.innerTrace != null) message = "$message\n${error.innerTrace}"; |
| 105 log.fine(message); |
| 106 } |
| 107 |
| 108 if (options['trace']) { |
| 109 log.dumpTranscript(); |
| 110 } else if (!isUserFacingException(error)) { |
| 111 log.error(""" |
| 112 This is an unexpected error. Please run |
| 113 |
| 114 pub --trace ${arguments.map((arg) => "'$arg'").join(' ')} |
| 115 |
| 116 and include the results in a bug report on http://dartbug.com/new. |
| 117 """); |
| 118 } |
| 119 |
| 120 return flushThenExit(chooseExitCode(error)); |
| 121 }).then((_) { |
| 122 // Explicitly exit on success to ensure that any dangling dart:io handles |
| 123 // don't cause the process to never terminate. |
| 124 return flushThenExit(exit_codes.SUCCESS); |
86 }); | 125 }); |
87 } | 126 } |
88 | 127 |
| 128 /// Returns the appropriate exit code for [exception], falling back on 1 if no |
| 129 /// appropriate exit code could be found. |
| 130 int chooseExitCode(exception) { |
| 131 if (exception is HttpException || exception is HttpException || |
| 132 exception is SocketException || exception is PubHttpException) { |
| 133 return exit_codes.UNAVAILABLE; |
| 134 } else if (exception is FormatException) { |
| 135 return exit_codes.DATA; |
| 136 } else if (exception is UsageException) { |
| 137 return exit_codes.USAGE; |
| 138 } else { |
| 139 return 1; |
| 140 } |
| 141 } |
| 142 |
| 143 /// Walks the command tree and runs the selected pub command. |
| 144 Future invokeCommand(String cacheDir, ArgResults mainOptions) { |
| 145 var commands = PubCommand.mainCommands; |
| 146 var command; |
| 147 var commandString = "pub"; |
| 148 var options = mainOptions; |
| 149 |
| 150 while (commands.isNotEmpty) { |
| 151 if (options.command == null) { |
| 152 if (options.rest.isEmpty) { |
| 153 if (command == null) { |
| 154 // No top-level command was chosen. |
| 155 PubCommand.printGlobalUsage(); |
| 156 return new Future.value(); |
| 157 } |
| 158 |
| 159 command.usageError('Missing subcommand for "$commandString".'); |
| 160 } else { |
| 161 if (command == null) { |
| 162 PubCommand.usageErrorWithCommands(commands, |
| 163 'Could not find a command named "${options.rest[0]}".'); |
| 164 } |
| 165 |
| 166 command.usageError('Could not find a subcommand named ' |
| 167 '"${options.rest[0]}" for "$commandString".'); |
| 168 } |
| 169 } |
| 170 |
| 171 // Step into the command. |
| 172 options = options.command; |
| 173 command = commands[options.name]; |
| 174 commands = command.subcommands; |
| 175 commandString += " ${options.name}"; |
| 176 |
| 177 if (options['help']) { |
| 178 command.printUsage(); |
| 179 return new Future.value(); |
| 180 } |
| 181 } |
| 182 |
| 183 // Make sure there aren't unexpected arguments. |
| 184 if (!command.takesArguments && options.rest.isNotEmpty) { |
| 185 command.usageError( |
| 186 'Command "${options.name}" does not take any arguments.'); |
| 187 } |
| 188 |
| 189 return syncFuture(() { |
| 190 return command.run(cacheDir, options); |
| 191 }).whenComplete(() { |
| 192 command.cache.deleteTempDir(); |
| 193 }); |
| 194 } |
| 195 |
89 /// Checks that pub is running on a supported platform. If it isn't, it prints | 196 /// Checks that pub is running on a supported platform. If it isn't, it prints |
90 /// an error message and exits. Completes when the validation is done. | 197 /// an error message and exits. Completes when the validation is done. |
91 Future validatePlatform() { | 198 Future validatePlatform() { |
92 return syncFuture(() { | 199 return syncFuture(() { |
93 if (Platform.operatingSystem != 'windows') return null; | 200 if (Platform.operatingSystem != 'windows') return null; |
94 | 201 |
95 return runProcess('ver', []).then((result) { | 202 return runProcess('ver', []).then((result) { |
96 if (result.stdout.join('\n').contains('XP')) { | 203 if (result.stdout.join('\n').contains('XP')) { |
97 log.error('Sorry, but pub is not supported on Windows XP.'); | 204 log.error('Sorry, but pub is not supported on Windows XP.'); |
98 return flushThenExit(exit_codes.USAGE); | 205 return flushThenExit(exit_codes.USAGE); |
99 } | 206 } |
100 }); | 207 }); |
101 }); | 208 }); |
102 } | 209 } |
OLD | NEW |