mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
Server clean-up
R=scheglov@google.com Review URL: https://codereview.chromium.org//795833005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@42780 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
cc33fcab6b
commit
4a8c341ecf
|
@ -1,183 +0,0 @@
|
|||
// Copyright (c) 2014, 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/src/analysis_manager.dart';
|
||||
import 'package:args/args.dart';
|
||||
|
||||
/**
|
||||
* Start analysis server as a separate process and use the websocket protocol
|
||||
* to analyze the application specified on the command line.
|
||||
*/
|
||||
void main(List<String> args) {
|
||||
new _DartDependencyAnalyzer(args).run().catchError((error, stack) {
|
||||
print('Analysis failed: $error');
|
||||
if (stack != null) {
|
||||
print(stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of [_DartDependencyAnalyzer] launch an analysis server and use
|
||||
* that server to analyze the dependencies of an application.
|
||||
*/
|
||||
class _DartDependencyAnalyzer {
|
||||
/**
|
||||
* The name of the application that is used to start the dependency analyzer.
|
||||
*/
|
||||
static const BINARY_NAME = 'dartdeps';
|
||||
|
||||
/**
|
||||
* The name of the option used to specify the Dart SDK.
|
||||
*/
|
||||
static const String DART_SDK_OPTION = 'dart-sdk';
|
||||
|
||||
/**
|
||||
* The name of the option used to print usage information.
|
||||
*/
|
||||
static const String HELP_OPTION = 'help';
|
||||
|
||||
/**
|
||||
* The name of the option used to specify an already running server.
|
||||
*/
|
||||
static const String SERVER_OPTION = 'server';
|
||||
|
||||
/**
|
||||
* The command line arguments.
|
||||
*/
|
||||
final List<String> args;
|
||||
|
||||
/**
|
||||
* The path to the Dart SDK used during analysis.
|
||||
*/
|
||||
String sdkPath;
|
||||
|
||||
/**
|
||||
* The manager for the analysis server.
|
||||
*/
|
||||
AnalysisManager manager;
|
||||
|
||||
_DartDependencyAnalyzer(this.args);
|
||||
|
||||
/**
|
||||
* Use the given manager to perform the analysis.
|
||||
*/
|
||||
void analyze(AnalysisManager manager) {
|
||||
if (manager == null) {
|
||||
return;
|
||||
}
|
||||
this.manager = manager;
|
||||
print('Analyzing...');
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information about how to use the server.
|
||||
*/
|
||||
void printUsage(ArgParser parser) {
|
||||
print('Usage: $BINARY_NAME [flags] <application_directory>');
|
||||
print('');
|
||||
print('Supported flags are:');
|
||||
print(parser.usage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the command line arguments to determine the application to be
|
||||
* analyzed, then launch and manage an analysis server to do the work.
|
||||
*/
|
||||
Future run() {
|
||||
return new Future(start).then(analyze).whenComplete(stop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the command line arguments to determine the application to be
|
||||
* analyzed, then launch an analysis server.
|
||||
* Return `null` if the command line arguments are invalid.
|
||||
*/
|
||||
Future<AnalysisManager> start() {
|
||||
ArgParser parser = new ArgParser();
|
||||
parser.addOption(DART_SDK_OPTION, help: '[sdkPath] path to Dart SDK');
|
||||
parser.addFlag(
|
||||
HELP_OPTION,
|
||||
help: 'print this help message without starting analysis',
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addOption(
|
||||
SERVER_OPTION,
|
||||
help: '[serverUrl] use an analysis server thats already running');
|
||||
|
||||
// Parse arguments
|
||||
ArgResults results;
|
||||
try {
|
||||
results = parser.parse(args);
|
||||
} on FormatException catch (e) {
|
||||
print(e.message);
|
||||
print('');
|
||||
printUsage(parser);
|
||||
exitCode = 1;
|
||||
return null;
|
||||
}
|
||||
if (results[HELP_OPTION]) {
|
||||
printUsage(parser);
|
||||
return null;
|
||||
}
|
||||
sdkPath = results[DART_SDK_OPTION];
|
||||
if (sdkPath is! String) {
|
||||
print('Missing path to Dart SDK');
|
||||
printUsage(parser);
|
||||
return null;
|
||||
}
|
||||
Directory sdkDir = new Directory(sdkPath);
|
||||
if (!sdkDir.existsSync()) {
|
||||
print('Specified Dart SDK does not exist: $sdkPath');
|
||||
printUsage(parser);
|
||||
return null;
|
||||
}
|
||||
if (results.rest.length == 0) {
|
||||
printUsage(parser);
|
||||
exitCode = 1;
|
||||
return null;
|
||||
}
|
||||
Directory appDir = new Directory(results.rest[0]);
|
||||
if (!appDir.existsSync()) {
|
||||
print('Specified application directory does not exist: $appDir');
|
||||
print('');
|
||||
printUsage(parser);
|
||||
exitCode = 1;
|
||||
return null;
|
||||
}
|
||||
if (results.rest.length > 1) {
|
||||
print('Unexpected arguments after $appDir');
|
||||
print('');
|
||||
printUsage(parser);
|
||||
exitCode = 1;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Connect to an already running analysis server
|
||||
String serverUrl = results[SERVER_OPTION];
|
||||
if (serverUrl != null) {
|
||||
return AnalysisManager.connect(serverUrl);
|
||||
}
|
||||
|
||||
// Launch and connect to a new analysis server
|
||||
// Assume that the analysis server entry point is in the same directory
|
||||
StringBuffer path = new StringBuffer();
|
||||
path.write(FileSystemEntity.parentOf(Platform.script.toFilePath()));
|
||||
path.write(Platform.pathSeparator);
|
||||
path.write('server.dart');
|
||||
return AnalysisManager.start(path.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the analysis server.
|
||||
*/
|
||||
void stop() {
|
||||
if (manager != null) {
|
||||
manager.stop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
// Copyright (c) 2014, 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:args/args.dart';
|
||||
import 'package:matcher/matcher.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'fuzz/server_manager.dart';
|
||||
|
||||
/**
|
||||
* Start analysis server as a separate process and use the stdio to communicate
|
||||
* with the server.
|
||||
*/
|
||||
void main(List<String> args) {
|
||||
new _FuzzTest().run(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of [_FuzzTest] launch and test an analysis server.
|
||||
* You must specify the location of the Dart SDK and the directory
|
||||
* containing sources to be analyzed.
|
||||
*/
|
||||
class _FuzzTest {
|
||||
/**
|
||||
* The name of the application that is used to start the fuzz tester.
|
||||
*/
|
||||
static const BINARY_NAME = 'fuzz';
|
||||
|
||||
//TODO (danrubel) extract common behavior for use in multiple test scenarios
|
||||
//TODO (danrubel) cleanup test to use async/await for better readability
|
||||
// VM flag --enable_async
|
||||
|
||||
static const String DART_SDK_OPTION = 'dart-sdk';
|
||||
static const String HELP_OPTION = 'help';
|
||||
|
||||
File serverSnapshot;
|
||||
Directory appDir;
|
||||
|
||||
/// Parse the arguments and initialize the receiver
|
||||
/// Return `true` if proper arguments were provided
|
||||
bool parseArgs(List<String> args) {
|
||||
ArgParser parser = new ArgParser();
|
||||
|
||||
void error(String errMsg) {
|
||||
stderr.writeln(errMsg);
|
||||
print('');
|
||||
_printUsage(parser);
|
||||
exitCode = 11;
|
||||
}
|
||||
|
||||
parser.addOption(DART_SDK_OPTION, help: '[sdkPath] path to Dart SDK');
|
||||
parser.addFlag(
|
||||
HELP_OPTION,
|
||||
help: 'print this help message without starting analysis',
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
|
||||
ArgResults results;
|
||||
try {
|
||||
results = parser.parse(args);
|
||||
} on FormatException catch (e) {
|
||||
error(e.message);
|
||||
return false;
|
||||
}
|
||||
if (results[HELP_OPTION]) {
|
||||
_printUsage(parser);
|
||||
return false;
|
||||
}
|
||||
String sdkPath = results[DART_SDK_OPTION];
|
||||
if (sdkPath is! String) {
|
||||
error('Missing path to Dart SDK');
|
||||
return false;
|
||||
}
|
||||
Directory sdkDir = new Directory(sdkPath);
|
||||
if (!sdkDir.existsSync()) {
|
||||
error('Specified Dart SDK does not exist: $sdkPath');
|
||||
return false;
|
||||
}
|
||||
if (results.rest.length == 0) {
|
||||
error('Expected directory to analyze');
|
||||
return false;
|
||||
}
|
||||
appDir = new Directory(results.rest[0]);
|
||||
if (!appDir.existsSync()) {
|
||||
error('Specified application directory does not exist: $appDir');
|
||||
return false;
|
||||
}
|
||||
if (results.rest.length > 1) {
|
||||
error('Unexpected arguments after $appDir');
|
||||
return false;
|
||||
}
|
||||
serverSnapshot = new File(
|
||||
path.join(sdkDir.path, 'bin', 'snapshots', 'analysis_server.dart.snapshot'));
|
||||
if (!serverSnapshot.existsSync()) {
|
||||
error('Analysis Server snapshot not found: $serverSnapshot');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Main entry point for launching, testing, and shutting down the server
|
||||
void run(List<String> args) {
|
||||
if (!parseArgs(args)) return;
|
||||
ServerManager.start(serverSnapshot.path).then((ServerManager manager) {
|
||||
runZoned(() {
|
||||
test(manager).then(manager.stop).then((_) {
|
||||
expect(manager.errorOccurred, isFalse);
|
||||
print('Test completed successfully');
|
||||
});
|
||||
}, onError: (error, stack) {
|
||||
stderr.writeln(error);
|
||||
print(stack);
|
||||
exitCode = 12;
|
||||
manager.stop();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Use manager to exercise the analysis server
|
||||
Future test(ServerManager manager) {
|
||||
|
||||
// perform initial analysis
|
||||
return manager.analyze(appDir).then((AnalysisResults analysisResults) {
|
||||
print(
|
||||
'Found ${analysisResults.errorCount} errors,'
|
||||
' ${analysisResults.warningCount} warnings,'
|
||||
' and ${analysisResults.hintCount} hints in ${analysisResults.elapsed}');
|
||||
|
||||
// edit a method body
|
||||
return manager.openFileNamed(
|
||||
'domain_completion.dart').then((Editor editor) {
|
||||
return editor.moveAfter('Response processRequest(Request request) {');
|
||||
}).then((Editor editor) {
|
||||
return editor.replace(0, '\nOb');
|
||||
}).then((Editor editor) {
|
||||
|
||||
// request code completion and assert results
|
||||
return editor.getSuggestions().then((List<CompletionResults> list) {
|
||||
expect(list, isNotNull);
|
||||
expect(list.length, equals(0));
|
||||
list.forEach((CompletionResults results) {
|
||||
print(
|
||||
'${results.elapsed} received ${results.suggestionCount} suggestions');
|
||||
});
|
||||
return editor;
|
||||
});
|
||||
|
||||
}).then((Editor editor) {
|
||||
print('tests complete');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// void _printAnalysisSummary(AnalysisResults results) {
|
||||
// print(
|
||||
// 'Found ${results.errorCount} errors, ${results.warningCount} warnings,'
|
||||
// ' and ${results.hintCount} hints in $results.elapsed');
|
||||
// }
|
||||
|
||||
/// Print information about how to use the server.
|
||||
void _printUsage(ArgParser parser) {
|
||||
print('Usage: $BINARY_NAME [flags] <application_directory>');
|
||||
print('');
|
||||
print('Supported flags are:');
|
||||
print(parser.usage);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
This directory contains code for fuzz testing the analysis server
|
||||
and for generating timing information for tracking server performance.
|
||||
|
||||
Run:
|
||||
Launch fuzz.dart located in the parent directory.
|
||||
|
||||
Overview:
|
||||
fuzz.dart - main entry point and simple example of exercizing server
|
||||
server_manager.dart - provides high level API for launching and driving server
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
part of server.manager;
|
||||
|
||||
/**
|
||||
* A client channel that logs communication to stdout
|
||||
* and handles errors received from the server.
|
||||
*/
|
||||
class LoggingClientChannel implements ClientCommunicationChannel {
|
||||
final ClientCommunicationChannel channel;
|
||||
int serverErrorCount = 0;
|
||||
|
||||
LoggingClientChannel(this.channel) {
|
||||
channel.notificationStream.listen((Notification notification) {
|
||||
_logNotification(notification);
|
||||
if (notification.event == 'server.error') {
|
||||
ServerErrorParams error =
|
||||
new ServerErrorParams.fromNotification(notification);
|
||||
_handleError(
|
||||
'Server reported error: ${error.message}',
|
||||
error.stackTrace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Notification> get notificationStream => channel.notificationStream;
|
||||
|
||||
@override
|
||||
void set notificationStream(Stream<Notification> _notificationStream) {
|
||||
throw 'invalid operation';
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Response> get responseStream => channel.responseStream;
|
||||
|
||||
@override
|
||||
void set responseStream(Stream<Response> _responseStream) {
|
||||
throw 'invalid operation';
|
||||
}
|
||||
|
||||
@override
|
||||
Future close() {
|
||||
print('Requesting client channel be closed');
|
||||
return channel.close().then((_) {
|
||||
print('Client channel closed');
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response> sendRequest(Request request) {
|
||||
_logOperation('=>', request);
|
||||
return channel.sendRequest(request).then((Response response) {
|
||||
RequestError error = response.error;
|
||||
if (error != null) {
|
||||
error.code;
|
||||
stderr.write('Server Error ${error.code}: ${error.message}');
|
||||
print(error.stackTrace);
|
||||
exitCode = 31;
|
||||
}
|
||||
_logOperation('<=', request);
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleError(String errMsg, String stackTrace) {
|
||||
//error.isFatal;
|
||||
stderr.writeln('>>> Server reported exception');
|
||||
stderr.writeln(errMsg);
|
||||
print(stackTrace);
|
||||
serverErrorCount++;
|
||||
}
|
||||
|
||||
void _logNotification(Notification notification) {
|
||||
print('<= ${notification.event}');
|
||||
}
|
||||
|
||||
void _logOperation(String direction, Request request) {
|
||||
String id = request.id.padLeft(5);
|
||||
String method = request.method.padRight(20);
|
||||
print('$direction $id $method');
|
||||
}
|
||||
}
|
|
@ -1,365 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library server.manager;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:matcher/matcher.dart';
|
||||
import 'package:analysis_server/src/protocol.dart';
|
||||
import 'package:analysis_server/src/channel/channel.dart';
|
||||
import 'package:analysis_server/src/channel/byte_stream_channel.dart';
|
||||
|
||||
part 'logging_client_channel.dart';
|
||||
|
||||
/**
|
||||
* The results returned by [ServerManager].analyze(...) once analysis
|
||||
* has finished.
|
||||
*/
|
||||
class AnalysisResults {
|
||||
Duration elapsed;
|
||||
int errorCount = 0;
|
||||
int hintCount = 0;
|
||||
int warningCount = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [CompletionResults] contains the completion results returned by the server
|
||||
* along with the elapse time to receive those completions.
|
||||
*/
|
||||
class CompletionResults {
|
||||
final Duration elapsed;
|
||||
final CompletionResultsParams params;
|
||||
|
||||
CompletionResults(this.elapsed, this.params);
|
||||
|
||||
int get suggestionCount => params.results.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Editor] is a virtual editor for inspecting and modifying a file's content
|
||||
* and updating the server with those modifications.
|
||||
*/
|
||||
class Editor {
|
||||
final ServerManager manager;
|
||||
final File file;
|
||||
int offset = 0;
|
||||
String _content = null;
|
||||
|
||||
Editor(this.manager, this.file);
|
||||
|
||||
/// Return a future that returns the file content
|
||||
Future<String> get content {
|
||||
if (_content != null) {
|
||||
return new Future.value(_content);
|
||||
}
|
||||
return file.readAsString().then((String content) {
|
||||
_content = content;
|
||||
return _content;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request completion suggestions from the server.
|
||||
* Return a future that completes with the completions sent.
|
||||
*/
|
||||
Future<List<CompletionResults>> getSuggestions() {
|
||||
Request request = new CompletionGetSuggestionsParams(
|
||||
file.path,
|
||||
offset).toRequest(manager._nextRequestId);
|
||||
Stopwatch stopwatch = new Stopwatch()..start();
|
||||
return manager.channel.sendRequest(request).then((Response response) {
|
||||
String completionId =
|
||||
new CompletionGetSuggestionsResult.fromResponse(response).id;
|
||||
var completer = new Completer<List<CompletionResults>>();
|
||||
List<CompletionResults> results = [];
|
||||
|
||||
// Listen for completion suggestions
|
||||
StreamSubscription<Notification> subscription;
|
||||
subscription =
|
||||
manager.channel.notificationStream.listen((Notification notification) {
|
||||
if (notification.event == 'completion.results') {
|
||||
CompletionResultsParams params =
|
||||
new CompletionResultsParams.fromNotification(notification);
|
||||
if (params.id == completionId) {
|
||||
results.add(new CompletionResults(stopwatch.elapsed, params));
|
||||
if (params.isLast) {
|
||||
stopwatch.stop();
|
||||
subscription.cancel();
|
||||
completer.complete(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the virtual cursor after the given pattern in the source.
|
||||
* Return a future that completes once the cursor has been moved.
|
||||
*/
|
||||
Future<Editor> moveAfter(String pattern) {
|
||||
return content.then((String content) {
|
||||
offset = content.indexOf(pattern);
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the specified number of characters at the current cursor location
|
||||
* with the given text, but do not save that content to disk.
|
||||
* Return a future that completes once the server has been notified.
|
||||
*/
|
||||
Future<Editor> replace(int replacementLength, String text) {
|
||||
return content.then((String oldContent) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write(oldContent.substring(0, offset));
|
||||
sb.write(text);
|
||||
sb.write(oldContent.substring(offset));
|
||||
_content = sb.toString();
|
||||
SourceEdit sourceEdit = new SourceEdit(offset, replacementLength, text);
|
||||
Request request = new AnalysisUpdateContentParams({
|
||||
file.path: new ChangeContentOverlay([sourceEdit])
|
||||
}).toRequest(manager._nextRequestId);
|
||||
offset += text.length;
|
||||
return manager.channel.sendRequest(request).then((Response response) {
|
||||
return this;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [ServerManager] is used to launch and manage an analysis server
|
||||
* running in a separate process.
|
||||
*/
|
||||
class ServerManager {
|
||||
|
||||
/**
|
||||
* The analysis server process being managed or `null` if not started.
|
||||
*/
|
||||
Process process;
|
||||
|
||||
/**
|
||||
* The root directory containing the Dart source files to be analyzed.
|
||||
*/
|
||||
Directory appDir;
|
||||
|
||||
/**
|
||||
* The channel used to communicate with the analysis server.
|
||||
*/
|
||||
LoggingClientChannel _channel;
|
||||
|
||||
/**
|
||||
* The identifier used in the most recent request to the server.
|
||||
* See [_nextRequestId].
|
||||
*/
|
||||
int _lastRequestId = 0;
|
||||
|
||||
/**
|
||||
* `true` if a server exception was detected on stderr as opposed to an
|
||||
* exception that the server reported via the server.error notification.
|
||||
*/
|
||||
bool _unreportedServerException = false;
|
||||
|
||||
/**
|
||||
* `true` if the [stop] method has been called.
|
||||
*/
|
||||
bool _stopRequested = false;
|
||||
|
||||
/**
|
||||
* Return the channel used to communicate with the analysis server.
|
||||
*/
|
||||
ClientCommunicationChannel get channel => _channel;
|
||||
|
||||
/**
|
||||
* Return `true` if a server error occurred.
|
||||
*/
|
||||
bool get errorOccurred =>
|
||||
_unreportedServerException || (_channel.serverErrorCount > 0);
|
||||
|
||||
String get _nextRequestId => (++_lastRequestId).toString();
|
||||
|
||||
/**
|
||||
* Direct the server to analyze all sources in the given directory,
|
||||
* all sub directories recursively, and any source referenced sources
|
||||
* outside this directory hierarch such as referenced packages.
|
||||
* Return a future that completes when the analysis is finished.
|
||||
*/
|
||||
Future<AnalysisResults> analyze(Directory appDir) {
|
||||
this.appDir = appDir;
|
||||
Stopwatch stopwatch = new Stopwatch()..start();
|
||||
Request request = new AnalysisSetAnalysisRootsParams(
|
||||
[appDir.path],
|
||||
[]).toRequest(_nextRequestId);
|
||||
|
||||
// Request analysis
|
||||
return channel.sendRequest(request).then((Response response) {
|
||||
AnalysisResults results = new AnalysisResults();
|
||||
StreamSubscription<Notification> subscription;
|
||||
Completer<AnalysisResults> completer = new Completer<AnalysisResults>();
|
||||
subscription =
|
||||
channel.notificationStream.listen((Notification notification) {
|
||||
|
||||
// Gather analysis results
|
||||
if (notification.event == 'analysis.errors') {
|
||||
AnalysisErrorsParams params =
|
||||
new AnalysisErrorsParams.fromNotification(notification);
|
||||
params.errors.forEach((AnalysisError error) {
|
||||
AnalysisErrorSeverity severity = error.severity;
|
||||
if (severity == AnalysisErrorSeverity.ERROR) {
|
||||
results.errorCount += 1;
|
||||
} else if (severity == AnalysisErrorSeverity.WARNING) {
|
||||
results.warningCount += 1;
|
||||
} else if (severity == AnalysisErrorSeverity.INFO) {
|
||||
results.hintCount += 1;
|
||||
} else {
|
||||
print('Unknown error severity: ${severity.name}');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Stop gathering once analysis is complete
|
||||
if (notification.event == 'server.status') {
|
||||
ServerStatusParams status =
|
||||
new ServerStatusParams.fromNotification(notification);
|
||||
AnalysisStatus analysis = status.analysis;
|
||||
if (analysis != null && !analysis.isAnalyzing) {
|
||||
stopwatch.stop();
|
||||
results.elapsed = stopwatch.elapsed;
|
||||
subscription.cancel();
|
||||
completer.complete(results);
|
||||
}
|
||||
}
|
||||
});
|
||||
return completer.future;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request to the server for its version information
|
||||
* and return a future that completes with the result.
|
||||
*/
|
||||
Future<ServerGetVersionResult> getVersion() {
|
||||
Request request = new ServerGetVersionParams().toRequest(_nextRequestId);
|
||||
return channel.sendRequest(request).then((Response response) {
|
||||
return new ServerGetVersionResult.fromResponse(response);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the server that the given file will be edited.
|
||||
* Return a virtual editor for inspecting and modifying the file's content.
|
||||
*/
|
||||
Future<Editor> openFileNamed(String fileName) {
|
||||
return _findFile(fileName, appDir).then((File file) {
|
||||
if (file == null) {
|
||||
throw 'Failed to find file named $fileName in ${appDir.path}';
|
||||
}
|
||||
file = file.absolute;
|
||||
Request request =
|
||||
new AnalysisSetPriorityFilesParams([file.path]).toRequest(_nextRequestId);
|
||||
return channel.sendRequest(request).then((Response response) {
|
||||
return new Editor(this, file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request for notifications.
|
||||
* Return when the server has acknowledged that request.
|
||||
*/
|
||||
Future setSubscriptions() {
|
||||
Request request = new ServerSetSubscriptionsParams(
|
||||
[ServerService.STATUS]).toRequest(_nextRequestId);
|
||||
return channel.sendRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the analysis server.
|
||||
* Return a future that completes when the server is terminated.
|
||||
*/
|
||||
Future stop([_]) {
|
||||
_stopRequested = true;
|
||||
print("Requesting server shutdown");
|
||||
Request request = new ServerShutdownParams().toRequest(_nextRequestId);
|
||||
Duration waitTime = new Duration(seconds: 5);
|
||||
return channel.sendRequest(request).timeout(waitTime, onTimeout: () {
|
||||
print('Expected shutdown response');
|
||||
}).then((Response response) {
|
||||
return channel.close().then((_) => process.exitCode);
|
||||
}).timeout(new Duration(seconds: 2), onTimeout: () {
|
||||
print('Expected server to shutdown');
|
||||
process.kill();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the given file in the directory tree.
|
||||
*/
|
||||
Future<File> _findFile(String fileName, Directory appDir) {
|
||||
return appDir.list(recursive: true).firstWhere((FileSystemEntity entity) {
|
||||
return entity is File && entity.path.endsWith(fileName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch an analysis server and open a connection to that server.
|
||||
*/
|
||||
Future<ServerManager> _launchServer(String pathToServer) {
|
||||
List<String> serverArgs = [pathToServer];
|
||||
return Process.start(Platform.executable, serverArgs).catchError((error) {
|
||||
exitCode = 21;
|
||||
throw 'Failed to launch analysis server: $error';
|
||||
}).then((Process process) {
|
||||
this.process = process;
|
||||
_channel = new LoggingClientChannel(
|
||||
new ByteStreamClientChannel(process.stdout, process.stdin));
|
||||
|
||||
// simple out of band exception handling
|
||||
process.stderr.transform(
|
||||
new Utf8Codec().decoder).transform(new LineSplitter()).listen((String line) {
|
||||
if (!_unreportedServerException) {
|
||||
_unreportedServerException = true;
|
||||
stderr.writeln('>>> Unreported server exception');
|
||||
}
|
||||
stderr.writeln('server.stderr: $line');
|
||||
});
|
||||
|
||||
// watch for unexpected process termination and catch the exit code
|
||||
process.exitCode.then((int code) {
|
||||
if (!_stopRequested) {
|
||||
fail('Unexpected server termination: $code');
|
||||
}
|
||||
if (code != null && code != 0) {
|
||||
exitCode = code;
|
||||
}
|
||||
print('Server stopped: $code');
|
||||
});
|
||||
|
||||
return channel.notificationStream.first.then((Notification notification) {
|
||||
print('Server connection established');
|
||||
return setSubscriptions().then((_) {
|
||||
return getVersion().then((ServerGetVersionResult result) {
|
||||
print('Server version ${result.version}');
|
||||
return this;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch analysis server in a separate process
|
||||
* and return a future with a manager for that analysis server.
|
||||
*/
|
||||
static Future<ServerManager> start(String serverPath) {
|
||||
return new ServerManager()._launchServer(serverPath);
|
||||
}
|
||||
}
|
|
@ -2,12 +2,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 'package:analysis_server/driver.dart';
|
||||
import 'package:analysis_server/starter.dart';
|
||||
|
||||
/**
|
||||
* Create and run an HTTP-based analysis server.
|
||||
* Create and run an analysis server.
|
||||
*/
|
||||
void main(List<String> args) {
|
||||
Driver driver = new Driver();
|
||||
driver.start(args);
|
||||
ServerStarter starter = new ServerStarter();
|
||||
starter.start(args);
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ library driver;
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analysis_server/http_server.dart';
|
||||
import 'package:analysis_server/plugin/plugin.dart';
|
||||
import 'package:analysis_server/src/analysis_server.dart';
|
||||
import 'package:analysis_server/src/plugin/plugin_impl.dart';
|
||||
import 'package:analysis_server/src/plugin/server_plugin.dart';
|
||||
import 'package:analysis_server/src/server/http_server.dart';
|
||||
import 'package:analysis_server/src/server/stdio_server.dart';
|
||||
import 'package:analysis_server/src/socket_server.dart';
|
||||
import 'package:analysis_server/stdio_server.dart';
|
||||
import 'package:analysis_server/starter.dart';
|
||||
import 'package:analyzer/instrumentation/instrumentation.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/incremental_logger.dart';
|
||||
|
@ -54,7 +55,7 @@ void _initIncrementalLogger(String spec) {
|
|||
* server application. It is responsible for parsing command line options
|
||||
* and starting the HTTP and/or stdio servers.
|
||||
*/
|
||||
class Driver {
|
||||
class Driver implements ServerStarter {
|
||||
/**
|
||||
* The name of the application that is used to start a server.
|
||||
*/
|
||||
|
@ -144,53 +145,11 @@ class Driver {
|
|||
}
|
||||
|
||||
/**
|
||||
* Use the given command-line arguments to start this server.
|
||||
* Use the given command-line [arguments] to start this server.
|
||||
*/
|
||||
void start(List<String> args) {
|
||||
ArgParser parser = new ArgParser();
|
||||
parser.addOption(
|
||||
CLIENT_ID,
|
||||
help: "an identifier used to identify the client");
|
||||
parser.addFlag(
|
||||
ENABLE_INCREMENTAL_RESOLUTION_API,
|
||||
help: "enable using incremental resolution for API changes",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addFlag(
|
||||
ENABLE_INSTRUMENTATION_OPTION,
|
||||
help: "enable sending instrumentation information to a server",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addFlag(
|
||||
HELP_OPTION,
|
||||
help: "print this help message without starting a server",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addOption(
|
||||
INCREMENTAL_RESOLUTION_LOG,
|
||||
help: "the description of the incremental resolution log");
|
||||
parser.addFlag(
|
||||
INCREMENTAL_RESOLUTION_VALIDATION,
|
||||
help: "enable validation of incremental resolution results (slow)",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addFlag(
|
||||
INTERNAL_PRINT_TO_CONSOLE,
|
||||
help: "enable sending `print` output to the console",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addOption(
|
||||
PORT_OPTION,
|
||||
help: "[port] the port on which the server will listen");
|
||||
parser.addOption(SDK_OPTION, help: "[path] the path to the sdk");
|
||||
parser.addFlag(
|
||||
NO_ERROR_NOTIFICATION,
|
||||
help:
|
||||
"disable sending all analysis error notifications to the server (not yet implemented)",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
|
||||
ArgResults results = parser.parse(args);
|
||||
void start(List<String> arguments) {
|
||||
ArgParser parser = _createArgParser();
|
||||
ArgResults results = parser.parse(arguments);
|
||||
if (results[HELP_OPTION]) {
|
||||
_printUsage(parser);
|
||||
return;
|
||||
|
@ -309,6 +268,56 @@ class Driver {
|
|||
return runZoned(callback, zoneSpecification: zoneSpecification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the parser used to parse the command-line arguments.
|
||||
*/
|
||||
ArgParser _createArgParser() {
|
||||
ArgParser parser = new ArgParser();
|
||||
parser.addOption(
|
||||
CLIENT_ID,
|
||||
help: "an identifier used to identify the client");
|
||||
parser.addFlag(
|
||||
ENABLE_INCREMENTAL_RESOLUTION_API,
|
||||
help: "enable using incremental resolution for API changes",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addFlag(
|
||||
ENABLE_INSTRUMENTATION_OPTION,
|
||||
help: "enable sending instrumentation information to a server",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addFlag(
|
||||
HELP_OPTION,
|
||||
help: "print this help message without starting a server",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addOption(
|
||||
INCREMENTAL_RESOLUTION_LOG,
|
||||
help: "the description of the incremental resolution log");
|
||||
parser.addFlag(
|
||||
INCREMENTAL_RESOLUTION_VALIDATION,
|
||||
help: "enable validation of incremental resolution results (slow)",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addFlag(
|
||||
INTERNAL_PRINT_TO_CONSOLE,
|
||||
help: "enable sending `print` output to the console",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
parser.addOption(
|
||||
PORT_OPTION,
|
||||
help: "[port] the port on which the server will listen");
|
||||
parser.addOption(SDK_OPTION, help: "[path] the path to the sdk");
|
||||
parser.addFlag(
|
||||
NO_ERROR_NOTIFICATION,
|
||||
help:
|
||||
"disable sending all analysis error notifications to the server (not yet implemented)",
|
||||
defaultsTo: false,
|
||||
negatable: false);
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information about how to use the server.
|
||||
*/
|
34
pkg/analysis_server/lib/starter.dart
Normal file
34
pkg/analysis_server/lib/starter.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
// 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.
|
||||
|
||||
library driver;
|
||||
|
||||
import 'package:analysis_server/plugin/plugin.dart';
|
||||
import 'package:analysis_server/src/server/driver.dart';
|
||||
import 'package:analyzer/instrumentation/instrumentation.dart';
|
||||
|
||||
/**
|
||||
* An object that can be used to start an analysis server.
|
||||
*/
|
||||
abstract class ServerStarter {
|
||||
/**
|
||||
* Initialize a newly created starter to start up an analysis server.
|
||||
*/
|
||||
factory ServerStarter() = Driver;
|
||||
|
||||
/**
|
||||
* Set the instrumentation [server] that is to be used by the analysis server.
|
||||
*/
|
||||
void set instrumentationServer(InstrumentationServer server);
|
||||
|
||||
/**
|
||||
* Set the [plugins] that are defined outside the analysis_server package.
|
||||
*/
|
||||
void set userDefinedPlugins(List<Plugin> plugins);
|
||||
|
||||
/**
|
||||
* Use the given command-line [arguments] to start this server.
|
||||
*/
|
||||
void start(List<String> arguments);
|
||||
}
|
Loading…
Reference in a new issue