1
1
import assert from 'assert' ;
2
2
import * as vscode from 'vscode' ;
3
3
import { adaExtState } from './extension' ;
4
- import { AdaMain , getAdaMains , getProjectFile } from './helpers' ;
4
+ import { AdaMain , exe , getAdaMains , getEvaluatedCustomEnv , getProjectFile } from './helpers' ;
5
5
import { BUILD_PROJECT_TASK_NAME , getBuildTaskName } from './taskProviders' ;
6
+ import path from 'path' ;
7
+ import { existsSync } from 'fs' ;
6
8
7
9
/**
8
10
* Ada Configuration for a debug session
9
11
*/
10
- interface AdaConfig extends vscode . DebugConfiguration {
12
+ export interface AdaConfig extends vscode . DebugConfiguration {
11
13
MIMode : string ;
12
14
program : string ;
13
15
cwd ?: string ;
@@ -20,8 +22,25 @@ interface AdaConfig extends vscode.DebugConfiguration {
20
22
ignoreFailures : boolean ;
21
23
} [ ] ;
22
24
processId ?: string ;
25
+ miDebuggerPath ?: string ;
23
26
}
24
27
28
+ export const adaDynamicDebugConfigProvider = {
29
+ async provideDebugConfigurations (
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ _folder ?: vscode . WorkspaceFolder
32
+ ) : Promise < vscode . DebugConfiguration [ ] > {
33
+ const quickpick = await createQuickPickItems ( 'Build & Debug' ) ;
34
+
35
+ const configs : AdaConfig [ ] = quickpick . flatMap ( ( i ) => {
36
+ assert ( i . adaMain ) ;
37
+ return [ initializeConfig ( i . adaMain ) , createAttachConfig ( i . adaMain ) ] ;
38
+ } ) ;
39
+
40
+ return configs ;
41
+ } ,
42
+ } ;
43
+
25
44
/**
26
45
* Initialize debugging support for Ada projects.
27
46
*
@@ -48,35 +67,66 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
48
67
ctx . subscriptions . push (
49
68
vscode . debug . registerDebugConfigurationProvider (
50
69
'ada' ,
51
- {
52
- async provideDebugConfigurations (
53
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
54
- _folder : vscode . WorkspaceFolder | undefined
55
- ) : Promise < vscode . DebugConfiguration [ ] > {
56
- const quickpick = await createQuickPickItems ( 'Build & Debug' ) ;
57
-
58
- const configs : AdaConfig [ ] = quickpick . flatMap ( ( i ) => {
59
- assert ( i . adaMain ) ;
60
- return [ initializeConfig ( i . adaMain ) , createAttachConfig ( i . adaMain ) ] ;
61
- } ) ;
62
-
63
- return configs ;
64
- } ,
65
- } ,
70
+ adaDynamicDebugConfigProvider ,
66
71
// The 'Dynamic' trigger type only works if the package.json lists
67
72
// "onDebugDynamicConfigurations:ada" as part of the
68
73
// activationEvents.
69
74
vscode . DebugConfigurationProviderTriggerKind . Dynamic
70
75
)
71
76
) ;
72
77
73
- // TODO it is also possible to register another provider with trigger kind
74
- // 'Dynamic', however the role of such a provider is unclear. In practical
75
- // experiments it ends up never being called. The above provider is enough
76
- // to make it possible to launch debug sessions without a launch.json.
77
-
78
78
return provider ;
79
79
}
80
+
81
+ let cachedGdb : string | undefined | null = undefined ;
82
+
83
+ /**
84
+ *
85
+ * @returns the full path to the `gdb` executable, taking into consideration the
86
+ * `PATH` variable in the `terminal.integrated.env.*` setting if set. Otherwise,
87
+ * the `PATH` variable of the current process environment is considered.
88
+ *
89
+ * The current process environment is unlikely to change during the lifetime of
90
+ * the extension, and we already prompt the User to reload the window in case
91
+ * the `terminal.integrated.env.*` variables change. For this reason, we compute
92
+ * the value only on the first call, and cache it for subsequent calls to return
93
+ * it efficiently.
94
+ */
95
+ function getOrFindGdb ( ) : string | undefined {
96
+ if ( cachedGdb == undefined ) {
97
+ /**
98
+ * If undefined yet, try to compute it.
99
+ */
100
+ const env = getEvaluatedCustomEnv ( ) ;
101
+ let pathVal : string ;
102
+ if ( env && 'PATH' in env ) {
103
+ pathVal = env . PATH ;
104
+ } else if ( 'PATH' in process . env ) {
105
+ pathVal = process . env . PATH ?? '' ;
106
+ } else {
107
+ pathVal = '' ;
108
+ }
109
+
110
+ const gdb = pathVal
111
+ . split ( path . delimiter )
112
+ . map < string > ( ( v ) => path . join ( v , 'gdb' + exe ) )
113
+ . find ( existsSync ) ;
114
+
115
+ if ( gdb ) {
116
+ // Found
117
+ cachedGdb = gdb ;
118
+ return cachedGdb ;
119
+ } else {
120
+ // Not found. Assign null to cache to avoid recomputing at every call.
121
+ cachedGdb = null ;
122
+ }
123
+ }
124
+
125
+ // When returning, coerce null to undefined because the distinction doesn't
126
+ // matter on the caller side.
127
+ return cachedGdb ?? undefined ;
128
+ }
129
+
80
130
/**
81
131
* Initialize a debug configuration based on 'cppdbg' for the given executable
82
132
* if specified. Otherwise the program field includes
@@ -109,6 +159,7 @@ function initializeConfig(main: AdaMain, name?: string): AdaConfig {
109
159
MIMode : 'gdb' ,
110
160
preLaunchTask : main ? getBuildTaskName ( main ) : BUILD_PROJECT_TASK_NAME ,
111
161
setupCommands : setupCmd ,
162
+ miDebuggerPath : getOrFindGdb ( ) ,
112
163
} ;
113
164
114
165
return config ;
@@ -364,5 +415,6 @@ function createAttachConfig(adaMain: AdaMain): AdaConfig {
364
415
* to trigger an unwanted rebuild, so we don't set a preLaunchTask.
365
416
*/
366
417
// preLaunchTask: adaMain ? getBuildTaskName(adaMain) : BUILD_PROJECT_TASK_NAME,
418
+ miDebuggerPath : getOrFindGdb ( ) ,
367
419
} ;
368
420
}
0 commit comments