// 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 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(); 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; 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; }