1 import * as path from "path";
2 import * as util from "util";
3 import * as vscode from "vscode";
4 import * as child_process from "child_process";
5 import * as fs from "node:fs/promises";
7 export async function isExecutable(path: string): Promise<Boolean> {
9 await fs.access(path, fs.constants.X_OK);
16 async function findWithXcrun(executable: string): Promise<string | undefined> {
17 if (process.platform === "darwin") {
19 const exec = util.promisify(child_process.execFile);
20 let { stdout, stderr } = await exec("/usr/bin/xcrun", [
25 return stdout.toString().trimEnd();
32 async function findInPath(executable: string): Promise<string | undefined> {
34 process.platform === "win32" ? process.env["Path"] : process.env["PATH"];
39 const paths = env_path.split(path.delimiter);
40 for (const p of paths) {
41 const exe_path = path.join(p, executable);
42 if (await isExecutable(exe_path)) {
49 async function findDAPExecutable(): Promise<string | undefined> {
50 const executable = process.platform === "win32" ? "lldb-dap.exe" : "lldb-dap";
52 // Prefer lldb-dap from Xcode on Darwin.
53 const xcrun_dap = findWithXcrun(executable);
58 // Find lldb-dap in the user's path.
59 const path_dap = findInPath(executable);
67 async function getDAPExecutable(
68 session: vscode.DebugSession,
69 ): Promise<string | undefined> {
70 const config = vscode.workspace.getConfiguration(
72 session.workspaceFolder,
75 // Prefer the explicitly specified path in the extension's configuration.
76 const configPath = config.get<string>("executable-path");
77 if (configPath && configPath.length !== 0) {
81 // Try finding the lldb-dap binary.
82 const foundPath = await findDAPExecutable();
91 * This class defines a factory used to find the lldb-dap binary to use
92 * depending on the session configuration.
94 export class LLDBDapDescriptorFactory
95 implements vscode.DebugAdapterDescriptorFactory
97 async createDebugAdapterDescriptor(
98 session: vscode.DebugSession,
99 executable: vscode.DebugAdapterExecutable | undefined,
100 ): Promise<vscode.DebugAdapterDescriptor | undefined> {
101 const config = vscode.workspace.getConfiguration(
103 session.workspaceFolder,
106 const log_path = config.get<string>("log-path");
107 let env: { [key: string]: string } = {};
109 env["LLDBDAP_LOG"] = log_path;
111 const configEnvironment =
112 config.get<{ [key: string]: string }>("environment") || {};
113 const dapPath = await getDAPExecutable(session);
116 ...executable?.options?.env,
117 ...configEnvironment,
122 if (!(await isExecutable(dapPath))) {
123 LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(dapPath);
126 return new vscode.DebugAdapterExecutable(dapPath, [], dbgOptions);
127 } else if (executable) {
128 if (!(await isExecutable(executable.command))) {
129 LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(executable.command);
132 return new vscode.DebugAdapterExecutable(
142 * Shows a message box when the debug adapter's path is not found
144 static async showLLDBDapNotFoundMessage(path: string) {
145 const openSettingsAction = "Open Settings";
146 const callbackValue = await vscode.window.showErrorMessage(
147 `Debug adapter path: ${path} is not a valid file`,
151 if (openSettingsAction === callbackValue) {
152 vscode.commands.executeCommand(
153 "workbench.action.openSettings",
154 "lldb-dap.executable-path",