[libc][test] Adjust header paths in tests (#119623)
[llvm-project.git] / lldb / tools / lldb-dap / src-ts / debug-adapter-factory.ts
blob55c2f3e9f7deb595400e9edde02681b33cf7cc94
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> {
8   try {
9     await fs.access(path, fs.constants.X_OK);
10   } catch {
11     return false;
12   }
13   return true;
16 async function findWithXcrun(executable: string): Promise<string | undefined> {
17   if (process.platform === "darwin") {
18     try {
19       const exec = util.promisify(child_process.execFile);
20       let { stdout, stderr } = await exec("/usr/bin/xcrun", [
21         "-find",
22         executable,
23       ]);
24       if (stdout) {
25         return stdout.toString().trimEnd();
26       }
27     } catch (error) {}
28   }
29   return undefined;
32 async function findInPath(executable: string): Promise<string | undefined> {
33   const env_path =
34     process.platform === "win32" ? process.env["Path"] : process.env["PATH"];
35   if (!env_path) {
36     return undefined;
37   }
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)) {
43       return exe_path;
44     }
45   }
46   return undefined;
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);
54   if (xcrun_dap) {
55     return xcrun_dap;
56   }
58   // Find lldb-dap in the user's path.
59   const path_dap = findInPath(executable);
60   if (path_dap) {
61     return path_dap;
62   }
64   return undefined;
67 async function getDAPExecutable(
68   session: vscode.DebugSession,
69 ): Promise<string | undefined> {
70   const config = vscode.workspace.getConfiguration(
71     "lldb-dap",
72     session.workspaceFolder,
73   );
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) {
78     return configPath;
79   }
81   // Try finding the lldb-dap binary.
82   const foundPath = await findDAPExecutable();
83   if (foundPath) {
84     return foundPath;
85   }
87   return undefined;
90 /**
91  * This class defines a factory used to find the lldb-dap binary to use
92  * depending on the session configuration.
93  */
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(
102       "lldb-dap",
103       session.workspaceFolder,
104     );
106     const log_path = config.get<string>("log-path");
107     let env: { [key: string]: string } = {};
108     if (log_path) {
109       env["LLDBDAP_LOG"] = log_path;
110     }
111     const configEnvironment =
112       config.get<{ [key: string]: string }>("environment") || {};
113     const dapPath = await getDAPExecutable(session);
114     const dbgOptions = {
115       env: {
116         ...executable?.options?.env,
117         ...configEnvironment,
118         ...env,
119       },
120     };
121     if (dapPath) {
122       if (!(await isExecutable(dapPath))) {
123         LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(dapPath);
124         return undefined;
125       }
126       return new vscode.DebugAdapterExecutable(dapPath, [], dbgOptions);
127     } else if (executable) {
128       if (!(await isExecutable(executable.command))) {
129         LLDBDapDescriptorFactory.showLLDBDapNotFoundMessage(executable.command);
130         return undefined;
131       }
132       return new vscode.DebugAdapterExecutable(
133         executable.command,
134         executable.args,
135         dbgOptions,
136       );
137     }
138     return undefined;
139   }
141   /**
142    * Shows a message box when the debug adapter's path is not found
143    */
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`,
148       openSettingsAction,
149     );
151     if (openSettingsAction === callbackValue) {
152       vscode.commands.executeCommand(
153         "workbench.action.openSettings",
154         "lldb-dap.executable-path",
155       );
156     }
157   }