Remove build mode from analyzer_cli.

Change-Id: I2236c728741837588394f896b37f181fc907d335
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/190726
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Devon Carew <devoncarew@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2021-03-12 17:05:37 +00:00 committed by commit-bot@chromium.org
parent e1414089cf
commit 00a45e425a
10 changed files with 15 additions and 1985 deletions

View file

@ -3,17 +3,12 @@
// 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:isolate';
import 'package:analyzer_cli/starter.dart';
/// The entry point for the command-line analyzer.
///
/// [sendPort] may be passed in when started in an isolate. If provided, it is
/// used for bazel worker communication instead of stdin/stdout.
void main(List<String> args, [SendPort sendPort]) async {
void main(List<String> args) async {
var starter = CommandLineStarter();
// Wait for the starter to complete.
await starter.start(args, sendPort: sendPort);
await starter.start(args);
}

View file

@ -1,730 +0,0 @@
// 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' as io;
import 'dart:isolate';
import 'dart:typed_data';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/context/packages.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/analysis/cache.dart';
import 'package:analyzer/src/dart/analysis/context_root.dart' as api;
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/session.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/source/source_resource.dart';
import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/summary/summary_sdk.dart' show SummaryBasedDartSdk;
import 'package:analyzer/src/summary2/bundle_reader.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/summary2/package_bundle_format.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer/src/workspace/bazel.dart';
import 'package:analyzer_cli/src/driver.dart';
import 'package:analyzer_cli/src/error_formatter.dart';
import 'package:analyzer_cli/src/error_severity.dart';
import 'package:analyzer_cli/src/options.dart';
import 'package:bazel_worker/bazel_worker.dart';
import 'package:collection/collection.dart';
import 'package:convert/convert.dart';
/// Persistent Bazel worker.
class AnalyzerWorkerLoop extends AsyncWorkerLoop {
final ResourceProvider resourceProvider;
final PerformanceLog logger = PerformanceLog(null);
final String dartSdkPath;
WorkerPackageBundleCache packageBundleCache;
final StringBuffer errorBuffer = StringBuffer();
final StringBuffer outBuffer = StringBuffer();
AnalyzerWorkerLoop(this.resourceProvider, AsyncWorkerConnection connection,
{this.dartSdkPath})
: super(connection: connection) {
packageBundleCache =
WorkerPackageBundleCache(resourceProvider, logger, 256 * 1024 * 1024);
}
factory AnalyzerWorkerLoop.sendPort(
ResourceProvider resourceProvider, SendPort sendPort,
{String dartSdkPath}) {
AsyncWorkerConnection connection = SendPortAsyncWorkerConnection(sendPort);
return AnalyzerWorkerLoop(resourceProvider, connection,
dartSdkPath: dartSdkPath);
}
factory AnalyzerWorkerLoop.std(ResourceProvider resourceProvider,
{io.Stdin stdinStream, io.Stdout stdoutStream, String dartSdkPath}) {
AsyncWorkerConnection connection = StdAsyncWorkerConnection(
inputStream: stdinStream, outputStream: stdoutStream);
return AnalyzerWorkerLoop(resourceProvider, connection,
dartSdkPath: dartSdkPath);
}
/// Performs analysis with given [options].
Future<void> analyze(
CommandLineOptions options, Map<String, WorkerInput> inputs) async {
var packageBundleProvider =
WorkerPackageBundleProvider(packageBundleCache, inputs);
var buildMode = BuildMode(resourceProvider, options, AnalysisStats(),
logger: logger, packageBundleProvider: packageBundleProvider);
await buildMode.analyze();
AnalysisEngine.instance.clearCaches();
}
/// Perform a single loop step.
@override
Future<WorkResponse> performRequest(WorkRequest request) async {
return logger.runAsync('Perform request', () async {
errorBuffer.clear();
outBuffer.clear();
try {
// Prepare inputs with their digests.
var inputs = <String, WorkerInput>{};
for (var input in request.inputs) {
inputs[input.path] = WorkerInput(input.path, input.digest);
}
// Add in the dart-sdk argument if `dartSdkPath` is not null,
// otherwise it will try to find the currently installed sdk.
var arguments = request.arguments.toList();
if (dartSdkPath != null &&
!arguments.any((arg) => arg.startsWith('--dart-sdk'))) {
arguments.add('--dart-sdk=$dartSdkPath');
}
// Prepare options.
var options = CommandLineOptions.parse(resourceProvider, arguments,
printAndFail: (String msg) {
throw ArgumentError(msg);
});
// Analyze and respond.
await analyze(options, inputs);
var msg = _getErrorOutputBuffersText();
return WorkResponse()
..exitCode = EXIT_CODE_OK
..output = msg;
} catch (e, st) {
var msg = _getErrorOutputBuffersText();
msg += '$e\n$st';
return WorkResponse()
..exitCode = EXIT_CODE_ERROR
..output = msg;
}
});
}
/// Run the worker loop.
@override
Future<void> run() async {
errorSink = errorBuffer;
outSink = outBuffer;
exitHandler = (int exitCode) {
throw StateError('Exit called: $exitCode');
};
await super.run();
}
String _getErrorOutputBuffersText() {
var msg = '';
if (errorBuffer.isNotEmpty) {
msg += errorBuffer.toString() + '\n';
}
if (outBuffer.isNotEmpty) {
msg += outBuffer.toString() + '\n';
}
return msg;
}
}
/// Analyzer used when the "--build-mode" option is supplied.
class BuildMode {
final ResourceProvider resourceProvider;
final CommandLineOptions options;
final AnalysisStats stats;
final PerformanceLog logger;
final PackageBundleProvider packageBundleProvider;
SummaryDataStore summaryDataStore;
AnalysisOptionsImpl analysisOptions;
Map<Uri, File> uriToFileMap;
final List<Source> explicitSources = <Source>[];
SourceFactory sourceFactory;
DeclaredVariables declaredVariables;
AnalysisDriver analysisDriver;
LinkedElementFactory elementFactory;
// May be null.
final DependencyTracker dependencyTracker;
BuildMode(this.resourceProvider, this.options, this.stats,
{PerformanceLog logger, PackageBundleProvider packageBundleProvider})
: logger = logger ?? PerformanceLog(null),
packageBundleProvider = packageBundleProvider ??
DirectPackageBundleProvider(resourceProvider),
dependencyTracker = options.summaryDepsOutput != null
? DependencyTracker(options.summaryDepsOutput)
: null;
bool get _shouldOutputSummary =>
options.buildSummaryOutput != null ||
options.buildSummaryOutputSemantic != null;
/// Perform package analysis according to the given [options].
Future<ErrorSeverity> analyze() async {
return await logger.runAsync('Analyze', () async {
// Write initial progress message.
if (!options.machineFormat) {
outSink.writeln("Analyzing ${options.sourceFiles.join(', ')}...");
}
// Create the URI to file map.
uriToFileMap = _createUriToFileMap(options.sourceFiles);
if (uriToFileMap == null) {
io.exitCode = ErrorSeverity.ERROR.ordinal;
return ErrorSeverity.ERROR;
}
// BuildMode expects sourceFiles in the format "<uri>|<filepath>",
// but the rest of the code base does not understand this format.
// Rewrite sourceFiles, stripping the "<uri>|" prefix, so that it
// does not cause problems with code that does not expect this format.
options.rewriteSourceFiles(options.sourceFiles
.map((String uriPipePath) =>
uriPipePath.substring(uriPipePath.indexOf('|') + 1))
.toList());
// Prepare the analysis driver.
try {
logger.run('Prepare analysis driver', () {
_createAnalysisDriver();
});
} on ConflictingSummaryException catch (e) {
errorSink.writeln('$e');
io.exitCode = ErrorSeverity.ERROR.ordinal;
return ErrorSeverity.ERROR;
}
// Add sources.
for (var uri in uriToFileMap.keys) {
var file = uriToFileMap[uri];
if (!file.exists) {
errorSink.writeln('File not found: ${file.path}');
io.exitCode = ErrorSeverity.ERROR.ordinal;
return ErrorSeverity.ERROR;
}
Source source = FileSource(file, uri);
explicitSources.add(source);
}
// Write summary.
if (_shouldOutputSummary) {
await logger.runAsync('Build and write output summary', () async {
// Build and assemble linked libraries.
var bytes = _computeLinkedLibraries2();
// Write the whole package bundle.
// TODO(scheglov) Remove support for `buildSummaryOutput`.
if (options.buildSummaryOutput != null) {
var file = io.File(options.buildSummaryOutput);
file.writeAsBytesSync(bytes, mode: io.FileMode.writeOnly);
}
if (options.buildSummaryOutputSemantic != null) {
var file = io.File(options.buildSummaryOutputSemantic);
file.writeAsBytesSync(bytes, mode: io.FileMode.writeOnly);
}
});
} else {
// Build the graph, e.g. associate parts with libraries.
for (var file in uriToFileMap.values) {
analysisDriver.fsState.getFileForPath(file.path);
}
}
ErrorSeverity severity;
if (options.buildSummaryOnly) {
severity = ErrorSeverity.NONE;
} else {
// Process errors.
await _printErrors(outputPath: options.buildAnalysisOutput);
severity = await _computeMaxSeverity();
}
if (dependencyTracker != null) {
var file = io.File(dependencyTracker.outputPath);
file.writeAsStringSync(dependencyTracker.dependencies.join('\n'));
}
return severity;
});
}
void _applyOptionsFile() {
analysisOptions = AnalysisOptionsImpl();
var path = options.contextBuilderOptions.defaultAnalysisOptionsFilePath;
var file = resourceProvider.getFile(path);
var provider = AnalysisOptionsProvider(sourceFactory);
var map = provider.getOptionsFromFile(file);
applyToAnalysisOptions(analysisOptions, map);
}
/// Use [elementFactory] filled with input summaries, and link libraries
/// in [explicitSources] to produce linked summary bytes.
Uint8List _computeLinkedLibraries2() {
return logger.run('Link output summary2', () {
var inputLibraries = <LinkInputLibrary>[];
for (var librarySource in explicitSources) {
var path = librarySource.fullName;
var parseResult = analysisDriver.parseFileSync(path);
if (parseResult == null) {
throw ArgumentError('No parsed unit for $path');
}
var unit = parseResult.unit;
var isPart = unit.directives.any((d) => d is PartOfDirective);
if (isPart) {
continue;
}
var inputUnits = <LinkInputUnit>[];
inputUnits.add(
LinkInputUnit(null, librarySource, false, unit),
);
for (var directive in unit.directives) {
if (directive is PartDirective) {
var partUri = directive.uri.stringValue;
var partSource = sourceFactory.resolveUri(librarySource, partUri);
// Add empty synthetic units for unresolved `part` URIs.
if (partSource == null) {
continue;
}
var partPath = partSource.fullName;
var partParseResult = analysisDriver.parseFileSync(partPath);
if (partParseResult == null) {
throw ArgumentError('No parsed unit for part $partPath in $path');
}
inputUnits.add(
LinkInputUnit(
partUri,
partSource,
false,
partParseResult.unit,
),
);
}
}
inputLibraries.add(
LinkInputLibrary(librarySource, inputUnits),
);
}
var linkResult = link(elementFactory, inputLibraries, false);
var bundleBuilder = PackageBundleBuilder();
for (var library in inputLibraries) {
bundleBuilder.addLibrary(
library.uriStr,
library.units.map((e) => e.uriStr).toList(),
);
}
return bundleBuilder.finish(
astBytes: linkResult.astBytes,
resolutionBytes: linkResult.resolutionBytes,
);
});
}
Future<ErrorSeverity> _computeMaxSeverity() async {
var maxSeverity = ErrorSeverity.NONE;
if (!options.buildSuppressExitCode) {
for (var source in explicitSources) {
var result = await analysisDriver.getErrors(source.fullName);
for (var error in result.errors) {
var processedSeverity = determineProcessedSeverity(
error, options, analysisDriver.analysisOptions);
if (processedSeverity != null) {
maxSeverity = maxSeverity.max(processedSeverity);
}
}
}
}
return maxSeverity;
}
void _createAnalysisDriver() {
// Read the summaries.
summaryDataStore = SummaryDataStore(<String>[]);
// Adds a bundle at `path` to `summaryDataStore`.
PackageBundleReader addBundle(String path) {
var bundle = packageBundleProvider.get(path);
summaryDataStore.addBundle(path, bundle);
return bundle;
}
SummaryBasedDartSdk sdk;
logger.run('Add SDK bundle', () {
sdk = SummaryBasedDartSdk(options.dartSdkSummaryPath, true);
summaryDataStore.addBundle(null, sdk.bundle);
});
var numInputs = options.buildSummaryInputs.length;
logger.run('Add $numInputs input summaries', () {
for (var path in options.buildSummaryInputs) {
addBundle(path);
}
});
var rootPath = options.sourceFiles.first;
var packages = _findPackages(rootPath);
sourceFactory = SourceFactory(<UriResolver>[
DartUriResolver(sdk),
TrackingInSummaryUriResolver(
InSummaryUriResolver(resourceProvider, summaryDataStore),
dependencyTracker),
ExplicitSourceResolver(uriToFileMap)
]);
_applyOptionsFile();
var scheduler = AnalysisDriverScheduler(logger);
analysisDriver = AnalysisDriver(
scheduler,
logger,
resourceProvider,
MemoryByteStore(),
FileContentOverlay(),
null,
sourceFactory,
analysisOptions,
externalSummaries: summaryDataStore,
packages: packages,
);
_setAnalysisDriverAnalysisContext(rootPath);
declaredVariables = DeclaredVariables.fromMap(options.definedVariables);
analysisDriver.declaredVariables = declaredVariables;
_createLinkedElementFactory();
scheduler.start();
}
void _createLinkedElementFactory() {
var analysisContext = AnalysisContextImpl(
SynchronousSession(analysisOptions, declaredVariables),
sourceFactory,
);
elementFactory = LinkedElementFactory(
analysisContext,
AnalysisSessionImpl(null),
Reference.root(),
);
for (var bundle in summaryDataStore.bundles) {
elementFactory.addBundle(
BundleReader(
elementFactory: elementFactory,
astBytes: bundle.astBytes,
resolutionBytes: bundle.resolutionBytes,
),
);
}
}
/// Convert [sourceEntities] (a list of file specifications of the form
/// "$uri|$path") to a map from URI to path. If an error occurs, report the
/// error and return null.
Map<Uri, File> _createUriToFileMap(List<String> sourceEntities) {
var uriToFileMap = <Uri, File>{};
for (var sourceFile in sourceEntities) {
var pipeIndex = sourceFile.indexOf('|');
if (pipeIndex == -1) {
// TODO(paulberry): add the ability to guess the URI from the path.
errorSink.writeln(
'Illegal input file (must be "\$uri|\$path"): $sourceFile');
return null;
}
var uri = Uri.parse(sourceFile.substring(0, pipeIndex));
var path = sourceFile.substring(pipeIndex + 1);
path = resourceProvider.pathContext.absolute(path);
path = resourceProvider.pathContext.normalize(path);
uriToFileMap[uri] = resourceProvider.getFile(path);
}
return uriToFileMap;
}
Packages _findPackages(String path) {
var configPath = options.packageConfigPath;
if (configPath != null) {
var configFile = resourceProvider.getFile(configPath);
return parsePackagesFile(resourceProvider, configFile);
}
if (path != null) {
var file = resourceProvider.getFile(path);
return findPackagesFrom(resourceProvider, file);
}
return Packages.empty;
}
/// Print errors for all explicit sources. If [outputPath] is supplied, output
/// is sent to a new file at that path.
Future<void> _printErrors({String outputPath}) async {
await logger.runAsync('Compute and print analysis errors', () async {
var buffer = StringBuffer();
var severityProcessor = (AnalysisError error) =>
determineProcessedSeverity(error, options, analysisOptions);
var formatter = options.machineFormat
? MachineErrorFormatter(buffer, options, stats,
severityProcessor: severityProcessor)
: HumanErrorFormatter(buffer, options, stats,
severityProcessor: severityProcessor);
for (var source in explicitSources) {
var result = await analysisDriver.getErrors(source.fullName);
formatter.formatErrors([result]);
}
formatter.flush();
if (!options.machineFormat) {
stats.print(buffer);
}
if (outputPath == null) {
var sink = options.machineFormat ? errorSink : outSink;
sink.write(buffer);
} else {
io.File(outputPath).writeAsStringSync(buffer.toString());
}
});
}
void _setAnalysisDriverAnalysisContext(String rootPath) {
rootPath = file_paths.absoluteNormalized(
resourceProvider.pathContext,
rootPath,
);
var workspace = BazelWorkspace.find(resourceProvider, rootPath);
var contextRoot = api.ContextRootImpl(
resourceProvider,
resourceProvider.getFolder(workspace.root),
workspace,
);
analysisDriver.configure(
analysisContext: api.DriverBasedAnalysisContext(
resourceProvider,
contextRoot,
analysisDriver,
),
);
}
}
/// Tracks paths to dependencies, really just a thin api around a Set<String>.
class DependencyTracker {
final _dependencies = <String>{};
/// The path to the file to create once tracking is done.
final String outputPath;
DependencyTracker(this.outputPath);
Iterable<String> get dependencies => _dependencies;
void record(String path) => _dependencies.add(path);
}
/// [PackageBundleProvider] that always reads from the [ResourceProvider].
class DirectPackageBundleProvider implements PackageBundleProvider {
final ResourceProvider resourceProvider;
DirectPackageBundleProvider(this.resourceProvider);
@override
PackageBundleReader get(String path) {
var bytes = io.File(path).readAsBytesSync();
return PackageBundleReader(bytes);
}
}
/// Instances of the class [ExplicitSourceResolver] map URIs to files on disk
/// using a fixed mapping provided at construction time.
class ExplicitSourceResolver extends UriResolver {
final Map<Uri, File> uriToFileMap;
final Map<String, Uri> pathToUriMap;
/// Construct an [ExplicitSourceResolver] based on the given [uriToFileMap].
ExplicitSourceResolver(Map<Uri, File> uriToFileMap)
: uriToFileMap = uriToFileMap,
pathToUriMap = _computePathToUriMap(uriToFileMap);
@override
Source resolveAbsolute(Uri uri, [Uri actualUri]) {
var file = uriToFileMap[uri];
actualUri ??= uri;
if (file == null) {
return null;
} else {
return FileSource(file, actualUri);
}
}
@override
Uri restoreAbsolute(Source source) {
return pathToUriMap[source.fullName];
}
/// Build the inverse mapping of [uriToSourceMap].
static Map<String, Uri> _computePathToUriMap(Map<Uri, File> uriToSourceMap) {
var pathToUriMap = <String, Uri>{};
uriToSourceMap.forEach((Uri uri, File file) {
pathToUriMap[file.path] = uri;
});
return pathToUriMap;
}
}
/// Provider for [PackageBundleReader]s by file paths.
abstract class PackageBundleProvider {
/// Return the [PackageBundleReader] for the file with the given [path].
PackageBundleReader get(String path);
}
/// Wrapper for [InSummaryUriResolver] that tracks accesses to summaries.
class TrackingInSummaryUriResolver extends UriResolver {
// May be null.
final DependencyTracker dependencyTracker;
final InSummaryUriResolver inSummaryUriResolver;
TrackingInSummaryUriResolver(
this.inSummaryUriResolver, this.dependencyTracker);
@override
Source resolveAbsolute(Uri uri) {
var source = inSummaryUriResolver.resolveAbsolute(uri);
if (dependencyTracker != null &&
source != null &&
source is InSummarySource) {
dependencyTracker.record(source.summaryPath);
}
return source;
}
}
/// Worker input.
///
/// Bazel does not specify the format of the digest, so we cannot assume that
/// the digest itself is enough to uniquely identify inputs. So, we use a pair
/// of path + digest.
class WorkerInput {
static const _digestEquality = ListEquality<int>();
final String path;
final List<int> digest;
WorkerInput(this.path, this.digest);
@override
int get hashCode => _digestEquality.hash(digest);
@override
bool operator ==(Object other) {
return other is WorkerInput &&
other.path == path &&
_digestEquality.equals(other.digest, digest);
}
@override
String toString() => '$path @ ${hex.encode(digest)}';
}
/// Value object for [WorkerPackageBundleCache].
class WorkerPackageBundle {
final List<int> bytes;
final PackageBundleReader bundle;
WorkerPackageBundle(this.bytes, this.bundle);
/// Approximation of a bundle size in memory.
int get size => bytes.length * 3;
}
/// Cache of [PackageBundleReader]s.
class WorkerPackageBundleCache {
final ResourceProvider resourceProvider;
final PerformanceLog logger;
final Cache<WorkerInput, WorkerPackageBundle> _cache;
WorkerPackageBundleCache(this.resourceProvider, this.logger, int maxSizeBytes)
: _cache = Cache<WorkerInput, WorkerPackageBundle>(
maxSizeBytes, (value) => value.size);
/// Get the [PackageBundleReader] from the file with the given [path] in the context
/// of the given worker [inputs].
PackageBundleReader get(Map<String, WorkerInput> inputs, String path) {
var input = inputs[path];
// The input must be not null, otherwise we're not expected to read
// this file, but we check anyway to be safe.
if (input == null) {
logger.writeln('Read $path outside of the inputs.');
var file = resourceProvider.getFile(path);
var bytes = file.readAsBytesSync() as Uint8List;
return PackageBundleReader(bytes);
}
return _cache.get(input, () {
logger.writeln('Read $input.');
var file = resourceProvider.getFile(path);
var bytes = file.readAsBytesSync() as Uint8List;
var bundle = PackageBundleReader(bytes);
return WorkerPackageBundle(bytes, bundle);
}).bundle;
}
}
/// [PackageBundleProvider] that reads from [WorkerPackageBundleCache] using
/// the request specific [inputs].
class WorkerPackageBundleProvider implements PackageBundleProvider {
final WorkerPackageBundleCache cache;
final Map<String, WorkerInput> inputs;
WorkerPackageBundleProvider(this.cache, this.inputs);
@override
PackageBundleReader get(String path) {
return cache.get(inputs, path);
}
}

View file

@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:io' as io;
import 'dart:isolate';
import 'package:analyzer/dart/sdk/build_sdk_summary.dart';
import 'package:analyzer/error/error.dart';
@ -31,7 +30,6 @@ import 'package:analyzer/src/util/file_paths.dart' as file_paths;
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/error_formatter.dart';
import 'package:analyzer_cli/src/error_severity.dart';
import 'package:analyzer_cli/src/options.dart';
@ -89,7 +87,7 @@ class Driver implements CommandLineStarter {
}
@override
Future<void> start(List<String> args, {SendPort sendPort}) async {
Future<void> start(List<String> args) async {
if (analysisDriver != null) {
throw StateError('start() can only be called once');
}
@ -105,13 +103,7 @@ class Driver implements CommandLineStarter {
_analysisContextProvider = _AnalysisContextProvider(resourceProvider);
// 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) {
if (options.batchMode) {
var batchRunner = BatchRunner(outSink, errorSink);
batchRunner.runAsBatch(args, (List<String> args) async {
var options = CommandLineOptions.parse(resourceProvider, args);
@ -375,25 +367,6 @@ class Driver implements CommandLineStarter {
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 {
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).analyze();
}
}
/// Collect all analyzable files at [filePath], recursively if it's a
/// directory, ignoring links.
Iterable<io.File> _collectFiles(String filePath, AnalysisOptions options) {
@ -475,8 +448,6 @@ class Driver implements CommandLineStarter {
previous.showPackageWarningsPrefix &&
newOptions.showSdkWarnings == previous.showSdkWarnings &&
newOptions.lints == previous.lints &&
_equalLists(
newOptions.buildSummaryInputs, previous.buildSummaryInputs) &&
newOptions.defaultLanguageVersion == previous.defaultLanguageVersion &&
newOptions.disableCacheFlushing == previous.disableCacheFlushing &&
_equalLists(newOptions.enabledExperiments, previous.enabledExperiments);

View file

@ -6,7 +6,6 @@ import 'dart:io' as io;
import 'package:analyzer/dart/analysis/features.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/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
@ -29,7 +28,6 @@ const _lintsFlag = 'lints';
const _noImplicitDynamicFlag = 'no-implicit-dynamic';
const _packagesOption = 'packages';
const _sdkPathOption = 'dart-sdk';
const _sdkSummaryPathOption = 'dart-sdk-summary';
/// Shared exit handler.
///
@ -53,40 +51,12 @@ typedef ExitHandler = void Function(int code);
class CommandLineOptions {
final ArgResults _argResults;
/// 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;
/// Whether to skip analysis when creating summaries in build mode.
final bool buildSummaryOnly;
/// 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.
final 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.
@ -124,7 +94,7 @@ class CommandLineOptions {
final bool showSdkWarnings;
/// The source files to analyze
List<String> _sourceFiles;
final List<String> sourceFiles;
/// Whether to treat warnings as fatal
final bool warningsAreFatal;
@ -146,31 +116,16 @@ class CommandLineOptions {
/// Dart analyzer snapshot.
final bool trainSnapshot;
/// Path to a file to dump summary dependency information to for any given
/// build.
final String summaryDepsOutput;
/// Initialize options from the given parsed [args].
CommandLineOptions._fromArgs(
ResourceProvider resourceProvider,
ArgResults args,
) : _argResults = args,
buildAnalysisOutput = cast(args['build-analysis-output']),
buildMode = cast(args['build-mode']),
buildModePersistentWorker = cast(args['persistent_worker']),
buildSummaryInputs =
(args['build-summary-input'] as List).cast<String>(),
buildSummaryOnly = cast(args['build-summary-only']),
buildSummaryOutput = cast(args['build-summary-output']),
buildSummaryOutputSemantic =
cast(args['build-summary-output-semantic']),
buildSuppressExitCode = cast(args['build-suppress-exit-code']),
contextBuilderOptions = _createContextBuilderOptions(
resourceProvider,
args,
),
dartSdkPath = cast(args[_sdkPathOption]),
dartSdkSummaryPath = cast(args[_sdkSummaryPathOption]),
disableCacheFlushing = cast(args['disable-cache-flushing']),
disableHints = cast(args['no-hints']),
displayVersion = cast(args['version']),
@ -184,14 +139,13 @@ class CommandLineOptions {
args['x-package-warnings-prefix'] != null,
showPackageWarningsPrefix = cast(args['x-package-warnings-prefix']),
showSdkWarnings = cast(args['sdk-warnings']),
_sourceFiles = args.rest,
sourceFiles = args.rest,
infosAreFatal = cast(args['fatal-infos']) || cast(args['fatal-hints']),
warningsAreFatal = cast(args['fatal-warnings']),
lintsAreFatal = cast(args['fatal-lints']),
trainSnapshot = cast(args['train-snapshot']),
verbose = cast(args['verbose']),
color = cast(args['color']),
summaryDepsOutput = cast(args['summary-deps-output']);
color = cast(args['color']);
/// The path to an analysis options file
String get analysisOptionsFile =>
@ -221,14 +175,6 @@ class CommandLineOptions {
/// The path to a `.packages` configuration file
String get packageConfigPath => contextBuilderOptions.defaultPackageFilePath;
/// 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;
}
/// Update the [analysisOptions] with flags that the user specified
/// explicitly. The [analysisOptions] are usually loaded from one of
/// `analysis_options.yaml` files, possibly with includes. We consider
@ -336,7 +282,7 @@ class CommandLineOptions {
}
// Check SDK.
if (!options.buildModePersistentWorker) {
{
var sdkPath = options.dartSdkPath;
// Check that SDK is existing directory.
@ -354,60 +300,9 @@ class CommandLineOptions {
options.dartSdkPath = file_paths.absoluteNormalized(pathContext, sdkPath);
}
// Build mode.
if (options.buildMode) {
if (options.dartSdkSummaryPath == null) {
// It is OK to not specify when persistent worker.
// We will be given another set of options with each request.
if (!options.buildModePersistentWorker) {
printAndFail(
'The option --build-mode also requires --dart-sdk-summary '
'to be specified.',
);
return null; // Only reachable in testing.
}
}
} else {
if (options.buildModePersistentWorker) {
printAndFail(
'The option --persistent_worker can be used only '
'together with --build-mode.',
);
return null; // Only reachable in testing.
}
}
return options;
}
/// Preprocess the given list of command line [args].
/// If the final arg is `@file_path` (Bazel worker mode),
/// then read in all the lines of that file and add those as args.
/// Always returns a new modifiable list.
static List<String> preprocessArgs(
ResourceProvider provider, List<String> args) {
args = List.from(args);
if (args.isEmpty) {
return args;
}
var lastArg = args.last;
if (lastArg.startsWith('@')) {
var argsFile = provider.getFile(lastArg.substring(1));
try {
args.removeLast();
args.addAll(argsFile
.readAsStringSync()
.replaceAll('\r\n', '\n')
.replaceAll('\r', '\n')
.split('\n')
.where((String line) => line.isNotEmpty));
} on FileSystemException catch (e) {
throw Exception('Failed to read file specified by $lastArg : $e');
}
}
return args;
}
/// Use the command-line [args] to create a context builder options.
static ContextBuilderOptions _createContextBuilderOptions(
ResourceProvider resourceProvider,
@ -428,9 +323,6 @@ class CommandLineOptions {
//
// File locations.
//
builderOptions.dartSdkSummaryPath = absoluteNormalizedPath(
cast(args[_sdkSummaryPathOption]),
);
builderOptions.defaultAnalysisOptionsFilePath = absoluteNormalizedPath(
cast(args[_analysisOptionsFileOption]),
);
@ -515,8 +407,6 @@ class CommandLineOptions {
help: 'The path to the package resolution configuration file, which '
'supplies a mapping of package names\nto paths.',
hide: ddc);
parser.addOption(_sdkSummaryPathOption,
help: 'The path to the Dart SDK summary file.', hide: hide);
parser.addFlag(_enableInitializingFormalAccessFlag,
help:
'Enable support for allowing access to field formal parameters in a '
@ -547,8 +437,6 @@ class CommandLineOptions {
ResourceProvider resourceProvider,
List<String> args,
) {
args = preprocessArgs(PhysicalResourceProvider.INSTANCE, args);
var verbose = args.contains('-v') || args.contains('--verbose');
var hide = !verbose;
@ -593,56 +481,10 @@ class CommandLineOptions {
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',
help: 'Run in build mode; '
'this is used to generate analyzer summaries for build systems.',
defaultsTo: false,
negatable: false,
hide: hide)
..addMultiOption('build-summary-input',
help: 'Path to a linked summary file that contains information from '
'a previous analysis run; may be specified multiple times.',
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-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)
..addOption('summary-deps-output',
help: 'Path to a file to dump summary dependency info to.',
hide: hide);
parser.addFlag('color',
help: 'Use ansi colors when printing messages.',
defaultsTo: ansi.terminalSupportsAnsi(),
hide: hide);
// Hidden flags.
if (!hide) {
@ -736,21 +578,6 @@ class CommandLineOptions {
}
var results = parser.parse(args);
// Persistent worker.
if (args.contains('--persistent_worker')) {
var hasBuildMode = args.contains('--build-mode');
var 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 CommandLineOptions._fromArgs(resourceProvider, results);
}
// Help requests.
if (cast(results['help'])) {
_showUsage(parser, fromHelp: true);
@ -766,20 +593,12 @@ class CommandLineOptions {
exitHandler(15);
return null; // Only reachable in testing.
}
} else if (cast(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 (cast(results['version'])) {
outSink.writeln('$_binaryName version ${_getVersion()}');
exitHandler(0);
return null; // Only reachable in testing.
} else {
if (results.rest.isEmpty && !cast<bool>(results['build-mode'])) {
if (results.rest.isEmpty) {
_showUsage(parser, fromHelp: true);
exitHandler(15);
return null; // Only reachable in testing.

View file

@ -2,8 +2,6 @@
// 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:isolate';
import 'package:analyzer_cli/src/driver.dart';
/// An object that can be used to start a command-line analysis. This class
@ -16,8 +14,5 @@ abstract class CommandLineStarter {
factory CommandLineStarter() = Driver;
/// Use the given command-line [arguments] to start this analyzer.
///
/// If [sendPort] is provided it is used for bazel worker communication
/// instead of stdin/stdout.
Future<void> start(List<String> arguments, {SendPort sendPort});
Future<void> start(List<String> arguments);
}

View file

@ -11,9 +11,6 @@ dependencies:
analyzer:
path: ../analyzer
args: '>=0.13.0 <2.0.0'
bazel_worker: ^0.1.0
collection: ^1.14.1
convert: any
linter: ^0.1.16
meta:
path: ../meta
@ -23,6 +20,5 @@ dependencies:
dev_dependencies:
pedantic: ^1.9.0
protobuf: ^0.13.0
test_reflective_loader: ^0.1.8
test: ^1.0.0

View file

@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'analysis_options_test.dart' as analysis_options;
import 'build_mode_test.dart' as build_mode;
import 'driver_test.dart' as driver;
import 'embedder_test.dart' as embedder;
import 'errors_reported_once_test.dart' as errors_reported_once;
@ -16,7 +15,6 @@ import 'strong_mode_test.dart' as strong_mode;
void main() {
analysis_options.main();
build_mode.main();
driver.main();
embedder.main();
errors_reported_once.main();

View file

@ -1,139 +0,0 @@
// Copyright (c) 2016, 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 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer_cli/src/build_mode.dart';
import 'package:analyzer_cli/src/driver.dart';
import 'package:analyzer_cli/src/options.dart';
import 'package:bazel_worker/bazel_worker.dart';
import 'package:bazel_worker/testing.dart';
import 'package:protobuf/protobuf.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
void main() {
defineReflectiveTests(WorkerLoopTest);
}
typedef _TestWorkerLoopAnalyze = void Function(CommandLineOptions options);
/// [AnalyzerWorkerLoop] for testing.
class TestAnalyzerWorkerLoop extends AnalyzerWorkerLoop {
final _TestWorkerLoopAnalyze _analyze;
TestAnalyzerWorkerLoop(AsyncWorkerConnection connection, [this._analyze])
: super(MemoryResourceProvider(), connection);
@override
Future<void> analyze(
CommandLineOptions options, Map<String, WorkerInput> inputs) async {
if (_analyze != null) {
_analyze(options);
}
}
}
@reflectiveTest
class WorkerLoopTest {
final TestStdinAsync stdinStream = TestStdinAsync();
final TestStdoutStream stdoutStream = TestStdoutStream();
TestAsyncWorkerConnection connection;
WorkerLoopTest() {
connection = TestAsyncWorkerConnection(stdinStream, stdoutStream);
}
void setUp() {}
Future<void> test_run() async {
var request = WorkRequest();
request.arguments.addAll([
'--build-summary-input=/tmp/1.sum',
'--build-summary-input=/tmp/2.sum',
'package:foo/foo.dart|/inputs/foo/lib/foo.dart',
'package:foo/bar.dart|/inputs/foo/lib/bar.dart',
]);
stdinStream.addInputBytes(_serializeProto(request));
stdinStream.close();
await TestAnalyzerWorkerLoop(connection, (CommandLineOptions options) {
expect(options.buildSummaryInputs,
unorderedEquals(['/tmp/1.sum', '/tmp/2.sum']));
expect(
options.sourceFiles,
unorderedEquals([
'package:foo/foo.dart|/inputs/foo/lib/foo.dart',
'package:foo/bar.dart|/inputs/foo/lib/bar.dart'
]));
outSink.writeln('outSink a');
errorSink.writeln('errorSink a');
outSink.writeln('outSink b');
errorSink.writeln('errorSink b');
}).run();
expect(connection.responses, hasLength(1));
var response = connection.responses[0];
expect(response.exitCode, EXIT_CODE_OK, reason: response.output);
expect(
response.output,
allOf(contains('errorSink a'), contains('errorSink a'),
contains('outSink a'), contains('outSink b')));
// Check that a serialized version was written to std out.
expect(stdoutStream.writes, hasLength(1));
expect(stdoutStream.writes[0], _serializeProto(response));
}
Future<void> test_run_invalidOptions() async {
var request = WorkRequest();
request.arguments.addAll(['--unknown-option', '/foo.dart', '/bar.dart']);
stdinStream.addInputBytes(_serializeProto(request));
stdinStream.close();
await TestAnalyzerWorkerLoop(connection).run();
expect(connection.responses, hasLength(1));
var response = connection.responses[0];
expect(response.exitCode, EXIT_CODE_ERROR);
expect(response.output, anything);
}
Future<void> test_run_invalidRequest_noArgumentsInputs() async {
stdinStream.addInputBytes(_serializeProto(WorkRequest()));
stdinStream.close();
await TestAnalyzerWorkerLoop(connection).run();
expect(connection.responses, hasLength(1));
var response = connection.responses[0];
expect(response.exitCode, EXIT_CODE_ERROR);
expect(response.output, anything);
}
Future<void> test_run_invalidRequest_randomBytes() async {
stdinStream.addInputBytes([1, 2, 3]);
stdinStream.close();
await TestAnalyzerWorkerLoop(connection).run();
expect(connection.responses, hasLength(1));
var response = connection.responses[0];
expect(response.exitCode, EXIT_CODE_ERROR);
expect(response.output, anything);
}
Future<void> test_run_stopAtEOF() async {
stdinStream.close();
await TestAnalyzerWorkerLoop(connection).run();
}
List<int> _serializeProto(GeneratedMessage message) {
var buffer = message.writeToBuffer();
var writer = CodedBufferWriter();
writer.writeInt32NoTag(buffer.length);
var result = <int>[];
result.addAll(writer.toBuffer());
result.addAll(buffer);
return result;
}
}

View file

@ -10,7 +10,6 @@ import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary2/package_bundle_format.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer/src/util/sdk.dart';
import 'package:analyzer_cli/src/ansi.dart' as ansi;
@ -25,8 +24,6 @@ import 'utils.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(BuildModeTest);
defineReflectiveTests(BuildModeSummaryDependenciesTest);
defineReflectiveTests(ExitCodesTest);
defineReflectiveTests(LinterTest);
defineReflectiveTests(NonDartFilesTest);
@ -34,52 +31,6 @@ void main() {
}, name: 'Driver');
}
class AbstractBuildModeTest extends BaseTest {
List<String> get _sdkSummaryArguments {
var sdkPath = path.dirname(
path.dirname(
Platform.resolvedExecutable,
),
);
var dartSdkSummaryPath = path.join(
sdkPath,
'lib',
'_internal',
'strong.sum',
);
return ['--dart-sdk-summary', dartSdkSummaryPath];
}
Future<void> _doDrive(
String filePath, {
String sourceArgument,
String fileUri,
List<String> additionalArgs = const [],
}) async {
filePath = _posixToPlatformPath(filePath);
var options = _posixToPlatformPath(
'data/options_tests_project/' + file_paths.analysisOptionsYaml,
);
var args = <String>[];
args.add('--build-mode');
args.add('--format=machine');
args.addAll(_sdkSummaryArguments);
args.addAll(additionalArgs);
if (sourceArgument == null) {
fileUri ??= 'file:///test_file.dart';
sourceArgument = '$fileUri|$filePath';
}
await drive(sourceArgument, args: args, options: options);
}
}
class BaseTest {
static const emptyOptionsFile = 'data/empty_options.yaml';
@ -173,597 +124,6 @@ class BaseTest {
}
}
@reflectiveTest
class BuildModeSummaryDependenciesTest extends AbstractBuildModeTest {
String tempDir;
/// Any direct export is a dependency.
Future<void> test_export_direct() async {
await _withTempDir(() async {
var a = await _buildPackage('a', [], 'class A {}');
await _assertDependencies('c', [a], '''
export 'package:a/a.dart';
''', [a]);
});
}
/// Imports of dependencies are not necessary dependencies.
/// Here our dependency does not use its dependency.
Future<void> test_import2_notUsed() async {
await _withTempDir(() async {
var a = await _buildPackage('a', [], '');
var b = await _buildPackage('b', [a], '''
import 'package:a/a.dart';
''');
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
''', [b]);
});
}
Future<void> test_import2_usedAsFieldType() async {
await _withTempDir(() async {
var a = await _buildPackage('a', [], 'class A {}');
var b = await _buildPackage('b', [a], '''
import 'package:a/a.dart';
class B {
A f;
}
''');
// We don't use `f`, so don't depend on "a".
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
var x = B();
''', [b]);
// We use `f` for type inference.
// So, dependency on "a".
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
var x = B().f;
''', [a, b]);
// We reference `f` in initializer, but not for type inference.
// So, no dependency on "a".
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
Object x = B().f;
''', [b]);
// We perform full analysis, so request the type of `f`;
// So, dependency on "a".
await _assertDependencies(
'c',
[a, b],
'''
import 'package:b/b.dart';
Object x = B().f;
''',
[a, b],
summaryOnly: false,
);
});
}
Future<void> test_import2_usedAsSupertype() async {
await _withTempDir(() async {
var a = await _buildPackage('a', [], 'class A {}');
var b = await _buildPackage('b', [], 'class B {}');
var c = await _buildPackage('c', [a], '''
import 'package:a/a.dart';
import 'package:b/b.dart';
class C1 extends A {}
class C2 extends B {}
''');
// When we instantiate `C1`, we ask `C1` for its type parameters.
// So, we apply resolution to the whole `C1` header (not members).
// So, we request `A` that is the superclass of `C1`.
// So, dependency on "a".
//
// But we don't access `C2`, so don't use its supertype `B`.
// So, no dependency on "b".
await _assertDependencies('d', [a, b, c], '''
import 'package:c/c.dart';
C1 x;
''', [a, c]);
});
}
Future<void> test_import2_usedAsTopLevelVariableType() async {
await _withTempDir(() async {
var a = await _buildPackage('a', [], 'class A {}');
var b = await _buildPackage('b', [a], '''
import 'package:a/a.dart';
A v;
''');
// We don't use `v`.
// So, no dependency on "a".
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
''', [b]);
// We use `v` for type inference.
// So, dependency on "a".
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
var x = v;
''', [a, b]);
// We don't use `v` for type inference.
// So, no dependency on "a".
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
Object x = v;
''', [b]);
// We perform full analysis, and request the type of `v`.
// So, dependency on "a".
await _assertDependencies(
'c',
[a, b],
'''
import 'package:b/b.dart';
Object x = v;
''',
[a, b],
summaryOnly: false,
);
// We use `v` in a method body.
// So, no dependency on "a".
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
main() {
v;
}
''', [b]);
// We perform full analysis, so ask for the type of `v`.
// So, dependency on "a".
await _assertDependencies(
'c',
[a, b],
'''
import 'package:b/b.dart';
main() {
v;
}
''',
[a, b],
summaryOnly: false,
);
});
}
/// Any direct import is a dependency.
Future<void> test_import_direct() async {
await _withTempDir(() async {
var a = await _buildPackage('a', [], '');
var b = await _buildPackage('b', [], '');
await _assertDependencies('c', [a, b], '''
import 'package:a/a.dart';
import 'package:b/b.dart';
''', [a, b]);
});
}
/// Exports of dependencies are dependencies.
Future<void> test_import_export() async {
await _withTempDir(() async {
var a = await _buildPackage('a', [], 'class A {}');
var b = await _buildPackage('b', [a], '''
export 'package:a/a.dart';
''');
await _assertDependencies('c', [a, b], '''
import 'package:b/b.dart';
''', [a, b]);
});
}
Future<void> _assertDependencies(
String name,
List<_DependencyPackage> inputPackages,
String content,
List<_DependencyPackage> expectedPackages, {
bool summaryOnly = true,
}) async {
var pkg = await _buildPackage(name, inputPackages, content,
summaryOnly: summaryOnly);
var depString = File(pkg.dep).readAsStringSync();
var expectedList = expectedPackages.map((p) => p.sum).toList();
expect(depString.split('\n'), unorderedEquals(expectedList));
}
Future<_DependencyPackage> _buildPackage(
String name,
List<_DependencyPackage> inputPackages,
String content, {
bool summaryOnly = true,
}) async {
var filePath = path.join(tempDir, '$name.dart');
File(filePath).writeAsStringSync(content);
var pkg = _DependencyPackage(
name: name,
path: filePath,
uri: 'package:$name/$name.dart',
sum: path.join(tempDir, '$name.sum'),
dep: path.join(tempDir, '$name.dep'),
);
var args = <String>[];
if (summaryOnly) {
args.add('--build-summary-only');
}
for (var input in inputPackages) {
args.add('--build-summary-input=${input.sum}');
}
args.add('--build-summary-output=${pkg.sum}');
args.add('--summary-deps-output=${pkg.dep}');
await _doDrive(pkg.path, fileUri: pkg.uri, additionalArgs: args);
expect(exitCode, 0);
return pkg;
}
Future<void> _withTempDir(Future<void> Function() f) async {
await withTempDirAsync((tempDir) async {
this.tempDir = tempDir;
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
await f();
});
}
}
@reflectiveTest
class BuildModeTest extends AbstractBuildModeTest {
Future<void> test_buildLinked() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var inputUri = 'package:dart.my/a.dart';
var inputPath = path.join(tempDir, 'dart', 'my', 'lib', 'a.dart');
(File(inputPath)..parent.createSync(recursive: true))
.writeAsStringSync('');
var outputPath = path.join(tempDir, 'test_file.dart.sum');
await _doDrive(inputPath, fileUri: inputUri, additionalArgs: [
'--build-summary-only',
'--build-summary-output=$outputPath'
]);
var output = File(outputPath);
expect(output.existsSync(), isTrue);
var bundle = PackageBundleReader(await output.readAsBytes());
expect(_linkedLibraryUriList(bundle), [inputUri]);
expect(
_linkedLibraryUnitUriList(bundle, inputUri),
[inputUri],
);
expect(exitCode, 0);
});
}
Future<void> test_buildLinked_invalidPartUri() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var aDart = path.join(tempDir, 'a.dart');
var aUri = 'package:aaa/a.dart';
var aSum = path.join(tempDir, 'a.sum');
File(aDart).writeAsStringSync('''
part '[invalid]';
''');
await _doDrive(aDart,
fileUri: aUri, additionalArgs: ['--build-summary-output=$aSum']);
expect(exitCode, ErrorSeverity.ERROR.ordinal);
var bytes = File(aSum).readAsBytesSync();
var bundle = PackageBundleReader(bytes);
expect(_linkedLibraryUriList(bundle), [aUri]);
expect(_linkedLibraryUnitUriList(bundle, aUri), [aUri]);
});
}
Future<void> test_buildSuppressExitCode_fail_whenFileNotFound() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
await _doDrive(path.join(tempDir, 'non_existent_file.dart'),
additionalArgs: ['--build-suppress-exit-code']);
expect(exitCode, isNot(0));
});
}
Future<void> test_buildSuppressExitCode_success_evenIfHasError() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var inputUri = 'package:dart.my/a.dart';
var inputPath = path.join(tempDir, 'dart', 'my', 'lib', 'a.dart');
(File(inputPath)..parent.createSync(recursive: true))
.writeAsStringSync('error');
await _doDrive(inputPath, fileUri: inputUri, additionalArgs: [
'--build-suppress-exit-code',
]);
expect(exitCode, 0);
});
}
Future<void> test_consumeLinked() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var aDart = path.join(tempDir, 'a.dart');
var bDart = path.join(tempDir, 'b.dart');
var cDart = path.join(tempDir, 'c.dart');
var aUri = 'package:aaa/a.dart';
var bUri = 'package:bbb/b.dart';
var cUri = 'package:ccc/c.dart';
var aSum = path.join(tempDir, 'a.sum');
var bSum = path.join(tempDir, 'b.sum');
var cSum = path.join(tempDir, 'c.sum');
File(aDart).writeAsStringSync('class A {}');
File(bDart).writeAsStringSync('''
export 'package:aaa/a.dart';
class B {}
''');
File(cDart).writeAsStringSync('''
import 'package:bbb/b.dart';
var a = new A();
var b = new B();
''');
// Analyze package:aaa/a.dart and compute summary.
{
await _doDrive(aDart,
fileUri: aUri, additionalArgs: ['--build-summary-output=$aSum']);
expect(exitCode, 0);
var bytes = File(aSum).readAsBytesSync();
var bundle = PackageBundleReader(bytes);
expect(_linkedLibraryUriList(bundle), [aUri]);
expect(_linkedLibraryUnitUriList(bundle, aUri), [aUri]);
}
// Analyze package:bbb/b.dart and compute summary.
{
await _doDrive(bDart, fileUri: bUri, additionalArgs: [
'--build-summary-input=$aSum',
'--build-summary-output=$bSum'
]);
expect(exitCode, 0);
var bytes = File(bSum).readAsBytesSync();
var bundle = PackageBundleReader(bytes);
expect(_linkedLibraryUriList(bundle), [bUri]);
expect(_linkedLibraryUnitUriList(bundle, bUri), [bUri]);
}
// Analyze package:ccc/c.dart and compute summary.
{
await _doDrive(cDart, fileUri: cUri, additionalArgs: [
'--build-summary-input=$aSum,$bSum',
'--build-summary-output=$cSum'
]);
expect(exitCode, 0);
var bytes = File(cSum).readAsBytesSync();
var bundle = PackageBundleReader(bytes);
expect(_linkedLibraryUriList(bundle), [cUri]);
expect(_linkedLibraryUnitUriList(bundle, cUri), [cUri]);
}
});
}
Future<void> test_error_notUriPipePath() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var testDart = path.join(tempDir, 'test.dart');
File(testDart).writeAsStringSync('var v = 42;');
// We pass just path, not "uri|path", this is a fatal error.
await _doDrive(
testDart,
additionalArgs: ['--build-mode', '--format=machine'],
sourceArgument: testDart,
);
expect(exitCode, ErrorSeverity.ERROR.ordinal);
});
}
Future<void> test_fail_whenHasError() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var inputUri = 'package:my/with_error.dart';
var inputPath = path.join(tempDir, 'my', 'lib', 'with_error.dart');
(File(inputPath)..parent.createSync(recursive: true))
.writeAsStringSync('error');
await _doDrive(inputPath, fileUri: inputUri);
expect(exitCode, isNot(0));
});
}
@FailingTest(reason: 'Why do we support this case?')
Future<void> test_noInputs() async {
await withTempDirAsync((tempDir) async {
var outputPath = path.join(tempDir, 'test.sum');
await driveMany([], args: [
'--build-mode',
'--format=machine',
..._sdkSummaryArguments,
'--build-summary-only',
'--build-summary-output=$outputPath',
]);
var output = File(outputPath);
expect(output.existsSync(), isTrue);
expect(exitCode, 0);
});
}
Future<void> test_noStatistics() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var inputUri = 'package:my/a.dart';
var inputPath = path.join(tempDir, 'my', 'lib', 'a.dart');
(File(inputPath)..parent.createSync(recursive: true))
.writeAsStringSync('class A {}');
await _doDrive(inputPath, fileUri: inputUri);
// Should not print statistics summary.
expect(outSink.toString(), isEmpty);
expect(errorSink.toString(), isEmpty);
expect(exitCode, 0);
});
}
Future<void> test_onlyErrors_partFirst() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var aDart = path.join(tempDir, 'a.dart');
var bDart = path.join(tempDir, 'b.dart');
var aUri = 'package:aaa/a.dart';
var bUri = 'package:aaa/b.dart';
File(aDart).writeAsStringSync(r'''
library lib;
part 'b.dart';
class A {}
''');
File(bDart).writeAsStringSync('''
part of lib;
class B {}
var a = new A();
var b = new B();
''');
// Analyze b.dart (part) and then a.dart (its library).
// No errors should be reported - the part should know its library.
await _doDrive(bDart, fileUri: bUri, additionalArgs: ['$aUri|$aDart']);
expect(errorSink, isEmpty);
});
}
Future<void> test_packageConfig_packagesOptions() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var packagesPath = path.join(tempDir, 'aaa.packages');
var aaaRoot = path.join(tempDir, 'packages', 'aaa');
var aPath = path.join(aaaRoot, 'lib', 'a.dart');
var aUri = 'package:aaa/a.dart';
File(packagesPath).createSync(recursive: true);
File(packagesPath).writeAsStringSync('''
{
"configVersion": 2,
"packages": [
{
"name": "aaa",
"rootUri": "${path.toUri(aaaRoot)}",
"packageUri": "lib/",
"languageVersion": "2.4"
}
]
}
''');
File(aPath).createSync(recursive: true);
File(aPath).writeAsStringSync(r'''
extension E on int {}
''');
// Analyze package:aaa/a.dart and compute errors.
await _doDrive(
aPath,
fileUri: aUri,
additionalArgs: [
'--packages=$packagesPath',
],
);
expect(exitCode, ErrorSeverity.ERROR.ordinal);
expect(errorSink.toString(), contains('extension-methods'));
});
}
Future<void> test_packageConfig_relativeToFile() async {
await withTempDirAsync((tempDir) async {
File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var packagesPath = path.join(tempDir, '.dart_tool/package_config.json');
var aaaRoot = path.join(tempDir, 'packages', 'aaa');
var aPath = path.join(aaaRoot, 'lib', 'a.dart');
var aUri = 'package:aaa/a.dart';
File(packagesPath).createSync(recursive: true);
File(packagesPath).writeAsStringSync('''
{
"configVersion": 2,
"packages": [
{
"name": "aaa",
"rootUri": "${path.toUri(aaaRoot)}",
"packageUri": "lib/",
"languageVersion": "2.4"
}
]
}
''');
File(aPath).createSync(recursive: true);
File(aPath).writeAsStringSync(r'''
extension E on int {}
''');
// Analyze package:aaa/a.dart and compute errors.
await _doDrive(
aPath,
fileUri: aUri,
additionalArgs: [],
);
expect(exitCode, ErrorSeverity.ERROR.ordinal);
expect(errorSink.toString(), contains('extension-methods'));
});
}
Iterable<String> _linkedLibraryUnitUriList(
PackageBundleReader bundle,
String libraryUriStr,
) {
var libraries = bundle.libraries;
var library = libraries.singleWhere((l) => l.uriStr == libraryUriStr);
return library.units.map((u) => u.uriStr).toList();
}
Iterable<String> _linkedLibraryUriList(PackageBundleReader bundle) {
var libraries = bundle.libraries;
return libraries.map((l) => l.uriStr).toList();
}
}
@reflectiveTest
class ExitCodesTest extends BaseTest {
@SkippedTest(reason: 'Fails on bots, passes locally. Do not know why.')
@ -1145,13 +505,3 @@ class TestSource implements Source {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _DependencyPackage {
final String name;
final String path;
final String uri;
final String sum;
final String dep;
_DependencyPackage({this.name, this.path, this.uri, this.sum, this.dep});
}

View file

@ -55,13 +55,6 @@ void main() {
test('defaults', () {
var options = parse(['--dart-sdk', '.', 'foo.dart']);
expect(options, isNotNull);
expect(options.buildMode, isFalse);
expect(options.buildAnalysisOutput, isNull);
expect(options.buildSummaryInputs, isEmpty);
expect(options.buildSummaryOnly, isFalse);
expect(options.buildSummaryOutput, isNull);
expect(options.buildSummaryOutputSemantic, isNull);
expect(options.buildSuppressExitCode, isFalse);
expect(options.dartSdkPath, isNotNull);
expect(options.disableCacheFlushing, isFalse);
expect(options.disableHints, isFalse);
@ -270,37 +263,7 @@ void main() {
});
});
});
defineReflectiveTests(CommandLineOptions_BuildMode_Test);
}
@reflectiveTest
class AbstractStatusTest {
int lastExitHandlerCode;
StringBuffer outStringBuffer = StringBuffer();
StringBuffer errorStringBuffer = StringBuffer();
StringSink savedOutSink, savedErrorSink;
int savedExitCode;
ExitHandler savedExitHandler;
void setUp() {
savedOutSink = outSink;
savedErrorSink = errorSink;
savedExitHandler = exitHandler;
savedExitCode = exitCode;
exitHandler = (int code) {
lastExitHandlerCode = code;
};
outSink = outStringBuffer;
errorSink = errorStringBuffer;
}
void tearDown() {
outSink = savedOutSink;
errorSink = savedErrorSink;
exitCode = savedExitCode;
exitHandler = savedExitHandler;
}
defineReflectiveTests(ArgumentsTest);
}
@reflectiveTest
@ -308,17 +271,6 @@ class ArgumentsTest with ResourceProviderMixin {
CommandLineOptions commandLineOptions;
String failureMessage;
void test_dartSdkSummaryPath() {
var expected = 'my_sdk.summary';
_parse(['--dart-sdk-summary=$expected', 'a.dart']);
var builderOptions = commandLineOptions.contextBuilderOptions;
expect(
builderOptions.dartSdkSummaryPath,
endsWith(expected),
);
}
void test_declaredVariables() {
_parse(['-Da=0', '-Db=', 'a.dart']);
@ -376,46 +328,6 @@ class ArgumentsTest with ResourceProviderMixin {
);
}
void test_preprocessArgs_noReplacement() {
var original = <String>['--xx' '--yy' 'baz'];
var result = CommandLineOptions.preprocessArgs(resourceProvider, original);
expect(result, orderedEquals(original));
expect(identical(original, result), isFalse);
}
void test_preprocessArgs_replacement_exists() {
var filePath = convertPath('/args.txt');
newFile(filePath, content: '''
-a
--xx
foo
bar
''');
var result = CommandLineOptions.preprocessArgs(
resourceProvider, ['--preserved', '@$filePath']);
expect(result, orderedEquals(['--preserved', '-a', '--xx', 'foo', 'bar']));
}
void test_preprocessArgs_replacement_nonexistent() {
var filePath = convertPath('/args.txt');
var args = <String>['ignored', '@$filePath'];
try {
CommandLineOptions.preprocessArgs(resourceProvider, args);
fail('Expect exception');
} on Exception catch (e) {
expect(e.toString(), contains('Failed to read file'));
expect(e.toString(), contains('@$filePath'));
}
}
void test_preprocessArgs_replacement_notLast() {
var filePath = convertPath('/args.txt');
var args = <String>['a', '@$filePath', 'b'];
var result = CommandLineOptions.preprocessArgs(resourceProvider, args);
expect(result, orderedEquals(args));
}
void test_updateAnalysisOptions_defaultLanguageVersion() {
_applyAnalysisOptions(
['a.dart'],
@ -656,140 +568,3 @@ bar
);
}
}
@reflectiveTest
class CommandLineOptions_BuildMode_Test extends AbstractStatusTest {
CommandLineOptions options;
String failureMessage;
void test_buildAnalysisOutput() {
_parseBuildMode([
'--build-analysis-output=//path/to/output.analysis',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
expect(options.buildAnalysisOutput, '//path/to/output.analysis');
}
void test_buildMode() {
_parseBuildMode([
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
}
void test_buildMode_allowsEmptyFileList() {
_parseBuildMode([]);
expect(options.buildMode, isTrue);
expect(options.sourceFiles, isEmpty);
}
void test_buildMode_noDartSdkSummary() {
_parseBuildMode(
['package:aaa/a.dart|/aaa/lib/a.dart'],
withDartSdkSummary: false,
);
expect(options, isNull);
expect(failureMessage, contains('--dart-sdk-summary'));
}
void test_buildSummaryInputs_commaSeparated() {
_parseBuildMode([
'--build-summary-input=/path/to/aaa.sum,/path/to/bbb.sum',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
expect(
options.buildSummaryInputs, ['/path/to/aaa.sum', '/path/to/bbb.sum']);
}
void test_buildSummaryInputs_commaSeparated_normalMode() {
_parse([
'--build-summary-input=/path/to/aaa.sum,/path/to/bbb.sum',
'/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isFalse);
expect(
options.buildSummaryInputs, ['/path/to/aaa.sum', '/path/to/bbb.sum']);
}
void test_buildSummaryInputs_separateFlags() {
_parseBuildMode([
'--build-summary-input=/path/to/aaa.sum',
'--build-summary-input=/path/to/bbb.sum',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
expect(
options.buildSummaryInputs, ['/path/to/aaa.sum', '/path/to/bbb.sum']);
}
void test_buildSummaryInputs_separateFlags_normalMode() {
_parse([
'--build-summary-input=/path/to/aaa.sum',
'--build-summary-input=/path/to/bbb.sum',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isFalse);
expect(
options.buildSummaryInputs, ['/path/to/aaa.sum', '/path/to/bbb.sum']);
}
void test_buildSummaryOnly() {
_parseBuildMode([
'--build-summary-output=/path/to/aaa.sum',
'--build-summary-only',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
expect(options.buildSummaryOnly, isTrue);
}
void test_buildSummaryOutput() {
_parseBuildMode([
'--build-summary-output=//path/to/output.sum',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
expect(options.buildSummaryOutput, '//path/to/output.sum');
}
void test_buildSummaryOutputSemantic() {
_parseBuildMode([
'--build-summary-output-semantic=//path/to/output.sum',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
expect(options.buildSummaryOutputSemantic, '//path/to/output.sum');
}
void test_buildSuppressExitCode() {
_parseBuildMode([
'--build-suppress-exit-code',
'package:p/foo.dart|/path/to/p/lib/foo.dart',
]);
expect(options.buildMode, isTrue);
expect(options.buildSuppressExitCode, isTrue);
}
void _parse(List<String> args) {
var resourceProvider = PhysicalResourceProvider.INSTANCE;
options =
CommandLineOptions.parse(resourceProvider, args, printAndFail: (msg) {
failureMessage = msg;
});
}
void _parseBuildMode(List<String> specificArguments,
{bool withDartSdkSummary = true}) {
var args = [
'--build-mode',
if (withDartSdkSummary) ...[
'--dart-sdk-summary',
'/sdk/lib/strong.sum',
],
...specificArguments
];
_parse(args);
}
}