| // 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 disableServiceOriginCheck = | |
| argResults[DartDevelopmentServiceOptions.disableServiceOriginCheckFlag]; | |
| 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, | |
| disableServiceOriginCheck: disableServiceOriginCheck, | |
| // 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, | |
| appName: argResults[DartDevelopmentServiceOptions.appNameOption], | |
| ); | |
| 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(), | |
| }, | |
| 'name': dds.appName, | |
| })); | |
| } 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(), | |
| })); | |
| } |