blob: 55c2f3e9f7deb595400e9edde02681b33cf7cc94 [file] [log] [blame]
import * as path from "path";
import * as util from "util";
import * as vscode from "vscode";
import * as child_process from "child_process";
import * as fs from "node:fs/promises";
export async function isExecutable(path: string): Promise<Boolean> {
try {
await fs.access(path, fs.constants.X_OK);
} catch {
return false;
}
return true;
}
async function findWithXcrun(executable: string): Promise<string | undefined> {
if (process.platform === "darwin") {
try {
const exec = util.promisify(child_process.execFile);
let { stdout, stderr } = await exec("/usr/bin/xcrun", [
"-find",
executable,
]);
if (stdout) {
return stdout.toString().trimEnd();
}
} catch (error) {}
}
return undefined;
}
async function findInPath(executable: string): Promise<string | undefined> {
const env_path =
process.platform === "win32" ? process.env["Path"] : process.env["PATH"];
if (!env_path) {
return undefined;
}
const paths = env_path.split(path.delimiter);
for (const p of paths) {
const exe_path = path.join(p, executable);
if (await isExecutable(exe_path)) {
return exe_path;
}
}
return undefined;
}
async function findDAPExecutable(): Promise<string | undefined> {
const executable = process.platform === "win32" ? "lldb-dap.exe" : "lldb-dap";
// Prefer lldb-dap from Xcode on Darwin.
const xcrun_dap = findWithXcrun(executable);
if (xcrun_dap) {
return xcrun_dap;
}
// Find lldb-dap in the user's path.
const path_dap = findInPath(executable);
if (path_dap) {
return path_dap;
}
return undefined;
}
async function getDAPExecutable(
session: vscode.DebugSession,
): Promise<string | undefined> {
const config = vscode.workspace.getConfiguration(
"lldb-dap",
session.workspaceFolder,
);
// Prefer the explicitly specified path in the extension's configuration.
const configPath = config.get<string>("executable-path");
if (configPath && configPath.length !== 0) {
return configPath;
}
// Try finding the lldb-dap binary.
const foundPath = await findDAPExecutable();
if (foundPath) {
return foundPath;
}
return undefined;
}
/**
* This class defines a factory used to find the lldb-dap binary to use
* depending on the session configuration.
*/
export class LLDBDapDescriptorFactory
implements vscode.DebugAdapterDescriptorFactory
{
async createDebugAdapterDescriptor(
session: vscode.DebugSession,
executable: vscode.DebugAdapterExecutable | undefined,
): Promise<vscode.DebugAdapterDescriptor | undefined> {
const config = vscode.workspace.getConfiguration(
"lldb-dap",
session.workspaceFolder,
);
const log_path = config.get<string>("log-path");
let env: { [key: string]: string } = {};
if (log_path) {
env["LLDBDAP_LOG"] = log_path;
}
const configEnvironment =
config.get<{ [key: string]: string }>("environment") || {};
const dapPath = await getDAPExecutable(session);
const dbgOptions = {
env: {
...executable?.options?.env,
...configEnvironment,
...env,
},
};
if (dapPath) {
if (!(await isExecutable(dapPath))) {
LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(dapPath);
return undefined;
}
return new vscode.DebugAdapterExecutable(dapPath, [], dbgOptions);
} else if (executable) {
if (!(await isExecutable(executable.command))) {
LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(executable.command);
return undefined;
}
return new vscode.DebugAdapterExecutable(
executable.command,
executable.args,
dbgOptions,
);
}
return undefined;
}
/**
* Shows a message box when the debug adapter's path is not found
*/
static async showLLDBDapNotFoundMessage(path: string) {
const openSettingsAction = "Open Settings";
const callbackValue = await vscode.window.showErrorMessage(
`Debug adapter path: ${path} is not a valid file`,
openSettingsAction,
);
if (openSettingsAction === callbackValue) {
vscode.commands.executeCommand(
"workbench.action.openSettings",
"lldb-dap.executable-path",
);
}
}
}