OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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' as io; | 6 import 'dart:io' as io; |
7 | 7 |
8 import 'package:analyzer/error/error.dart'; | 8 import 'package:analyzer/error/error.dart'; |
9 import 'package:analyzer/file_system/file_system.dart' as file_system; | 9 import 'package:analyzer/file_system/file_system.dart' as file_system; |
10 import 'package:analyzer/file_system/file_system.dart'; | 10 import 'package:analyzer/file_system/file_system.dart'; |
(...skipping 24 matching lines...) Expand all Loading... |
35 import 'package:analyzer_cli/src/batch_mode.dart'; | 35 import 'package:analyzer_cli/src/batch_mode.dart'; |
36 import 'package:analyzer_cli/src/build_mode.dart'; | 36 import 'package:analyzer_cli/src/build_mode.dart'; |
37 import 'package:analyzer_cli/src/error_formatter.dart'; | 37 import 'package:analyzer_cli/src/error_formatter.dart'; |
38 import 'package:analyzer_cli/src/error_severity.dart'; | 38 import 'package:analyzer_cli/src/error_severity.dart'; |
39 import 'package:analyzer_cli/src/options.dart'; | 39 import 'package:analyzer_cli/src/options.dart'; |
40 import 'package:analyzer_cli/src/perf_report.dart'; | 40 import 'package:analyzer_cli/src/perf_report.dart'; |
41 import 'package:analyzer_cli/starter.dart' show CommandLineStarter; | 41 import 'package:analyzer_cli/starter.dart' show CommandLineStarter; |
42 import 'package:front_end/src/base/performace_logger.dart'; | 42 import 'package:front_end/src/base/performace_logger.dart'; |
43 import 'package:front_end/src/incremental/byte_store.dart'; | 43 import 'package:front_end/src/incremental/byte_store.dart'; |
44 import 'package:linter/src/rules.dart' as linter; | 44 import 'package:linter/src/rules.dart' as linter; |
| 45 import 'package:meta/meta.dart'; |
45 import 'package:package_config/discovery.dart' as pkg_discovery; | 46 import 'package:package_config/discovery.dart' as pkg_discovery; |
46 import 'package:package_config/packages.dart' show Packages; | 47 import 'package:package_config/packages.dart' show Packages; |
47 import 'package:package_config/packages_file.dart' as pkgfile show parse; | 48 import 'package:package_config/packages_file.dart' as pkgfile show parse; |
48 import 'package:package_config/src/packages_impl.dart' show MapPackages; | 49 import 'package:package_config/src/packages_impl.dart' show MapPackages; |
49 import 'package:path/path.dart' as path; | 50 import 'package:path/path.dart' as path; |
50 import 'package:plugin/manager.dart'; | 51 import 'package:plugin/manager.dart'; |
51 import 'package:plugin/plugin.dart'; | 52 import 'package:plugin/plugin.dart'; |
| 53 import 'package:telemetry/crash_reporting.dart'; |
| 54 import 'package:telemetry/telemetry.dart' as telemetry; |
52 import 'package:yaml/yaml.dart'; | 55 import 'package:yaml/yaml.dart'; |
53 | 56 |
54 /// Shared IO sink for standard error reporting. | 57 /// Shared IO sink for standard error reporting. |
55 /// | 58 @visibleForTesting |
56 /// *Visible for testing.* | |
57 StringSink errorSink = io.stderr; | 59 StringSink errorSink = io.stderr; |
58 | 60 |
59 /// Shared IO sink for standard out reporting. | 61 /// Shared IO sink for standard out reporting. |
60 /// | 62 @visibleForTesting |
61 /// *Visible for testing.* | |
62 StringSink outSink = io.stdout; | 63 StringSink outSink = io.stdout; |
63 | 64 |
64 /// Test this option map to see if it specifies lint rules. | 65 /// Test this option map to see if it specifies lint rules. |
65 bool containsLintRuleEntry(Map<String, YamlNode> options) { | 66 bool containsLintRuleEntry(Map<String, YamlNode> options) { |
66 var linterNode = options['linter']; | 67 var linterNode = options['linter']; |
67 return linterNode is YamlMap && linterNode.containsKey('rules'); | 68 return linterNode is YamlMap && linterNode.containsKey('rules'); |
68 } | 69 } |
69 | 70 |
| 71 telemetry.Analytics _analytics; |
| 72 |
| 73 const _analyticsID = 'UA-26406144-28'; |
| 74 |
| 75 /// The analytics instance for analyzer-cli. |
| 76 telemetry.Analytics get analytics => (_analytics ??= |
| 77 telemetry.createAnalyticsInstance(_analyticsID, 'analyzer-cli')); |
| 78 |
| 79 /// Make sure that we create an analytics instance that doesn't send for this |
| 80 /// session. |
| 81 void disableAnalyticsForSession() { |
| 82 _analytics = telemetry.createAnalyticsInstance(_analyticsID, 'analyzer-cli', |
| 83 disableForSession: true); |
| 84 } |
| 85 |
| 86 @visibleForTesting |
| 87 void setAnalytics(telemetry.Analytics replacementAnalytics) { |
| 88 _analytics = replacementAnalytics; |
| 89 } |
| 90 |
70 class Driver implements CommandLineStarter { | 91 class Driver implements CommandLineStarter { |
71 static final PerformanceTag _analyzeAllTag = | 92 static final PerformanceTag _analyzeAllTag = |
72 new PerformanceTag("Driver._analyzeAll"); | 93 new PerformanceTag("Driver._analyzeAll"); |
73 | 94 |
74 /// Cache of [AnalysisOptionsImpl] objects that correspond to directories | 95 /// Cache of [AnalysisOptionsImpl] objects that correspond to directories |
75 /// with analyzed files, used to reduce searching for `analysis_options.yaml` | 96 /// with analyzed files, used to reduce searching for `analysis_options.yaml` |
76 /// files. | 97 /// files. |
77 static Map<String, AnalysisOptionsImpl> _directoryToAnalysisOptions = {}; | 98 static Map<String, AnalysisOptionsImpl> _directoryToAnalysisOptions = {}; |
78 | 99 |
79 static ByteStore analysisDriverMemoryByteStore = new MemoryByteStore(); | 100 static ByteStore analysisDriverMemoryByteStore = new MemoryByteStore(); |
(...skipping 22 matching lines...) Expand all Loading... |
102 | 123 |
103 /** | 124 /** |
104 * The resource provider used to access the file system. | 125 * The resource provider used to access the file system. |
105 */ | 126 */ |
106 file_system.ResourceProvider resourceProvider = | 127 file_system.ResourceProvider resourceProvider = |
107 PhysicalResourceProvider.INSTANCE; | 128 PhysicalResourceProvider.INSTANCE; |
108 | 129 |
109 /// Collected analysis statistics. | 130 /// Collected analysis statistics. |
110 final AnalysisStats stats = new AnalysisStats(); | 131 final AnalysisStats stats = new AnalysisStats(); |
111 | 132 |
| 133 CrashReportSender _crashReportSender; |
| 134 |
| 135 // TODO(devoncarew): Replace with the real crash product ID. |
| 136 /// The crash reporting instance for analyzer-cli. |
| 137 CrashReportSender get crashReportSender => (_crashReportSender ??= |
| 138 new CrashReportSender('Dart_analyzer_cli', analytics)); |
| 139 |
| 140 /// Create a new Driver instance. |
| 141 /// |
| 142 /// [isTesting] is true if we're running in a test environment. |
| 143 Driver({bool isTesting: false}) { |
| 144 if (isTesting) { |
| 145 disableAnalyticsForSession(); |
| 146 } |
| 147 } |
| 148 |
112 /// This Driver's current analysis context. | 149 /// This Driver's current analysis context. |
113 /// | 150 @visibleForTesting |
114 /// *Visible for testing.* | |
115 AnalysisContext get context => _context; | 151 AnalysisContext get context => _context; |
116 | 152 |
117 @override | 153 @override |
118 void set userDefinedPlugins(List<Plugin> plugins) { | 154 void set userDefinedPlugins(List<Plugin> plugins) { |
119 _userDefinedPlugins = plugins ?? <Plugin>[]; | 155 _userDefinedPlugins = plugins ?? <Plugin>[]; |
120 } | 156 } |
121 | 157 |
122 @override | 158 @override |
123 Future<Null> start(List<String> args) async { | 159 Future<Null> start(List<String> args) async { |
124 if (_context != null) { | 160 if (_context != null) { |
125 throw new StateError("start() can only be called once"); | 161 throw new StateError("start() can only be called once"); |
126 } | 162 } |
127 int startTime = new DateTime.now().millisecondsSinceEpoch; | 163 int startTime = new DateTime.now().millisecondsSinceEpoch; |
128 | 164 |
129 StringUtilities.INTERNER = new MappedInterner(); | 165 StringUtilities.INTERNER = new MappedInterner(); |
130 | 166 |
131 _processPlugins(); | 167 _processPlugins(); |
132 | 168 |
133 // Parse commandline options. | 169 // Parse commandline options. |
134 CommandLineOptions options = CommandLineOptions.parse(args); | 170 CommandLineOptions options = CommandLineOptions.parse(args); |
135 | 171 |
| 172 if (options.batchMode || options.buildMode) { |
| 173 disableAnalyticsForSession(); |
| 174 } |
| 175 |
| 176 // Ping analytics with our initial call. |
| 177 analytics.sendScreenView('home'); |
| 178 |
| 179 var timer = analytics.startTimer('analyze'); |
| 180 |
136 // Do analysis. | 181 // Do analysis. |
137 if (options.buildMode) { | 182 if (options.buildMode) { |
138 ErrorSeverity severity = _buildModeAnalyze(options); | 183 ErrorSeverity severity = _buildModeAnalyze(options); |
139 // Propagate issues to the exit code. | 184 // Propagate issues to the exit code. |
140 if (_shouldBeFatal(severity, options)) { | 185 if (_shouldBeFatal(severity, options)) { |
141 io.exitCode = severity.ordinal; | 186 io.exitCode = severity.ordinal; |
142 } | 187 } |
143 } else if (options.shouldBatch) { | 188 } else if (options.batchMode) { |
144 BatchRunner batchRunner = new BatchRunner(outSink, errorSink); | 189 BatchRunner batchRunner = new BatchRunner(outSink, errorSink); |
145 batchRunner.runAsBatch(args, (List<String> args) async { | 190 batchRunner.runAsBatch(args, (List<String> args) async { |
146 CommandLineOptions options = CommandLineOptions.parse(args); | 191 CommandLineOptions options = CommandLineOptions.parse(args); |
147 return await _analyzeAll(options); | 192 return await _analyzeAll(options); |
148 }); | 193 }); |
149 } else { | 194 } else { |
150 ErrorSeverity severity = await _analyzeAll(options); | 195 ErrorSeverity severity = await _analyzeAll(options); |
151 // Propagate issues to the exit code. | 196 // Propagate issues to the exit code. |
152 if (_shouldBeFatal(severity, options)) { | 197 if (_shouldBeFatal(severity, options)) { |
153 io.exitCode = severity.ordinal; | 198 io.exitCode = severity.ordinal; |
154 } | 199 } |
155 } | 200 } |
156 | 201 |
157 if (_context != null) { | 202 if (_context != null) { |
158 _analyzedFileCount += _context.sources.length; | 203 _analyzedFileCount += _context.sources.length; |
159 } | 204 } |
160 | 205 |
| 206 // Send how long analysis took. |
| 207 timer.finish(); |
| 208 |
| 209 // Send how many files were analyzed. |
| 210 analytics.sendEvent('analyze', 'fileCount', value: _analyzedFileCount); |
| 211 |
161 if (options.perfReport != null) { | 212 if (options.perfReport != null) { |
162 String json = makePerfReport( | 213 String json = makePerfReport( |
163 startTime, currentTimeMillis, options, _analyzedFileCount, stats); | 214 startTime, currentTimeMillis, options, _analyzedFileCount, stats); |
164 new io.File(options.perfReport).writeAsStringSync(json); | 215 new io.File(options.perfReport).writeAsStringSync(json); |
165 } | 216 } |
| 217 |
| 218 // Wait a brief time for any analytics calls to finish. |
| 219 await analytics.waitForLastPing(timeout: new Duration(milliseconds: 200)); |
| 220 analytics.close(); |
166 } | 221 } |
167 | 222 |
168 Future<ErrorSeverity> _analyzeAll(CommandLineOptions options) async { | 223 Future<ErrorSeverity> _analyzeAll(CommandLineOptions options) async { |
169 PerformanceTag previous = _analyzeAllTag.makeCurrent(); | 224 PerformanceTag previous = _analyzeAllTag.makeCurrent(); |
170 try { | 225 try { |
171 return await _analyzeAllImpl(options); | 226 return await _analyzeAllImpl(options); |
| 227 } catch (e, st) { |
| 228 crashReportSender.sendReport(e, stackTrace: st); |
| 229 rethrow; |
172 } finally { | 230 } finally { |
173 previous.makeCurrent(); | 231 previous.makeCurrent(); |
174 } | 232 } |
175 } | 233 } |
176 | 234 |
177 /// Perform analysis according to the given [options]. | 235 /// Perform analysis according to the given [options]. |
178 Future<ErrorSeverity> _analyzeAllImpl(CommandLineOptions options) async { | 236 Future<ErrorSeverity> _analyzeAllImpl(CommandLineOptions options) async { |
179 if (!options.machineFormat) { | 237 if (!options.machineFormat) { |
180 List<String> fileNames = options.sourceFiles.map((String file) { | 238 List<String> fileNames = options.sourceFiles.map((String file) { |
181 file = path.normalize(file); | 239 file = path.normalize(file); |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 return new BuildMode(resourceProvider, options, stats).analyze(); | 372 return new BuildMode(resourceProvider, options, stats).analyze(); |
315 } | 373 } |
316 }); | 374 }); |
317 } | 375 } |
318 | 376 |
319 /// Decide on the appropriate policy for which files need to be fully parsed | 377 /// Decide on the appropriate policy for which files need to be fully parsed |
320 /// and which files need to be diet parsed, based on [options], and return an | 378 /// and which files need to be diet parsed, based on [options], and return an |
321 /// [AnalyzeFunctionBodiesPredicate] that implements this policy. | 379 /// [AnalyzeFunctionBodiesPredicate] that implements this policy. |
322 AnalyzeFunctionBodiesPredicate _chooseDietParsingPolicy( | 380 AnalyzeFunctionBodiesPredicate _chooseDietParsingPolicy( |
323 CommandLineOptions options) { | 381 CommandLineOptions options) { |
324 if (options.shouldBatch) { | 382 if (options.batchMode) { |
325 // As analyzer is currently implemented, once a file has been diet | 383 // As analyzer is currently implemented, once a file has been diet |
326 // parsed, it can't easily be un-diet parsed without creating a brand new | 384 // parsed, it can't easily be un-diet parsed without creating a brand new |
327 // context and losing caching. In batch mode, we can't predict which | 385 // context and losing caching. In batch mode, we can't predict which |
328 // files we'll need to generate errors and warnings for in the future, so | 386 // files we'll need to generate errors and warnings for in the future, so |
329 // we can't safely diet parse anything. | 387 // we can't safely diet parse anything. |
330 return (Source source) => true; | 388 return (Source source) => true; |
331 } | 389 } |
332 | 390 |
333 return (Source source) { | 391 return (Source source) { |
334 if (options.sourceFiles.contains(source.fullName)) { | 392 if (options.sourceFiles.contains(source.fullName)) { |
(...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
932 for (var package in packages) { | 990 for (var package in packages) { |
933 var packageName = path.basename(package.path); | 991 var packageName = path.basename(package.path); |
934 var realPath = package.resolveSymbolicLinksSync(); | 992 var realPath = package.resolveSymbolicLinksSync(); |
935 result[packageName] = [ | 993 result[packageName] = [ |
936 PhysicalResourceProvider.INSTANCE.getFolder(realPath) | 994 PhysicalResourceProvider.INSTANCE.getFolder(realPath) |
937 ]; | 995 ]; |
938 } | 996 } |
939 return result; | 997 return result; |
940 } | 998 } |
941 } | 999 } |
OLD | NEW |