[ CLI ] Add dart development-service command
The `dart development-service` command will be used by tooling to launch DDS from the SDK instead of shipping DDS via package:dds. TEST=Existing service test suite Change-Id: Ib928aa5b8961caf87d7074884c3d226b5c096ccd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/361180 Reviewed-by: Derek Xu <derekx@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import 'src/commands/compilation_server.dart';
|
||||
import 'src/commands/compile.dart';
|
||||
import 'src/commands/create.dart';
|
||||
import 'src/commands/debug_adapter.dart';
|
||||
import 'src/commands/development_service.dart';
|
||||
import 'src/commands/devtools.dart';
|
||||
import 'src/commands/doc.dart';
|
||||
import 'src/commands/fix.dart';
|
||||
@@ -106,6 +107,7 @@ class DartdevRunner extends CommandRunner<int> {
|
||||
));
|
||||
addCommand(CreateCommand(verbose: verbose));
|
||||
addCommand(DebugAdapterCommand(verbose: verbose));
|
||||
addCommand(DevelopmentServiceCommand(verbose: verbose));
|
||||
addCommand(DevToolsCommand(verbose: verbose));
|
||||
addCommand(DocCommand(verbose: verbose));
|
||||
addCommand(FixCommand(verbose: verbose));
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2024, 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:args/args.dart';
|
||||
import 'package:dds/src/arg_parser.dart';
|
||||
|
||||
import '../core.dart';
|
||||
import '../sdk.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
class DevelopmentServiceCommand extends DartdevCommand {
|
||||
static const String commandName = 'development-service';
|
||||
|
||||
static const String commandDescription = "Start Dart's development service.";
|
||||
|
||||
DevelopmentServiceCommand({bool verbose = false})
|
||||
: super(
|
||||
commandName,
|
||||
commandDescription,
|
||||
verbose,
|
||||
hidden: !verbose,
|
||||
);
|
||||
|
||||
@override
|
||||
ArgParser createArgParser() {
|
||||
return DartDevelopmentServiceOptions.createArgParser(
|
||||
usageLineLength: dartdevUsageLineLength,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> run() async {
|
||||
// Need to make a copy as argResults!.arguments is an
|
||||
// UnmodifiableListView object which cannot be passed as
|
||||
// the args for spawnUri.
|
||||
final args = [...argResults!.arguments];
|
||||
return await runFromSnapshot(
|
||||
snapshot: sdk.ddsSnapshot,
|
||||
args: args,
|
||||
verbose: verbose,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:analysis_server/src/server/driver.dart' as server;
|
||||
import 'package:args/args.dart';
|
||||
@@ -43,8 +42,6 @@ For more information about the server's capabilities and configuration, see:
|
||||
const protocol = server.Driver.SERVER_PROTOCOL;
|
||||
const lsp = server.Driver.PROTOCOL_LSP;
|
||||
|
||||
if (!Sdk.checkArtifactExists(sdk.analysisServerSnapshot)) return 255;
|
||||
|
||||
var args = argResults!.arguments;
|
||||
if (!args.any((arg) => arg.startsWith('--$protocol'))) {
|
||||
args = [...args, '--$protocol=$lsp'];
|
||||
@@ -54,31 +51,10 @@ For more information about the server's capabilities and configuration, see:
|
||||
// the args for spawnUri.
|
||||
args = [...args];
|
||||
}
|
||||
|
||||
var retval = 0;
|
||||
final result = Completer<int>();
|
||||
final exitPort = ReceivePort()
|
||||
..listen((msg) {
|
||||
result.complete(0);
|
||||
});
|
||||
final errorPort = ReceivePort()
|
||||
..listen((error) {
|
||||
log.stderr(error.toString());
|
||||
result.complete(255);
|
||||
});
|
||||
try {
|
||||
await Isolate.spawnUri(Uri.file(sdk.analysisServerSnapshot), args, null,
|
||||
onExit: exitPort.sendPort, onError: errorPort.sendPort);
|
||||
retval = await result.future;
|
||||
} catch (e, st) {
|
||||
log.stderr(e.toString());
|
||||
if (verbose) {
|
||||
log.stderr(st.toString());
|
||||
}
|
||||
retval = 255;
|
||||
}
|
||||
errorPort.close();
|
||||
exitPort.close();
|
||||
return retval;
|
||||
return await runFromSnapshot(
|
||||
snapshot: sdk.analysisServerSnapshot,
|
||||
args: args,
|
||||
verbose: verbose,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:dtd_impl/dart_tooling_daemon.dart' as dtd
|
||||
@@ -39,33 +38,10 @@ class ToolingDaemonCommand extends DartdevCommand {
|
||||
// UnmodifiableListView object which cannot be passed as
|
||||
// the args for spawnUri.
|
||||
final args = [...argResults!.arguments];
|
||||
|
||||
if (!Sdk.checkArtifactExists(sdk.dtdSnapshot)) return 255;
|
||||
|
||||
var retval = 0;
|
||||
final result = Completer<int>();
|
||||
final exitPort = ReceivePort()
|
||||
..listen((msg) {
|
||||
result.complete(0);
|
||||
});
|
||||
final errorPort = ReceivePort()
|
||||
..listen((error) {
|
||||
log.stderr(error.toString());
|
||||
result.complete(255);
|
||||
});
|
||||
try {
|
||||
await Isolate.spawnUri(Uri.file(sdk.dtdSnapshot), args, null,
|
||||
onExit: exitPort.sendPort, onError: errorPort.sendPort);
|
||||
retval = await result.future;
|
||||
} catch (e, st) {
|
||||
log.stderr(e.toString());
|
||||
if (verbose) {
|
||||
log.stderr(st.toString());
|
||||
}
|
||||
retval = 255;
|
||||
}
|
||||
errorPort.close();
|
||||
exitPort.close();
|
||||
return retval;
|
||||
return await runFromSnapshot(
|
||||
snapshot: sdk.dtdSnapshot,
|
||||
args: args,
|
||||
verbose: verbose,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,88 +20,108 @@ class Sdk {
|
||||
/// The SDK's semantic versioning version (x.y.z-a.b.channel).
|
||||
final String version;
|
||||
|
||||
final bool _runFromBuildRoot;
|
||||
|
||||
factory Sdk() => _instance;
|
||||
|
||||
Sdk._(this.sdkPath, this.version);
|
||||
Sdk._(this.sdkPath, this.version, bool runFromBuildRoot)
|
||||
: _runFromBuildRoot = runFromBuildRoot;
|
||||
|
||||
// Assume that we want to use the same Dart executable that we used to spawn
|
||||
// DartDev. We should be able to run programs with out/ReleaseX64/dart even
|
||||
// if the SDK isn't completely built.
|
||||
String get dart => Platform.resolvedExecutable;
|
||||
|
||||
String get dartAotRuntime => path.join(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'dartaotruntime${Platform.isWindows ? '.exe' : ''}',
|
||||
);
|
||||
String get dartAotRuntime => _runFromBuildRoot
|
||||
? path.absolute(
|
||||
sdkPath,
|
||||
Platform.isWindows
|
||||
? 'dart_precompiled_runtime_product.exe'
|
||||
: 'dart_precompiled_runtime_product',
|
||||
)
|
||||
: path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
Platform.isWindows ? 'dartaotruntime.exe' : 'dartaotruntime',
|
||||
);
|
||||
|
||||
String get analysisServerSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get analysisServerSnapshot => _snapshotPathFor(
|
||||
'analysis_server.dart.snapshot',
|
||||
);
|
||||
|
||||
String get dart2jsSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get dart2jsSnapshot => _snapshotPathFor(
|
||||
'dart2js.dart.snapshot',
|
||||
);
|
||||
|
||||
String get dart2wasmSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get dart2wasmSnapshot => _snapshotPathFor(
|
||||
'dart2wasm_product.snapshot',
|
||||
);
|
||||
|
||||
String get ddsSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get ddsSnapshot => _snapshotPathFor(
|
||||
'dds.dart.snapshot',
|
||||
);
|
||||
|
||||
String get ddsAotSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get ddsAotSnapshot => _snapshotPathFor(
|
||||
'dds_aot.dart.snapshot',
|
||||
);
|
||||
|
||||
String get frontendServerSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get frontendServerSnapshot => _snapshotPathFor(
|
||||
'frontend_server.dart.snapshot',
|
||||
);
|
||||
|
||||
String get frontendServerAotSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get frontendServerAotSnapshot => _snapshotPathFor(
|
||||
'frontend_server_aot.dart.snapshot',
|
||||
);
|
||||
|
||||
String get dtdSnapshot => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
String get dtdSnapshot => _snapshotPathFor(
|
||||
'dart_tooling_daemon.dart.snapshot',
|
||||
);
|
||||
|
||||
String get devToolsBinaries => path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'resources',
|
||||
_runFromBuildRoot
|
||||
? sdkPath
|
||||
: path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'resources',
|
||||
),
|
||||
'devtools',
|
||||
);
|
||||
|
||||
String get wasmOpt => path.join(sdkPath, 'bin', 'utils',
|
||||
Platform.isWindows ? 'wasm-opt.exe' : 'wasm-opt');
|
||||
String get wasmOpt => path.absolute(
|
||||
_runFromBuildRoot
|
||||
? sdkPath
|
||||
: path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'utils',
|
||||
),
|
||||
Platform.isWindows ? 'wasm-opt.exe' : 'wasm-opt',
|
||||
);
|
||||
|
||||
String get librariesJson => path.absolute(sdkPath, 'lib', 'libraries.json');
|
||||
// This file is only generated when building the SDK and isn't generated for
|
||||
// non-SDK build targets.
|
||||
String get librariesJson {
|
||||
if (_runFromBuildRoot) {
|
||||
log.stderr(
|
||||
"WARNING: attempting to access 'libraries.json' from a build root "
|
||||
'executable. This file is only present in the context of a full Dart '
|
||||
'SDK.',
|
||||
);
|
||||
}
|
||||
return path.absolute(sdkPath, 'lib', 'libraries.json');
|
||||
}
|
||||
|
||||
String _snapshotPathFor(String snapshotName) => path.absolute(
|
||||
_runFromBuildRoot
|
||||
? sdkPath
|
||||
: path.absolute(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
),
|
||||
snapshotName,
|
||||
);
|
||||
|
||||
static bool checkArtifactExists(String path, {bool logError = true}) {
|
||||
if (!File(path).existsSync()) {
|
||||
@@ -124,17 +144,18 @@ class Sdk {
|
||||
var sdkPath =
|
||||
path.absolute(path.dirname(path.dirname(Platform.resolvedExecutable)));
|
||||
var snapshotsDir = path.join(sdkPath, 'bin', 'snapshots');
|
||||
var runFromBuildRoot = false;
|
||||
if (!Directory(snapshotsDir).existsSync()) {
|
||||
// This is the less common case where the user is in
|
||||
// the checked out Dart SDK, and is executing `dart` via:
|
||||
// ./out/ReleaseX64/dart ...
|
||||
// We confirm in a similar manner with the snapshot directory existence
|
||||
// We confirm in a similar manner with the gen directory existence
|
||||
// and then return the correct sdk path:
|
||||
var altPath =
|
||||
path.absolute(path.dirname(Platform.resolvedExecutable), 'dart-sdk');
|
||||
var snapshotsDir = path.join(altPath, 'bin', 'snapshots');
|
||||
if (Directory(snapshotsDir).existsSync()) {
|
||||
final altPath = path.absolute(path.dirname(Platform.resolvedExecutable));
|
||||
final genPath = path.join(altPath, 'gen');
|
||||
if (Directory(genPath).existsSync()) {
|
||||
sdkPath = altPath;
|
||||
runFromBuildRoot = true;
|
||||
}
|
||||
// If that snapshot dir does not exist either,
|
||||
// we use the first guess anyway.
|
||||
@@ -143,7 +164,7 @@ class Sdk {
|
||||
// Defer to [Runtime] for the version.
|
||||
var version = Runtime.runtime.version;
|
||||
|
||||
return Sdk._(sdkPath, version);
|
||||
return Sdk._(sdkPath, version, runFromBuildRoot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,64 @@
|
||||
// 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 'dart:isolate';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'core.dart';
|
||||
import 'sdk.dart';
|
||||
|
||||
/// For commands where we are able to initialize the [ArgParser], this value
|
||||
/// is used as the usageLineLength.
|
||||
int? get dartdevUsageLineLength =>
|
||||
stdout.hasTerminal ? stdout.terminalColumns : null;
|
||||
|
||||
/// Runs a tool's snapshot in an isolate.
|
||||
///
|
||||
/// Waits for the spawned isolate to exit before returning.
|
||||
Future<int> runFromSnapshot({
|
||||
required String snapshot,
|
||||
required List<String> args,
|
||||
required bool verbose,
|
||||
}) async {
|
||||
if (!Sdk.checkArtifactExists(snapshot)) return 255;
|
||||
|
||||
int retval = 0;
|
||||
final result = Completer<int>();
|
||||
final exitPort = ReceivePort()
|
||||
..listen((msg) {
|
||||
result.complete(0);
|
||||
});
|
||||
final errorPort = ReceivePort()
|
||||
..listen((error) {
|
||||
log.stderr(error.toString());
|
||||
result.complete(255);
|
||||
});
|
||||
try {
|
||||
await Isolate.spawnUri(
|
||||
Uri.file(snapshot),
|
||||
args,
|
||||
null,
|
||||
onExit: exitPort.sendPort,
|
||||
onError: errorPort.sendPort,
|
||||
);
|
||||
retval = await result.future;
|
||||
} catch (e, st) {
|
||||
log.stderr(e.toString());
|
||||
if (verbose) {
|
||||
log.stderr(st.toString());
|
||||
}
|
||||
retval = 255;
|
||||
}
|
||||
errorPort.close();
|
||||
exitPort.close();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Global options for dartdev.
|
||||
///
|
||||
/// ** READ THIS BEFORE MODIFYING **
|
||||
|
||||
+5
-45
@@ -5,51 +5,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:dds/dds.dart';
|
||||
|
||||
abstract class DartDevelopmentServiceOptions {
|
||||
static const vmServiceUriOption = 'vm-service-uri';
|
||||
static const bindAddressOption = 'bind-address';
|
||||
static const bindPortOption = 'bind-port';
|
||||
static const disableServiceAuthCodesFlag = 'disable-service-auth-codes';
|
||||
static const serveDevToolsFlag = 'serve-devtools';
|
||||
static const enableServicePortFallbackFlag = 'enable-service-port-fallback';
|
||||
|
||||
static ArgParser createArgParser() {
|
||||
return ArgParser()
|
||||
..addOption(
|
||||
vmServiceUriOption,
|
||||
help: 'The VM service URI DDS will connect to.',
|
||||
valueHelp: 'uri',
|
||||
mandatory: true,
|
||||
)
|
||||
..addOption(bindAddressOption,
|
||||
help: 'The address DDS should bind to.',
|
||||
valueHelp: 'address',
|
||||
defaultsTo: 'localhost')
|
||||
..addOption(
|
||||
bindPortOption,
|
||||
help: 'The port DDS should be served on.',
|
||||
valueHelp: 'port',
|
||||
defaultsTo: '0',
|
||||
)
|
||||
..addFlag(
|
||||
disableServiceAuthCodesFlag,
|
||||
help: 'Disables authentication codes.',
|
||||
)
|
||||
..addFlag(
|
||||
serveDevToolsFlag,
|
||||
help: 'If provided, DDS will serve DevTools.',
|
||||
)
|
||||
..addFlag(
|
||||
enableServicePortFallbackFlag,
|
||||
help: 'Bind to a random port if DDS fails to bind to the provided '
|
||||
'port.',
|
||||
)
|
||||
..addFlag('help', negatable: false);
|
||||
}
|
||||
}
|
||||
import 'package:dds/src/arg_parser.dart';
|
||||
|
||||
Uri _getDevToolsAssetPath() {
|
||||
final dartPath = Uri.parse(Platform.resolvedExecutable);
|
||||
@@ -71,7 +28,9 @@ Uri _getDevToolsAssetPath() {
|
||||
}
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
final argParser = DartDevelopmentServiceOptions.createArgParser();
|
||||
final argParser = DartDevelopmentServiceOptions.createArgParser(
|
||||
includeHelp: true,
|
||||
);
|
||||
final argResults = argParser.parse(args);
|
||||
if (args.isEmpty || argResults.wasParsed('help')) {
|
||||
print('''
|
||||
@@ -164,5 +123,6 @@ void writeErrorResponse(Object e, StackTrace st) {
|
||||
'state': 'error',
|
||||
'error': '$e',
|
||||
'stacktrace': '$st',
|
||||
if (e is DartDevelopmentServiceException) 'ddsExceptionDetails': e.toJson(),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -206,6 +206,11 @@ class DartDevelopmentServiceException implements Exception {
|
||||
@override
|
||||
String toString() => 'DartDevelopmentServiceException: $message';
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'error_code': errorCode,
|
||||
'message': message,
|
||||
};
|
||||
|
||||
final int errorCode;
|
||||
final String message;
|
||||
}
|
||||
@@ -226,6 +231,12 @@ class ExistingDartDevelopmentServiceException
|
||||
/// not the WebSocket URI (which can be obtained by mapping the scheme to
|
||||
/// `ws` (or `wss`) and appending `ws` to the path segments).
|
||||
final Uri? ddsUri;
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson() => {
|
||||
...super.toJson(),
|
||||
'uri': ddsUri.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
class DevToolsConfiguration {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2024, 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:args/args.dart';
|
||||
|
||||
abstract class DartDevelopmentServiceOptions {
|
||||
static const vmServiceUriOption = 'vm-service-uri';
|
||||
static const bindAddressOption = 'bind-address';
|
||||
static const bindPortOption = 'bind-port';
|
||||
static const disableServiceAuthCodesFlag = 'disable-service-auth-codes';
|
||||
static const serveDevToolsFlag = 'serve-devtools';
|
||||
static const enableServicePortFallbackFlag = 'enable-service-port-fallback';
|
||||
|
||||
static ArgParser createArgParser({
|
||||
int? usageLineLength,
|
||||
bool includeHelp = false,
|
||||
}) {
|
||||
final args = ArgParser(usageLineLength: usageLineLength)
|
||||
..addOption(
|
||||
vmServiceUriOption,
|
||||
help: 'The VM service URI DDS will connect to.',
|
||||
valueHelp: 'uri',
|
||||
mandatory: true,
|
||||
)
|
||||
..addOption(bindAddressOption,
|
||||
help: 'The address DDS should bind to.',
|
||||
valueHelp: 'address',
|
||||
defaultsTo: 'localhost')
|
||||
..addOption(
|
||||
bindPortOption,
|
||||
help: 'The port DDS should be served on.',
|
||||
valueHelp: 'port',
|
||||
defaultsTo: '0',
|
||||
)
|
||||
..addFlag(
|
||||
disableServiceAuthCodesFlag,
|
||||
help: 'Disables authentication codes.',
|
||||
)
|
||||
..addFlag(
|
||||
serveDevToolsFlag,
|
||||
help: 'If provided, DDS will serve DevTools.',
|
||||
)
|
||||
..addFlag(
|
||||
enableServicePortFallbackFlag,
|
||||
help: 'Bind to a random port if DDS fails to bind to the provided '
|
||||
'port.',
|
||||
);
|
||||
if (includeHelp) {
|
||||
args.addFlag('help', negatable: false);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
}
|
||||
@@ -97,16 +97,14 @@ class _DebuggingSession {
|
||||
bool enableDevTools,
|
||||
) async {
|
||||
final dartDir = File(Platform.resolvedExecutable).parent.path;
|
||||
final fullSdk = dartDir.endsWith('bin');
|
||||
final snapshotName = [
|
||||
final executable = [
|
||||
dartDir,
|
||||
fullSdk ? 'snapshots' : 'gen',
|
||||
'dds.dart.snapshot',
|
||||
'dart${Platform.isWindows ? '.exe' : ''}',
|
||||
].join(Platform.pathSeparator);
|
||||
_process = await Process.start(
|
||||
Platform.resolvedExecutable,
|
||||
executable,
|
||||
[
|
||||
snapshotName,
|
||||
'development-service',
|
||||
'--vm-service-uri=${server!.serverAddress!}',
|
||||
'--bind-address=$host',
|
||||
'--bind-port=$port',
|
||||
|
||||
Reference in New Issue
Block a user