Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: pkg/analyzer_cli/lib/src/driver.dart

Issue 2840703002: Refactoring analyzer_cli for code hygiene. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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.driver;
6
7 import 'dart:async';
8 import 'dart:convert';
9 import 'dart:io' as io;
10
11 import 'package:analyzer/error/error.dart';
12 import 'package:analyzer/file_system/file_system.dart' as file_system;
13 import 'package:analyzer/file_system/file_system.dart';
14 import 'package:analyzer/file_system/physical_file_system.dart';
15 import 'package:analyzer/plugin/resolver_provider.dart';
16 import 'package:analyzer/source/package_map_provider.dart';
17 import 'package:analyzer/source/package_map_resolver.dart';
18 import 'package:analyzer/source/pub_package_map_provider.dart';
19 import 'package:analyzer/source/sdk_ext.dart';
20 import 'package:analyzer/src/context/builder.dart';
21 import 'package:analyzer/src/dart/analysis/byte_store.dart';
22 import 'package:analyzer/src/dart/analysis/driver.dart';
23 import 'package:analyzer/src/dart/analysis/file_state.dart';
24 import 'package:analyzer/src/dart/sdk/sdk.dart';
25 import 'package:analyzer/src/generated/constant.dart';
26 import 'package:analyzer/src/generated/engine.dart';
27 import 'package:analyzer/src/generated/interner.dart';
28 import 'package:analyzer/src/generated/java_engine.dart';
29 import 'package:analyzer/src/generated/sdk.dart';
30 import 'package:analyzer/src/generated/source.dart';
31 import 'package:analyzer/src/generated/source_io.dart';
32 import 'package:analyzer/src/generated/utilities_general.dart'
33 show PerformanceTag;
34 import 'package:analyzer/src/source/source_resource.dart';
35 import 'package:analyzer/src/summary/idl.dart';
36 import 'package:analyzer/src/summary/package_bundle_reader.dart';
37 import 'package:analyzer/src/summary/summary_sdk.dart' show SummaryBasedDartSdk;
38 import 'package:analyzer_cli/src/analyzer_impl.dart';
39 import 'package:analyzer_cli/src/build_mode.dart';
40 import 'package:analyzer_cli/src/error_formatter.dart';
41 import 'package:analyzer_cli/src/error_severity.dart';
42 import 'package:analyzer_cli/src/options.dart';
43 import 'package:analyzer_cli/src/perf_report.dart';
44 import 'package:analyzer_cli/starter.dart' show CommandLineStarter;
45 import 'package:linter/src/rules.dart' as linter;
46 import 'package:package_config/discovery.dart' as pkg_discovery;
47 import 'package:package_config/packages.dart' show Packages;
48 import 'package:package_config/packages_file.dart' as pkgfile show parse;
49 import 'package:package_config/src/packages_impl.dart' show MapPackages;
50 import 'package:path/path.dart' as path;
51 import 'package:plugin/manager.dart';
52 import 'package:plugin/plugin.dart';
53 import 'package:yaml/yaml.dart';
54
55 /// Shared IO sink for standard error reporting.
56 ///
57 /// *Visible for testing.*
58 StringSink errorSink = io.stderr;
59
60 /// Shared IO sink for standard out reporting.
61 ///
62 /// *Visible for testing.*
63 StringSink outSink = io.stdout;
64
65 /// Test this option map to see if it specifies lint rules.
66 bool containsLintRuleEntry(Map<String, YamlNode> options) {
67 var linterNode = options['linter'];
68 return linterNode is YamlMap && linterNode.containsKey('rules');
69 }
70
71 typedef Future<ErrorSeverity> _BatchRunnerHandler(List<String> args);
72
73 class Driver implements CommandLineStarter {
74 static final PerformanceTag _analyzeAllTag =
75 new PerformanceTag("Driver._analyzeAll");
76
77 static ByteStore analysisDriverMemoryByteStore = new MemoryByteStore();
78
79 /// The plugins that are defined outside the `analyzer_cli` package.
80 List<Plugin> _userDefinedPlugins = <Plugin>[];
81
82 /// The context that was most recently created by a call to [_analyzeAll], or
83 /// `null` if [_analyzeAll] hasn't been called yet.
84 InternalAnalysisContext _context;
85
86 AnalysisDriver analysisDriver;
87
88 /// The total number of source files loaded by an AnalysisContext.
89 int _analyzedFileCount = 0;
90
91 /// If [_context] is not `null`, the [CommandLineOptions] that guided its
92 /// creation.
93 CommandLineOptions _previousOptions;
94
95 @override
96 ResolverProvider packageResolverProvider;
97
98 /// SDK instance.
99 DartSdk sdk;
100
101 /**
102 * The resource provider used to access the file system.
103 */
104 file_system.ResourceProvider resourceProvider =
105 PhysicalResourceProvider.INSTANCE;
106
107 /// Collected analysis statistics.
108 final AnalysisStats stats = new AnalysisStats();
109
110 /// This Driver's current analysis context.
111 ///
112 /// *Visible for testing.*
113 AnalysisContext get context => _context;
114
115 @override
116 void set userDefinedPlugins(List<Plugin> plugins) {
117 _userDefinedPlugins = plugins ?? <Plugin>[];
118 }
119
120 @override
121 Future<Null> start(List<String> args) async {
122 if (_context != null) {
123 throw new StateError("start() can only be called once");
124 }
125 int startTime = new DateTime.now().millisecondsSinceEpoch;
126
127 StringUtilities.INTERNER = new MappedInterner();
128
129 _processPlugins();
130
131 // Parse commandline options.
132 CommandLineOptions options = CommandLineOptions.parse(args);
133
134 // Do analysis.
135 if (options.buildMode) {
136 ErrorSeverity severity = _buildModeAnalyze(options);
137 // In case of error propagate exit code.
138 if (severity == ErrorSeverity.ERROR) {
139 io.exitCode = severity.ordinal;
140 }
141 } else if (options.shouldBatch) {
142 _BatchRunner.runAsBatch(args, (List<String> args) async {
143 CommandLineOptions options = CommandLineOptions.parse(args);
144 return await _analyzeAll(options);
145 });
146 } else {
147 ErrorSeverity severity = await _analyzeAll(options);
148 // In case of error propagate exit code.
149 if (severity == ErrorSeverity.ERROR) {
150 io.exitCode = severity.ordinal;
151 }
152 }
153
154 if (_context != null) {
155 _analyzedFileCount += _context.sources.length;
156 }
157
158 if (options.perfReport != null) {
159 String json = makePerfReport(
160 startTime, currentTimeMillis, options, _analyzedFileCount, stats);
161 new io.File(options.perfReport).writeAsStringSync(json);
162 }
163 }
164
165 Future<ErrorSeverity> _analyzeAll(CommandLineOptions options) async {
166 PerformanceTag previous = _analyzeAllTag.makeCurrent();
167 try {
168 return await _analyzeAllImpl(options);
169 } finally {
170 previous.makeCurrent();
171 }
172 }
173
174 /// Perform analysis according to the given [options].
175 Future<ErrorSeverity> _analyzeAllImpl(CommandLineOptions options) async {
176 if (!options.machineFormat) {
177 List<String> fileNames = options.sourceFiles.map((String file) {
178 file = path.normalize(file);
179 if (file == '.') {
180 file = path.basename(path.current);
181 } else if (file == '..') {
182 file = path.basename(path.normalize(path.absolute(file)));
183 }
184 return file;
185 }).toList();
186
187 outSink.writeln("Analyzing ${fileNames.join(', ')}...");
188 }
189
190 // Create a context, or re-use the previous one.
191 try {
192 _createAnalysisContext(options);
193 } on _DriverError catch (error) {
194 outSink.writeln(error.msg);
195 return ErrorSeverity.ERROR;
196 }
197
198 // Add all the files to be analyzed en masse to the context. Skip any
199 // files that were added earlier (whether explicitly or implicitly) to
200 // avoid causing those files to be unnecessarily re-read.
201 Set<Source> knownSources = context.sources.toSet();
202 Set<Source> sourcesToAnalyze = new Set<Source>();
203 ChangeSet changeSet = new ChangeSet();
204 for (String sourcePath in options.sourceFiles) {
205 sourcePath = sourcePath.trim();
206
207 // Collect files for analysis.
208 // Note that these files will all be analyzed in the same context.
209 // This should be updated when the ContextManager re-work is complete
210 // (See: https://github.com/dart-lang/sdk/issues/24133)
211 Iterable<io.File> files = _collectFiles(sourcePath);
212 if (files.isEmpty) {
213 errorSink.writeln('No dart files found at: $sourcePath');
214 io.exitCode = ErrorSeverity.ERROR.ordinal;
215 return ErrorSeverity.ERROR;
216 }
217
218 for (io.File file in files) {
219 Source source = _computeLibrarySource(file.absolute.path);
220 if (!knownSources.contains(source)) {
221 changeSet.addedSource(source);
222 }
223 sourcesToAnalyze.add(source);
224 }
225
226 if (analysisDriver == null) {
227 context.applyChanges(changeSet);
228 }
229 }
230
231 // Analyze the libraries.
232 ErrorSeverity allResult = ErrorSeverity.NONE;
233 List<Uri> libUris = <Uri>[];
234 Set<Source> partSources = new Set<Source>();
235
236 SeverityProcessor defaultSeverityProcessor = (AnalysisError error) {
237 return determineProcessedSeverity(
238 error, options, _context.analysisOptions);
239 };
240
241 // We currently print out to stderr to ensure that when in batch mode we
242 // print to stderr, this is because the prints from batch are made to
243 // stderr. The reason that options.shouldBatch isn't used is because when
244 // the argument flags are constructed in BatchRunner and passed in from
245 // batch mode which removes the batch flag to prevent the "cannot have the
246 // batch flag and source file" error message.
247 ErrorFormatter formatter;
248 if (options.machineFormat) {
249 formatter = new MachineErrorFormatter(errorSink, options, stats,
250 severityProcessor: defaultSeverityProcessor);
251 } else {
252 formatter = new HumanErrorFormatter(outSink, options, stats,
253 severityProcessor: defaultSeverityProcessor);
254 }
255
256 for (Source source in sourcesToAnalyze) {
257 SourceKind sourceKind = analysisDriver != null
258 ? await analysisDriver.getSourceKind(source.fullName)
259 : context.computeKindOf(source);
260 if (sourceKind == SourceKind.PART) {
261 partSources.add(source);
262 continue;
263 }
264 ErrorSeverity status = await _runAnalyzer(source, options, formatter);
265 allResult = allResult.max(status);
266 libUris.add(source.uri);
267 }
268
269 formatter.flush();
270
271 // Check that each part has a corresponding source in the input list.
272 for (Source partSource in partSources) {
273 bool found = false;
274 if (analysisDriver != null) {
275 var partFile =
276 analysisDriver.fsState.getFileForPath(partSource.fullName);
277 if (libUris.contains(partFile.library?.uri)) {
278 found = true;
279 }
280 } else {
281 for (var lib in context.getLibrariesContaining(partSource)) {
282 if (libUris.contains(lib.uri)) {
283 found = true;
284 }
285 }
286 }
287 if (!found) {
288 errorSink.writeln(
289 "${partSource.fullName} is a part and cannot be analyzed.");
290 errorSink.writeln("Please pass in a library that contains this part.");
291 io.exitCode = ErrorSeverity.ERROR.ordinal;
292 allResult = allResult.max(ErrorSeverity.ERROR);
293 }
294 }
295
296 if (!options.machineFormat) {
297 stats.print(outSink);
298 }
299
300 return allResult;
301 }
302
303 /// Perform analysis in build mode according to the given [options].
304 ErrorSeverity _buildModeAnalyze(CommandLineOptions options) {
305 return _analyzeAllTag.makeCurrentWhile(() {
306 if (options.buildModePersistentWorker) {
307 new AnalyzerWorkerLoop.std(resourceProvider,
308 dartSdkPath: options.dartSdkPath)
309 .run();
310 } else {
311 return new BuildMode(resourceProvider, options, stats).analyze();
312 }
313 });
314 }
315
316 /// Determine whether the context created during a previous call to
317 /// [_analyzeAll] can be re-used in order to analyze using [options].
318 bool _canContextBeReused(CommandLineOptions options) {
319 // TODO(paulberry): add a command-line option that disables context re-use.
320 if (_context == null) {
321 return false;
322 }
323 if (options.packageRootPath != _previousOptions.packageRootPath) {
324 return false;
325 }
326 if (options.packageConfigPath != _previousOptions.packageConfigPath) {
327 return false;
328 }
329 if (!_equalMaps(
330 options.definedVariables, _previousOptions.definedVariables)) {
331 return false;
332 }
333 if (options.log != _previousOptions.log) {
334 return false;
335 }
336 if (options.disableHints != _previousOptions.disableHints) {
337 return false;
338 }
339 if (options.enableStrictCallChecks !=
340 _previousOptions.enableStrictCallChecks) {
341 return false;
342 }
343 if (options.enableAssertInitializer !=
344 _previousOptions.enableAssertInitializer) {
345 return false;
346 }
347 if (options.showPackageWarnings != _previousOptions.showPackageWarnings) {
348 return false;
349 }
350 if (options.showPackageWarningsPrefix !=
351 _previousOptions.showPackageWarningsPrefix) {
352 return false;
353 }
354 if (options.showSdkWarnings != _previousOptions.showSdkWarnings) {
355 return false;
356 }
357 if (options.lints != _previousOptions.lints) {
358 return false;
359 }
360 if (options.strongMode != _previousOptions.strongMode) {
361 return false;
362 }
363 if (options.enableSuperMixins != _previousOptions.enableSuperMixins) {
364 return false;
365 }
366 if (!_equalLists(
367 options.buildSummaryInputs, _previousOptions.buildSummaryInputs)) {
368 return false;
369 }
370 if (options.disableCacheFlushing != _previousOptions.disableCacheFlushing) {
371 return false;
372 }
373 return true;
374 }
375
376 /// Decide on the appropriate policy for which files need to be fully parsed
377 /// and which files need to be diet parsed, based on [options], and return an
378 /// [AnalyzeFunctionBodiesPredicate] that implements this policy.
379 AnalyzeFunctionBodiesPredicate _chooseDietParsingPolicy(
380 CommandLineOptions options) {
381 if (options.shouldBatch) {
382 // As analyzer is currently implemented, once a file has been diet
383 // parsed, it can't easily be un-diet parsed without creating a brand new
384 // context and losing caching. In batch mode, we can't predict which
385 // files we'll need to generate errors and warnings for in the future, so
386 // we can't safely diet parse anything.
387 return (Source source) => true;
388 }
389
390 return (Source source) {
391 if (options.sourceFiles.contains(source.fullName)) {
392 return true;
393 } else if (source.uri.scheme == 'dart') {
394 return options.showSdkWarnings;
395 } else {
396 // TODO(paulberry): diet parse 'package:' imports when we don't want
397 // diagnostics. (Full parse is still needed for "self" packages.)
398 return true;
399 }
400 };
401 }
402
403 /// Decide on the appropriate method for resolving URIs based on the given
404 /// [options] and [customUrlMappings] settings, and return a
405 /// [SourceFactory] that has been configured accordingly.
406 /// When [includeSdkResolver] is `false`, return a temporary [SourceFactory]
407 /// for the purpose of resolved analysis options file `include:` directives.
408 /// In this situation, [analysisOptions] is ignored and can be `null`.
409 SourceFactory _chooseUriResolutionPolicy(
410 CommandLineOptions options,
411 Map<file_system.Folder, YamlMap> embedderMap,
412 _PackageInfo packageInfo,
413 SummaryDataStore summaryDataStore,
414 bool includeSdkResolver,
415 AnalysisOptions analysisOptions) {
416 // Create a custom package resolver if one has been specified.
417 if (packageResolverProvider != null) {
418 file_system.Folder folder = resourceProvider.getResource('.');
419 UriResolver resolver = packageResolverProvider(folder);
420 if (resolver != null) {
421 // TODO(brianwilkerson) This doesn't handle sdk extensions.
422 List<UriResolver> resolvers = <UriResolver>[];
423 if (includeSdkResolver) {
424 resolvers.add(new DartUriResolver(sdk));
425 }
426 resolvers
427 .add(new InSummaryUriResolver(resourceProvider, summaryDataStore));
428 resolvers.add(resolver);
429 resolvers.add(new file_system.ResourceUriResolver(resourceProvider));
430 return new SourceFactory(resolvers);
431 }
432 }
433
434 UriResolver packageUriResolver;
435
436 if (options.packageRootPath != null) {
437 ContextBuilderOptions builderOptions = new ContextBuilderOptions();
438 builderOptions.defaultPackagesDirectoryPath = options.packageRootPath;
439 ContextBuilder builder = new ContextBuilder(resourceProvider, null, null,
440 options: builderOptions);
441 packageUriResolver = new PackageMapUriResolver(resourceProvider,
442 builder.convertPackagesToMap(builder.createPackageMap('')));
443 } else if (options.packageConfigPath == null) {
444 // TODO(pq): remove?
445 if (packageInfo.packageMap == null) {
446 // Fall back to pub list-package-dirs.
447 PubPackageMapProvider pubPackageMapProvider =
448 new PubPackageMapProvider(resourceProvider, sdk);
449 file_system.Resource cwd = resourceProvider.getResource('.');
450 PackageMapInfo packageMapInfo =
451 pubPackageMapProvider.computePackageMap(cwd);
452 Map<String, List<file_system.Folder>> packageMap =
453 packageMapInfo.packageMap;
454
455 // Only create a packageUriResolver if pub list-package-dirs succeeded.
456 // If it failed, that's not a problem; it simply means we have no way
457 // to resolve packages.
458 if (packageMapInfo.packageMap != null) {
459 packageUriResolver =
460 new PackageMapUriResolver(resourceProvider, packageMap);
461 }
462 }
463 }
464
465 // Now, build our resolver list.
466 List<UriResolver> resolvers = [];
467
468 // 'dart:' URIs come first.
469
470 // Setup embedding.
471 if (includeSdkResolver) {
472 EmbedderSdk embedderSdk = new EmbedderSdk(resourceProvider, embedderMap);
473 if (embedderSdk.libraryMap.size() == 0) {
474 // The embedder uri resolver has no mappings. Use the default Dart SDK
475 // uri resolver.
476 resolvers.add(new DartUriResolver(sdk));
477 } else {
478 // The embedder uri resolver has mappings, use it instead of the default
479 // Dart SDK uri resolver.
480 embedderSdk.analysisOptions = analysisOptions;
481 resolvers.add(new DartUriResolver(embedderSdk));
482 }
483 }
484
485 // Next SdkExts.
486 if (packageInfo.packageMap != null) {
487 resolvers.add(new SdkExtUriResolver(packageInfo.packageMap));
488 }
489
490 // Then package URIs from summaries.
491 resolvers.add(new InSummaryUriResolver(resourceProvider, summaryDataStore));
492
493 // Then package URIs.
494 if (packageUriResolver != null) {
495 resolvers.add(packageUriResolver);
496 }
497
498 // Finally files.
499 resolvers.add(new file_system.ResourceUriResolver(resourceProvider));
500
501 return new SourceFactory(resolvers, packageInfo.packages);
502 }
503
504 // TODO(devoncarew): This needs to respect analysis_options excludes.
505
506 /// Collect all analyzable files at [filePath], recursively if it's a
507 /// directory, ignoring links.
508 Iterable<io.File> _collectFiles(String filePath) {
509 List<io.File> files = <io.File>[];
510 io.File file = new io.File(filePath);
511 if (file.existsSync()) {
512 files.add(file);
513 } else {
514 io.Directory directory = new io.Directory(filePath);
515 if (directory.existsSync()) {
516 for (io.FileSystemEntity entry
517 in directory.listSync(recursive: true, followLinks: false)) {
518 String relative = path.relative(entry.path, from: directory.path);
519 if (AnalysisEngine.isDartFileName(entry.path) &&
520 !_isInHiddenDir(relative)) {
521 files.add(entry);
522 }
523 }
524 }
525 }
526 return files;
527 }
528
529 /// Convert the given [sourcePath] (which may be relative to the current
530 /// working directory) to a [Source] object that can be fed to the analysis
531 /// context.
532 Source _computeLibrarySource(String sourcePath) {
533 sourcePath = _normalizeSourcePath(sourcePath);
534 File sourceFile = resourceProvider.getFile(sourcePath);
535 Source source = sdk.fromFileUri(sourceFile.toUri());
536 if (source != null) {
537 return source;
538 }
539 source = new FileSource(sourceFile, sourceFile.toUri());
540 Uri uri = _context.sourceFactory.restoreUri(source);
541 if (uri == null) {
542 return source;
543 }
544 return new FileSource(sourceFile, uri);
545 }
546
547 /// Create an analysis context that is prepared to analyze sources according
548 /// to the given [options], and store it in [_context].
549 void _createAnalysisContext(CommandLineOptions options) {
550 if (_canContextBeReused(options)) {
551 return;
552 }
553 _previousOptions = options;
554
555 // Save stats from previous context before clobbering it.
556 if (_context != null) {
557 _analyzedFileCount += _context.sources.length;
558 }
559
560 // Find package info.
561 _PackageInfo packageInfo = _findPackages(options);
562
563 // Process embedders.
564 Map<file_system.Folder, YamlMap> embedderMap =
565 new EmbedderYamlLocator(packageInfo.packageMap).embedderYamls;
566
567 // Scan for SDK extenders.
568 bool hasSdkExt = _hasSdkExt(packageInfo.packageMap?.values);
569
570 // No summaries in the presence of embedders or extenders.
571 bool useSummaries = embedderMap.isEmpty && !hasSdkExt;
572
573 if (!useSummaries && options.buildSummaryInputs.isNotEmpty) {
574 throw new _DriverError(
575 'Summaries are not yet supported when using Flutter.');
576 }
577
578 // Read any input summaries.
579 SummaryDataStore summaryDataStore = new SummaryDataStore(
580 useSummaries ? options.buildSummaryInputs : <String>[]);
581
582 AnalysisOptionsImpl analysisOptions =
583 createAnalysisOptionsForCommandLineOptions(resourceProvider, options);
584 analysisOptions.analyzeFunctionBodiesPredicate =
585 _chooseDietParsingPolicy(options);
586
587 // Once options and embedders are processed, setup the SDK.
588 _setupSdk(options, useSummaries, analysisOptions);
589
590 PackageBundle sdkBundle = sdk.getLinkedBundle();
591 if (sdkBundle != null) {
592 summaryDataStore.addBundle(null, sdkBundle);
593 }
594
595 // Choose a package resolution policy and a diet parsing policy based on
596 // the command-line options.
597 SourceFactory sourceFactory = _chooseUriResolutionPolicy(options,
598 embedderMap, packageInfo, summaryDataStore, true, analysisOptions);
599
600 // Create a context.
601 _context = AnalysisEngine.instance.createAnalysisContext();
602 setupAnalysisContext(_context, options, analysisOptions);
603 _context.sourceFactory = sourceFactory;
604
605 if (options.enableNewAnalysisDriver) {
606 PerformanceLog log = new PerformanceLog(null);
607 AnalysisDriverScheduler scheduler = new AnalysisDriverScheduler(log);
608 analysisDriver = new AnalysisDriver(
609 scheduler,
610 log,
611 resourceProvider,
612 analysisDriverMemoryByteStore,
613 new FileContentOverlay(),
614 null,
615 context.sourceFactory,
616 context.analysisOptions);
617 analysisDriver.results.listen((_) {});
618 analysisDriver.exceptions.listen((_) {});
619 scheduler.start();
620 } else {
621 if (sdkBundle != null) {
622 _context.resultProvider =
623 new InputPackagesResultProvider(_context, summaryDataStore);
624 }
625 }
626 }
627
628 /// Return discovered packagespec, or `null` if none is found.
629 Packages _discoverPackagespec(Uri root) {
630 try {
631 Packages packages = pkg_discovery.findPackagesFromFile(root);
632 if (packages != Packages.noPackages) {
633 return packages;
634 }
635 } catch (_) {
636 // Ignore and fall through to null.
637 }
638
639 return null;
640 }
641
642 _PackageInfo _findPackages(CommandLineOptions options) {
643 if (packageResolverProvider != null) {
644 // The resolver provider will do all the work later.
645 return new _PackageInfo(null, null);
646 }
647
648 Packages packages;
649 Map<String, List<file_system.Folder>> packageMap;
650
651 if (options.packageConfigPath != null) {
652 String packageConfigPath = options.packageConfigPath;
653 Uri fileUri = new Uri.file(packageConfigPath);
654 try {
655 io.File configFile = new io.File.fromUri(fileUri).absolute;
656 List<int> bytes = configFile.readAsBytesSync();
657 Map<String, Uri> map = pkgfile.parse(bytes, configFile.uri);
658 packages = new MapPackages(map);
659 packageMap = _getPackageMap(packages);
660 } catch (e) {
661 printAndFail(
662 'Unable to read package config data from $packageConfigPath: $e');
663 }
664 } else if (options.packageRootPath != null) {
665 packageMap = _PackageRootPackageMapBuilder
666 .buildPackageMap(options.packageRootPath);
667 } else {
668 file_system.Resource cwd = resourceProvider.getResource('.');
669 // Look for .packages.
670 packages = _discoverPackagespec(new Uri.directory(cwd.path));
671 packageMap = _getPackageMap(packages);
672 }
673
674 return new _PackageInfo(packages, packageMap);
675 }
676
677 Map<String, List<file_system.Folder>> _getPackageMap(Packages packages) {
678 if (packages == null) {
679 return null;
680 }
681
682 Map<String, List<file_system.Folder>> folderMap =
683 new Map<String, List<file_system.Folder>>();
684 packages.asMap().forEach((String packagePath, Uri uri) {
685 folderMap[packagePath] = [resourceProvider.getFolder(path.fromUri(uri))];
686 });
687 return folderMap;
688 }
689
690 bool _hasSdkExt(Iterable<List<file_system.Folder>> folders) {
691 if (folders != null) {
692 //TODO: ideally share this traversal with SdkExtUriResolver
693 for (Iterable<file_system.Folder> libDirs in folders) {
694 if (libDirs.any((file_system.Folder libDir) =>
695 libDir.getChild(SdkExtUriResolver.SDK_EXT_NAME).exists)) {
696 return true;
697 }
698 }
699 }
700 return false;
701 }
702
703 /// Returns `true` if this relative path is a hidden directory.
704 bool _isInHiddenDir(String relative) =>
705 path.split(relative).any((part) => part.startsWith("."));
706
707 void _processPlugins() {
708 List<Plugin> plugins = <Plugin>[];
709 plugins.addAll(AnalysisEngine.instance.requiredPlugins);
710 plugins.addAll(_userDefinedPlugins);
711
712 ExtensionManager manager = new ExtensionManager();
713 manager.processPlugins(plugins);
714
715 linter.registerLintRules();
716 }
717
718 /// Analyze a single source.
719 Future<ErrorSeverity> _runAnalyzer(Source source, CommandLineOptions options,
720 ErrorFormatter formatter) async {
721 int startTime = currentTimeMillis;
722 AnalyzerImpl analyzer = new AnalyzerImpl(_context.analysisOptions, _context,
723 analysisDriver, source, options, stats, startTime);
724 ErrorSeverity errorSeverity = await analyzer.analyze(formatter);
725 if (errorSeverity == ErrorSeverity.ERROR) {
726 io.exitCode = errorSeverity.ordinal;
727 }
728 if (options.warningsAreFatal && errorSeverity == ErrorSeverity.WARNING) {
729 io.exitCode = errorSeverity.ordinal;
730 }
731 return errorSeverity;
732 }
733
734 void _setupSdk(CommandLineOptions options, bool useSummaries,
735 AnalysisOptions analysisOptions) {
736 if (sdk == null) {
737 if (options.dartSdkSummaryPath != null) {
738 sdk = new SummaryBasedDartSdk(
739 options.dartSdkSummaryPath, options.strongMode);
740 } else {
741 String dartSdkPath = options.dartSdkPath;
742 FolderBasedDartSdk dartSdk = new FolderBasedDartSdk(resourceProvider,
743 resourceProvider.getFolder(dartSdkPath), options.strongMode);
744 dartSdk.useSummary = useSummaries &&
745 options.sourceFiles.every((String sourcePath) {
746 sourcePath = path.absolute(sourcePath);
747 sourcePath = path.normalize(sourcePath);
748 return !path.isWithin(dartSdkPath, sourcePath);
749 });
750 dartSdk.analysisOptions = analysisOptions;
751 sdk = dartSdk;
752 }
753 }
754 }
755
756 static AnalysisOptionsImpl createAnalysisOptionsForCommandLineOptions(
757 ResourceProvider resourceProvider, CommandLineOptions options) {
758 if (options.analysisOptionsFile != null) {
759 file_system.File file =
760 resourceProvider.getFile(options.analysisOptionsFile);
761 if (!file.exists) {
762 printAndFail('Options file not found: ${options.analysisOptionsFile}',
763 exitCode: ErrorSeverity.ERROR.ordinal);
764 }
765 }
766
767 String contextRoot;
768 if (options.sourceFiles.isEmpty) {
769 contextRoot = path.current;
770 } else {
771 contextRoot = options.sourceFiles[0];
772 if (!path.isAbsolute(contextRoot)) {
773 contextRoot = path.absolute(contextRoot);
774 }
775 }
776
777 void verbosePrint(String text) {
778 outSink.writeln(text);
779 }
780
781 AnalysisOptionsImpl contextOptions = new ContextBuilder(
782 resourceProvider, null, null,
783 options: options.contextBuilderOptions)
784 .getAnalysisOptions(contextRoot,
785 verbosePrint: options.verbose ? verbosePrint : null);
786
787 contextOptions.trackCacheDependencies = false;
788 contextOptions.disableCacheFlushing = options.disableCacheFlushing;
789 contextOptions.hint = !options.disableHints;
790 contextOptions.generateImplicitErrors = options.showPackageWarnings;
791 contextOptions.generateSdkErrors = options.showSdkWarnings;
792 contextOptions.enableAssertInitializer = options.enableAssertInitializer;
793
794 return contextOptions;
795 }
796
797 static void setAnalysisContextOptions(
798 file_system.ResourceProvider resourceProvider,
799 AnalysisContext context,
800 CommandLineOptions options,
801 void configureContextOptions(AnalysisOptionsImpl contextOptions)) {
802 AnalysisOptionsImpl analysisOptions =
803 createAnalysisOptionsForCommandLineOptions(resourceProvider, options);
804 configureContextOptions(analysisOptions);
805 setupAnalysisContext(context, options, analysisOptions);
806 }
807
808 static void setupAnalysisContext(AnalysisContext context,
809 CommandLineOptions options, AnalysisOptionsImpl analysisOptions) {
810 Map<String, String> definedVariables = options.definedVariables;
811 if (definedVariables.isNotEmpty) {
812 DeclaredVariables declaredVariables = context.declaredVariables;
813 definedVariables.forEach((String variableName, String value) {
814 declaredVariables.define(variableName, value);
815 });
816 }
817
818 if (options.log) {
819 AnalysisEngine.instance.logger = new StdLogger();
820 }
821
822 // Set context options.
823 context.analysisOptions = analysisOptions;
824 }
825
826 /// Perform a deep comparison of two string lists.
827 static bool _equalLists(List<String> l1, List<String> l2) {
828 if (l1.length != l2.length) {
829 return false;
830 }
831 for (int i = 0; i < l1.length; i++) {
832 if (l1[i] != l2[i]) {
833 return false;
834 }
835 }
836 return true;
837 }
838
839 /// Perform a deep comparison of two string maps.
840 static bool _equalMaps(Map<String, String> m1, Map<String, String> m2) {
841 if (m1.length != m2.length) {
842 return false;
843 }
844 for (String key in m1.keys) {
845 if (!m2.containsKey(key) || m1[key] != m2[key]) {
846 return false;
847 }
848 }
849 return true;
850 }
851
852 /// Convert [sourcePath] into an absolute path.
853 static String _normalizeSourcePath(String sourcePath) =>
854 path.normalize(new io.File(sourcePath).absolute.path);
855 }
856
857 /// Provides a framework to read command line options from stdin and feed them
858 /// to a callback.
859 class _BatchRunner {
860 /// Run the tool in 'batch' mode, receiving command lines through stdin and
861 /// returning pass/fail status through stdout. This feature is intended for
862 /// use in unit testing.
863 static void runAsBatch(List<String> sharedArgs, _BatchRunnerHandler handler) {
864 outSink.writeln('>>> BATCH START');
865 Stopwatch stopwatch = new Stopwatch();
866 stopwatch.start();
867 int testsFailed = 0;
868 int totalTests = 0;
869 ErrorSeverity batchResult = ErrorSeverity.NONE;
870 // Read line from stdin.
871 Stream cmdLine =
872 io.stdin.transform(UTF8.decoder).transform(new LineSplitter());
873 cmdLine.listen((String line) async {
874 // Maybe finish.
875 if (line.isEmpty) {
876 var time = stopwatch.elapsedMilliseconds;
877 outSink.writeln(
878 '>>> BATCH END (${totalTests - testsFailed}/$totalTests) ${time}ms') ;
879 io.exitCode = batchResult.ordinal;
880 }
881 // Prepare arguments.
882 var lineArgs = line.split(new RegExp('\\s+'));
883 var args = new List<String>();
884 args.addAll(sharedArgs);
885 args.addAll(lineArgs);
886 args.remove('-b');
887 args.remove('--batch');
888 // Analyze single set of arguments.
889 try {
890 totalTests++;
891 ErrorSeverity result = await handler(args);
892 bool resultPass = result != ErrorSeverity.ERROR;
893 if (!resultPass) {
894 testsFailed++;
895 }
896 batchResult = batchResult.max(result);
897 // Write stderr end token and flush.
898 errorSink.writeln('>>> EOF STDERR');
899 String resultPassString = resultPass ? 'PASS' : 'FAIL';
900 outSink.writeln(
901 '>>> TEST $resultPassString ${stopwatch.elapsedMilliseconds}ms');
902 } catch (e, stackTrace) {
903 errorSink.writeln(e);
904 errorSink.writeln(stackTrace);
905 errorSink.writeln('>>> EOF STDERR');
906 outSink.writeln('>>> TEST CRASH');
907 }
908 });
909 }
910 }
911
912 class _DriverError implements Exception {
913 String msg;
914 _DriverError(this.msg);
915 }
916
917 class _PackageInfo {
918 Packages packages;
919 Map<String, List<file_system.Folder>> packageMap;
920 _PackageInfo(this.packages, this.packageMap);
921 }
922
923 /// [SdkExtUriResolver] needs a Map from package name to folder. In the case
924 /// that the analyzer is invoked with a --package-root option, we need to
925 /// manually create this mapping. Given [packageRootPath],
926 /// [_PackageRootPackageMapBuilder] creates a simple mapping from package name
927 /// to full path on disk (resolving any symbolic links).
928 class _PackageRootPackageMapBuilder {
929 static Map<String, List<file_system.Folder>> buildPackageMap(
930 String packageRootPath) {
931 var packageRoot = new io.Directory(packageRootPath);
932 if (!packageRoot.existsSync()) {
933 throw new _DriverError(
934 'Package root directory ($packageRootPath) does not exist.');
935 }
936 var packages = packageRoot.listSync(followLinks: false);
937 var result = new Map<String, List<file_system.Folder>>();
938 for (var package in packages) {
939 var packageName = path.basename(package.path);
940 var realPath = package.resolveSymbolicLinksSync();
941 result[packageName] = [
942 PhysicalResourceProvider.INSTANCE.getFolder(realPath)
943 ];
944 }
945 return result;
946 }
947 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698