Skip to content

Commit bcaad08

Browse files
committed
Merge branch 'topic/debug-attach' into 'master'
Improve vscode tasks and launch configurations Closes #1265 See merge request eng/ide/ada_language_server!1478
2 parents f077994 + 537d85d commit bcaad08

File tree

8 files changed

+743
-299
lines changed

8 files changed

+743
-299
lines changed

.vscode/extensions.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dbaeumer.vscode-eslint",
88
"esbenp.prettier-vscode",
99
"gruntfuggly.triggertaskonsave",
10-
"davidanson.vscode-markdownlint"
10+
"davidanson.vscode-markdownlint",
11+
"adacore.ada"
1112
]
1213
}

.vscode/tasks.json

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@
4646
}
4747
},
4848
{
49-
// This task starts a background npm process that monitors changes to
50-
// TS files and recompiles them as needed. It is configured to be run
51-
// before the (vscode)-based launch configurations to make sure the TS
52-
// files are compiled and re-compiled upon changes.
5349
"type": "npm",
5450
"script": "watch",
5551
"path": "integration/vscode/ada",
@@ -68,6 +64,24 @@
6864
"problemMatcher": ["$ada"],
6965
"group": "build",
7066
"label": "ada: Check current file"
67+
},
68+
{
69+
"type": "npm",
70+
"script": "watch",
71+
"path": "integration/vscode/ada",
72+
"group": "build",
73+
"problemMatcher": [
74+
{
75+
"base": "$tsc-watch",
76+
"fileLocation": [
77+
"relative",
78+
"${workspaceFolder}/integration/vscode/ada"
79+
]
80+
}
81+
],
82+
"label": "npm: watch - integration/vscode/ada",
83+
"detail": "node ./node_modules/typescript/bin/tsc -watch",
84+
"isBackground": true
7185
}
7286
]
7387
}

integration/vscode/ada/package.json

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -482,8 +482,7 @@
482482
"configuration": {
483483
"type": "object",
484484
"required": [
485-
"kind",
486-
"projectFile"
485+
"kind"
487486
],
488487
"properties": {
489488
"kind": {
@@ -494,7 +493,16 @@
494493
"checkFile",
495494
"cleanProject",
496495
"buildMain",
496+
"runMain",
497497
"buildAndRunMain"
498+
],
499+
"enumDescriptions": [
500+
"Build a GPR project",
501+
"Run semantic checks on an Ada file",
502+
"Clean a GPR project",
503+
"Build a main program specified in a GPR project",
504+
"Run a main program specified in a GPR project",
505+
"Run a build task and a run task in sequence for a given main program"
498506
]
499507
},
500508
"projectFile": {
@@ -554,19 +562,14 @@
554562
"properties": {
555563
"kind": {
556564
"enum": [
557-
"buildAndRunMain"
565+
"runMain"
558566
]
559567
},
560568
"projectFile": true,
561-
"args": true,
562569
"main": {
563570
"type": "string",
564571
"description": "Path to main source file"
565572
},
566-
"executable": {
567-
"type": "string",
568-
"description": "Path to main executable file (if it cannot be computed automatically)"
569-
},
570573
"mainArgs": {
571574
"type": "array",
572575
"items": {
@@ -576,6 +579,29 @@
576579
}
577580
},
578581
"additionalProperties": false
582+
},
583+
{
584+
"required": [
585+
"buildTask",
586+
"runTask"
587+
],
588+
"$comment": "Each oneOf is evaluated regardless of the parent schema. That's why valid properties of the parent must be repeated here in order to be allowed.",
589+
"properties": {
590+
"kind": {
591+
"enum": [
592+
"buildAndRunMain"
593+
]
594+
},
595+
"buildTask": {
596+
"type": "string",
597+
"description": "Name of the task that builds the main executable"
598+
},
599+
"runTask": {
600+
"type": "string",
601+
"description": "Name of the task that runs the main executable"
602+
}
603+
},
604+
"additionalProperties": false
579605
}
580606
]
581607
}

integration/vscode/ada/src/commands.ts

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import { ExtensionState } from './ExtensionState';
88
import { getOrAskForProgram } from './debugConfigProvider';
99
import { adaExtState, mainOutputChannel } from './extension';
1010
import { getProjectFileRelPath } from './helpers';
11-
import { CustomTaskDefinition, getEnclosingSymbol } from './taskProviders';
11+
import {
12+
CustomTaskDefinition,
13+
getConventionalTaskLabel,
14+
getEnclosingSymbol,
15+
isFromWorkspace,
16+
} from './taskProviders';
1217

