| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:io'; |
| |
| import 'package:analyzer/file_system/physical_file_system.dart'; |
| import 'package:analyzer/src/command_line/arguments.dart'; |
| import 'package:analyzer/src/context/builder.dart'; |
| import 'package:analyzer/src/util/sdk.dart'; |
| import 'package:analyzer_cli/src/ansi.dart' as ansi; |
| import 'package:analyzer_cli/src/driver.dart'; |
| import 'package:args/args.dart'; |
| |
| const _binaryName = 'dartanalyzer'; |
| |
| /// Shared exit handler. |
| /// |
| /// *Visible for testing.* |
| ExitHandler exitHandler = exit; |
| |
| /// Print the given [message] to stderr and exit with the given [exitCode]. |
| void printAndFail(String message, {int exitCode: 15}) { |
| errorSink.writeln(message); |
| exitHandler(exitCode); |
| } |
| |
| /// Exit handler. |
| /// |
| /// *Visible for testing.* |
| typedef void ExitHandler(int code); |
| |
| /// Analyzer commandline configuration options. |
| class CommandLineOptions { |
| final bool enableNewAnalysisDriver = true; |
| |
| /// Return `true` if the parser is to parse asserts in the initializer list of |
| /// a constructor. |
| final bool enableAssertInitializer; |
| |
| /// The path to output analysis results when in build mode. |
| final String buildAnalysisOutput; |
| |
| /// Whether to use build mode. |
| final bool buildMode; |
| |
| /// Whether to use build mode as a Bazel persistent worker. |
| final bool buildModePersistentWorker; |
| |
| /// List of summary file paths to use in build mode. |
| final List<String> buildSummaryInputs; |
| |
| /// List of unlinked summary file paths to use in build mode. |
| final List<String> buildSummaryUnlinkedInputs; |
| |
| /// Whether to skip analysis when creating summaries in build mode. |
| final bool buildSummaryOnly; |
| |
| /// Whether to use diet parsing, i.e. skip function bodies. We don't need to |
| /// analyze function bodies to use summaries during future compilation steps. |
| final bool buildSummaryOnlyDiet; |
| |
| /// Whether to only produce unlinked summaries instead of linked summaries. |
| /// Must be used in combination with `buildSummaryOnly`. |
| final bool buildSummaryOnlyUnlinked; |
| |
| /// The path to output the summary when creating summaries in build mode. |
| final String buildSummaryOutput; |
| |
| /// The path to output the semantic-only summary when creating summaries in |
| /// build mode. |
| final String buildSummaryOutputSemantic; |
| |
| /// Whether to suppress a nonzero exit code in build mode. |
| final bool buildSuppressExitCode; |
| |
| /// The options defining the context in which analysis is performed. |
| final ContextBuilderOptions contextBuilderOptions; |
| |
| /// The path to the dart SDK. |
| String dartSdkPath; |
| |
| /// The path to the dart SDK summary file. |
| String dartSdkSummaryPath; |
| |
| /// Whether to disable cache flushing. This option can improve analysis |
| /// speed at the expense of memory usage. It may also be useful for working |
| /// around bugs. |
| final bool disableCacheFlushing; |
| |
| /// Whether to report hints |
| final bool disableHints; |
| |
| /// Whether to display version information |
| final bool displayVersion; |
| |
| /// Whether to treat type mismatches found during constant evaluation as |
| /// errors. |
| final bool enableTypeChecks; |
| |
| /// Whether to treat hints as fatal |
| final bool hintsAreFatal; |
| |
| /// Whether to ignore unrecognized flags |
| final bool ignoreUnrecognizedFlags; |
| |
| /// Whether to report lints |
| final bool lints; |
| |
| /// Whether to log additional analysis messages and exceptions |
| final bool log; |
| |
| /// Whether to use machine format for error display |
| final bool machineFormat; |
| |
| /// The path to a file to write a performance log. |
| /// (Or null if not enabled.) |
| final String perfReport; |
| |
| /// Batch mode (for unit testing) |
| final bool shouldBatch; |
| |
| /// Whether to show package: warnings |
| final bool showPackageWarnings; |
| |
| /// If not null, show package: warnings only for matching packages. |
| final String showPackageWarningsPrefix; |
| |
| /// Whether to show SDK warnings |
| final bool showSdkWarnings; |
| |
| /// The source files to analyze |
| List<String> _sourceFiles; |
| |
| /// Whether to treat warnings as fatal |
| final bool warningsAreFatal; |
| |
| /// Whether to use strong static checking. |
| final bool strongMode; |
| |
| /// Whether implicit casts are enabled (in strong mode) |
| final bool implicitCasts; |
| |
| /// Whether implicit dynamic is enabled (mainly for strong mode users) |
| final bool implicitDynamic; |
| |
| /// Whether to treat lints as fatal |
| final bool lintsAreFatal; |
| |
| /// Whether to use memory byte store for analysis driver. |
| final bool useAnalysisDriverMemoryByteStore; |
| |
| /// Emit output in a verbose mode. |
| final bool verbose; |
| |
| /// Use ANSI color codes for output. |
| final bool color; |
| |
| /// Initialize options from the given parsed [args]. |
| CommandLineOptions._fromArgs(ArgResults args) |
| : buildAnalysisOutput = args['build-analysis-output'], |
| buildMode = args['build-mode'], |
| buildModePersistentWorker = args['persistent_worker'], |
| buildSummaryInputs = args['build-summary-input'] as List<String>, |
| buildSummaryUnlinkedInputs = |
| args['build-summary-unlinked-input'] as List<String>, |
| buildSummaryOnly = args['build-summary-only'], |
| buildSummaryOnlyDiet = args['build-summary-only-diet'], |
| buildSummaryOnlyUnlinked = args['build-summary-only-unlinked'], |
| buildSummaryOutput = args['build-summary-output'], |
| buildSummaryOutputSemantic = args['build-summary-output-semantic'], |
| buildSuppressExitCode = args['build-suppress-exit-code'], |
| contextBuilderOptions = createContextBuilderOptions(args), |
| dartSdkPath = args['dart-sdk'], |
| dartSdkSummaryPath = args['dart-sdk-summary'], |
| disableCacheFlushing = args['disable-cache-flushing'], |
| disableHints = args['no-hints'], |
| displayVersion = args['version'], |
| enableTypeChecks = args['enable_type_checks'], |
| enableAssertInitializer = args['enable-assert-initializers'], |
| hintsAreFatal = args['fatal-hints'], |
| ignoreUnrecognizedFlags = args['ignore-unrecognized-flags'], |
| lints = args[lintsFlag], |
| log = args['log'], |
| machineFormat = args['format'] == 'machine', |
| perfReport = args['x-perf-report'], |
| shouldBatch = args['batch'], |
| showPackageWarnings = args['show-package-warnings'] || |
| args['package-warnings'] || |
| args['x-package-warnings-prefix'] != null, |
| showPackageWarningsPrefix = args['x-package-warnings-prefix'], |
| showSdkWarnings = args['sdk-warnings'], |
| _sourceFiles = args.rest, |
| warningsAreFatal = args['fatal-warnings'], |
| strongMode = args['strong'], |
| implicitCasts = !args['no-implicit-casts'], |
| implicitDynamic = !args['no-implicit-dynamic'], |
| lintsAreFatal = args['fatal-lints'], |
| useAnalysisDriverMemoryByteStore = |
| args['use-analysis-driver-memory-byte-store'], |
| verbose = args['verbose'], |
| color = args['color']; |
| |
| /// The path to an analysis options file |
| String get analysisOptionsFile => |
| contextBuilderOptions.defaultAnalysisOptionsFilePath; |
| |
| /// A table mapping the names of defined variables to their values. |
| Map<String, String> get definedVariables => |
| contextBuilderOptions.declaredVariables; |
| |
| /// Whether to strictly follow the specification when generating warnings on |
| /// "call" methods (fixes dartbug.com/21938). |
| bool get enableStrictCallChecks => |
| contextBuilderOptions.defaultOptions.enableStrictCallChecks; |
| |
| /// Whether to relax restrictions on mixins (DEP 34). |
| bool get enableSuperMixins => |
| contextBuilderOptions.defaultOptions.enableSuperMixins; |
| |
| /// The path to a `.packages` configuration file |
| String get packageConfigPath => contextBuilderOptions.defaultPackageFilePath; |
| |
| /// The path to the package root |
| String get packageRootPath => |
| contextBuilderOptions.defaultPackagesDirectoryPath; |
| |
| /// The source files to analyze |
| List<String> get sourceFiles => _sourceFiles; |
| |
| /// Replace the sourceFiles parsed from the command line. |
| void rewriteSourceFiles(List<String> newSourceFiles) { |
| _sourceFiles = newSourceFiles; |
| } |
| |
| /// Parse [args] into [CommandLineOptions] describing the specified |
| /// analyzer options. In case of a format error, calls [printAndFail], which |
| /// by default prints an error message to stderr and exits. |
| static CommandLineOptions parse(List<String> args, |
| [printAndFail(String msg) = printAndFail]) { |
| CommandLineOptions options = _parse(args); |
| // Check SDK. |
| if (!options.buildModePersistentWorker) { |
| // Infer if unspecified. |
| options.dartSdkPath ??= getSdkPath(args); |
| |
| String sdkPath = options.dartSdkPath; |
| |
| // Check that SDK is specified. |
| if (sdkPath == null) { |
| printAndFail('No Dart SDK found.'); |
| return null; // Only reachable in testing. |
| } |
| // Check that SDK is existing directory. |
| if (!(new Directory(sdkPath)).existsSync()) { |
| printAndFail('Invalid Dart SDK path: $sdkPath'); |
| return null; // Only reachable in testing. |
| } |
| } |
| |
| // Check package config. |
| { |
| if (options.packageRootPath != null && |
| options.packageConfigPath != null) { |
| printAndFail("Cannot specify both '--package-root' and '--packages."); |
| return null; // Only reachable in testing. |
| } |
| } |
| |
| // Build mode. |
| if (options.buildModePersistentWorker && !options.buildMode) { |
| printAndFail('The option --persisten_worker can be used only ' |
| 'together with --build-mode.'); |
| return null; // Only reachable in testing. |
| } |
| if (options.buildSummaryOnlyDiet && !options.buildSummaryOnly) { |
| printAndFail('The option --build-summary-only-diet can be used only ' |
| 'together with --build-summary-only.'); |
| return null; // Only reachable in testing. |
| } |
| |
| if (options.buildSummaryOnlyUnlinked) { |
| if (!options.buildSummaryOnly) { |
| printAndFail( |
| 'The option --build-summary-only-unlinked can be used only ' |
| 'together with --build-summary-only.'); |
| return null; // Only reachable in testing. |
| } |
| if (options.buildSummaryInputs.isNotEmpty || |
| options.buildSummaryUnlinkedInputs.isNotEmpty) { |
| printAndFail('No summaries should be provided in combination with ' |
| '--build-summary-only-unlinked, they aren\'t needed.'); |
| return null; // Only reachable in testing. |
| } |
| } |
| |
| return options; |
| } |
| |
| static String _getVersion() { |
| try { |
| // This is relative to bin/snapshot, so ../.. |
| String versionPath = |
| Platform.script.resolve('../../version').toFilePath(); |
| File versionFile = new File(versionPath); |
| return versionFile.readAsStringSync().trim(); |
| } catch (_) { |
| // This happens when the script is not running in the context of an SDK. |
| return "<unknown>"; |
| } |
| } |
| |
| static CommandLineOptions _parse(List<String> args) { |
| args = preprocessArgs(PhysicalResourceProvider.INSTANCE, args); |
| |
| bool verbose = args.contains('-v') || args.contains('--verbose'); |
| bool hide = !verbose; |
| |
| ArgParser parser = new ArgParser(allowTrailingOptions: true); |
| |
| if (!hide) { |
| parser.addSeparator('General options:'); |
| } |
| |
| // TODO(devoncarew): This defines some hidden flags, which would be better |
| // defined with the rest of the hidden flags below (to group well with the |
| // other flags). |
| defineAnalysisArguments(parser, hide: hide); |
| |
| parser |
| ..addOption('format', |
| help: 'Specifies the format in which errors are displayed; the only ' |
| 'currently allowed value is \'machine\'.') |
| ..addFlag('version', |
| help: 'Print the analyzer version.', |
| defaultsTo: false, |
| negatable: false) |
| ..addFlag('no-hints', |
| help: 'Do not show hint results.', |
| defaultsTo: false, |
| negatable: false) |
| ..addFlag('fatal-hints', |
| help: 'Treat hints as fatal.', defaultsTo: false, negatable: false) |
| ..addFlag('fatal-warnings', |
| help: 'Treat non-type warnings as fatal.', |
| defaultsTo: false, |
| negatable: false) |
| ..addFlag('fatal-lints', |
| help: 'Treat lints as fatal.', defaultsTo: false, negatable: false) |
| ..addFlag('package-warnings', |
| help: 'Show warnings from package: imports.', |
| defaultsTo: false, |
| negatable: false) |
| ..addFlag('help', |
| abbr: 'h', |
| help: |
| 'Display this help message. Add --verbose to show hidden options.', |
| defaultsTo: false, |
| negatable: false) |
| ..addFlag('verbose', |
| abbr: 'v', |
| defaultsTo: false, |
| help: 'Verbose output.', |
| negatable: false); |
| |
| // Build mode options. |
| if (!hide) { |
| parser.addSeparator('Build mode flags:'); |
| } |
| |
| parser |
| ..addFlag('persistent_worker', |
| help: 'Enable Bazel persistent worker mode.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addOption('build-analysis-output', |
| help: |
| 'Specifies the path to the file where analysis results should be written.', |
| hide: hide) |
| ..addFlag('build-mode', |
| // TODO(paulberry): add more documentation. |
| help: 'Enable build mode.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addOption('build-summary-input', |
| help: 'Path to a linked summary file that contains information from ' |
| 'a previous analysis run; may be specified multiple times.', |
| allowMultiple: true, |
| hide: hide) |
| ..addOption('build-summary-unlinked-input', |
| help: 'Path to an unlinked summary file that contains information ' |
| 'from a previous analysis run; may be specified multiple times.', |
| allowMultiple: true, |
| hide: hide) |
| ..addOption('build-summary-output', |
| help: 'Specifies the path to the file where the full summary ' |
| 'information should be written.', |
| hide: hide) |
| ..addOption('build-summary-output-semantic', |
| help: 'Specifies the path to the file where the semantic summary ' |
| 'information should be written.', |
| hide: hide) |
| ..addFlag('build-summary-only', |
| help: 'Disable analysis (only generate summaries).', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('build-summary-only-diet', |
| help: 'Diet parse function bodies.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('build-summary-only-unlinked', |
| help: 'Only output the unlinked summary.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('build-suppress-exit-code', |
| help: 'Exit with code 0 even if errors are found.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('color', |
| help: 'Use ansi colors when printing messages.', |
| defaultsTo: ansi.terminalSupportsAnsi(), |
| hide: hide); |
| |
| // Hidden flags. |
| if (!hide) { |
| parser.addSeparator('Less frequently used flags:'); |
| } |
| |
| parser |
| ..addFlag('batch', |
| help: 'Read commands from standard input (for testing).', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag(ignoreUnrecognizedFlagsFlag, |
| help: 'Ignore unrecognized command line flags.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('disable-cache-flushing', defaultsTo: false, hide: hide) |
| ..addOption('x-perf-report', |
| help: 'Writes a performance report to the given file (experimental).', |
| hide: hide) |
| ..addOption('x-package-warnings-prefix', |
| help: |
| 'Show warnings from package: imports that match the given prefix.', |
| hide: hide) |
| ..addFlag('enable-conditional-directives', |
| help: |
| 'deprecated -- Enable support for conditional directives (DEP 40).', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('show-package-warnings', |
| help: 'Show warnings from package: imports (deprecated).', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('sdk-warnings', |
| help: 'Show warnings from SDK imports.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('log', |
| help: 'Log additional messages and exceptions.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('enable_type_checks', |
| help: 'Check types in constant evaluation.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addFlag('enable-assert-initializers', |
| help: 'Enable parsing of asserts in constructor initializers.', |
| defaultsTo: null, |
| negatable: false, |
| hide: hide) |
| ..addFlag('use-analysis-driver-memory-byte-store', |
| help: 'Use memory byte store, not the file system cache.', |
| defaultsTo: false, |
| negatable: false, |
| hide: hide) |
| ..addOption('url-mapping', |
| help: '--url-mapping=libraryUri,/path/to/library.dart directs the ' |
| 'analyzer to use "library.dart" as the source for an import ' |
| 'of "libraryUri".', |
| allowMultiple: true, |
| splitCommas: false, |
| hide: hide); |
| |
| try { |
| if (args.contains('--$ignoreUnrecognizedFlagsFlag')) { |
| args = filterUnknownArguments(args, parser); |
| } |
| ArgResults results = parser.parse(args); |
| |
| // Persistent worker. |
| if (args.contains('--persistent_worker')) { |
| bool hasBuildMode = args.contains('--build-mode'); |
| bool onlyDartSdkArg = args.length == 2 || |
| (args.length == 3 && args.any((a) => a.startsWith('--dart-sdk'))) || |
| (args.length == 4 && args.contains('--dart-sdk')); |
| if (!(hasBuildMode && onlyDartSdkArg)) { |
| printAndFail('The --persistent_worker flag should be used with and ' |
| 'only with the --build-mode flag, and possibly the --dart-sdk ' |
| 'option. Got: $args'); |
| return null; // Only reachable in testing. |
| } |
| return new CommandLineOptions._fromArgs(results); |
| } |
| |
| // Help requests. |
| if (results['help']) { |
| _showUsage(parser); |
| exitHandler(0); |
| return null; // Only reachable in testing. |
| } |
| // Batch mode and input files. |
| if (results['batch']) { |
| if (results.rest.isNotEmpty) { |
| errorSink.writeln('No source files expected in the batch mode.'); |
| _showUsage(parser); |
| exitHandler(15); |
| return null; // Only reachable in testing. |
| } |
| } else if (results['persistent_worker']) { |
| if (results.rest.isNotEmpty) { |
| errorSink.writeln( |
| 'No source files expected in the persistent worker mode.'); |
| _showUsage(parser); |
| exitHandler(15); |
| return null; // Only reachable in testing. |
| } |
| } else if (results['version']) { |
| outSink.writeln('$_binaryName version ${_getVersion()}'); |
| exitHandler(0); |
| return null; // Only reachable in testing. |
| } else { |
| if (results.rest.isEmpty && !results['build-mode']) { |
| _showUsage(parser); |
| exitHandler(15); |
| return null; // Only reachable in testing. |
| } |
| } |
| return new CommandLineOptions._fromArgs(results); |
| } on FormatException catch (e) { |
| errorSink.writeln(e.message); |
| _showUsage(parser); |
| exitHandler(15); |
| return null; // Only reachable in testing. |
| } |
| } |
| |
| static _showUsage(ArgParser parser) { |
| errorSink.writeln( |
| 'Usage: $_binaryName [options...] <directory or list of files>'); |
| errorSink.writeln(''); |
| errorSink.writeln(parser.usage); |
| errorSink.writeln(''); |
| errorSink.writeln(''' |
| Run "dartanalyzer -h -v" for verbose help output, including less commonly used options. |
| For more information, see http://www.dartlang.org/tools/analyzer. |
| '''); |
| } |
| } |