blob: e3622f8cf94da98cb8111bbbe9c1e1676f1ccdc3 [file] [log] [blame]
// 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.
// TODO(bkonyi): move this file to lib/dds_cli_entrypoint.dart once package:dds
// is no longer shipped via pub.
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import '../dds.dart';
import 'arg_parser.dart';
import 'bazel_uri_converter.dart';
Uri _getDevToolsAssetPath() {
final dartDir = File(Platform.resolvedExecutable).parent.path;
final fullSdk = dartDir.endsWith('bin');
return Uri.file(
fullSdk
? path.absolute(
dartDir,
'resources',
'devtools',
)
: path.absolute(
dartDir,
'devtools',
),
);
}
// TODO(bkonyi): allow for injection of custom DevTools handlers in google3.
Future<void> runDartDevelopmentServiceFromCLI(
List<String> args, {
String? Function(String)? uriConverter,
}) async {
final argParser = DartDevelopmentServiceOptions.createArgParser(
includeHelp: true,
);
final argResults = argParser.parse(args);
if (args.isEmpty || argResults.wasParsed('help')) {
print('''
Starts a Dart Development Service (DDS) instance.
Usage:
${argParser.usage}
''');
return;
}
// Check if the remote VM Service address can be resolved to an IPv4 address.
final remoteVmServiceUri = Uri.parse(
argResults[DartDevelopmentServiceOptions.vmServiceUriOption],
);
bool doesVmServiceAddressResolveToIpv4Address = false;
try {
final addresses = await InternetAddress.lookup(remoteVmServiceUri.host);
for (final address in addresses) {
if (address.type == InternetAddressType.IPv4) {
doesVmServiceAddressResolveToIpv4Address = true;
}
}
} on SocketException catch (e, st) {
writeErrorResponse(
'Invalid --${DartDevelopmentServiceOptions.vmServiceUriOption} argument: '
'$remoteVmServiceUri',
st,
);
return;
}
// Ensure that the bind address, which is potentially provided by the user,
// can be resolved at all, and check whether it can be resolved to an IPv4
// address.
final bindAddress =
argResults[DartDevelopmentServiceOptions.bindAddressOption];
bool doesBindAddressResolveToIpv4Address = false;
try {
final addresses = await InternetAddress.lookup(bindAddress);
for (final address in addresses) {
if (address.type == InternetAddressType.IPv4) {
doesBindAddressResolveToIpv4Address = true;
}
}
} on SocketException catch (e, st) {
writeErrorResponse('Invalid bind address: $bindAddress', st);
return;
}
final portString = argResults[DartDevelopmentServiceOptions.bindPortOption];
int port;
try {
port = int.parse(portString);
} on FormatException catch (e, st) {
writeErrorResponse('Invalid port: $portString', st);
return;
}
final serviceUri = Uri(
scheme: 'http',
host: bindAddress,
port: port,
);
final disableServiceAuthCodes =
argResults[DartDevelopmentServiceOptions.disableServiceAuthCodesFlag];
final serveDevTools =
argResults[DartDevelopmentServiceOptions.serveDevToolsFlag];
final devToolsServerAddressStr =
argResults[DartDevelopmentServiceOptions.devToolsServerAddressOption];
Uri? devToolsBuildDirectory;
final devToolsServerAddress = devToolsServerAddressStr == null
? null
: Uri.parse(devToolsServerAddressStr);
if (serveDevTools) {
devToolsBuildDirectory = _getDevToolsAssetPath();
}
final enableServicePortFallback =
argResults[DartDevelopmentServiceOptions.enableServicePortFallbackFlag];
final google3WorkspaceRoot =
argResults[DartDevelopmentServiceOptions.google3WorkspaceRootOption];
if (google3WorkspaceRoot != null) {
uriConverter = BazelUriConverter(google3WorkspaceRoot).uriToPath;
}
try {
final dds = await DartDevelopmentService.startDartDevelopmentService(
remoteVmServiceUri,
serviceUri: serviceUri,
enableAuthCodes: !disableServiceAuthCodes,
// Only use IPv6 to serve DDS if either the remote VM Service address or
// the bind address cannot be resolved to an IPv4 address.
ipv6: !doesVmServiceAddressResolveToIpv4Address ||
!doesBindAddressResolveToIpv4Address,
devToolsConfiguration: serveDevTools && devToolsBuildDirectory != null
? DevToolsConfiguration(
enable: serveDevTools,
customBuildDirectoryPath: devToolsBuildDirectory,
devToolsServerAddress: devToolsServerAddress,
)
: null,
enableServicePortFallback: enableServicePortFallback,
uriConverter: uriConverter,
);
final dtdInfo = dds.hostedDartToolingDaemon;
stderr.write(json.encode({
'state': 'started',
'ddsUri': dds.uri.toString(),
if (dds.devToolsUri != null) 'devToolsUri': dds.devToolsUri.toString(),
if (dtdInfo != null)
'dtd': {
// For DDS-hosted DTD, there's only ever a local URI since there
// is no mechanism for exposing URIs.
'uri': dtdInfo.localUri.toString(),
},
}));
} catch (e, st) {
writeErrorResponse(e, st);
} finally {
// Always close stderr to notify tooling that DDS has finished writing
// launch details.
await stderr.close();
}
}
void writeErrorResponse(Object e, StackTrace st) {
stderr.write(json.encode({
'state': 'error',
'error': '$e',
'stacktrace': '$st',
if (e is DartDevelopmentServiceException) 'ddsExceptionDetails': e.toJson(),
}));
}