OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 library analyzer_cli.src.options; |
| 6 |
| 7 import 'dart:io'; |
| 8 |
| 9 import 'package:analyzer_cli/src/driver.dart'; |
| 10 import 'package:args/args.dart'; |
| 11 import 'package:cli_util/cli_util.dart' show getSdkDir; |
| 12 |
| 13 const _binaryName = 'dartanalyzer'; |
| 14 |
| 15 /// Shared exit handler. |
| 16 /// |
| 17 /// *Visible for testing.* |
| 18 ExitHandler exitHandler = exit; |
| 19 |
| 20 /// Print the given message to stderr and exit with the given [exitCode] |
| 21 void printAndFail(String message, {int exitCode: 15}) { |
| 22 errorSink.writeln(message); |
| 23 exitHandler(exitCode); |
| 24 } |
| 25 |
| 26 /// Exit handler. |
| 27 /// |
| 28 /// *Visible for testing.* |
| 29 typedef void ExitHandler(int code); |
| 30 |
| 31 /// Analyzer commandline configuration options. |
| 32 class CommandLineOptions { |
| 33 /// The path to an analysis options file |
| 34 final String analysisOptionsFile; |
| 35 |
| 36 /// The path to the dart SDK |
| 37 String dartSdkPath; |
| 38 |
| 39 /// A table mapping the names of defined variables to their values. |
| 40 final Map<String, String> definedVariables; |
| 41 |
| 42 /// Whether to report hints |
| 43 final bool disableHints; |
| 44 |
| 45 /// Whether to display version information |
| 46 final bool displayVersion; |
| 47 |
| 48 /// Whether to enable null-aware operators (DEP 9). |
| 49 final bool enableNullAwareOperators; |
| 50 |
| 51 /// Whether to strictly follow the specification when generating warnings on |
| 52 /// "call" methods (fixes dartbug.com/21938). |
| 53 final bool enableStrictCallChecks; |
| 54 |
| 55 /// Whether to relax restrictions on mixins (DEP 34). |
| 56 final bool enableSuperMixins; |
| 57 |
| 58 /// Whether to treat type mismatches found during constant evaluation as |
| 59 /// errors. |
| 60 final bool enableTypeChecks; |
| 61 |
| 62 /// Whether to treat hints as fatal |
| 63 final bool hintsAreFatal; |
| 64 |
| 65 /// Whether to ignore unrecognized flags |
| 66 final bool ignoreUnrecognizedFlags; |
| 67 |
| 68 /// Whether to report lints |
| 69 final bool lints; |
| 70 |
| 71 /// Whether to log additional analysis messages and exceptions |
| 72 final bool log; |
| 73 |
| 74 /// Whether to use machine format for error display |
| 75 final bool machineFormat; |
| 76 |
| 77 /// The path to the package root |
| 78 final String packageRootPath; |
| 79 |
| 80 /// The path to a `.packages` configuration file |
| 81 final String packageConfigPath; |
| 82 |
| 83 /// Batch mode (for unit testing) |
| 84 final bool shouldBatch; |
| 85 |
| 86 /// Whether to show package: warnings |
| 87 final bool showPackageWarnings; |
| 88 |
| 89 /// Whether to show SDK warnings |
| 90 final bool showSdkWarnings; |
| 91 |
| 92 /// The source files to analyze |
| 93 final List<String> sourceFiles; |
| 94 |
| 95 /// Whether to treat warnings as fatal |
| 96 final bool warningsAreFatal; |
| 97 |
| 98 /// Whether to use strong static checking. |
| 99 final bool strongMode; |
| 100 |
| 101 /// Initialize options from the given parsed [args]. |
| 102 CommandLineOptions._fromArgs( |
| 103 ArgResults args, Map<String, String> definedVariables) |
| 104 : dartSdkPath = args['dart-sdk'], |
| 105 this.definedVariables = definedVariables, |
| 106 analysisOptionsFile = args['options'], |
| 107 disableHints = args['no-hints'], |
| 108 displayVersion = args['version'], |
| 109 enableNullAwareOperators = args['enable-null-aware-operators'], |
| 110 enableStrictCallChecks = args['enable-strict-call-checks'], |
| 111 enableSuperMixins = args['supermixin'], |
| 112 enableTypeChecks = args['enable_type_checks'], |
| 113 hintsAreFatal = args['fatal-hints'], |
| 114 ignoreUnrecognizedFlags = args['ignore-unrecognized-flags'], |
| 115 lints = args['lints'], |
| 116 log = args['log'], |
| 117 machineFormat = args['machine'] || args['format'] == 'machine', |
| 118 packageConfigPath = args['packages'], |
| 119 packageRootPath = args['package-root'], |
| 120 shouldBatch = args['batch'], |
| 121 showPackageWarnings = |
| 122 args['show-package-warnings'] || args['package-warnings'], |
| 123 showSdkWarnings = args['show-sdk-warnings'] || args['warnings'], |
| 124 sourceFiles = args.rest, |
| 125 warningsAreFatal = args['fatal-warnings'], |
| 126 strongMode = args['strong']; |
| 127 |
| 128 /// Parse [args] into [CommandLineOptions] describing the specified |
| 129 /// analyzer options. In case of a format error, calls [printAndFail], which |
| 130 /// by default prints an error message to stderr and exits. |
| 131 static CommandLineOptions parse(List<String> args, |
| 132 [printAndFail = printAndFail]) { |
| 133 CommandLineOptions options = _parse(args); |
| 134 // Check SDK. |
| 135 { |
| 136 // Infer if unspecified. |
| 137 if (options.dartSdkPath == null) { |
| 138 Directory sdkDir = getSdkDir(args); |
| 139 if (sdkDir != null) { |
| 140 options.dartSdkPath = sdkDir.path; |
| 141 } |
| 142 } |
| 143 |
| 144 var sdkPath = options.dartSdkPath; |
| 145 |
| 146 // Check that SDK is specified. |
| 147 if (sdkPath == null) { |
| 148 printAndFail('No Dart SDK found.'); |
| 149 } |
| 150 // Check that SDK is existing directory. |
| 151 if (!(new Directory(sdkPath)).existsSync()) { |
| 152 printAndFail('Invalid Dart SDK path: $sdkPath'); |
| 153 } |
| 154 } |
| 155 |
| 156 // Check package config. |
| 157 { |
| 158 if (options.packageRootPath != null && |
| 159 options.packageConfigPath != null) { |
| 160 printAndFail("Cannot specify both '--package-root' and '--packages."); |
| 161 } |
| 162 } |
| 163 |
| 164 // OK. Report deprecated options. |
| 165 if (options.enableNullAwareOperators) { |
| 166 stderr.writeln( |
| 167 "Info: Option '--enable-null-aware-operators' is no longer needed. Nul
l aware operators are supported by default."); |
| 168 } |
| 169 |
| 170 return options; |
| 171 } |
| 172 |
| 173 static String _getVersion() { |
| 174 try { |
| 175 // This is relative to bin/snapshot, so ../.. |
| 176 String versionPath = |
| 177 Platform.script.resolve('../../version').toFilePath(); |
| 178 File versionFile = new File(versionPath); |
| 179 return versionFile.readAsStringSync().trim(); |
| 180 } catch (_) { |
| 181 // This happens when the script is not running in the context of an SDK. |
| 182 return "<unknown>"; |
| 183 } |
| 184 } |
| 185 |
| 186 static CommandLineOptions _parse(List<String> args) { |
| 187 args = args.expand((String arg) => arg.split('=')).toList(); |
| 188 var parser = new CommandLineParser() |
| 189 ..addFlag('batch', |
| 190 abbr: 'b', |
| 191 help: 'Read commands from standard input (for testing).', |
| 192 defaultsTo: false, |
| 193 negatable: false) |
| 194 ..addOption('dart-sdk', help: 'The path to the Dart SDK.') |
| 195 ..addOption('packages', |
| 196 help: |
| 197 'Path to the package resolution configuration file, which supplies
a mapping of package names to paths. This option cannot be used with --package
-root.') |
| 198 ..addOption('package-root', |
| 199 abbr: 'p', |
| 200 help: |
| 201 'Path to a package root directory (deprecated). This option cannot
be used with --packages.') |
| 202 ..addOption('options', help: 'Path to an analysis options file.') |
| 203 ..addOption('format', |
| 204 help: 'Specifies the format in which errors are displayed.') |
| 205 ..addFlag('machine', |
| 206 help: 'Print errors in a format suitable for parsing (deprecated).', |
| 207 defaultsTo: false, |
| 208 negatable: false) |
| 209 ..addFlag('version', |
| 210 help: 'Print the analyzer version.', |
| 211 defaultsTo: false, |
| 212 negatable: false) |
| 213 ..addFlag('lints', |
| 214 help: 'Show lint results.', defaultsTo: false, negatable: false) |
| 215 ..addFlag('no-hints', |
| 216 help: 'Do not show hint results.', |
| 217 defaultsTo: false, |
| 218 negatable: false) |
| 219 ..addFlag('ignore-unrecognized-flags', |
| 220 help: 'Ignore unrecognized command line flags.', |
| 221 defaultsTo: false, |
| 222 negatable: false) |
| 223 ..addFlag('fatal-hints', |
| 224 help: 'Treat hints as fatal.', defaultsTo: false, negatable: false) |
| 225 ..addFlag('fatal-warnings', |
| 226 help: 'Treat non-type warnings as fatal.', |
| 227 defaultsTo: false, |
| 228 negatable: false) |
| 229 ..addFlag('package-warnings', |
| 230 help: 'Show warnings from package: imports.', |
| 231 defaultsTo: false, |
| 232 negatable: false) |
| 233 ..addFlag('show-package-warnings', |
| 234 help: 'Show warnings from package: imports (deprecated).', |
| 235 defaultsTo: false, |
| 236 negatable: false) |
| 237 ..addFlag('warnings', |
| 238 help: 'Show warnings from SDK imports.', |
| 239 defaultsTo: false, |
| 240 negatable: false) |
| 241 ..addFlag('show-sdk-warnings', |
| 242 help: 'Show warnings from SDK imports (deprecated).', |
| 243 defaultsTo: false, |
| 244 negatable: false) |
| 245 ..addFlag('help', |
| 246 abbr: 'h', |
| 247 help: 'Display this help message.', |
| 248 defaultsTo: false, |
| 249 negatable: false) |
| 250 ..addOption('url-mapping', |
| 251 help: '--url-mapping=libraryUri,/path/to/library.dart directs the ' |
| 252 'analyzer to use "library.dart" as the source for an import ' |
| 253 'of "libraryUri".', |
| 254 allowMultiple: true, |
| 255 splitCommas: false) |
| 256 // |
| 257 // Hidden flags. |
| 258 // |
| 259 ..addFlag('enable-async', |
| 260 help: 'Enable support for the proposed async feature.', |
| 261 defaultsTo: false, |
| 262 negatable: false, |
| 263 hide: true) |
| 264 ..addFlag('enable-enum', |
| 265 help: 'Enable support for the proposed enum feature.', |
| 266 defaultsTo: false, |
| 267 negatable: false, |
| 268 hide: true) |
| 269 ..addFlag('enable-null-aware-operators', |
| 270 help: 'Enable support for null-aware operators (DEP 9).', |
| 271 defaultsTo: false, |
| 272 negatable: false, |
| 273 hide: true) |
| 274 ..addFlag('enable-strict-call-checks', |
| 275 help: 'Fix issue 21938.', |
| 276 defaultsTo: false, |
| 277 negatable: false, |
| 278 hide: true) |
| 279 ..addFlag('enable-new-task-model', |
| 280 help: 'Ennable new task model.', |
| 281 defaultsTo: false, |
| 282 negatable: false, |
| 283 hide: true) |
| 284 ..addFlag('supermixin', |
| 285 help: 'Relax restrictions on mixins (DEP 34).', |
| 286 defaultsTo: false, |
| 287 negatable: false, |
| 288 hide: true) |
| 289 ..addFlag('log', |
| 290 help: 'Log additional messages and exceptions.', |
| 291 defaultsTo: false, |
| 292 negatable: false, |
| 293 hide: true) |
| 294 ..addFlag('enable_type_checks', |
| 295 help: 'Check types in constant evaluation.', |
| 296 defaultsTo: false, |
| 297 negatable: false, |
| 298 hide: true) |
| 299 ..addFlag('strong', |
| 300 help: 'Enable strong static checks (https://goo.gl/DqcBsw)'); |
| 301 |
| 302 try { |
| 303 // TODO(scheglov) https://code.google.com/p/dart/issues/detail?id=11061 |
| 304 args = |
| 305 args.map((String arg) => arg == '-batch' ? '--batch' : arg).toList(); |
| 306 Map<String, String> definedVariables = <String, String>{}; |
| 307 var results = parser.parse(args, definedVariables); |
| 308 // Help requests. |
| 309 if (results['help']) { |
| 310 _showUsage(parser); |
| 311 exit(0); |
| 312 } |
| 313 // Batch mode and input files. |
| 314 if (results['batch']) { |
| 315 if (results.rest.isNotEmpty) { |
| 316 stderr.writeln('No source files expected in the batch mode.'); |
| 317 _showUsage(parser); |
| 318 exit(15); |
| 319 } |
| 320 } else if (results['version']) { |
| 321 print('$_binaryName version ${_getVersion()}'); |
| 322 exit(0); |
| 323 } else { |
| 324 if (results.rest.isEmpty) { |
| 325 _showUsage(parser); |
| 326 exit(15); |
| 327 } |
| 328 } |
| 329 return new CommandLineOptions._fromArgs(results, definedVariables); |
| 330 } on FormatException catch (e) { |
| 331 stderr.writeln(e.message); |
| 332 _showUsage(parser); |
| 333 exit(15); |
| 334 } |
| 335 } |
| 336 |
| 337 static _showUsage(parser) { |
| 338 stderr |
| 339 .writeln('Usage: $_binaryName [options...] <libraries to analyze...>'); |
| 340 stderr.writeln(parser.getUsage()); |
| 341 stderr.writeln(''); |
| 342 stderr.writeln( |
| 343 'For more information, see http://www.dartlang.org/tools/analyzer.'); |
| 344 } |
| 345 } |
| 346 |
| 347 /// Commandline argument parser. |
| 348 /// |
| 349 /// TODO(pquitslund): when the args package supports ignoring unrecognized |
| 350 /// options/flags, this class can be replaced with a simple [ArgParser] |
| 351 /// instance. |
| 352 class CommandLineParser { |
| 353 final List<String> _knownFlags; |
| 354 final bool _alwaysIgnoreUnrecognized; |
| 355 final ArgParser _parser; |
| 356 |
| 357 /// Creates a new command line parser. |
| 358 CommandLineParser({bool alwaysIgnoreUnrecognized: false}) |
| 359 : _knownFlags = <String>[], |
| 360 _alwaysIgnoreUnrecognized = alwaysIgnoreUnrecognized, |
| 361 _parser = new ArgParser(allowTrailingOptions: true); |
| 362 |
| 363 ArgParser get parser => _parser; |
| 364 |
| 365 /// Defines a flag. |
| 366 /// See [ArgParser.addFlag()]. |
| 367 void addFlag(String name, |
| 368 {String abbr, |
| 369 String help, |
| 370 bool defaultsTo: false, |
| 371 bool negatable: true, |
| 372 void callback(bool value), |
| 373 bool hide: false}) { |
| 374 _knownFlags.add(name); |
| 375 _parser.addFlag(name, |
| 376 abbr: abbr, |
| 377 help: help, |
| 378 defaultsTo: defaultsTo, |
| 379 negatable: negatable, |
| 380 callback: callback, |
| 381 hide: hide); |
| 382 } |
| 383 |
| 384 /// Defines a value-taking option. |
| 385 /// See [ArgParser.addOption()]. |
| 386 void addOption(String name, |
| 387 {String abbr, |
| 388 String help, |
| 389 List<String> allowed, |
| 390 Map<String, String> allowedHelp, |
| 391 String defaultsTo, |
| 392 void callback(value), |
| 393 bool allowMultiple: false, |
| 394 bool splitCommas}) { |
| 395 _knownFlags.add(name); |
| 396 _parser.addOption(name, |
| 397 abbr: abbr, |
| 398 help: help, |
| 399 allowed: allowed, |
| 400 allowedHelp: allowedHelp, |
| 401 defaultsTo: defaultsTo, |
| 402 callback: callback, |
| 403 allowMultiple: allowMultiple, |
| 404 splitCommas: splitCommas); |
| 405 } |
| 406 |
| 407 /// Generates a string displaying usage information for the defined options. |
| 408 /// See [ArgParser.usage]. |
| 409 String getUsage() => _parser.usage; |
| 410 |
| 411 /// Parses [args], a list of command-line arguments, matches them against the |
| 412 /// flags and options defined by this parser, and returns the result. The |
| 413 /// values of any defined variables are captured in the given map. |
| 414 /// See [ArgParser]. |
| 415 ArgResults parse(List<String> args, Map<String, String> definedVariables) => |
| 416 _parser.parse( |
| 417 _filterUnknowns(parseDefinedVariables(args, definedVariables))); |
| 418 |
| 419 List<String> parseDefinedVariables( |
| 420 List<String> args, Map<String, String> definedVariables) { |
| 421 int count = args.length; |
| 422 List<String> remainingArgs = <String>[]; |
| 423 for (int i = 0; i < count; i++) { |
| 424 String arg = args[i]; |
| 425 if (arg == '--') { |
| 426 while (i < count) { |
| 427 remainingArgs.add(args[i++]); |
| 428 } |
| 429 } else if (arg.startsWith("-D")) { |
| 430 definedVariables[arg.substring(2)] = args[++i]; |
| 431 } else { |
| 432 remainingArgs.add(arg); |
| 433 } |
| 434 } |
| 435 return remainingArgs; |
| 436 } |
| 437 |
| 438 List<String> _filterUnknowns(List<String> args) { |
| 439 // Only filter args if the ignore flag is specified, or if |
| 440 // _alwaysIgnoreUnrecognized was set to true. |
| 441 if (_alwaysIgnoreUnrecognized || |
| 442 args.contains('--ignore-unrecognized-flags')) { |
| 443 //TODO(pquitslund): replace w/ the following once library skew issues are |
| 444 // sorted out |
| 445 //return args.where((arg) => !arg.startsWith('--') || |
| 446 // _knownFlags.contains(arg.substring(2))); |
| 447 |
| 448 // Filter all unrecognized flags and options. |
| 449 List<String> filtered = <String>[]; |
| 450 for (int i = 0; i < args.length; ++i) { |
| 451 String arg = args[i]; |
| 452 if (arg.startsWith('--') && arg.length > 2) { |
| 453 String option = arg.substring(2); |
| 454 // strip the last '=value' |
| 455 int equalsOffset = option.lastIndexOf('='); |
| 456 if (equalsOffset != -1) { |
| 457 option = option.substring(0, equalsOffset); |
| 458 } |
| 459 // Check the option |
| 460 if (!_knownFlags.contains(option)) { |
| 461 //"eat" params by advancing to the next flag/option |
| 462 i = _getNextFlagIndex(args, i); |
| 463 } else { |
| 464 filtered.add(arg); |
| 465 } |
| 466 } else { |
| 467 filtered.add(arg); |
| 468 } |
| 469 } |
| 470 |
| 471 return filtered; |
| 472 } else { |
| 473 return args; |
| 474 } |
| 475 } |
| 476 |
| 477 int _getNextFlagIndex(args, i) { |
| 478 for (; i < args.length; ++i) { |
| 479 if (args[i].startsWith('--')) { |
| 480 return i; |
| 481 } |
| 482 } |
| 483 return i; |
| 484 } |
| 485 } |
OLD | NEW |