blob: 29417e8eaf472361f4ecc20918acb984577fa19e [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.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:devtools_shared/devtools_shared.dart' show DtdInfo;
import 'package:path/path.dart' as path;
import 'utils.dart';
String getDTDSnapshotDir() {
// This logic is originally from pkg/dartdev/lib/src/sdk.dart
//
// Find SDK path.
(String, bool)? trySDKPath(String executablePath) {
// The common case, and how cli_util.dart computes the Dart SDK directory,
// [path.dirname] called twice on Platform.executable. We confirm by
// asserting that the directory `./bin/snapshots/` exists in this directory:
var sdkPath = path.absolute(path.dirname(path.dirname(executablePath)));
var snapshotsDir = path.join(sdkPath, 'bin', 'snapshots');
var runFromBuildRoot = false;
final type = FileSystemEntity.typeSync(snapshotsDir);
if (type != FileSystemEntityType.directory &&
type != FileSystemEntityType.link) {
// This is the less common case where the user is in
// the checked out Dart SDK, and is executing `dart` via:
// ./out/ReleaseX64/dart ... or in google3.
sdkPath = path.absolute(path.dirname(executablePath));
snapshotsDir = sdkPath;
runFromBuildRoot = true;
}
// Try to locate the DDS snapshot to determine if we're able to find
// the SDK snapshots with this SDK path. This is meant to handle
// non-standard SDK layouts that can involve symlinks (e.g., Brew
// installations, google3 tests, etc).
if (!File(
path.join(snapshotsDir, 'dds_aot.dart.snapshot'),
).existsSync()) {
// We do not have an AOT snpashot and hence look for the JIT snapshot.
if (!File(
path.join(snapshotsDir, 'dds.dart.snapshot'),
).existsSync()) {
return null;
}
}
return (sdkPath, runFromBuildRoot);
}
final (sdkPath, runFromBuildRoot) = trySDKPath(Platform.resolvedExecutable) ??
trySDKPath(Platform.executable)!;
final String snapshotDir;
if (runFromBuildRoot) {
snapshotDir = sdkPath;
} else {
snapshotDir = path.absolute(sdkPath, 'bin', 'snapshots');
}
return snapshotDir;
}
Future<DtdInfo?> startDtd({
required bool machineMode,
required bool printDtdUri,
}) async {
final snapshotDir = getDTDSnapshotDir();
final dtdAotSnapshot = path.absolute(
snapshotDir,
'dart_tooling_daemon_aot.dart.snapshot',
);
final completer = Completer<DtdInfo?>();
void completeForError() => completer.complete(null);
final exitPort = ReceivePort()
..listen((_) {
completeForError();
});
final errorPort = ReceivePort()
..listen((_) {
completeForError();
});
final receivePort = ReceivePort()
..listen((message) {
try {
// [message] is a JSON encoded String from package:dtd_impl.
final json = jsonDecode(message) as Map<String, Object?>;
if (json
case {
'tooling_daemon_details': {
'uri': String uri,
'trusted_client_secret': String secret,
}
}) {
if (printDtdUri || machineMode) {
DevToolsUtils.printOutput(
'Serving the Dart Tooling Daemon at $uri',
{
'event': 'server.dtdStarted',
'params': {'uri': uri},
},
machineMode: machineMode,
);
}
completer.complete(DtdInfo(Uri.parse(uri), secret: secret));
}
} catch (_) {
completeForError();
}
});
try {
// Try to spawn an isolate using the AOT snapshot of the tooling daemon.
await Isolate.spawnUri(
Uri.file(dtdAotSnapshot),
['--machine'],
receivePort.sendPort,
onExit: exitPort.sendPort,
onError: errorPort.sendPort,
);
} catch (_, __) {
// Spawning an isolate using the AOT snapshot of the tooling daemon failed,
// try again using the JIT snapshot of the tooling daemon.
final dtdSnapshot = path.absolute(
snapshotDir,
'dart_tooling_daemon.dart.snapshot',
);
try {
await Isolate.spawnUri(
Uri.file(dtdSnapshot),
['--machine'],
receivePort.sendPort,
onExit: exitPort.sendPort,
onError: errorPort.sendPort,
);
} catch (_, __) {
completeForError();
}
}
final result = await completer.future.timeout(
const Duration(seconds: 5),
onTimeout: () => null,
);
receivePort.close();
errorPort.close();
exitPort.close();
return result;
}