3b0b1bd863
Changes: ``` > git log --format="%C(auto) %h %s" 8d5f750..b044aca https://dart.googlesource.com/lints.git/+/b044aca add several rules to core and recommended (150) https://dart.googlesource.com/lints.git/+/81100a2 fix a dangling table link (146) ``` Diff: https://dart.googlesource.com/lints.git/+/8d5f7500024320654adb1e799e49fc10c5304ae7..b044acab9f6669b3d8e781923a8ff86877801177/ Change-Id: I031333ade99af700a7009b14a36d3aadba12fc94 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/327321 Reviewed-by: Nicholas Shahan <nshahan@google.com> Commit-Queue: Devon Carew <devoncarew@google.com> Reviewed-by: Phil Quitslund <pquitslund@google.com>
198 lines
6.6 KiB
Dart
198 lines
6.6 KiB
Dart
// 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:analysis_server_client/protocol.dart';
|
|
import 'package:analysis_server_client/src/server_base.dart';
|
|
import 'package:path/path.dart';
|
|
|
|
export 'package:analysis_server_client/src/server_base.dart'
|
|
show NotificationProcessor;
|
|
|
|
/// Instances of the class [Server] manage a server process,
|
|
/// and facilitate communication to and from the server.
|
|
///
|
|
/// Clients may not extend, implement or mix-in this class.
|
|
class Server extends ServerBase {
|
|
/// Server process object, or `null` if server hasn't been started yet
|
|
/// or if the server has already been stopped.
|
|
Process? _process;
|
|
|
|
/// The stderr subscription or `null` if either
|
|
/// [listenToOutput] has not been called or [stop] has been called.
|
|
StreamSubscription<String>? _stderrSubscription;
|
|
|
|
/// The stdout subscription or `null` if either
|
|
/// [listenToOutput] has not been called or [stop] has been called.
|
|
StreamSubscription<String>? _stdoutSubscription;
|
|
|
|
Server({super.listener, Process? process, super.stdioPassthrough})
|
|
: _process = process;
|
|
|
|
/// Force kill the server. Returns exit code future.
|
|
@override
|
|
Future<int> kill({String reason = 'none'}) {
|
|
listener?.killingServerProcess(reason);
|
|
final process = _process!;
|
|
_process = null;
|
|
process.kill();
|
|
return process.exitCode;
|
|
}
|
|
|
|
/// Start listening to output from the server,
|
|
/// and deliver notifications to [notificationProcessor].
|
|
@override
|
|
void listenToOutput({NotificationProcessor? notificationProcessor}) {
|
|
_stdoutSubscription = _process!.stdout
|
|
.transform(utf8.decoder)
|
|
.transform(LineSplitter())
|
|
.listen((line) => outputProcessor(line, notificationProcessor));
|
|
_stderrSubscription = _process!.stderr
|
|
.transform(utf8.decoder)
|
|
.transform(LineSplitter())
|
|
.listen((line) => errorProcessor(line, notificationProcessor));
|
|
}
|
|
|
|
/// 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.
|
|
@override
|
|
Future<Map<String, Object?>?> send(
|
|
String method, Map<String, Object?>? params) =>
|
|
sendCommandWith(method, params, _process!.stdin.add);
|
|
|
|
/// 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.
|
|
///
|
|
/// If [enableAsserts] is specified, then asserts will be enabled in the new
|
|
/// dart process for that server. This is typically just useful to enable
|
|
/// locally for debugging.
|
|
@override
|
|
Future start({
|
|
String? clientId,
|
|
String? clientVersion,
|
|
int? diagnosticPort,
|
|
String? instrumentationLogFile,
|
|
bool profileServer = false,
|
|
String? sdkPath,
|
|
String? serverPath,
|
|
int? servicePort,
|
|
bool suppressAnalytics = true,
|
|
bool useAnalysisHighlight2 = false,
|
|
bool enableAsserts = false,
|
|
String? dartBinary,
|
|
}) async {
|
|
if (_process != null) {
|
|
throw Exception('Process already started');
|
|
}
|
|
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'));
|
|
}
|
|
}
|
|
|
|
var arguments = <String>[];
|
|
//
|
|
// Add VM arguments.
|
|
//
|
|
if (profileServer) {
|
|
if (servicePort == null) {
|
|
arguments.add('--observe');
|
|
} else {
|
|
arguments.add('--observe=$servicePort');
|
|
}
|
|
arguments.add('--pause-isolates-on-exit');
|
|
} else if (servicePort != null) {
|
|
arguments.add('--enable-vm-service=$servicePort');
|
|
}
|
|
if (Platform.packageConfig != null) {
|
|
arguments.add('--packages=${Platform.packageConfig}');
|
|
}
|
|
if (enableAsserts) {
|
|
arguments.add('--enable-asserts');
|
|
}
|
|
//
|
|
// Add the server executable.
|
|
//
|
|
arguments.add(serverPath);
|
|
|
|
arguments.addAll(getServerArguments(
|
|
clientId: clientId,
|
|
clientVersion: clientVersion,
|
|
suppressAnalytics: suppressAnalytics,
|
|
diagnosticPort: diagnosticPort,
|
|
instrumentationLogFile: instrumentationLogFile,
|
|
sdkPath: sdkPath,
|
|
useAnalysisHighlight2: useAnalysisHighlight2));
|
|
|
|
listener?.startingServer(dartBinary, arguments);
|
|
final process = await Process.start(dartBinary, arguments);
|
|
_process = process;
|
|
// ignore: unawaited_futures
|
|
process.exitCode.then((int code) {
|
|
if (code != 0 && _process != null) {
|
|
// Report an error if server abruptly terminated
|
|
listener?.unexpectedStop(code);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Attempt to gracefully shutdown the server.
|
|
/// If that fails, then kill the process.
|
|
@override
|
|
Future<int> stop({Duration? timeLimit}) async {
|
|
timeLimit ??= const Duration(seconds: 5);
|
|
|
|
final process = _process;
|
|
if (process == null) {
|
|
// Process already exited
|
|
return -1;
|
|
}
|
|
|
|
final future = send(SERVER_REQUEST_SHUTDOWN, null);
|
|
_process = null;
|
|
await future
|
|
// fall through to wait for exit
|
|
.timeout(timeLimit, onTimeout: () {
|
|
return {};
|
|
}).whenComplete(() async {
|
|
await _stderrSubscription?.cancel();
|
|
_stderrSubscription = null;
|
|
await _stdoutSubscription?.cancel();
|
|
_stdoutSubscription = null;
|
|
});
|
|
return process.exitCode.timeout(
|
|
timeLimit,
|
|
onTimeout: () {
|
|
listener?.killingServerProcess('server failed to exit');
|
|
process.kill();
|
|
return process.exitCode;
|
|
},
|
|
);
|
|
}
|
|
}
|