1318
export function registerCommands(context: vscode.ExtensionContext, clients: ExtensionState) {
1419
context.subscriptions.push(vscode.commands.registerCommand('ada.otherFile', otherFileHandler));
@@ -39,10 +44,12 @@ export function registerCommands(context: vscode.ExtensionContext, clients: Exte
3944

4045
// This is a hidden command that gets called in the default debug
4146
// configuration snippet that gets offered in the launch.json file.
47+
// It is expected to return the relative path of the main program chosen for
48+
// debugging.
4249
context.subscriptions.push(
4350
vscode.commands.registerCommand('ada.getOrAskForProgram', async () => {
4451
const p = await getOrAskForProgram();
45-
return p;
52+
return p?.execRelPath();
4653
})
4754
);
4855

@@ -157,27 +164,6 @@ function getTaskLabel(task: vscode.Task): string {
157164
return isFromWorkspace(task) ? `(From Workspace) ${task.name}` : getConventionalTaskLabel(task);
158165
}
159166

160-
/**
161-
*
162-
* @param task - a task
163-
* @returns the label typically generated for that task by vscode. For tasks not
164-
* defined explicitely in the workspace, this is `ada: <task name>`. For tasks
165-
* defined in the workspace simply return the name which should already include
166-
* the convention.
167-
*/
168-
function getConventionalTaskLabel(task: vscode.Task): string {
169-
return isFromWorkspace(task) ? task.name : `${task.source}: ${task.name}`;
170-
}
171-
172-
/**
173-
*
174-
* @param task - a task
175-
* @returns `true` if the task is defined explicitely in the workspace's tasks.json
176-
*/
177-
function isFromWorkspace(task: vscode.Task) {
178-
return task.source == 'Workspace';
179-
}
180-
181167
interface TaskQuickPickItem extends vscode.QuickPickItem {
182168
task: vscode.Task;
183169
}

integration/vscode/ada/src/debugConfigProvider.ts

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,24 @@ import assert from 'assert';
22
import * as vscode from 'vscode';
33
import { adaExtState } from './extension';
44
import { AdaMain, getAdaMains, getProjectFile } from './helpers';
5+
import { BUILD_PROJECT_TASK_NAME, getBuildTaskName } from './taskProviders';
56

67
/**
78
* Ada Configuration for a debug session
89
*/
910
interface AdaConfig extends vscode.DebugConfiguration {
1011
MIMode: string;
1112
program: string;
12-
cwd: string;
13-
targetArchitecture: string;
14-
stopAtEntry: boolean;
15-
args: string[];
16-
setupCommands: {
13+
cwd?: string;
14+
targetArchitecture?: string;
15+
stopAtEntry?: boolean;
16+
args?: string[];
17+
setupCommands?: {
1718
description: string;
1819
text: string;
1920
ignoreFailures: boolean;
2021
}[];
22+
processId?: string;
2123
}
2224

2325
/**
@@ -53,9 +55,9 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
5355
): Promise<vscode.DebugConfiguration[]> {
5456
const quickpick = await createQuickPickItems('Build & Debug');
5557

56-
const configs: vscode.DebugConfiguration[] = quickpick.map((i) => {
58+
const configs: AdaConfig[] = quickpick.flatMap((i) => {
5759
assert(i.adaMain);
58-
return initializeConfig(i.adaMain.execFullPath, i.adaMain.mainRelPath());
60+
return [initializeConfig(i.adaMain), createAttachConfig(i.adaMain)];
5961
});
6062

6163
return configs;
@@ -89,27 +91,23 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
8991
* 'program' parameter.
9092
* @returns an AdaConfig
9193
*/
92-
function initializeConfig(program?: string, main?: string, name?: string): AdaConfig {
94+
function initializeConfig(main: AdaMain, name?: string): AdaConfig {
9395
// TODO it would be nice if this and the package.json configuration snippet
9496
// were the same.
9597
const config: AdaConfig = {
9698
type: 'cppdbg',
97-
name:
98-
name ??
99-
(main
100-
? `Ada: Debug main - ${main}`
101-
: program
102-
? `Ada: Debug executable - ${program.replace('${workspaceFolder}/', '')}`
103-
: 'Ada: Debugger Launch'),
99+
name: name ?? (main ? `Ada: Debug main - ${main.mainRelPath()}` : 'Ada: Debugger Launch'),
104100
request: 'launch',
105101
targetArchitecture: process.arch,
106102
cwd: '${workspaceFolder}',
107-
program: program ?? '${workspaceFolder}/${command:ada.getOrAskForProgram}',
103+
program: main
104+
? `\${workspaceFolder}/${main.execRelPath()}`
105+
: '${workspaceFolder}/${command:ada.getOrAskForProgram}',
108106
stopAtEntry: false,
109107
externalConsole: false,
110108
args: [],
111109
MIMode: 'gdb',
112-
preLaunchTask: 'ada: Build current project',
110+
preLaunchTask: main ? getBuildTaskName(main) : BUILD_PROJECT_TASK_NAME,
113111
setupCommands: setupCmd,
114112
};
115113

@@ -155,12 +153,7 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
155153
const item = quickpick[i];
156154
if (item != generateAll) {
157155
assert(item.adaMain);
158-
configs.push(
159-
initializeConfig(
160-
`\${workspaceFolder}/${item.adaMain.execRelPath()}`,
161-
item.adaMain.mainRelPath()
162-
)
163-
);
156+
configs.push(initializeConfig(item.adaMain));
164157
}
165158
}
166159
} else if (selectedProgram) {
@@ -169,10 +162,7 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
169162
// The cppdbg debug configuration exepects the executable to be
170163
// a full path rather than a path relative to the specified
171164
// cwd. That is why we include ${workspaceFolder}.
172-
const configuration = initializeConfig(
173-
`\${workspaceFolder}/${selectedProgram.adaMain.execRelPath()}`,
174-
selectedProgram.label
175-
);
165+
const configuration = initializeConfig(selectedProgram.adaMain);
176166
configs.push(configuration);
177167
} else {
178168
return Promise.reject('Cancelled');
@@ -212,10 +202,10 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
212202
// configuration on the fly.
213203
return debugConfiguration;
214204
} else {
215-
const exec = await getOrAskForProgram();
205+
const main = await getOrAskForProgram();
216206

217-
if (exec) {
218-
return initializeConfig(`\${workspaceFolder}/${exec}`);
207+
if (main) {
208+
return initializeConfig(main);
219209
}
220210
}
221211

@@ -237,11 +227,6 @@ const setupCmd = [
237227
text: '-enable-pretty-printing',
238228
ignoreFailures: true,
239229
},
240-
{
241-
description: 'Set Disassembly Flavor to Intel',
242-
text: '-gdb-set disassembly-flavor intel',
243-
ignoreFailures: true,
244-
},
245230
];
246231

247232
type QuickPickAdaMain = {
@@ -308,31 +293,29 @@ async function assertProjectHasMains(mains?: AdaMain[]) {
308293
* matches one of the mains, the corresponding executable is returned.
309294
*
310295
* Otherwise, the list of mains is offered to the user as a QuickPicker to
311-
* choose a main file. The executable corresponding to the selected main
312-
* file is returned.
313-
*
314-
* Note that paths are returned relative to the workspace.
296+
* choose a main file. The object corresponding to the selected main file is
297+
* returned.
315298
*
316299
* @param mains - a list of AdaMains if available at the caller site, otherwise
317300
* it is computed by the call.
318-
* @returns the path of the executable to debug *relative to the workspace*,
319-
* or *undefined* if no selection was made.
301+
* @returns the object representing the selected main, or *undefined* if no
302+
* selection was made.
320303
*/
321-
export async function getOrAskForProgram(mains?: AdaMain[]): Promise<string | undefined> {
304+
export async function getOrAskForProgram(mains?: AdaMain[]): Promise<AdaMain | undefined> {
322305
// Compute list of mains if not provided by the caller
323306
mains = mains ?? (await getAdaMains());
324307

325308
await assertProjectHasMains(mains);
326309

327-
if (mains.length == 1) return mains[0].execRelPath();
310+
if (mains.length == 1) return mains[0];
328311

329312
// Check if the current file matches one of the mains of the project. If
330313
// so, use it.
331314
const currentFile = vscode.window.activeTextEditor?.document.uri.path;
332315
if (currentFile != undefined) {
333316
const adaMain = await getAdaMainForSourceFile(currentFile, mains);
334317
if (adaMain) {
335-
return adaMain.execRelPath();
318+
return adaMain;
336319
}
337320
}
338321

@@ -343,7 +326,7 @@ export async function getOrAskForProgram(mains?: AdaMain[]): Promise<string | un
343326
placeHolder: 'Select a main file to debug',
344327
});
345328
if (selectedProgram) {
346-
return selectedProgram.adaMain?.execRelPath();
329+
return selectedProgram.adaMain;
347330
}
348331

349332
return undefined;
@@ -366,3 +349,20 @@ async function getAdaMainForSourceFile(
366349

367350
return mains.find((val) => srcPath == val.mainFullPath);
368351
}
352+
353+
function createAttachConfig(adaMain: AdaMain): AdaConfig {
354+
return {
355+
name: `Ada: Attach debugger to running process - ${adaMain.mainRelPath()}`,
356+
type: 'cppdbg',
357+
request: 'attach',
358+
program: `\${workspaceFolder}/${adaMain.execRelPath()}`,
359+
processId: '${command:pickProcess}',
360+
MIMode: 'gdb',
361+
/**
362+
* If the User is trying to attach to a running process, we have to
363+
* assume that they already built the project. It would be detrimental
364+
* to trigger an unwanted rebuild, so we don't set a preLaunchTask.
365+
*/
366+
// preLaunchTask: adaMain ? getBuildTaskName(adaMain) : BUILD_PROJECT_TASK_NAME,
367+
};
368+
}

0 commit comments

Comments
 (0)