[ DDS ] Refactor DDS entrypoint to allow for overriding behavior in google3
This moves the CLI logic into lib/src/dds_cli_entrypoint.dart so it can be invoked by a custom google3 entrypoint (bin/dds.dart can't be imported using a package: URI). Since google3 will be able to perform some custom configuration of DDS, google3 related logic (e.g., `BazelUriConverter`) is also removed. This is technically a breaking change, but should be safe as this functionality isn't currently being used. Change-Id: I54d8a9927ff2df70e013ca5c8bc1d510b0b95f02 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/371520 Reviewed-by: Derek Xu <derekx@google.com> Commit-Queue: Derek Xu <derekx@google.com> Auto-Submit: Ben Konyi <bkonyi@google.com>
This commit is contained in:
+5
-141
@@ -2,147 +2,11 @@
|
||||
// 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:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dds/dds.dart';
|
||||
import 'package:dds/src/arg_parser.dart';
|
||||
import 'package:dds/src/bazel_uri_converter.dart';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
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',
|
||||
),
|
||||
);
|
||||
}
|
||||
import 'package:dds/src/dds_cli_entrypoint.dart';
|
||||
|
||||
Future<void> main(List<String> args) 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;
|
||||
}
|
||||
|
||||
// This URI is provided by the VM service directly so don't bother doing a
|
||||
// lookup.
|
||||
final remoteVmServiceUri = Uri.parse(
|
||||
argResults[DartDevelopmentServiceOptions.vmServiceUriOption],
|
||||
);
|
||||
|
||||
// Resolve the address which is potentially provided by the user.
|
||||
late InternetAddress address;
|
||||
final bindAddress =
|
||||
argResults[DartDevelopmentServiceOptions.bindAddressOption];
|
||||
try {
|
||||
final addresses = await InternetAddress.lookup(bindAddress);
|
||||
// Prefer IPv4 addresses.
|
||||
for (int i = 0; i < addresses.length; i++) {
|
||||
address = addresses[i];
|
||||
if (address.type == InternetAddressType.IPv4) break;
|
||||
}
|
||||
} 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: address.address,
|
||||
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 cachedUserTags =
|
||||
argResults[DartDevelopmentServiceOptions.cachedUserTagsOption];
|
||||
final google3WorkspaceRoot =
|
||||
argResults[DartDevelopmentServiceOptions.google3WorkspaceRootOption];
|
||||
|
||||
try {
|
||||
final dds = await DartDevelopmentService.startDartDevelopmentService(
|
||||
remoteVmServiceUri,
|
||||
serviceUri: serviceUri,
|
||||
enableAuthCodes: !disableServiceAuthCodes,
|
||||
ipv6: address.type == InternetAddressType.IPv6,
|
||||
devToolsConfiguration: serveDevTools && devToolsBuildDirectory != null
|
||||
? DevToolsConfiguration(
|
||||
enable: serveDevTools,
|
||||
customBuildDirectoryPath: devToolsBuildDirectory,
|
||||
devToolsServerAddress: devToolsServerAddress,
|
||||
)
|
||||
: null,
|
||||
enableServicePortFallback: enableServicePortFallback,
|
||||
cachedUserTags: cachedUserTags,
|
||||
uriConverter: google3WorkspaceRoot != null
|
||||
? BazelUriConverter(google3WorkspaceRoot).uriToPath
|
||||
: null,
|
||||
);
|
||||
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': {
|
||||
'uri': dtdInfo.uri,
|
||||
},
|
||||
}));
|
||||
stderr.close();
|
||||
} 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(),
|
||||
}));
|
||||
// This level of indirection is only here so DDS can be configured for
|
||||
// google3 specific functionality as it's not possible to import files under
|
||||
// a package's bin directory to wrap the entrypoint.
|
||||
await runDartDevelopmentServiceFromCLI(args);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ abstract class DartDevelopmentServiceOptions {
|
||||
static const enableServicePortFallbackFlag = 'enable-service-port-fallback';
|
||||
static const cachedUserTagsOption = 'cached-user-tags';
|
||||
static const devToolsServerAddressOption = 'devtools-server-address';
|
||||
static const google3WorkspaceRootOption = 'google3-workspace-root';
|
||||
|
||||
static ArgParser createArgParser({
|
||||
int? usageLineLength,
|
||||
@@ -78,12 +77,6 @@ abstract class DartDevelopmentServiceOptions {
|
||||
help: 'A set of UserTag names used to determine which CPU samples are '
|
||||
'cached by DDS.',
|
||||
defaultsTo: <String>[],
|
||||
)
|
||||
..addOption(
|
||||
google3WorkspaceRootOption,
|
||||
help: 'Sets the Google3 workspace root used for google3:// URI '
|
||||
'resolution.',
|
||||
hide: !verbose,
|
||||
);
|
||||
if (includeHelp) {
|
||||
argParser.addFlag('help', negatable: false);
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
// 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): consider moving to lib/ once this package is no longer shipped
|
||||
// via pub.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'package:path/path.dart';
|
||||
|
||||
/// A URI converter able to handle google3 URIs.
|
||||
class BazelUriConverter {
|
||||
final _absoluteUriToFileCache = HashMap<Uri, String>();
|
||||
final Context _context = context;
|
||||
final _binPaths = <String>[];
|
||||
final String _root;
|
||||
String? _genfiles;
|
||||
final _bazelCandidateFiles = StreamController<BazelSearchInfo>.broadcast();
|
||||
|
||||
BazelUriConverter(String originalPath) : _root = originalPath {
|
||||
_ensureAbsoluteAndNormalized(originalPath);
|
||||
// Note: The analyzer code this code is based on checked multiple things
|
||||
// while trying to find a google3 workspace - the presence of a blaze-out
|
||||
// directory, a READONLY file or a WORKSPACE file. If the blaze-out
|
||||
// structure changes, then potentially check for one of the others.
|
||||
final blazeOutDir =
|
||||
io.Directory(normalize(join(originalPath, 'blaze-out')));
|
||||
if (blazeOutDir.existsSync()) {
|
||||
_binPaths.addAll(_findBinFolderPaths(blazeOutDir));
|
||||
_binPaths.add(normalize(join(originalPath, 'blaze-bin')));
|
||||
_genfiles = normalize(join(originalPath, 'blaze-genfiles'));
|
||||
}
|
||||
}
|
||||
|
||||
String? uriToPath(String uriStr) {
|
||||
final uri = Uri.parse(uriStr);
|
||||
final cached = _absoluteUriToFileCache[uri];
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
if (uri.isScheme('file')) {
|
||||
final path = fileUriToNormalizedPath(_context, uri);
|
||||
final pathRelativeToRoot = _relativeToRoot(path);
|
||||
if (pathRelativeToRoot == null) return null;
|
||||
final fullFilePath = _context.join(_root, pathRelativeToRoot);
|
||||
final file = _findFile(fullFilePath);
|
||||
if (file != null) {
|
||||
_absoluteUriToFileCache[uri] = file.path;
|
||||
return file.path;
|
||||
}
|
||||
}
|
||||
// If the URI passed has a google3 scheme, this means we don't need to
|
||||
// convert from a package URI and we only need to prepend the root path
|
||||
// (i.e. the path that contains the user's workspace name).
|
||||
if (uri.isScheme('google3')) {
|
||||
// Remove the first character since uri.path starts with '/', though we
|
||||
// know this is not an absolute path.
|
||||
return _context.join(_root, uri.path.substring(1));
|
||||
}
|
||||
if (!uri.isScheme('package')) {
|
||||
// TODO(b/261234406): Handle `dart:` URIs.
|
||||
if (uri.isScheme('dart')) {
|
||||
// This doesn't return the actual location of the Dart SDK, which is
|
||||
// more complicated. The purpose of returning something here is to
|
||||
// avoid having the external version of URI converter resolving a path
|
||||
// that is incorrect in a way that confuse VS Code's stack trace.
|
||||
return _context.join(_root, uri.path);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
final uriPath = Uri.decodeComponent(uri.path);
|
||||
final slash = uriPath.indexOf('/');
|
||||
|
||||
// If the path either starts with a slash or has no slash, it is invalid.
|
||||
if (slash < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (uriPath.contains('//') || uriPath.contains('..')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final packageName = uriPath.substring(0, slash);
|
||||
|
||||
final fileUriPart = uriPath.substring(slash + 1);
|
||||
if (fileUriPart.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final filePath = fileUriPart.replaceAll('/', _context.separator);
|
||||
|
||||
if (!packageName.contains('.')) {
|
||||
final fullFilePath = _context.join(
|
||||
_root, 'third_party', 'dart', packageName, 'lib', filePath);
|
||||
final file = _findFile(fullFilePath);
|
||||
if (file != null) {
|
||||
_absoluteUriToFileCache[uri] = file.path;
|
||||
return file.path;
|
||||
}
|
||||
} else {
|
||||
final packagePath = packageName.replaceAll('.', _context.separator);
|
||||
final fullFilePath = _context.join(_root, packagePath, 'lib', filePath);
|
||||
final file = _findFile(fullFilePath);
|
||||
if (file != null) {
|
||||
_absoluteUriToFileCache[uri] = file.path;
|
||||
return file.path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String fileUriToNormalizedPath(Context context, Uri fileUri) {
|
||||
assert(fileUri.isScheme('file'));
|
||||
var contextPath = context.fromUri(fileUri);
|
||||
contextPath = context.normalize(contextPath);
|
||||
return contextPath;
|
||||
}
|
||||
|
||||
/// The file system abstraction supports only absolute and normalized paths.
|
||||
/// This method is used to validate any input paths to prevent errors later.
|
||||
void _ensureAbsoluteAndNormalized(String path) {
|
||||
assert(() {
|
||||
if (!_context.isAbsolute(path)) {
|
||||
throw ArgumentError('Path must be absolute : $path');
|
||||
}
|
||||
if (_context.normalize(path) != path) {
|
||||
throw ArgumentError('Path must be normalized : $path');
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
|
||||
List<String> _findBinFolderPaths(io.Directory blazeOutDir) {
|
||||
final binPaths = <String>[];
|
||||
for (final child
|
||||
in blazeOutDir.listSync(recursive: false).whereType<io.Directory>()) {
|
||||
// Children are folders denoting architectures and build flags, like
|
||||
// 'k8-opt', 'k8-fastbuild', perhaps 'host'.
|
||||
|
||||
final possibleBin = io.Directory(normalize(join(child.path, 'bin')));
|
||||
if (possibleBin.existsSync()) {
|
||||
binPaths.add(possibleBin.path);
|
||||
}
|
||||
}
|
||||
return binPaths;
|
||||
}
|
||||
|
||||
String? _relativeToRoot(String p) {
|
||||
// genfiles
|
||||
if (_genfiles != null && _context.isWithin(_genfiles!, p)) {
|
||||
return context.relative(p, from: _genfiles!);
|
||||
}
|
||||
// bin
|
||||
for (final bin in _binPaths) {
|
||||
if (context.isWithin(bin, p)) {
|
||||
return context.relative(p, from: bin);
|
||||
}
|
||||
}
|
||||
|
||||
// We are no longer checking for READONLY? Or should I add back?
|
||||
// READONLY
|
||||
// final readonly = this.readonly;
|
||||
// if (readonly != null) {
|
||||
// if (context.isWithin(readonly, p)) {
|
||||
// return context.relative(p, from: readonly);
|
||||
// }
|
||||
// }
|
||||
// Not generated
|
||||
if (context.isWithin(_root, p)) {
|
||||
return context.relative(p, from: _root);
|
||||
}
|
||||
// Failed reverse lookup
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Return the file with the given [absolutePath], looking first into
|
||||
/// directories for generated files: `bazel-bin` and `bazel-genfiles`, and
|
||||
/// then into the workspace originalPath. The file in the workspace originalPath is returned
|
||||
/// even if it does not exist. Return `null` if the given [absolutePath] is
|
||||
/// not in the workspace [originalPath].
|
||||
io.File? _findFile(String absolutePath) {
|
||||
try {
|
||||
final relative = _context.relative(absolutePath, from: _root);
|
||||
if (relative == '.') {
|
||||
return null;
|
||||
}
|
||||
// First check genfiles and bin directories. Note that we always use the
|
||||
// symlinks and not the [binPaths] or [genfiles] to make sure we use the
|
||||
// files corresponding to the most recent build configuration and get
|
||||
// consistent view of all the generated files.
|
||||
final generatedCandidates = ['blaze-genfiles', 'blaze-bin']
|
||||
.map((prefix) => context.join(_root, context.join(prefix, relative)));
|
||||
for (final path in generatedCandidates) {
|
||||
_ensureAbsoluteAndNormalized(path);
|
||||
final file = io.File(path);
|
||||
if (file.existsSync()) {
|
||||
_bazelCandidateFiles
|
||||
.add(BazelSearchInfo(relative, generatedCandidates.toList()));
|
||||
return file;
|
||||
}
|
||||
}
|
||||
// Writable
|
||||
_ensureAbsoluteAndNormalized(absolutePath);
|
||||
final writableFile = io.File(absolutePath);
|
||||
if (writableFile.existsSync()) {
|
||||
return writableFile;
|
||||
}
|
||||
|
||||
// If we couldn't find the file, assume that it has not yet been
|
||||
// generated, so send an event with all the paths that we tried.
|
||||
_bazelCandidateFiles
|
||||
.add(BazelSearchInfo(relative, generatedCandidates.toList()));
|
||||
// Not generated, return the default one.
|
||||
return writableFile;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Notification that we issue when searching for generated files in a Bazel
|
||||
/// workspace.
|
||||
///
|
||||
/// This allows clients to watch for changes to the generated files.
|
||||
class BazelSearchInfo {
|
||||
/// Candidate paths that we searched.
|
||||
final List<String> candidatePaths;
|
||||
|
||||
/// Absolute path that we tried searching for.
|
||||
///
|
||||
/// This is not necessarily the path of the actual file that will be used. See
|
||||
/// `BazelWorkspace.findFile` for details.
|
||||
final String requestedPath;
|
||||
|
||||
BazelSearchInfo(this.requestedPath, this.candidatePaths);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// 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';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// This URI is provided by the VM service directly so don't bother doing a
|
||||
// lookup.
|
||||
final remoteVmServiceUri = Uri.parse(
|
||||
argResults[DartDevelopmentServiceOptions.vmServiceUriOption],
|
||||
);
|
||||
|
||||
// Resolve the address which is potentially provided by the user.
|
||||
late InternetAddress address;
|
||||
final bindAddress =
|
||||
argResults[DartDevelopmentServiceOptions.bindAddressOption];
|
||||
try {
|
||||
final addresses = await InternetAddress.lookup(bindAddress);
|
||||
// Prefer IPv4 addresses.
|
||||
for (int i = 0; i < addresses.length; i++) {
|
||||
address = addresses[i];
|
||||
if (address.type == InternetAddressType.IPv4) break;
|
||||
}
|
||||
} 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: address.address,
|
||||
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 cachedUserTags =
|
||||
argResults[DartDevelopmentServiceOptions.cachedUserTagsOption];
|
||||
|
||||
try {
|
||||
final dds = await DartDevelopmentService.startDartDevelopmentService(
|
||||
remoteVmServiceUri,
|
||||
serviceUri: serviceUri,
|
||||
enableAuthCodes: !disableServiceAuthCodes,
|
||||
ipv6: address.type == InternetAddressType.IPv6,
|
||||
devToolsConfiguration: serveDevTools && devToolsBuildDirectory != null
|
||||
? DevToolsConfiguration(
|
||||
enable: serveDevTools,
|
||||
customBuildDirectoryPath: devToolsBuildDirectory,
|
||||
devToolsServerAddress: devToolsServerAddress,
|
||||
)
|
||||
: null,
|
||||
enableServicePortFallback: enableServicePortFallback,
|
||||
cachedUserTags: cachedUserTags,
|
||||
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': {
|
||||
'uri': dtdInfo.uri,
|
||||
},
|
||||
}));
|
||||
stderr.close();
|
||||
} 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(),
|
||||
}));
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dds/dds.dart';
|
||||
import 'package:dds/src/bazel_uri_converter.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:vm_service/vm_service_io.dart';
|
||||
@@ -77,54 +76,4 @@ void main() {
|
||||
},
|
||||
timeout: Timeout.none,
|
||||
);
|
||||
|
||||
test(
|
||||
'DDS can handle basic google3:// paths',
|
||||
() async {
|
||||
Uri serviceUri = remoteVmServiceUri;
|
||||
dds = await DartDevelopmentService.startDartDevelopmentService(
|
||||
remoteVmServiceUri,
|
||||
uriConverter: BazelUriConverter('/workspace/root').uriToPath,
|
||||
);
|
||||
serviceUri = dds!.wsUri!;
|
||||
expect(dds!.isRunning, true);
|
||||
final service = await vmServiceConnectUri(serviceUri.toString());
|
||||
|
||||
IsolateRef isolate;
|
||||
while (true) {
|
||||
final vm = await service.getVM();
|
||||
if (vm.isolates!.isNotEmpty) {
|
||||
isolate = vm.isolates!.first;
|
||||
try {
|
||||
isolate = await service.getIsolate(isolate.id!);
|
||||
if ((isolate as Isolate).runnable!) {
|
||||
break;
|
||||
}
|
||||
} on SentinelException {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
expect(isolate, isNotNull);
|
||||
|
||||
final unresolvedUris = <String>[
|
||||
// TODO(b/261234406): Handle `dart:` URIs.
|
||||
'dart:io', // dart:io -> file:///workspace/root/io
|
||||
'google3://workspace_name/my/script.dart', // google3://workspace_name/my/script.dart -> file:///workspace/root/my/script.dart
|
||||
'package:foo/foo.dart', // package:foo/foo.dart -> file:///workspace/root/third_party/dart/foo/lib/foo.dart
|
||||
];
|
||||
final result = await service.lookupResolvedPackageUris(
|
||||
isolate.id!,
|
||||
unresolvedUris,
|
||||
local: true,
|
||||
);
|
||||
|
||||
expect(result.uris?[0], 'file:///workspace/root/io');
|
||||
expect(result.uris?[1], 'file:///workspace/root/my/script.dart');
|
||||
expect(result.uris?[2],
|
||||
'file:///workspace/root/third_party/dart/foo/lib/foo.dart');
|
||||
},
|
||||
timeout: Timeout.none,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user