mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:26:38 +00:00
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:
parent
af613c7479
commit
7ddc4ce8ba
189
pkg/analysis_server/lib/src/server/dev_server.dart
Normal file
189
pkg/analysis_server/lib/src/server/dev_server.dart
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue