Train the analysis server snapshot on pkg/analyzer_cli.

Change-Id: I07dcaec344d681a4abf8f09558d83a68efdd70e8
Reviewed-on: https://dart-review.googlesource.com/65575
Commit-Queue: Devon Carew <devoncarew@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Devon Carew 2018-07-18 21:32:09 +00:00 committed by commit-bot@chromium.org
parent af613c7479
commit 7ddc4ce8ba
3 changed files with 233 additions and 12 deletions

View file

@ -0,0 +1,189 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/src/channel/channel.dart';
import 'package:analysis_server/src/socket_server.dart';
import 'package:path/path.dart' as path;
/**
* Instances of the class [DevAnalysisServer] implement a simple analysis
* server implementation, used to analyze one or more packages and then
* terminate the server.
*/
class DevAnalysisServer {
static bool get _terminalSupportsAnsi {
return stdout.supportsAnsiEscapes &&
stdioType(stdout) == StdioType.terminal;
}
/**
* An object that can handle either a WebSocket connection or a connection
* to the client over stdio.
*/
final SocketServer socketServer;
/**
* Initialize a newly created stdio server.
*/
DevAnalysisServer(this.socketServer);
/**
* Analyze the given directories and display any results to stdout.
*
* Return a future that will be completed with an exit code when analysis
* finishes.
*/
Future<int> processDirectories(List<String> directories) async {
final String bold = _terminalSupportsAnsi ? '\u001b[1m' : '';
final String none = _terminalSupportsAnsi ? '\u001b[0m' : '';
print('Analyzing ${directories.join(', ')}...');
Stopwatch timer = new Stopwatch()..start();
Completer<int> whenComplete = new Completer();
DevChannel channel = new DevChannel();
int exitCode = 0;
void handleStatusNotification(Notification notification) {
Map<String, dynamic> params = notification.params;
if (params.containsKey('analysis')) {
bool isAnalyzing = params['analysis']['isAnalyzing'];
if (!isAnalyzing) {
timer.stop();
double seconds = timer.elapsedMilliseconds / 1000.0;
print('Completed in ${seconds.toStringAsFixed(1)}s.');
whenComplete.complete(exitCode);
}
}
}
void handleErrorsNotification(Notification notification) {
String filePath = notification.params['file'];
List<Map> errors = notification.params['errors'];
if (errors.isEmpty) {
return;
}
filePath = path.relative(filePath);
for (Map error in errors) {
if (error['type'] == 'TODO') {
continue;
}
String severity = error['severity'].toLowerCase();
if (severity == 'warning' && exitCode < 1) {
exitCode = 1;
} else if (severity == 'error' && exitCode < 2) {
exitCode = 2;
}
String message = error['message'];
if (message.endsWith('.')) {
message = message.substring(0, message.length - 1);
}
String code = error['code'];
int line = error['location']['startLine'];
int column = error['location']['startColumn'];
print(' $severity$bold$message$none at $filePath:$line:$column'
'($code)');
}
}
void handleServerError(Notification notification) {
Map<String, dynamic> params = notification.params;
String message = params['message'];
String stackTrace = params['stackTrace'];
print(message);
if (stackTrace != null) {
print(stackTrace);
}
exitCode = 3;
}
channel.onNotification.listen((Notification notification) {
if (notification.event == 'server.status') {
handleStatusNotification(notification);
} else if (notification.event == 'analysis.errors') {
handleErrorsNotification(notification);
} else if (notification.event == 'server.error') {
handleServerError(notification);
}
});
socketServer.createAnalysisServer(channel);
int id = 0;
channel.sendRequest(new Request('${id++}', 'server.setSubscriptions', {
'subscriptions': ['STATUS'],
}));
directories =
directories.map((dir) => path.normalize(path.absolute(dir))).toList();
channel.sendRequest(new Request('${id++}', 'analysis.setAnalysisRoots', {
'included': directories,
'excluded': [],
}));
return whenComplete.future;
}
}
class DevChannel implements ServerCommunicationChannel {
StreamController<Request> _requestController = new StreamController();
StreamController<Notification> _notificationController =
new StreamController();
Map<String, Completer<Response>> _responseCompleters = {};
@override
void close() {
_notificationController.close();
}
@override
void listen(
void Function(Request request) onRequest, {
Function onError,
void Function() onDone,
}) {
_requestController.stream.listen(
onRequest,
onError: onError,
onDone: onDone,
);
}
@override
void sendNotification(Notification notification) {
_notificationController.add(notification);
}
@override
void sendResponse(Response response) {
Completer<Response> completer = _responseCompleters.remove(response.id);
completer?.complete(response);
}
Future<Response> sendRequest(Request request) {
Completer<Response> completer = new Completer();
_responseCompleters[request.id] = completer;
_requestController.add(request);
return completer.future;
}
Stream<Notification> get onNotification => _notificationController.stream;
}

