fb0578159e
See https://github.com/dart-lang/linter/pull/2189#issuecomment-665984938 Change-Id: I6becb616dd8bcb0c9945ea5ad5ac6119bb93e330 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/156441 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
746 lines
26 KiB
Dart
746 lines
26 KiB
Dart
// 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:async';
|
|
import 'dart:io' as io;
|
|
import 'dart:isolate';
|
|
|
|
import 'package:analyzer/dart/analysis/context_locator.dart' as api;
|
|
import 'package:analyzer/dart/sdk/build_sdk_summary.dart';
|
|
import 'package:analyzer/error/error.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
import 'package:analyzer/src/context/builder.dart';
|
|
import 'package:analyzer/src/context/context_root.dart';
|
|
import 'package:analyzer/src/context/packages.dart';
|
|
import 'package:analyzer/src/dart/analysis/byte_store.dart';
|
|
import 'package:analyzer/src/dart/analysis/driver.dart';
|
|
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'
|
|
as api;
|
|
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
|
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
|
|
import 'package:analyzer/src/dart/analysis/results.dart';
|
|
import 'package:analyzer/src/dart/sdk/sdk.dart';
|
|
import 'package:analyzer/src/file_system/file_system.dart';
|
|
import 'package:analyzer/src/generated/engine.dart';
|
|
import 'package:analyzer/src/generated/interner.dart';
|
|
import 'package:analyzer/src/generated/java_engine.dart';
|
|
import 'package:analyzer/src/generated/sdk.dart';
|
|
import 'package:analyzer/src/generated/source.dart';
|
|
import 'package:analyzer/src/generated/utilities_general.dart'
|
|
show PerformanceTag;
|
|
import 'package:analyzer/src/manifest/manifest_validator.dart';
|
|
import 'package:analyzer/src/pubspec/pubspec_validator.dart';
|
|
import 'package:analyzer/src/source/package_map_resolver.dart';
|
|
import 'package:analyzer/src/source/path_filter.dart';
|
|
import 'package:analyzer/src/summary/package_bundle_reader.dart';
|
|
import 'package:analyzer/src/summary/summary_sdk.dart' show SummaryBasedDartSdk;
|
|
import 'package:analyzer/src/task/options.dart';
|
|
import 'package:analyzer/src/util/yaml.dart';
|
|
import 'package:analyzer_cli/src/analyzer_impl.dart';
|
|
import 'package:analyzer_cli/src/batch_mode.dart';
|
|
import 'package:analyzer_cli/src/build_mode.dart';
|
|
import 'package:analyzer_cli/src/context_cache.dart';
|
|
import 'package:analyzer_cli/src/error_formatter.dart';
|
|
import 'package:analyzer_cli/src/error_severity.dart';
|
|
import 'package:analyzer_cli/src/has_context_mixin.dart';
|
|
import 'package:analyzer_cli/src/options.dart';
|
|
import 'package:analyzer_cli/src/perf_report.dart';
|
|
import 'package:analyzer_cli/starter.dart' show CommandLineStarter;
|
|
import 'package:linter/src/rules.dart' as linter;
|
|
import 'package:meta/meta.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:yaml/yaml.dart';
|
|
|
|
/// Shared IO sink for standard error reporting.
|
|
@visibleForTesting
|
|
StringSink errorSink = io.stderr;
|
|
|
|
/// Shared IO sink for standard out reporting.
|
|
@visibleForTesting
|
|
StringSink outSink = io.stdout;
|
|
|
|
/// Test this option map to see if it specifies lint rules.
|
|
bool containsLintRuleEntry(YamlMap options) {
|
|
var linterNode = getValue(options, 'linter');
|
|
return linterNode is YamlMap && getValue(linterNode, 'rules') != null;
|
|
}
|
|
|
|
class Driver with HasContextMixin implements CommandLineStarter {
|
|
static final PerformanceTag _analyzeAllTag =
|
|
PerformanceTag('Driver._analyzeAll');
|
|
|
|
static final ByteStore analysisDriverMemoryByteStore = MemoryByteStore();
|
|
|
|
@override
|
|
ContextCache contextCache;
|
|
|
|
/// The driver that was most recently created by a call to [_analyzeAll], or
|
|
/// `null` if [_analyzeAll] hasn't been called yet.
|
|
@visibleForTesting
|
|
AnalysisDriver analysisDriver;
|
|
|
|
/// The total number of source files loaded by an AnalysisContext.
|
|
int _analyzedFileCount = 0;
|
|
|
|
/// If [analysisDriver] is not `null`, the [CommandLineOptions] that guided
|
|
/// its creation.
|
|
CommandLineOptions _previousOptions;
|
|
|
|
/// SDK instance.
|
|
DartSdk sdk;
|
|
|
|
/// The resource provider used to access the file system.
|
|
@override
|
|
final ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE;
|
|
|
|
/// Collected analysis statistics.
|
|
final AnalysisStats stats = AnalysisStats();
|
|
|
|
/// The [PathFilter] for excluded files with wildcards, etc.
|
|
PathFilter pathFilter;
|
|
|
|
/// Create a new Driver instance.
|
|
///
|
|
/// [isTesting] is true if we're running in a test environment.
|
|
Driver({bool isTesting = false});
|
|
|
|
/// Converts the given [filePath] into absolute and normalized.
|
|
String normalizePath(String filePath) {
|
|
filePath = filePath.trim();
|
|
filePath = resourceProvider.pathContext.absolute(filePath);
|
|
filePath = resourceProvider.pathContext.normalize(filePath);
|
|
return filePath;
|
|
}
|
|
|
|
@override
|
|
Future<void> start(List<String> args, {SendPort sendPort}) async {
|
|
if (analysisDriver != null) {
|
|
throw StateError('start() can only be called once');
|
|
}
|
|
var startTime = DateTime.now().millisecondsSinceEpoch;
|
|
|
|
StringUtilities.INTERNER = MappedInterner();
|
|
|
|
linter.registerLintRules();
|
|
|
|
// Parse commandline options.
|
|
var options = CommandLineOptions.parse(resourceProvider, args);
|
|
|
|
// Do analysis.
|
|
if (options.buildMode) {
|
|
var severity = await _buildModeAnalyze(options, sendPort);
|
|
// Propagate issues to the exit code.
|
|
if (_shouldBeFatal(severity, options)) {
|
|
io.exitCode = severity.ordinal;
|
|
}
|
|
} else if (options.batchMode) {
|
|
var batchRunner = BatchRunner(outSink, errorSink);
|
|
batchRunner.runAsBatch(args, (List<String> args) async {
|
|
// TODO(brianwilkerson) Determine whether this await is necessary.
|
|
await null;
|
|
var options = CommandLineOptions.parse(resourceProvider, args);
|
|
return await _analyzeAll(options);
|
|
});
|
|
} else {
|
|
var severity = await _analyzeAll(options);
|
|
// Propagate issues to the exit code.
|
|
if (_shouldBeFatal(severity, options)) {
|
|
io.exitCode = severity.ordinal;
|
|
}
|
|
}
|
|
|
|
// When training a snapshot, in addition to training regular analysis
|
|
// (above), we train build mode as well.
|
|
if (options.trainSnapshot) {
|
|
// TODO(devoncarew): Iterate on this training to make it more
|
|
// representative of what we see internally; call into _buildModeAnalyze()
|
|
// with some appropriate options.
|
|
print('\nGenerating strong mode summary...');
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
for (var i = 0; i < 3; i++) {
|
|
buildSdkSummary(
|
|
resourceProvider: PhysicalResourceProvider.INSTANCE,
|
|
sdkPath: options.dartSdkPath,
|
|
);
|
|
}
|
|
|
|
print('Done in ${stopwatch.elapsedMilliseconds} ms.');
|
|
}
|
|
|
|
if (analysisDriver != null) {
|
|
_analyzedFileCount += analysisDriver.knownFiles.length;
|
|
}
|
|
|
|
if (options.perfReport != null) {
|
|
var json = makePerfReport(
|
|
startTime, currentTimeMillis, options, _analyzedFileCount, stats);
|
|
io.File(options.perfReport).writeAsStringSync(json);
|
|
}
|
|
}
|
|
|
|
Future<ErrorSeverity> _analyzeAll(CommandLineOptions options) async {
|
|
// TODO(brianwilkerson) Determine whether this await is necessary.
|
|
await null;
|
|
var previous = _analyzeAllTag.makeCurrent();
|
|
try {
|
|
return await _analyzeAllImpl(options);
|
|
} finally {
|
|
previous.makeCurrent();
|
|
}
|
|
}
|
|
|
|
/// Perform analysis according to the given [options].
|
|
Future<ErrorSeverity> _analyzeAllImpl(CommandLineOptions options) async {
|
|
// TODO(brianwilkerson) Determine whether this await is necessary.
|
|
await null;
|
|
if (!options.machineFormat) {
|
|
var fileNames = options.sourceFiles.map((String file) {
|
|
file = path.normalize(file);
|
|
if (file == '.') {
|
|
file = path.basename(path.current);
|
|
} else if (file == '..') {
|
|
file = path.basename(path.normalize(path.absolute(file)));
|
|
}
|
|
return file;
|
|
}).toList();
|
|
|
|
outSink.writeln("Analyzing ${fileNames.join(', ')}...");
|
|
}
|
|
|
|
// These are used to do part file analysis across sources.
|
|
var dartFiles = <String>{};
|
|
var libraryFiles = <FileState>{};
|
|
var danglingParts = <FileState>{};
|
|
|
|
// Note: This references analysisDriver via closure, so it will change over
|
|
// time during the following analysis.
|
|
var defaultSeverityProcessor = (AnalysisError error) {
|
|
return determineProcessedSeverity(
|
|
error, options, analysisDriver.analysisOptions);
|
|
};
|
|
|
|
// We currently print out to stderr to ensure that when in batch mode we
|
|
// print to stderr, this is because the prints from batch are made to
|
|
// stderr. The reason that options.shouldBatch isn't used is because when
|
|
// the argument flags are constructed in BatchRunner and passed in from
|
|
// batch mode which removes the batch flag to prevent the "cannot have the
|
|
// batch flag and source file" error message.
|
|
ErrorFormatter formatter;
|
|
if (options.machineFormat) {
|
|
formatter = MachineErrorFormatter(errorSink, options, stats,
|
|
severityProcessor: defaultSeverityProcessor);
|
|
} else {
|
|
formatter = HumanErrorFormatter(outSink, options, stats,
|
|
severityProcessor: defaultSeverityProcessor);
|
|
}
|
|
|
|
var allResult = ErrorSeverity.NONE;
|
|
|
|
void reportPartError(String partPath) {
|
|
errorSink.writeln('$partPath is a part and cannot be analyzed.');
|
|
errorSink.writeln('Please pass in a library that contains this part.');
|
|
io.exitCode = ErrorSeverity.ERROR.ordinal;
|
|
allResult = allResult.max(ErrorSeverity.ERROR);
|
|
}
|
|
|
|
for (var sourcePath in options.sourceFiles) {
|
|
sourcePath = normalizePath(sourcePath);
|
|
|
|
// Create a context, or re-use the previous one.
|
|
try {
|
|
_createContextAndAnalyze(options, sourcePath);
|
|
} on _DriverError catch (error) {
|
|
outSink.writeln(error.msg);
|
|
return ErrorSeverity.ERROR;
|
|
}
|
|
|
|
// Add all the files to be analyzed en masse to the context. Skip any
|
|
// files that were added earlier (whether explicitly or implicitly) to
|
|
// avoid causing those files to be unnecessarily re-read.
|
|
var filesToAnalyze = <String>{};
|
|
|
|
// Collect files for analysis.
|
|
// Note that these files will all be analyzed in the same context.
|
|
// This should be updated when the ContextManager re-work is complete
|
|
// (See: https://github.com/dart-lang/sdk/issues/24133)
|
|
var files = _collectFiles(sourcePath, analysisDriver.analysisOptions);
|
|
if (files.isEmpty) {
|
|
errorSink.writeln('No dart files found at: $sourcePath');
|
|
io.exitCode = ErrorSeverity.ERROR.ordinal;
|
|
return ErrorSeverity.ERROR;
|
|
}
|
|
|
|
for (var file in files) {
|
|
filesToAnalyze.add(file.absolute.path);
|
|
}
|
|
|
|
// Analyze the libraries.
|
|
for (var path in filesToAnalyze) {
|
|
var shortName = resourceProvider.pathContext.basename(path);
|
|
if (shortName == AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE) {
|
|
var file = resourceProvider.getFile(path);
|
|
var content = file.readAsStringSync();
|
|
var lineInfo = LineInfo.fromContent(content);
|
|
var errors = analyzeAnalysisOptions(
|
|
file.createSource(), content, analysisDriver.sourceFactory);
|
|
formatter.formatErrors([
|
|
ErrorsResultImpl(analysisDriver.currentSession, path, null,
|
|
lineInfo, false, errors)
|
|
]);
|
|
for (var error in errors) {
|
|
var severity = determineProcessedSeverity(
|
|
error, options, analysisDriver.analysisOptions);
|
|
if (severity != null) {
|
|
allResult = allResult.max(severity);
|
|
}
|
|
}
|
|
} else if (shortName == AnalysisEngine.PUBSPEC_YAML_FILE) {
|
|
try {
|
|
var file = resourceProvider.getFile(path);
|
|
var content = file.readAsStringSync();
|
|
var node = loadYamlNode(content);
|
|
if (node is YamlMap) {
|
|
var validator =
|
|
PubspecValidator(resourceProvider, file.createSource());
|
|
var lineInfo = LineInfo.fromContent(content);
|
|
var errors = validator.validate(node.nodes);
|
|
formatter.formatErrors([
|
|
ErrorsResultImpl(analysisDriver.currentSession, path, null,
|
|
lineInfo, false, errors)
|
|
]);
|
|
for (var error in errors) {
|
|
var severity = determineProcessedSeverity(
|
|
error, options, analysisDriver.analysisOptions);
|
|
allResult = allResult.max(severity);
|
|
}
|
|
}
|
|
} catch (exception) {
|
|
// If the file cannot be analyzed, ignore it.
|
|
}
|
|
} else if (shortName == AnalysisEngine.ANDROID_MANIFEST_FILE) {
|
|
try {
|
|
var file = resourceProvider.getFile(path);
|
|
var content = file.readAsStringSync();
|
|
var validator = ManifestValidator(file.createSource());
|
|
var lineInfo = LineInfo.fromContent(content);
|
|
var errors = validator.validate(
|
|
content, analysisDriver.analysisOptions.chromeOsManifestChecks);
|
|
formatter.formatErrors([
|
|
ErrorsResultImpl(analysisDriver.currentSession, path, null,
|
|
lineInfo, false, errors)
|
|
]);
|
|
for (var error in errors) {
|
|
var severity = determineProcessedSeverity(
|
|
error, options, analysisDriver.analysisOptions);
|
|
allResult = allResult.max(severity);
|
|
}
|
|
} catch (exception) {
|
|
// If the file cannot be analyzed, ignore it.
|
|
}
|
|
} else {
|
|
dartFiles.add(path);
|
|
var file = analysisDriver.fsState.getFileForPath(path);
|
|
|
|
if (file.isPart) {
|
|
if (!libraryFiles.contains(file.library)) {
|
|
danglingParts.add(file);
|
|
}
|
|
continue;
|
|
}
|
|
libraryFiles.add(file);
|
|
|
|
var status = await _runAnalyzer(file, options, formatter);
|
|
allResult = allResult.max(status);
|
|
|
|
// Mark previously dangling parts as no longer dangling.
|
|
for (var part in file.partedFiles) {
|
|
danglingParts.remove(part);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We are done analyzing this batch of files.
|
|
// The next batch should not be affected by a previous batch.
|
|
// E.g. the same parts in both batches, but with different libraries.
|
|
for (var path in dartFiles) {
|
|
analysisDriver.removeFile(path);
|
|
}
|
|
|
|
// Any dangling parts still in this list were definitely dangling.
|
|
for (var partFile in danglingParts) {
|
|
reportPartError(partFile.path);
|
|
}
|
|
|
|
formatter.flush();
|
|
|
|
if (!options.machineFormat) {
|
|
stats.print(outSink);
|
|
}
|
|
|
|
return allResult;
|
|
}
|
|
|
|
/// Perform analysis in build mode according to the given [options].
|
|
///
|
|
/// If [sendPort] is provided it is used for bazel worker communication
|
|
/// instead of stdin/stdout.
|
|
Future<ErrorSeverity> _buildModeAnalyze(
|
|
CommandLineOptions options, SendPort sendPort) async {
|
|
// TODO(brianwilkerson) Determine whether this await is necessary.
|
|
await null;
|
|
var previous = _analyzeAllTag.makeCurrent();
|
|
try {
|
|
if (options.buildModePersistentWorker) {
|
|
var workerLoop = sendPort == null
|
|
? AnalyzerWorkerLoop.std(resourceProvider,
|
|
dartSdkPath: options.dartSdkPath)
|
|
: AnalyzerWorkerLoop.sendPort(resourceProvider, sendPort,
|
|
dartSdkPath: options.dartSdkPath);
|
|
await workerLoop.run();
|
|
return ErrorSeverity.NONE;
|
|
} else {
|
|
return await BuildMode(resourceProvider, options, stats,
|
|
ContextCache(resourceProvider, options, verbosePrint))
|
|
.analyze();
|
|
}
|
|
} finally {
|
|
previous.makeCurrent();
|
|
}
|
|
}
|
|
|
|
/// Decide on the appropriate method for resolving URIs based on the given
|
|
/// [options] and [customUrlMappings] settings, and return a
|
|
/// [SourceFactory] that has been configured accordingly.
|
|
/// When [includeSdkResolver] is `false`, return a temporary [SourceFactory]
|
|
/// for the purpose of resolved analysis options file `include:` directives.
|
|
/// In this situation, [analysisOptions] is ignored and can be `null`.
|
|
SourceFactory _chooseUriResolutionPolicy(
|
|
CommandLineOptions options,
|
|
Map<Folder, YamlMap> embedderMap,
|
|
_PackageInfo packageInfo,
|
|
SummaryDataStore summaryDataStore,
|
|
bool includeSdkResolver,
|
|
AnalysisOptions analysisOptions) {
|
|
UriResolver packageUriResolver;
|
|
|
|
if (packageInfo.packageMap != null) {
|
|
packageUriResolver = PackageMapUriResolver(
|
|
resourceProvider,
|
|
packageInfo.packageMap,
|
|
);
|
|
}
|
|
|
|
// Now, build our resolver list.
|
|
var resolvers = <UriResolver>[];
|
|
|
|
// 'dart:' URIs come first.
|
|
|
|
// Setup embedding.
|
|
if (includeSdkResolver) {
|
|
var embedderSdk = EmbedderSdk(resourceProvider, embedderMap);
|
|
if (embedderSdk.libraryMap.size() == 0) {
|
|
// The embedder uri resolver has no mappings. Use the default Dart SDK
|
|
// uri resolver.
|
|
resolvers.add(DartUriResolver(sdk));
|
|
} else {
|
|
// The embedder uri resolver has mappings, use it instead of the default
|
|
// Dart SDK uri resolver.
|
|
embedderSdk.analysisOptions = analysisOptions;
|
|
resolvers.add(DartUriResolver(embedderSdk));
|
|
}
|
|
}
|
|
|
|
// Then package URIs from summaries.
|
|
resolvers.add(InSummaryUriResolver(resourceProvider, summaryDataStore));
|
|
|
|
// Then package URIs.
|
|
if (packageUriResolver != null) {
|
|
resolvers.add(packageUriResolver);
|
|
}
|
|
|
|
// Finally files.
|
|
resolvers.add(ResourceUriResolver(resourceProvider));
|
|
|
|
return SourceFactory(resolvers);
|
|
}
|
|
|
|
/// Collect all analyzable files at [filePath], recursively if it's a
|
|
/// directory, ignoring links.
|
|
Iterable<io.File> _collectFiles(String filePath, AnalysisOptions options) {
|
|
var files = <io.File>[];
|
|
var file = io.File(filePath);
|
|
if (file.existsSync() && !pathFilter.ignored(filePath)) {
|
|
files.add(file);
|
|
} else {
|
|
var directory = io.Directory(filePath);
|
|
if (directory.existsSync()) {
|
|
for (var entry
|
|
in directory.listSync(recursive: true, followLinks: false)) {
|
|
var relative = path.relative(entry.path, from: directory.path);
|
|
if ((AnalysisEngine.isDartFileName(entry.path) ||
|
|
AnalysisEngine.isManifestFileName(entry.path)) &&
|
|
entry is io.File &&
|
|
!pathFilter.ignored(entry.path) &&
|
|
!_isInHiddenDir(relative)) {
|
|
files.add(entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return files;
|
|
}
|
|
|
|
/// Create an analysis driver that is prepared to analyze sources according
|
|
/// to the given [options], and store it in [analysisDriver].
|
|
void _createContextAndAnalyze(CommandLineOptions options, String source) {
|
|
// If not the same command-line options, clear cached information.
|
|
if (!_equalCommandLineOptions(_previousOptions, options)) {
|
|
_previousOptions = options;
|
|
contextCache = ContextCache(resourceProvider, options, verbosePrint);
|
|
analysisDriver = null;
|
|
}
|
|
|
|
var analysisOptions =
|
|
createAnalysisOptionsForCommandLineOptions(options, source);
|
|
|
|
// Store the [PathFilter] for this context to properly exclude files
|
|
pathFilter = PathFilter(getContextInfo(options, source).analysisRoot,
|
|
analysisOptions.excludePatterns);
|
|
|
|
// If we have the analysis driver, and the new analysis options are the
|
|
// same, we can reuse this analysis driver.
|
|
if (analysisDriver != null &&
|
|
AnalysisOptions.signaturesEqual(
|
|
analysisDriver.analysisOptions.signature,
|
|
analysisOptions.signature)) {
|
|
return;
|
|
}
|
|
|
|
// Set up logging.
|
|
if (options.log) {
|
|
AnalysisEngine.instance.instrumentationService = StdInstrumentation();
|
|
}
|
|
|
|
// Save stats from previous context before clobbering it.
|
|
if (analysisDriver != null) {
|
|
_analyzedFileCount += analysisDriver.knownFiles.length;
|
|
}
|
|
|
|
// Find package info.
|
|
var packageInfo = _findPackages(options);
|
|
|
|
// Process embedders.
|
|
var embedderMap = <Folder, YamlMap>{};
|
|
if (packageInfo.packageMap != null) {
|
|
var libFolder = packageInfo.packageMap['sky_engine']?.first;
|
|
if (libFolder != null) {
|
|
var locator = EmbedderYamlLocator.forLibFolder(libFolder);
|
|
embedderMap = locator.embedderYamls;
|
|
}
|
|
}
|
|
|
|
// No summaries in the presence of embedders.
|
|
var useSummaries = embedderMap.isEmpty;
|
|
|
|
if (!useSummaries && options.buildSummaryInputs.isNotEmpty) {
|
|
throw _DriverError('Summaries are not yet supported when using Flutter.');
|
|
}
|
|
|
|
// Read any input summaries.
|
|
var summaryDataStore = SummaryDataStore(
|
|
useSummaries ? options.buildSummaryInputs : <String>[]);
|
|
|
|
// Once options and embedders are processed, setup the SDK.
|
|
_setupSdk(options, analysisOptions);
|
|
|
|
// Choose a package resolution policy and a diet parsing policy based on
|
|
// the command-line options.
|
|
var sourceFactory = _chooseUriResolutionPolicy(options, embedderMap,
|
|
packageInfo, summaryDataStore, true, analysisOptions);
|
|
|
|
var log = PerformanceLog(null);
|
|
var scheduler = AnalysisDriverScheduler(log);
|
|
|
|
analysisDriver = AnalysisDriver(
|
|
scheduler,
|
|
log,
|
|
resourceProvider,
|
|
analysisDriverMemoryByteStore,
|
|
FileContentOverlay(),
|
|
ContextRoot(source, [], pathContext: resourceProvider.pathContext),
|
|
sourceFactory,
|
|
analysisOptions,
|
|
packages: packageInfo.packages);
|
|
analysisDriver.results.listen((_) {});
|
|
analysisDriver.exceptions.listen((_) {});
|
|
_setAnalysisDriverAnalysisContext(source);
|
|
scheduler.start();
|
|
}
|
|
|
|
_PackageInfo _findPackages(CommandLineOptions options) {
|
|
Packages packages;
|
|
Map<String, List<Folder>> packageMap;
|
|
|
|
if (options.packageConfigPath != null) {
|
|
var path = normalizePath(options.packageConfigPath);
|
|
try {
|
|
packages = parsePackagesFile(
|
|
resourceProvider,
|
|
resourceProvider.getFile(path),
|
|
);
|
|
packageMap = _getPackageMap(packages);
|
|
} catch (e) {
|
|
printAndFail('Unable to read package config data from $path: $e');
|
|
}
|
|
} else {
|
|
var cwd = resourceProvider.getResource(path.current);
|
|
// Look for .packages.
|
|
packages = findPackagesFrom(resourceProvider, cwd);
|
|
packageMap = _getPackageMap(packages);
|
|
}
|
|
|
|
return _PackageInfo(packages, packageMap);
|
|
}
|
|
|
|
Map<String, List<Folder>> _getPackageMap(Packages packages) {
|
|
if (packages == null) {
|
|
return null;
|
|
}
|
|
|
|
var packageMap = <String, List<Folder>>{};
|
|
for (var package in packages.packages) {
|
|
packageMap[package.name] = [package.libFolder];
|
|
}
|
|
return packageMap;
|
|
}
|
|
|
|
/// Returns `true` if this relative path is a hidden directory.
|
|
bool _isInHiddenDir(String relative) =>
|
|
path.split(relative).any((part) => part.startsWith('.'));
|
|
|
|
/// Analyze a single source.
|
|
Future<ErrorSeverity> _runAnalyzer(
|
|
FileState file, CommandLineOptions options, ErrorFormatter formatter) {
|
|
var startTime = currentTimeMillis;
|
|
var analyzer = AnalyzerImpl(analysisDriver.analysisOptions, analysisDriver,
|
|
file, options, stats, startTime);
|
|
return analyzer.analyze(formatter);
|
|
}
|
|
|
|
void _setAnalysisDriverAnalysisContext(String rootPath) {
|
|
var apiContextRoots = api.ContextLocator(
|
|
resourceProvider: resourceProvider,
|
|
).locateRoots(
|
|
includedPaths: [rootPath],
|
|
excludedPaths: [],
|
|
);
|
|
|
|
if (apiContextRoots.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
analysisDriver.configure(
|
|
analysisContext: api.DriverBasedAnalysisContext(
|
|
resourceProvider,
|
|
apiContextRoots.first,
|
|
analysisDriver,
|
|
),
|
|
);
|
|
}
|
|
|
|
void _setupSdk(CommandLineOptions options, AnalysisOptions analysisOptions) {
|
|
if (sdk == null) {
|
|
if (options.dartSdkSummaryPath != null) {
|
|
sdk = SummaryBasedDartSdk(options.dartSdkSummaryPath, true);
|
|
} else {
|
|
var dartSdkPath = options.dartSdkPath;
|
|
var dartSdk = FolderBasedDartSdk(
|
|
resourceProvider, resourceProvider.getFolder(dartSdkPath));
|
|
dartSdk.analysisOptions = analysisOptions;
|
|
sdk = dartSdk;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _shouldBeFatal(ErrorSeverity severity, CommandLineOptions options) {
|
|
if (severity == ErrorSeverity.ERROR) {
|
|
return true;
|
|
} else if (severity == ErrorSeverity.WARNING &&
|
|
(options.warningsAreFatal || options.infosAreFatal)) {
|
|
return true;
|
|
} else if (severity == ErrorSeverity.INFO && options.infosAreFatal) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void verbosePrint(String text) {
|
|
outSink.writeln(text);
|
|
}
|
|
|
|
/// Return whether the [newOptions] are equal to the [previous].
|
|
static bool _equalCommandLineOptions(
|
|
CommandLineOptions previous, CommandLineOptions newOptions) {
|
|
return previous != null &&
|
|
newOptions != null &&
|
|
newOptions.packageConfigPath == previous.packageConfigPath &&
|
|
_equalMaps(newOptions.definedVariables, previous.definedVariables) &&
|
|
newOptions.log == previous.log &&
|
|
newOptions.disableHints == previous.disableHints &&
|
|
newOptions.showPackageWarnings == previous.showPackageWarnings &&
|
|
newOptions.showPackageWarningsPrefix ==
|
|
previous.showPackageWarningsPrefix &&
|
|
newOptions.showSdkWarnings == previous.showSdkWarnings &&
|
|
newOptions.lints == previous.lints &&
|
|
newOptions.strongMode == previous.strongMode &&
|
|
_equalLists(
|
|
newOptions.buildSummaryInputs, previous.buildSummaryInputs) &&
|
|
newOptions.disableCacheFlushing == previous.disableCacheFlushing &&
|
|
_equalLists(newOptions.enabledExperiments, previous.enabledExperiments);
|
|
}
|
|
|
|
/// Perform a deep comparison of two string lists.
|
|
static bool _equalLists(List<String> l1, List<String> l2) {
|
|
if (l1.length != l2.length) {
|
|
return false;
|
|
}
|
|
for (var i = 0; i < l1.length; i++) {
|
|
if (l1[i] != l2[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Perform a deep comparison of two string maps.
|
|
static bool _equalMaps(Map<String, String> m1, Map<String, String> m2) {
|
|
if (m1.length != m2.length) {
|
|
return false;
|
|
}
|
|
for (var key in m1.keys) {
|
|
if (!m2.containsKey(key) || m1[key] != m2[key]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class _DriverError implements Exception {
|
|
final String msg;
|
|
|
|
_DriverError(this.msg);
|
|
}
|
|
|
|
class _PackageInfo {
|
|
final Packages packages;
|
|
final Map<String, List<Folder>> packageMap;
|
|
|
|
_PackageInfo(this.packages, this.packageMap);
|
|
}
|