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