Meld server runner in dart fix with analysis_server_client

Change-Id: I86a2e7b815ff6d3beea2d968c5e34c4e3feae2c3
Reviewed-on: https://dart-review.googlesource.com/c/82080
Commit-Queue: Dan Rubel <danrubel@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Dan Rubel
2018-10-30 20:04:58 +00:00
committed by commit-bot@chromium.org
parent 98a0309fbd
commit 645ec2e8b5
8 changed files with 406 additions and 487 deletions
@@ -1,101 +0,0 @@
// Copyright (c) 2017, 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:convert';
import 'dart:io';
/// Type of callbacks used to process notification.
typedef void NotificationProcessor(String event, Map<String, Object> params);
/// Instances of the class [AnalysisServerClient] manage a connection to an
/// [AnalysisServer] process, and facilitate communication to and from the
/// client/user.
class AnalysisServerClient {
/// AnalysisServer process object, or null if the server has been shut down.
final Process _process;
/// Commands that have been sent to the server but not yet acknowledged,
/// and the [Completer] objects which should be completed when
/// acknowledgement is received.
final Map<String, Completer> _pendingCommands = <String, Completer>{};
/// Number which should be used to compute the 'id' to send to the next
/// command sent to the server.
int _nextId = 0;
AnalysisServerClient(this._process);
/// Return a future that will complete when all commands that have been
/// sent to the server so far have been flushed to the OS buffer.
Future<Null> flushCommands() {
return _process.stdin.flush();
}
/// Force kill the server. Returns exit code future.
Future<int> kill() {
_process.kill();
return _process.exitCode;
}
void listenToOutput({NotificationProcessor notificationProcessor}) {
_process.stdout
.transform((new Utf8Codec()).decoder)
.transform(new LineSplitter())
.listen((String line) {
String trimmedLine = line.trim();
if (trimmedLine.startsWith('Observatory listening on ')) {
return;
}
final result = json.decoder.convert(trimmedLine) as Map;
if (result.containsKey('id')) {
final id = result['id'] as String;
final completer = _pendingCommands.remove(id);
if (result.containsKey('error')) {
completer.completeError(new ServerErrorMessage(result['error']));
} else {
completer.complete(result['result']);
}
} else if (notificationProcessor != null && result.containsKey('event')) {
// Message is a notification. It should have an event and possibly
// params.
notificationProcessor(result['event'], result['params']);
}
});
}
/// Sends a command to the server. An 'id' will be automatically assigned.
/// The returned [Future] will be completed when the server acknowledges
/// the command with a response. If the server acknowledges the command
/// with a normal (non-error) response, the future will be completed
/// with the 'result' field from the response. If the server acknowledges
/// the command with an error response, the future will be completed with an
/// error.
Future send(String method, Map<String, dynamic> params) {
String id = '${_nextId++}';
Map<String, dynamic> command = <String, dynamic>{
'id': id,
'method': method
};
if (params != null) {
command['params'] = params;
}
Completer completer = new Completer();
_pendingCommands[id] = completer;
String commandAsJson = json.encode(command);
_process.stdin.add(utf8.encoder.convert('$commandAsJson\n'));
return completer.future;
}
}
class ServerErrorMessage {
final Map errorJson;
ServerErrorMessage(this.errorJson);
String get code => errorJson['code'].toString();
String get message => errorJson['message'];
String get stackTrace => errorJson['stackTrace'];
}
@@ -0,0 +1,55 @@
// 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 'package:analysis_server_client/server.dart';
/// A subclass of [Server] that caches all messages exchanged with the server.
/// This is primarily used when testing and debugging the analysis server.
/// Most clients will want to use [Server] rather than this class.
class RecordingServer extends Server {
/// True if we are currently printing out messages exchanged with the server.
bool _echoMessages = false;
/// Messages which have been exchanged with the server; we buffer these
/// up until the test finishes, so that they can be examined in the debugger
/// or printed out in response to a call to [echoMessages].
final _messages = <String>[];
/// Print out any messages exchanged with the server. If some messages have
/// already been exchanged with the server, they are printed out immediately.
void echoMessages() {
if (_echoMessages) {
return;
}
_echoMessages = true;
for (String line in _messages) {
print(line);
}
}
@override
Future<int> kill([String reason = 'none']) {
echoMessages();
return super.kill(reason);
}
@override
void logBadDataFromServer(String details, {bool silent: false}) {
echoMessages();
super.logBadDataFromServer(details, silent: silent);
}
/// Record a message that was exchanged with the server,
/// and print it out if [echoMessages] has been called.
@override
void logMessage(String prefix, String details) {
String line = '$currentElapseTime: $prefix $details';
if (_echoMessages) {
print(line);
}
_messages.add(line);
}
}
+275
View File
@@ -0,0 +1,275 @@
// 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:convert';
import 'dart:io';
import 'package:path/path.dart';
/// Type of callbacks used to process notifications.
typedef void NotificationProcessor(String event, Map<String, dynamic> params);
/// Instances of the class [Server] manage a connection to a server process,
/// and facilitate communication to and from the server.
class Server {
/// Server process object, or `null` if server hasn't been started yet.
Process _process;
/// Commands that have been sent to the server but not yet acknowledged,
/// and the [Completer] objects which should be completed
/// when acknowledgement is received.
final _pendingCommands = <String, Completer<Map<String, dynamic>>>{};
/// Number which should be used to compute the 'id'
/// to send in the next command sent to the server.
int _nextId = 0;
/// True if we've received bad data from the server.
bool _receivedBadDataFromServer = false;
/// Stopwatch that we use to generate timing information for debug output.
Stopwatch _time = new Stopwatch();
/// The [currentElapseTime] at which the last communication was received from
/// the server or `null` if no communication has been received.
double lastCommunicationTime;
Server([Process process]) : this._process = process;
/// The current elapse time (seconds) since the server was started.
double get currentElapseTime => _time.elapsedTicks / _time.frequency;
/// Future that completes when the server process exits.
Future<int> get exitCode => _process.exitCode;
/// Return a future that will complete when all commands that have been sent
/// to the server so far have been flushed to the OS buffer.
Future<void> flushCommands() => _process.stdin.flush();
/// Force kill the server. Returns exit code future.
Future<int> kill([String reason = 'none']) {
logMessage('FORCIBLY TERMINATING PROCESS: ', reason);
_process.kill();
return _process.exitCode;
}
/// Start listening to output from the server,
/// and deliver notifications to [notificationProcessor].
void listenToOutput({NotificationProcessor notificationProcessor}) {
_process.stdout
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((String line) {
lastCommunicationTime = currentElapseTime;
String trimmedLine = line.trim();
// Guard against lines like:
// {"event":"server.connected","params":{...}}Observatory listening on ...
const observatoryMessage = 'Observatory listening on ';
if (trimmedLine.contains(observatoryMessage)) {
trimmedLine = trimmedLine
.substring(0, trimmedLine.indexOf(observatoryMessage))
.trim();
}
if (trimmedLine.isEmpty) {
return;
}
logMessage('<== ', trimmedLine);
Map<String, dynamic> message;
try {
message = json.decoder.convert(trimmedLine);
} catch (exception) {
logBadDataFromServer('JSON decode failure: $exception');
return;
}
final id = message['id'];
if (id != null) {
// Handle response
final completer = _pendingCommands.remove(id);
if (completer == null) {
throw 'Unexpected response from server: id=$id';
}
if (message.containsKey('error')) {
completer.completeError(new ServerErrorMessage(message));
} else {
completer.complete(message['result']);
}
} else {
// Handle notification
final String event = message['event'];
if (event != null) {
if (notificationProcessor != null) {
notificationProcessor(event, message['params']);
}
} else {
logBadDataFromServer('Unexpected message from server');
}
}
});
_process.stderr
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((String line) {
String trimmedLine = line.trim();
logMessage('ERR: ', trimmedLine);
logBadDataFromServer('Message received on stderr', silent: true);
});
}
/// Send a command to the server. An 'id' will be automatically assigned.
/// The returned [Future] will be completed when the server acknowledges
/// the command with a response.
/// If the server acknowledges the command with a normal (non-error) response,
/// the future will be completed with the 'result' field from the response.
/// If the server acknowledges the command with an error response,
/// the future will be completed with an error.
Future<Map<String, dynamic>> send(
String method, Map<String, dynamic> params) {
String id = '${_nextId++}';
Map<String, dynamic> command = <String, dynamic>{
'id': id,
'method': method
};
if (params != null) {
command['params'] = params;
}
final completer = new Completer<Map<String, dynamic>>();
_pendingCommands[id] = completer;
String line = json.encode(command);
logMessage('==> ', line);
_process.stdin.add(utf8.encoder.convert("$line\n"));
return completer.future;
}
/**
* Start the server.
*
* If [profileServer] is `true`, the server will be started
* with "--observe" and "--pause-isolates-on-exit", allowing the observatory
* to be used.
*
* If [serverPath] is specified, then that analysis server will be launched,
* otherwise the analysis server snapshot in the SDK will be launched.
*/
Future start({
int diagnosticPort,
String instrumentationLogFile,
bool profileServer: false,
String sdkPath,
String serverPath,
int servicesPort,
bool suppressAnalytics: true,
bool useAnalysisHighlight2: false,
}) async {
if (_process != null) {
throw new Exception('Process already started');
}
_time.start();
String dartBinary = Platform.executable;
// The integration tests run 3x faster when run from snapshots
// (you need to run test.py with --use-sdk).
if (serverPath == null) {
// Look for snapshots/analysis_server.dart.snapshot.
serverPath = normalize(join(dirname(Platform.resolvedExecutable),
'snapshots', 'analysis_server.dart.snapshot'));
if (!FileSystemEntity.isFileSync(serverPath)) {
// Look for dart-sdk/bin/snapshots/analysis_server.dart.snapshot.
serverPath = normalize(join(dirname(Platform.resolvedExecutable),
'dart-sdk', 'bin', 'snapshots', 'analysis_server.dart.snapshot'));
}
}
List<String> arguments = [];
//
// Add VM arguments.
//
if (profileServer) {
if (servicesPort == null) {
arguments.add('--observe');
} else {
arguments.add('--observe=$servicesPort');
}
arguments.add('--pause-isolates-on-exit');
} else if (servicesPort != null) {
arguments.add('--enable-vm-service=$servicesPort');
}
if (Platform.packageConfig != null) {
arguments.add('--packages=${Platform.packageConfig}');
}
//
// Add the server executable.
//
arguments.add(serverPath);
//
// Add server arguments.
//
if (suppressAnalytics) {
arguments.add('--suppress-analytics');
}
if (diagnosticPort != null) {
arguments.add('--port');
arguments.add(diagnosticPort.toString());
}
if (instrumentationLogFile != null) {
arguments.add('--instrumentation-log-file=$instrumentationLogFile');
}
if (sdkPath != null) {
arguments.add('--sdk=$sdkPath');
}
if (useAnalysisHighlight2) {
arguments.add('--useAnalysisHighlight2');
}
logMessage(
'Starting analysis server: ', '$dartBinary ${arguments.join(' ')}');
_process = await Process.start(dartBinary, arguments);
_process.exitCode.then((int code) {
if (code != 0) {
logBadDataFromServer('server terminated with exit code $code');
}
});
}
/// Deal with bad data received from the server.
void logBadDataFromServer(String details, {bool silent: false}) {
if (!silent) {
logMessage('BAD DATA FROM SERVER: ', details);
}
if (_receivedBadDataFromServer) {
// We're already dealing with it.
return;
}
_receivedBadDataFromServer = true;
// Give the server 1 second to continue outputting bad data
// such as outputting a stacktrace.
new Future.delayed(new Duration(seconds: 1), () {
throw 'Bad data received from server: $details';
});
}
/// Log a message that was exchanged with the server.
/// Subclasses may override as needed.
void logMessage(String prefix, String details) {
// no-op
}
}
/// An error result from a server request.
class ServerErrorMessage {
final Map<String, dynamic> message;
ServerErrorMessage(this.message);
Map<String, dynamic> get error => message['error'];
get errorCode => error['code'];
get errorMessage => error['message'];
get stackTrace => error['stackTrace'];
String toString() => message.toString();
}
+9
View File
@@ -0,0 +1,9 @@
// 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 'server_test.dart' as server_test;
main() {
server_test.main();
}
@@ -6,23 +6,24 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:analysis_server_client/analysis_server_client.dart';
import 'package:analysis_server_client/server.dart';
import 'package:test/test.dart';
void main() {
MockProcess process;
AnalysisServerClient serverWrapper;
Server server;
setUp(() async {
process = new MockProcess();
serverWrapper = new AnalysisServerClient(process);
server = new Server(process);
});
test('test_listenToOutput_good', () async {
process.stdout = _goodMessage();
process.stderr = _noMessage();
final future = serverWrapper.send('blahMethod', null);
serverWrapper.listenToOutput();
final future = server.send('blahMethod', null);
server.listenToOutput();
final response = await future;
expect(response, const TypeMatcher<Map>());
@@ -32,19 +33,22 @@ void main() {
test('test_listenToOutput_error', () async {
process.stdout = _badMessage();
final future = serverWrapper.send('blahMethod', null);
process.stderr = _noMessage();
final future = server.send('blahMethod', null);
future.catchError((e) {
expect(e, const TypeMatcher<ServerErrorMessage>());
final e2 = e as ServerErrorMessage;
expect(e2.code, 'someErrorCode');
expect(e2.message, 'something went wrong');
expect(e2.stackTrace, 'some long stack trace');
final error = e as ServerErrorMessage;
expect(error.errorCode, 'someErrorCode');
expect(error.errorMessage, 'something went wrong');
expect(error.stackTrace, 'some long stack trace');
});
serverWrapper.listenToOutput();
server.listenToOutput();
});
test('test_listenToOutput_event', () async {
process.stdout = _eventMessage();
process.stderr = _noMessage();
void eventHandler(String event, Map<String, Object> params) {
expect(event, 'fooEvent');
@@ -53,8 +57,8 @@ void main() {
expect(params['baz'] as String, 'bang');
}
serverWrapper.send('blahMethod', null);
serverWrapper.listenToOutput(notificationProcessor: eventHandler);
server.send('blahMethod', null);
server.listenToOutput(notificationProcessor: eventHandler);
});
}
@@ -88,6 +92,10 @@ Stream<List<int>> _goodMessage() async* {
yield utf8.encoder.convert(json.encode(sampleJson));
}
Stream<List<int>> _noMessage() async* {
yield utf8.encoder.convert('');
}
class MockProcess implements Process {
@override
Stream<List<int>> stderr;
+29 -13
View File
@@ -6,10 +6,11 @@ import 'dart:async';
import 'dart:io' show File, Platform;
import 'package:analysis_server_client/protocol.dart';
import 'package:analysis_server_client/server.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:dartfix/src/context.dart';
import 'package:dartfix/src/verbose_server.dart';
import 'package:dartfix/src/options.dart';
import 'package:dartfix/src/server.dart';
import 'package:path/path.dart' as path;
class Driver {
@@ -25,11 +26,24 @@ class Driver {
Ansi get ansi => logger.ansi;
bool get runAnalysisServerFromSource {
// Automatically run analysis server from source
// if this command line tool is being run from source
// within the source tree.
return Server.findRoot() != null;
/// Return the analysis_server executable by proceeding upward
/// until finding the Dart SDK repository root then returning
/// the analysis_server executable within the repository.
/// Return `null` if it cannot be found.
String findServerPath() {
String pathname = Platform.script.toFilePath();
while (true) {
String parent = path.dirname(pathname);
if (parent.length >= pathname.length) {
return null;
}
String serverPath =
path.join(parent, 'pkg', 'analysis_server', 'bin', 'server.dart');
if (new File(serverPath).existsSync()) {
return serverPath;
}
pathname = parent;
}
}
Future start(List<String> args) async {
@@ -66,20 +80,22 @@ class Driver {
}
Future startServer(Options options) async {
server = new Server(logger);
server = logger.isVerbose ? new VerboseServer(logger) : new Server();
const connectTimeout = const Duration(seconds: 15);
serverConnected = new Completer();
if (options.verbose) {
server.debugStdio();
logger.trace('Dart SDK version ${Platform.version}');
logger.trace(' ${Platform.resolvedExecutable}');
logger.trace('dartfix');
logger.trace(' ${Platform.script.toFilePath()}');
}
final runFromSource = runAnalysisServerFromSource;
logger.trace(runFromSource ? 'Starting from source...' : 'Starting...');
await server.start(sdkPath: options.sdkPath, useSnapshot: !runFromSource);
server.listenToOutput(dispatchNotification);
// Automatically run analysis server from source
// if this command line tool is being run from source within the SDK repo.
String serverPath = findServerPath();
logger
.trace(serverPath != null ? 'Starting from source...' : 'Starting...');
await server.start(sdkPath: options.sdkPath, serverPath: serverPath);
server.listenToOutput(notificationProcessor: handleEvent);
await serverConnected.future.timeout(connectTimeout, onTimeout: () {
logger.stderr('Failed to connect to server');
context.exit(15);
@@ -192,7 +208,7 @@ class Driver {
/// Dispatch the notification named [event], and containing parameters
/// [params], to the appropriate stream.
void dispatchNotification(String event, params) {
void handleEvent(String event, params) {
ResponseDecoder decoder = new ResponseDecoder(null);
switch (event) {
case SERVER_NOTIFICATION_CONNECTED:
-360
View File
@@ -1,360 +0,0 @@
// 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:convert';
import 'dart:io';
import 'package:cli_util/cli_logging.dart';
import 'package:path/path.dart';
/**
* Type of callbacks used to process notifications.
*/
typedef void NotificationProcessor(String event, params);
/**
* Instances of the class [Server] manage a connection to a server process, and
* facilitate communication to and from the server.
*/
class Server {
/**
* Server process object, or null if server hasn't been started yet.
*/
Process _process;
/**
* Commands that have been sent to the server but not yet acknowledged, and
* the [Completer] objects which should be completed when acknowledgement is
* received.
*/
final Map<String, Completer<Map<String, dynamic>>> _pendingCommands =
<String, Completer<Map<String, dynamic>>>{};
/**
* Number which should be used to compute the 'id' to send in the next command
* sent to the server.
*/
int _nextId = 0;
/**
* Messages which have been exchanged with the server; we buffer these
* up until the test finishes, so that they can be examined in the debugger
* or printed out in response to a call to [debugStdio].
*/
final List<String> _recordedStdio = <String>[];
/**
* True if we are currently printing out messages exchanged with the server.
*/
bool _debuggingStdio = false;
/**
* True if we've received bad data from the server, and we are aborting the
* test.
*/
bool _receivedBadDataFromServer = false;
/**
* Stopwatch that we use to generate timing information for debug output.
*/
Stopwatch _time = new Stopwatch();
/**
* The [currentElapseTime] at which the last communication was received from the server
* or `null` if no communication has been received.
*/
double lastCommunicationTime;
/**
* The current elapse time (seconds) since the server was started.
*/
double get currentElapseTime => _time.elapsedTicks / _time.frequency;
/**
* Future that completes when the server process exits.
*/
Future<int> get exitCode => _process.exitCode;
final Logger logger;
Server(this.logger);
/**
* Print out any messages exchanged with the server. If some messages have
* already been exchanged with the server, they are printed out immediately.
*/
void debugStdio() {
if (_debuggingStdio) {
return;
}
_debuggingStdio = true;
for (String line in _recordedStdio) {
logger.trace(line);
}
}
/**
* Find the root directory of the analysis_server package by proceeding
* upward until finding the Dart SDK repository root then returning
* the analysis_server package root within the repository.
* Return `null` if it cannot be found.
*/
static String findRoot([String pathname]) {
pathname ??= Platform.script.toFilePath(windows: Platform.isWindows);
while (true) {
String parent = dirname(pathname);
if (parent.length >= pathname.length) {
return null;
}
String root = normalize(join(parent, 'pkg', 'analysis_server'));
String server = join(root, 'bin', 'server.dart');
if (new File(server).existsSync()) {
return root;
}
pathname = parent;
}
}
/**
* Return a future that will complete when all commands that have been sent
* to the server so far have been flushed to the OS buffer.
*/
Future flushCommands() {
return _process.stdin.flush();
}
/**
* Stop the server.
*/
Future<int> kill(String reason) {
debugStdio();
_recordStdio('FORCIBLY TERMINATING PROCESS: $reason');
_process.kill();
return _process.exitCode;
}
/**
* Start listening to output from the server, and deliver notifications to
* [notificationProcessor].
*/
void listenToOutput(NotificationProcessor notificationProcessor) {
_process.stdout
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((String line) {
lastCommunicationTime = currentElapseTime;
String trimmedLine = line.trim();
// Guard against lines like:
// {"event":"server.connected","params":{...}}Observatory listening on ...
final String observatoryMessage = 'Observatory listening on ';
if (trimmedLine.contains(observatoryMessage)) {
trimmedLine = trimmedLine
.substring(0, trimmedLine.indexOf(observatoryMessage))
.trim();
}
if (trimmedLine.isEmpty) {
return;
}
_recordStdio('<== $trimmedLine');
var message;
try {
message = json.decoder.convert(trimmedLine);
} catch (exception) {
_badDataFromServer('JSON decode failure: $exception');
return;
}
Map messageAsMap = message;
if (messageAsMap.containsKey('id')) {
String id = message['id'];
Completer<Map<String, dynamic>> completer = _pendingCommands[id];
if (completer == null) {
throw 'Unexpected response from server: id=$id';
} else {
_pendingCommands.remove(id);
}
if (messageAsMap.containsKey('error')) {
completer.completeError(new ServerErrorMessage(messageAsMap));
} else {
Map<String, dynamic> result = messageAsMap['result'];
completer.complete(result);
}
} else {
String event = messageAsMap['event'];
notificationProcessor(event, messageAsMap['params']);
}
});
_process.stderr
.transform((new Utf8Codec()).decoder)
.transform(new LineSplitter())
.listen((String line) {
String trimmedLine = line.trim();
_recordStdio('ERR: $trimmedLine');
_badDataFromServer('Message received on stderr', silent: true);
});
}
/**
* Send a command to the server. An 'id' will be automatically assigned.
* The returned [Future] will be completed when the server acknowledges the
* command with a response. If the server acknowledges the command with a
* normal (non-error) response, the future will be completed with the 'result'
* field from the response. If the server acknowledges the command with an
* error response, the future will be completed with an error.
*/
Future<Map<String, dynamic>> send(
String method, Map<String, dynamic> params) {
String id = '${_nextId++}';
Map<String, dynamic> command = <String, dynamic>{
'id': id,
'method': method
};
if (params != null) {
command['params'] = params;
}
Completer<Map<String, dynamic>> completer =
new Completer<Map<String, dynamic>>();
_pendingCommands[id] = completer;
String line = json.encode(command);
_recordStdio('==> $line');
_process.stdin.add(utf8.encoder.convert("$line\n"));
return completer.future;
}
/**
* Start the server. If [profileServer] is `true`, the server will be started
* with "--observe" and "--pause-isolates-on-exit", allowing the observatory
* to be used.
*/
Future start({
int diagnosticPort,
String instrumentationLogFile,
bool profileServer: false,
String sdkPath,
int servicesPort,
bool useAnalysisHighlight2: false,
bool useSnapshot: true,
}) async {
if (_process != null) {
throw new Exception('Process already started');
}
_time.start();
String dartBinary = Platform.executable;
String serverPath;
// The integration tests run 3x faster when run from snapshots (you need to
// run test.py with --use-sdk).
if (useSnapshot) {
// Look for snapshots/analysis_server.dart.snapshot.
serverPath = normalize(join(dirname(Platform.resolvedExecutable),
'snapshots', 'analysis_server.dart.snapshot'));
if (!FileSystemEntity.isFileSync(serverPath)) {
// Look for dart-sdk/bin/snapshots/analysis_server.dart.snapshot.
serverPath = normalize(join(dirname(Platform.resolvedExecutable),
'dart-sdk', 'bin', 'snapshots', 'analysis_server.dart.snapshot'));
}
} else {
String rootDir = Server.findRoot();
if (rootDir == null) {
throw new Exception("Can't find analysis server root directory");
}
serverPath = normalize(join(rootDir, 'bin', 'server.dart'));
}
List<String> arguments = [];
//
// Add VM arguments.
//
if (profileServer) {
if (servicesPort == null) {
arguments.add('--observe');
} else {
arguments.add('--observe=$servicesPort');
}
arguments.add('--pause-isolates-on-exit');
} else if (servicesPort != null) {
arguments.add('--enable-vm-service=$servicesPort');
}
if (Platform.packageConfig != null) {
arguments.add('--packages=${Platform.packageConfig}');
}
//
// Add the server executable.
//
arguments.add(serverPath);
//
// Add server arguments.
//
arguments.add('--suppress-analytics');
if (diagnosticPort != null) {
arguments.add('--port');
arguments.add(diagnosticPort.toString());
}
if (instrumentationLogFile != null) {
arguments.add('--instrumentation-log-file=$instrumentationLogFile');
}
if (sdkPath != null) {
arguments.add('--sdk=$sdkPath');
}
if (useAnalysisHighlight2) {
arguments.add('--useAnalysisHighlight2');
}
_process = await Process.start(dartBinary, arguments);
_process.exitCode.then((int code) {
if (code != 0) {
_badDataFromServer('server terminated with exit code $code');
}
});
}
/**
* Deal with bad data received from the server.
*/
void _badDataFromServer(String details, {bool silent: false}) {
if (!silent) {
_recordStdio('BAD DATA FROM SERVER: $details');
}
if (_receivedBadDataFromServer) {
// We're already dealing with it.
return;
}
_receivedBadDataFromServer = true;
debugStdio();
// Give the server 1 second to continue outputting bad data
// such as outputting a stacktrace.
new Future.delayed(new Duration(seconds: 1), () {
throw 'Bad data received from server: $details';
});
}
/**
* Record a message that was exchanged with the server, and print it out if
* [debugStdio] has been called.
*/
void _recordStdio(String line) {
double elapsedTime = currentElapseTime;
line = "$elapsedTime: $line";
if (_debuggingStdio) {
logger.trace(line);
}
_recordedStdio.add(line);
}
}
/**
* An error result from a server request.
*/
class ServerErrorMessage {
final Map message;
ServerErrorMessage(this.message);
dynamic get error => message['error'];
String toString() => message.toString();
}
+17
View File
@@ -0,0 +1,17 @@
// 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 'package:analysis_server_client/server.dart';
import 'package:cli_util/cli_logging.dart';
class VerboseServer extends Server {
final Logger logger;
VerboseServer(this.logger);
@override
void logMessage(String prefix, String details) {
logger.trace('$currentElapseTime: $prefix $details');
}
}