View file

@ -7,6 +7,7 @@ import 'dart:io';
import 'dart:math';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/server/dev_server.dart';
import 'package:analysis_server/src/server/diagnostic_server.dart';
import 'package:analysis_server/src/server/http_server.dart';
import 'package:analysis_server/src/server/stdio_server.dart';
@ -31,6 +32,8 @@ import 'package:telemetry/telemetry.dart' as telemetry;
/// TODO(pquitslund): replaces with a simple [ArgParser] instance
/// when the args package supports ignoring unrecognized
/// options/flags (https://github.com/dart-lang/args/issues/9).
/// TODO(devoncarew): Consider removing the ability to support unrecognized
/// flags for the analysis server.
class CommandLineParser {
final List<String> _knownFlags;
final bool _alwaysIgnoreUnrecognized;
@ -267,6 +270,11 @@ class Driver implements ServerStarter {
*/
static const String USE_FASTA_PARSER = "use-fasta-parser";
/**
* A directory to analyze in order to train an analysis server snapshot.
*/
static const String TRAIN_USING = "train-using";
/**
* The instrumentation server that is to be used by the analysis server.
*/
@ -288,8 +296,6 @@ class Driver implements ServerStarter {
HttpAnalysisServer httpServer;
StdioAnalysisServer stdioServer;
Driver();
/**
@ -354,6 +360,15 @@ class Driver implements ServerStarter {
return null;
}
String trainDirectory = results[TRAIN_USING];
if (trainDirectory != null) {
if (!FileSystemEntity.isDirectorySync(trainDirectory)) {
print("Training directory '$trainDirectory' not found.\n");
exitCode = 1;
return null;
}
}
int port;
bool serve_http = false;
if (results[PORT_OPTION] != null) {
@ -430,26 +445,38 @@ class Driver implements ServerStarter {
fileResolverProvider,
packageResolverProvider);
httpServer = new HttpAnalysisServer(socketServer);
stdioServer = new StdioAnalysisServer(socketServer);
diagnosticServer.httpServer = httpServer;
if (serve_http) {
diagnosticServer.startOnPort(port);
}
_captureExceptions(instrumentationService, () {
stdioServer.serveStdio().then((_) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
if (trainDirectory != null) {
DevAnalysisServer devServer = new DevAnalysisServer(socketServer);
devServer.processDirectories([trainDirectory]).then((int exitCode) async {
if (serve_http) {
httpServer.close();
}
await instrumentationService.shutdown();
exit(0);
exit(exitCode);
});
},
print:
results[INTERNAL_PRINT_TO_CONSOLE] ? null : httpServer.recordPrint);
} else {
_captureExceptions(instrumentationService, () {
StdioAnalysisServer stdioServer = new StdioAnalysisServer(socketServer);
stdioServer.serveStdio().then((_) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
if (serve_http) {
httpServer.close();
}
await instrumentationService.shutdown();
exit(0);
});
},
print: results[INTERNAL_PRINT_TO_CONSOLE]
? null
: httpServer.recordPrint);
}
return socketServer.analysisServer;
}
@ -546,6 +573,9 @@ class Driver implements ServerStarter {
help: "Enable the Dart 2.0 Common Front End implementation");
parser.addFlag(USE_FASTA_PARSER,
help: "Whether to enable parsing via the Fasta parser");
parser.addOption(TRAIN_USING,
help: "Pass in a directory to analyze for purposes of training an "
"analysis server snapshot.");
return parser;
}

View file

@ -7,5 +7,7 @@ import("../application_snapshot.gni")
application_snapshot("analysis_server") {
dart_version = 2
main_dart = "../../pkg/analysis_server/bin/server.dart"
training_args = [ "--help" ]
training_args = [
"--train-using=" + rebase_path("../../pkg/analyzer_cli"),
]
}