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:
committed by
commit-bot@chromium.org
parent
e1414089cf
commit
00a45e425a
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user