Skip to content

Commit 00f3857

Browse files
authored
Add lldb-dap (#317)
* fix path * fix path * remove logs * remove logs * fix plugin name
1 parent f9367ca commit 00f3857

File tree

7 files changed

+248
-73
lines changed

7 files changed

+248
-73
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,11 @@
707707
"default": "default",
708708
"enum": [
709709
"default",
710-
"codelldb"
710+
"codelldb",
711+
"lldb-dap",
712+
"gdb-dap"
711713
],
712-
"description": "The Debugging Configuration Type, .e.g default|codelldb"
714+
"description": "The Debugging Configuration Type, .e.g default|codelldb|lldb-dap|gdb-dap"
713715
},
714716
"xmake.customDebugConfig": {
715717
"type": "object",

src/completion.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {log} from './log';
88
import * as path from 'path';
99
import { config } from './config';
1010
import * as process from './process';
11+
import * as utils from './utils';
1112

1213
// get lua keyword list
1314
function getLuaKeywordList(): Promise<string> {
@@ -19,7 +20,7 @@ function getLuaKeywordList(): Promise<string> {
1920
// get xmake command list
2021
function getXMakeCommandList(): Promise<string> {
2122
return new Promise(async function (resolve, reject) {
22-
let getApisScript = path.join(__dirname, `../../assets/apis.lua`);
23+
let getApisScript = utils.getAssetsScriptPath("apis.lua");
2324
if (fs.existsSync(getApisScript)) {
2425
let result = (await process.iorunv(config.executable, ["l", getApisScript], { "COLORTERM": "nocolor" },
2526
config.workingDirectory)).stdout.trim();

src/debugger.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as os from 'os';
88
import * as encoding from 'encoding';
99
import * as process from './process';
1010
import { log } from './log';
11+
import * as utils from './utils';
1112
import { config } from './config';
1213

1314
// the debugger class
@@ -27,7 +28,7 @@ export class Debugger implements vscode.Disposable {
2728
// find gdb path
2829
async findGdbPath() {
2930
var gdbPath = null;
30-
let findGdbScript = path.join(__dirname, `../../assets/find_gdb.lua`);
31+
let findGdbScript = utils.getAssetsScriptPath("find_gdb.lua");
3132
if (fs.existsSync(findGdbScript)) {
3233
gdbPath = (await process.iorunv(config.executable, ["l", findGdbScript], {"COLORTERM": "nocolor"}, config.workingDirectory)).stdout.trim();
3334
if (gdbPath) {
@@ -67,6 +68,18 @@ export class Debugger implements vscode.Disposable {
6768
codelldb = true;
6869
}
6970

71+
// uses lldb-dap debugger?
72+
var lldbdap = false;
73+
if (config.debugConfigType == "lldb-dap" && vscode.extensions.getExtension("llvm-vs-code-extensions.lldb-dap")) {
74+
lldbdap = true;
75+
}
76+
77+
// uses gdb-dap debugger?
78+
var gdbdap = false;
79+
if (config.debugConfigType == "gdb-dap" && vscode.extensions.getExtension("ms-vscode.cpptools")) {
80+
gdbdap = true;
81+
}
82+
7083
// get target run directory
7184
if (!targetRunDir) {
7285
targetRunDir = path.dirname(targetProgram);
@@ -106,6 +119,54 @@ export class Debugger implements vscode.Disposable {
106119
env: envs,
107120
externalConsole: false,
108121
};
122+
} else if (lldbdap) {
123+
// LLDB DAP configuration
124+
var envs = {};
125+
for (let item of (targetRunEnvs as Array<Object>)) {
126+
let map = item as Map<String, String>;
127+
if (map) {
128+
let name = map["name"];
129+
let value = map["value"];
130+
if (name && value) {
131+
envs[name] = value;
132+
}
133+
}
134+
}
135+
debugConfig = {
136+
name: `launch: ${targetName}`,
137+
type: 'lldb-dap',
138+
request: 'launch',
139+
program: targetProgram,
140+
args: args,
141+
stopAtEntry: true,
142+
cwd: targetRunDir,
143+
env: envs,
144+
externalConsole: false,
145+
};
146+
} else if (gdbdap) {
147+
// GDB DAP configuration
148+
var gdbEnvs = [];
149+
for (let item of (targetRunEnvs as Array<Object>)) {
150+
let map = item as Map<String, String>;
151+
if (map) {
152+
let name = map["name"];
153+
let value = map["value"];
154+
if (name && value) {
155+
gdbEnvs.push({name: name, value: value});
156+
}
157+
}
158+
}
159+
debugConfig = {
160+
name: `launch: ${targetName}`,
161+
type: 'gdb',
162+
request: 'launch',
163+
program: targetProgram,
164+
args: args,
165+
stopAtEntry: true,
166+
cwd: targetRunDir,
167+
environment: gdbEnvs,
168+
externalConsole: false,
169+
};
109170
} else {
110171
// uses cpptools debugger
111172
if (os.platform() == "darwin") {

src/explorer.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as path from 'path';
77
import * as process from './process';
88
import { config } from './config';
99
import { log } from './log';
10+
import * as utils from './utils';
1011

1112
// Different tree view items contain different data
1213
// Following types store specific info that we should keep in each tree view item
@@ -76,36 +77,35 @@ class XMakeExplorerItem extends vscode.TreeItem {
7677
// Set the icon depending on the type
7778
// Each known file type is assigned a language specific icon
7879
// If the file type is recognized then the default file icon is assigned
79-
const resDirPath = [__dirname, "..", "..", "res"];
8080

8181
switch (info.type) {
8282
case XMakeExplorerItemType.GROUP:
8383
this.iconPath = {
84-
dark: path.join(...resDirPath, "dark", "symbol-misc.svg"),
85-
light: path.join(...resDirPath, "light", "symbol-misc.svg")
84+
dark: utils.getResourcePath("dark/symbol-misc.svg"),
85+
light: utils.getResourcePath("light/symbol-misc.svg")
8686
}
8787
break;
8888
case XMakeExplorerItemType.TARGET:
8989
if (info.kind === "binary")
9090
this.iconPath = {
91-
dark: path.join(...resDirPath, "dark", "window.svg"),
92-
light: path.join(...resDirPath, "light", "window.svg")
91+
dark: utils.getResourcePath("dark/window.svg"),
92+
light: utils.getResourcePath("light/window.svg")
9393
}
9494
else if (info.kind === "shared")
9595
this.iconPath = {
96-
dark: path.join(...resDirPath, "dark", "gear.svg"),
97-
light: path.join(...resDirPath, "light", "gear.svg")
96+
dark: utils.getResourcePath("dark/gear.svg"),
97+
light: utils.getResourcePath("light/gear.svg")
9898
}
9999
else if (info.kind == "static")
100100
this.iconPath = {
101-
dark: path.join(...resDirPath, "dark", "library.svg"),
102-
light: path.join(...resDirPath, "light", "library.svg")
101+
dark: utils.getResourcePath("dark/library.svg"),
102+
light: utils.getResourcePath("light/library.svg")
103103
}
104104
else {
105105
// Icon for phony target
106106
this.iconPath = {
107-
dark: path.join(...resDirPath, "dark", "archive.svg"),
108-
light: path.join(...resDirPath, "light", "archive.svg")
107+
dark: utils.getResourcePath("dark/archive.svg"),
108+
light: utils.getResourcePath("light/archive.svg")
109109
}
110110
}
111111
break;
@@ -801,7 +801,7 @@ export class XMakeExplorer implements vscode.Disposable {
801801
// Helper function to read all the information used by the explorer from xmake.lua
802802
private async readInfo() {
803803
try {
804-
const getExplorerTargetsScript = path.join(__dirname, `../../assets/explorer.lua`);
804+
const getExplorerTargetsScript = utils.getAssetsScriptPath("explorer.lua");
805805
if (fs.existsSync(getExplorerTargetsScript)) {
806806
const infoJson = (await process.iorunv(config.executable, ["l", getExplorerTargetsScript], { "COLORTERM": "nocolor" }, config.workingDirectory)).stdout.trim();
807807
const info = JSON.parse(infoJson);

src/launchDebugger.ts

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ import * as fs from 'fs';
55
import * as os from 'os';
66
import * as path from 'path';
77
import * as vscode from 'vscode';
8-
import { WorkspaceFolder } from 'vscode';
9-
import * as process from './process';
10-
11-
import { config as settings } from './config';
128
import { log } from './log';
9+
import * as utils from './utils';
10+
import { config } from './config';
1311
import { Option } from './option';
12+
import * as process from './process';
1413

1514
/**
1615
* integrated: Use integrated terminal in VSCode
@@ -39,9 +38,9 @@ interface TargetInformations {
3938

4039
async function getDebuggableTargets(): Promise<Array<string>> {
4140
let targets = "";
42-
let getTargetsPathScript = path.join(__dirname, `../../assets/debuggable_targets.lua`);
41+
let getTargetsPathScript = utils.getAssetsScriptPath("debuggable_targets.lua");
4342
if (fs.existsSync(getTargetsPathScript)) {
44-
targets = (await process.iorunv(settings.executable, ["l", getTargetsPathScript], { "COLORTERM": "nocolor" }, settings.workingDirectory)).stdout.trim();
43+
targets = (await process.iorunv(config.executable, ["l", getTargetsPathScript], { "COLORTERM": "nocolor" }, config.workingDirectory)).stdout.trim();
4544
}
4645
if (targets) {
4746
return process.getAnnotatedJSON(targets)[0];
@@ -55,12 +54,25 @@ async function getDebuggableTargets(): Promise<Array<string>> {
5554
* @returns TargetInformations
5655
*/
5756
async function getInformations(targetName: string): Promise<TargetInformations> {
58-
let getTargetInformationsScript = path.join(__dirname, `../../assets/target_informations.lua`);
57+
let getTargetInformationsScript = utils.getAssetsScriptPath("target_informations.lua");
5958
if (fs.existsSync(getTargetInformationsScript)) {
60-
let targetInformations = (await process.iorunv(settings.executable, ["l", getTargetInformationsScript, targetName], { "COLORTERM": "nocolor" }, settings.workingDirectory)).stdout.trim();
61-
if (targetInformations) {
62-
return process.getAnnotatedJSON(targetInformations)[0];
59+
try {
60+
const result = await process.iorunv(config.executable, ["l", getTargetInformationsScript, targetName], { "COLORTERM": "nocolor" }, config.workingDirectory);
61+
const targetInformations = result.stdout.trim();
62+
63+
if (targetInformations) {
64+
const parsed = process.getAnnotatedJSON(targetInformations);
65+
if (parsed && parsed.length > 0) {
66+
return parsed[0];
67+
}
68+
}
69+
70+
log.error(`Failed to get target informations for ${targetName}. Result: ${targetInformations}`);
71+
} catch (error) {
72+
log.error(`Error executing target_informations.lua: ${error}`);
6373
}
74+
} else {
75+
log.error(`target_informations.lua script not found at ${getTargetInformationsScript}`);
6476
}
6577

6678
return null;
@@ -72,9 +84,9 @@ async function getInformations(targetName: string): Promise<TargetInformations>
7284
*/
7385
async function findGdbPath(): Promise<string> {
7486
let gdbPath = "";
75-
let findGdbScript = path.join(__dirname, `../../assets/find_gdb.lua`);
87+
let findGdbScript = utils.getAssetsScriptPath("find_gdb.lua");
7688
if (fs.existsSync(findGdbScript)) {
77-
gdbPath = (await process.iorunv(settings.executable, ["l", findGdbScript], { "COLORTERM": "nocolor" }, settings.workingDirectory)).stdout.trim();
89+
gdbPath = (await process.iorunv(config.executable, ["l", findGdbScript], { "COLORTERM": "nocolor" }, config.workingDirectory)).stdout.trim();
7890
if (gdbPath) {
7991
gdbPath = gdbPath.split('\n')[0].trim();
8092
}
@@ -158,7 +170,7 @@ class XmakeConfigurationProvider implements vscode.DebugConfigurationProvider {
158170
* @param token
159171
* @returns the modified config to cpptols or codelldb
160172
*/
161-
public async resolveDebugConfiguration(folder: WorkspaceFolder | undefined, config: XmakeDebugConfiguration, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration> {
173+
public async resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: XmakeDebugConfiguration, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration> {
162174

163175
// If target is not set, resolve it with the status
164176
if (!config.target) {
@@ -180,7 +192,7 @@ class XmakeConfigurationProvider implements vscode.DebugConfigurationProvider {
180192

181193
// Set the program path
182194
if (!(targetInformations.path && fs.existsSync(targetInformations.path))) {
183-
await vscode.window.showErrorMessage('The target program not found!');
195+
await vscode.window.showErrorMessage("The target program not found! Please build the project first.");
184196
return { name: "Error: Target not found", type: "cppdbg", request: "launch" }; // Return a fake config
185197
}
186198
config.program = targetInformations.path;
@@ -199,24 +211,24 @@ class XmakeConfigurationProvider implements vscode.DebugConfigurationProvider {
199211
if (!config.args) {
200212
let args = [];
201213

202-
if (config.target in settings.debuggingTargetsArguments)
203-
args = settings.debuggingTargetsArguments[config.target];
204-
else if ("default" in settings.debuggingTargetsArguments)
205-
args = settings.debuggingTargetsArguments["default"];
206-
else if (config.target in settings.runningTargetsArguments)
214+
if (config.target in config.debuggingTargetsArguments)
215+
args = config.debuggingTargetsArguments[config.target];
216+
else if ("default" in config.debuggingTargetsArguments)
217+
args = config.debuggingTargetsArguments["default"];
218+
else if (config.target in config.runningTargetsArguments)
207219
args = config.runningTargetsArguments[config.target];
208-
else if ("default" in settings.runningTargetsArguments)
209-
args = settings.runningTargetsArguments["default"];
220+
else if ("default" in config.runningTargetsArguments)
221+
args = config.runningTargetsArguments["default"];
210222

211223
config.args = args;
212224
}
213225

214226
// Get xmake env and merge it with config envs
215227
const sep = os.platform() == "win32" ? ';' : ':'
216228
let xmakeEnvs = targetInformations.envs;
217-
if (settings.envBehaviour === 'override') {
229+
if (config.envBehaviour === 'override') {
218230
config.env = { ...xmakeEnvs, ...config.env };
219-
} else if (settings.envBehaviour === 'merge' && config.env !== undefined) {
231+
} else if (config.envBehaviour === 'merge' && config.env !== undefined) {
220232
// Merge behaviour between xmake envs and launch.json envs
221233
for (const key in xmakeEnvs) {
222234
// If the key exist in debug envs
@@ -242,7 +254,7 @@ class XmakeConfigurationProvider implements vscode.DebugConfigurationProvider {
242254
}
243255

244256
// Switch to lldb if needed
245-
if (settings.debugConfigType == "codelldb") {
257+
if (config.debugConfigType == "codelldb") {
246258
config.type = 'lldb';
247259
config.stopOnEntry = config.stopAtEntry;
248260
// Code LLDB doesn't support newExternal
@@ -256,6 +268,39 @@ class XmakeConfigurationProvider implements vscode.DebugConfigurationProvider {
256268
config.program = `${targetInformations.name}.exe`;
257269
}
258270
}
271+
272+
// Switch to LLDB DAP if needed
273+
if (config.debugConfigType == "lldb-dap") {
274+
config.type = 'lldb-dap';
275+
config.stopOnEntry = config.stopOnEntry;
276+
// LLDB DAP doesn't support newExternal
277+
if (config.terminal == 'newExternal') {
278+
config.terminal = 'external';
279+
}
280+
281+
// LLDB DAP use program key for search a running process
282+
if (config.request == 'attach') {
283+
config.stopOnEntry = false;
284+
config.program = `${targetInformations.name}.exe`;
285+
}
286+
}
287+
288+
// Switch to GDB DAP if needed
289+
if (config.debugConfigType == "gdb-dap") {
290+
config.type = 'gdb';
291+
config.stopOnEntry = config.stopOnEntry;
292+
// GDB DAP doesn't support newExternal
293+
if (config.terminal == 'newExternal') {
294+
config.terminal = 'external';
295+
}
296+
297+
// GDB DAP use program key for search a running process
298+
if (config.request == 'attach') {
299+
config.stopOnEntry = false;
300+
config.program = `${targetInformations.name}.exe`;
301+
}
302+
}
303+
259304
// Set MIMode for macos
260305
if (os.platform() == 'darwin') {
261306
config.MIMode = "lldb";
@@ -285,7 +330,7 @@ class XmakeConfigurationProvider implements vscode.DebugConfigurationProvider {
285330
config = { ...config, ...setupCommands };
286331

287332
// Merge the custom debug config with actual config
288-
config = { ...config, ...settings.customDebugConfig };
333+
config = { ...config, ...config.customDebugConfig };
289334

290335
return config;
291336
}
@@ -294,20 +339,26 @@ class XmakeConfigurationProvider implements vscode.DebugConfigurationProvider {
294339
export function initDebugger(context: vscode.ExtensionContext, option: Option) {
295340
const cpptools = vscode.extensions.getExtension("ms-vscode.cpptools");
296341
const codelldb = vscode.extensions.getExtension("vadimcn.vscode-lldb");
342+
const lldbdap = vscode.extensions.getExtension("llvm-vs-code-extensions.lldb-dap");
297343

298-
if (!cpptools && !codelldb) {
299-
log.error("Neither CppTools or Code LLDB is installed");
300-
vscode.window.showErrorMessage("Neither CppTools or Code LLDB is installed. Install one of this extension to debug");
344+
if (!cpptools && !codelldb && !lldbdap) {
345+
log.error("No debugging extensions are installed");
346+
vscode.window.showErrorMessage("No debugging extensions found. Please install CppTools, CodeLLDB, or LLDB DAP extension to debug");
301347
return;
302348
}
303349

304-
if (!codelldb && settings.debugConfigType == 'codelldb') {
305-
vscode.window.showErrorMessage("Code LLDB is not installed. Install it first to use the debugger.");
350+
// Check if the configured debugger is available
351+
if (config.debugConfigType == "lldb-dap" && !lldbdap) {
352+
log.info("LLDB DAP is configured but not available, falling back to available debuggers");
353+
}
354+
if (config.debugConfigType == "codelldb" && !codelldb) {
355+
log.info("CodeLLDB is configured but not available, falling back to available debuggers");
306356
}
307357

308-
// Activate the two extensions
358+
// Activate all available debugging extensions
309359
cpptools?.activate();
310-
codelldb?.activate()
360+
codelldb?.activate();
361+
lldbdap?.activate();
311362

312363
const provider = new XmakeConfigurationProvider(option);
313364
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('xmake', provider, vscode.DebugConfigurationProviderTriggerKind.Dynamic));

0 commit comments

Comments
 (0)