Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 119 additions & 27 deletions lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,99 @@
import * as path from "path";
import * as util from "util";
import * as vscode from "vscode";
import { LLDBDapOptions } from "./types";
import * as child_process from "child_process";
import * as fs from "node:fs/promises";

/**
* 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
{
private lldbDapOptions: LLDBDapOptions;

constructor(lldbDapOptions: LLDBDapOptions) {
this.lldbDapOptions = lldbDapOptions;
export async function isExecutable(path: string): Promise<Boolean> {
try {
await fs.access(path, fs.constants.X_OK);
} catch {
return false;
}
return true;
}

static async isValidDebugAdapterPath(
pathUri: vscode.Uri,
): Promise<Boolean> {
async function findWithXcrun(executable: string): Promise<string | undefined> {
if (process.platform === "darwin") {
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to validate that the file is executable. It looks like the VS Code FS API doesn't have that info but we could use the node FS instead, something like:

import { access, constants } from 'node:fs/promises';

try {
  await access('<path>', constants.X_OK);
  console.log('can execute');
} catch {
  console.error('cannot execute');
} 

const fileStats = await vscode.workspace.fs.stat(pathUri);
if (!(fileStats.type & vscode.FileType.File)) {
return false;
const exec = util.promisify(child_process.execFile);
let { stdout, stderr } = await exec("/usr/bin/xcrun", [
"-find",
executable,
]);
if (stdout) {
return stdout.toString().trimEnd();
}
} catch (err) {
return false;
} 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 true;
}
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,
Expand All @@ -36,14 +102,40 @@ export class LLDBDapDescriptorFactory
"lldb-dap",
session.workspaceFolder,
);
const customPath = config.get<string>("executable-path");
const path: string = customPath || executable!!.command;

const fileUri = vscode.Uri.file(path);
if (!(await LLDBDapDescriptorFactory.isValidDebugAdapterPath(fileUri))) {
LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(fileUri.path);
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 this.lldbDapOptions.createDapExecutableCommand(session, executable);
return undefined;
}

/**
Expand Down
79 changes: 12 additions & 67 deletions lldb/tools/lldb-dap/src-ts/extension.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,24 @@
import * as path from "path";
import * as util from "util";
import * as vscode from "vscode";
import { LLDBDapOptions } from "./types";
import { DisposableContext } from "./disposable-context";
import { LLDBDapDescriptorFactory } from "./debug-adapter-factory";

/**
* This creates the configurations for this project if used as a standalone
* extension.
*/
function createDefaultLLDBDapOptions(): LLDBDapOptions {
return {
debuggerType: "lldb-dap",
async createDapExecutableCommand(
session: vscode.DebugSession,
packageJSONExecutable: vscode.DebugAdapterExecutable | undefined,
): Promise<vscode.DebugAdapterExecutable | undefined> {
const config = vscode.workspace.getConfiguration(
"lldb-dap",
session.workspaceFolder,
);
const path = config.get<string>("executable-path");
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") || {};
if (path) {
const dbgOptions = {
env: {
...configEnvironment,
...env,
}
};
return new vscode.DebugAdapterExecutable(path, [], dbgOptions);
} else if (packageJSONExecutable) {
return new vscode.DebugAdapterExecutable(
packageJSONExecutable.command,
packageJSONExecutable.args,
{
...packageJSONExecutable.options,
env: {
...packageJSONExecutable.options?.env,
...configEnvironment,
...env,
},
},
);
} else {
return undefined;
}
},
};
}
import {
LLDBDapDescriptorFactory,
isExecutable,
} from "./debug-adapter-factory";
import { DisposableContext } from "./disposable-context";

/**
* This class represents the extension and manages its life cycle. Other extensions
* using it as as library should use this class as the main entry point.
*/
export class LLDBDapExtension extends DisposableContext {
private lldbDapOptions: LLDBDapOptions;

constructor(lldbDapOptions: LLDBDapOptions) {
constructor() {
super();
this.lldbDapOptions = lldbDapOptions;

this.pushSubscription(
vscode.debug.registerDebugAdapterDescriptorFactory(
this.lldbDapOptions.debuggerType,
new LLDBDapDescriptorFactory(this.lldbDapOptions),
"lldb-dap",
new LLDBDapDescriptorFactory(),
),
);

Expand All @@ -80,10 +30,7 @@ export class LLDBDapExtension extends DisposableContext {
.get<string>("executable-path");

if (dapPath) {
const fileUri = vscode.Uri.file(dapPath);
if (
await LLDBDapDescriptorFactory.isValidDebugAdapterPath(fileUri)
) {
if (await isExecutable(dapPath)) {
return;
}
}
Expand All @@ -98,7 +45,5 @@ export class LLDBDapExtension extends DisposableContext {
* This is the entry point when initialized by VS Code.
*/
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
new LLDBDapExtension(createDefaultLLDBDapOptions()),
);
context.subscriptions.push(new LLDBDapExtension());
}
23 changes: 0 additions & 23 deletions lldb/tools/lldb-dap/src-ts/types.ts

This file was deleted.

Loading