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