Skip to content

Commit 7fa7cb1

Browse files
prompt for CodeLLDB settings only when launching a debug session (#1381)
1 parent 3725797 commit 7fa7cb1

File tree

6 files changed

+312
-579
lines changed

6 files changed

+312
-579
lines changed

src/WorkspaceContext.ts

Lines changed: 1 addition & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ import * as path from "path";
1717
import { FolderContext } from "./FolderContext";
1818
import { StatusItem } from "./ui/StatusItem";
1919
import { SwiftOutputChannel } from "./ui/SwiftOutputChannel";
20-
import { swiftLibraryPathKey, getErrorDescription } from "./utilities/utilities";
20+
import { swiftLibraryPathKey } from "./utilities/utilities";
2121
import { pathExists, isPathInsidePath } from "./utilities/filesystem";
22-
import { getLLDBLibPath } from "./debugger/lldb";
2322
import { LanguageClientManager } from "./sourcekit-lsp/LanguageClientManager";
2423
import { TemporaryFolder } from "./utilities/tempFolder";
2524
import { TaskManager } from "./tasks/TaskManager";
@@ -29,7 +28,6 @@ import configuration from "./configuration";
2928
import contextKeys from "./contextKeys";
3029
import { setSnippetContextKey } from "./SwiftSnippets";
3130
import { CommentCompletionProviders } from "./editor/CommentCompletion";
32-
import { DebugAdapter, LaunchConfigType } from "./debugger/debugAdapter";
3331
import { SwiftBuildStatus } from "./ui/SwiftBuildStatus";
3432
import { SwiftToolchain } from "./toolchain/toolchain";
3533
import { DiagnosticsManager } from "./DiagnosticsManager";
@@ -444,76 +442,6 @@ export class WorkspaceContext implements vscode.Disposable {
444442
return { dispose: () => this.swiftFileObservers.delete(listener) };
445443
}
446444

447-
/** find LLDB version and setup path in CodeLLDB */
448-
async setLLDBVersion() {
449-
// check we are using CodeLLDB
450-
if (DebugAdapter.getLaunchConfigType(this.swiftVersion) !== LaunchConfigType.CODE_LLDB) {
451-
return;
452-
}
453-
const libPathResult = await getLLDBLibPath(this.toolchain);
454-
if (!libPathResult.success) {
455-
// if failure message is undefined then fail silently
456-
if (!libPathResult.failure) {
457-
return;
458-
}
459-
const errorMessage = `Error: ${getErrorDescription(libPathResult.failure)}`;
460-
vscode.window.showErrorMessage(
461-
`Failed to setup CodeLLDB for debugging of Swift code. Debugging may produce unexpected results. ${errorMessage}`
462-
);
463-
this.outputChannel.log(`Failed to setup CodeLLDB: ${errorMessage}`);
464-
return;
465-
}
466-
467-
const libPath = libPathResult.success;
468-
const lldbConfig = vscode.workspace.getConfiguration("lldb");
469-
const configLLDBPath = lldbConfig.get<string>("library");
470-
const expressions = lldbConfig.get<string>("launch.expressions");
471-
if (configLLDBPath === libPath && expressions === "native") {
472-
return;
473-
}
474-
475-
// show dialog for setting up LLDB
476-
vscode.window
477-
.showInformationMessage(
478-
"The Swift extension needs to update some CodeLLDB settings to enable debugging features. Do you want to set this up in your global settings or the workspace settings?",
479-
"Global",
480-
"Workspace",
481-
"Cancel"
482-
)
483-
.then(result => {
484-
switch (result) {
485-
case "Global":
486-
lldbConfig.update("library", libPath, vscode.ConfigurationTarget.Global);
487-
lldbConfig.update(
488-
"launch.expressions",
489-
"native",
490-
vscode.ConfigurationTarget.Global
491-
);
492-
// clear workspace setting
493-
lldbConfig.update(
494-
"library",
495-
undefined,
496-
vscode.ConfigurationTarget.Workspace
497-
);
498-
// clear workspace setting
499-
lldbConfig.update(
500-
"launch.expressions",
501-
undefined,
502-
vscode.ConfigurationTarget.Workspace
503-
);
504-
break;
505-
case "Workspace":
506-
lldbConfig.update("library", libPath, vscode.ConfigurationTarget.Workspace);
507-
lldbConfig.update(
508-
"launch.expressions",
509-
"native",
510-
vscode.ConfigurationTarget.Workspace
511-
);
512-
break;
513-
}
514-
});
515-
}
516-
517445
/** set focus based on the file a TextEditor is editing */
518446
async focusTextEditor(editor?: vscode.TextEditor) {
519447
await this.focusUri(editor?.document.uri);

src/debugger/debugAdapter.ts

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,9 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
import * as vscode from "vscode";
1615
import configuration from "../configuration";
17-
import contextKeys from "../contextKeys";
18-
import { fileExists } from "../utilities/filesystem";
1916
import { Version } from "../utilities/version";
2017
import { SwiftToolchain } from "../toolchain/toolchain";
21-
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
2218

2319
/**
2420
* The launch configuration type added by the Swift extension that will delegate to the appropriate
@@ -61,59 +57,11 @@ export class DebugAdapter {
6157
* @param toolchain The Swift toolchain to use
6258
* @returns A path to the debug adapter for the user's toolchain and configuration
6359
**/
64-
public static async debugAdapterPath(toolchain: SwiftToolchain): Promise<string> {
60+
public static async getLLDBDebugAdapterPath(toolchain: SwiftToolchain): Promise<string> {
6561
const customDebugAdapterPath = configuration.debugger.customDebugAdapterPath;
6662
if (customDebugAdapterPath.length > 0) {
6763
return customDebugAdapterPath;
6864
}
69-
70-
const debugAdapter = this.getLaunchConfigType(toolchain.swiftVersion);
71-
switch (debugAdapter) {
72-
case LaunchConfigType.LLDB_DAP:
73-
return toolchain.getLLDBDebugAdapter();
74-
case LaunchConfigType.CODE_LLDB:
75-
return toolchain.getLLDB();
76-
}
77-
}
78-
79-
/**
80-
* Verify that the toolchain debug adapter exists and display an error message to the user
81-
* if it doesn't.
82-
*
83-
* Has the side effect of setting the `swift.lldbVSCodeAvailable` context key depending
84-
* on the result.
85-
*
86-
* @param workspace WorkspaceContext
87-
* @param quiet Whether or not the dialog should be displayed if the adapter does not exist
88-
* @returns Whether or not the debug adapter exists
89-
*/
90-
public static async verifyDebugAdapterExists(
91-
toolchain: SwiftToolchain,
92-
outputChannel: SwiftOutputChannel,
93-
quiet = false
94-
): Promise<boolean> {
95-
const lldbDebugAdapterPath = await this.debugAdapterPath(toolchain).catch(error => {
96-
outputChannel.log(error);
97-
return undefined;
98-
});
99-
100-
if (!lldbDebugAdapterPath || !(await fileExists(lldbDebugAdapterPath))) {
101-
if (!quiet) {
102-
const debugAdapterName = this.getLaunchConfigType(toolchain.swiftVersion);
103-
vscode.window.showErrorMessage(
104-
configuration.debugger.customDebugAdapterPath.length > 0
105-
? `Cannot find ${debugAdapterName} debug adapter specified in setting Swift.Debugger.Path.`
106-
: `Cannot find ${debugAdapterName} debug adapter in your Swift toolchain.`
107-
);
108-
}
109-
if (lldbDebugAdapterPath) {
110-
outputChannel.log(`Failed to find ${lldbDebugAdapterPath}`);
111-
}
112-
contextKeys.lldbVSCodeAvailable = false;
113-
return false;
114-
}
115-
116-
contextKeys.lldbVSCodeAvailable = true;
117-
return true;
65+
return toolchain.getLLDBDebugAdapter();
11866
}
11967
}

src/debugger/debugAdapterFactory.ts

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import { DebugAdapter, LaunchConfigType, SWIFT_LAUNCH_CONFIG_TYPE } from "./debu
1919
import { registerLoggingDebugAdapterTracker } from "./logTracker";
2020
import { SwiftToolchain } from "../toolchain/toolchain";
2121
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
22+
import { fileExists } from "../utilities/filesystem";
23+
import { getLLDBLibPath } from "./lldb";
24+
import { getErrorDescription } from "../utilities/utilities";
2225

2326
/**
2427
* Registers the active debugger with the extension, and reregisters it
@@ -27,35 +30,11 @@ import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
2730
* @returns A disposable to be disposed when the extension is deactivated
2831
*/
2932
export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Disposable {
30-
async function updateDebugAdapter() {
31-
await workspaceContext.setLLDBVersion();
32-
33-
// Verify that the adapter exists, but only after registration. This async method
34-
// is basically an unstructured task so we don't want to run it before the adapter
35-
// registration above as it could cause code executing immediately after register()
36-
// to use the incorrect adapter.
37-
DebugAdapter.verifyDebugAdapterExists(
38-
workspaceContext.toolchain,
39-
workspaceContext.outputChannel,
40-
true
41-
).catch(error => {
42-
workspaceContext.outputChannel.log(error);
43-
});
44-
}
45-
4633
const subscriptions: vscode.Disposable[] = [
4734
registerLoggingDebugAdapterTracker(),
4835
registerLLDBDebugAdapter(workspaceContext.toolchain, workspaceContext.outputChannel),
49-
vscode.workspace.onDidChangeConfiguration(event => {
50-
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
51-
updateDebugAdapter();
52-
}
53-
}),
5436
];
5537

56-
// Perform the initial registration, then reregister every time the settings change.
57-
updateDebugAdapter();
58-
5938
return {
6039
dispose: () => {
6140
subscriptions.map(sub => sub.dispose());
@@ -79,7 +58,7 @@ function registerLLDBDebugAdapter(
7958

8059
const debugConfigProvider = vscode.debug.registerDebugConfigurationProvider(
8160
SWIFT_LAUNCH_CONFIG_TYPE,
82-
new LLDBDebugConfigurationProvider(process.platform, toolchain)
61+
new LLDBDebugConfigurationProvider(process.platform, toolchain, outputChannel)
8362
);
8463

8564
return {
@@ -117,8 +96,7 @@ export class LLDBDebugAdapterExecutableFactory implements vscode.DebugAdapterDes
11796
}
11897

11998
async createDebugAdapterDescriptor(): Promise<vscode.DebugAdapterDescriptor> {
120-
const path = await DebugAdapter.debugAdapterPath(this.toolchain);
121-
await DebugAdapter.verifyDebugAdapterExists(this.toolchain, this.outputChannel);
99+
const path = await DebugAdapter.getLLDBDebugAdapterPath(this.toolchain);
122100
return new vscode.DebugAdapterExecutable(path, [], {});
123101
}
124102
}
@@ -135,7 +113,8 @@ export class LLDBDebugAdapterExecutableFactory implements vscode.DebugAdapterDes
135113
export class LLDBDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
136114
constructor(
137115
private platform: NodeJS.Platform,
138-
private toolchain: SwiftToolchain
116+
private toolchain: SwiftToolchain,
117+
private outputChannel: SwiftOutputChannel
139118
) {}
140119

141120
async resolveDebugConfiguration(
@@ -155,15 +134,79 @@ export class LLDBDebugConfigurationProvider implements vscode.DebugConfiguration
155134
launchConfig.type = DebugAdapter.getLaunchConfigType(this.toolchain.swiftVersion);
156135
if (launchConfig.type === LaunchConfigType.CODE_LLDB) {
157136
launchConfig.sourceLanguages = ["swift"];
137+
// Prompt the user to update CodeLLDB settings if necessary
138+
await this.promptForCodeLldbSettings();
158139
} else if (launchConfig.type === LaunchConfigType.LLDB_DAP) {
159140
if (launchConfig.env) {
160141
launchConfig.env = this.convertEnvironmentVariables(launchConfig.env);
161142
}
143+
const lldbDapPath = await DebugAdapter.getLLDBDebugAdapterPath(this.toolchain);
144+
// Verify that the debug adapter exists or bail otherwise
145+
if (!(await fileExists(lldbDapPath))) {
146+
vscode.window.showErrorMessage(
147+
`Cannot find the LLDB debug adapter in your Swift toolchain: No such file or directory "${lldbDapPath}"`
148+
);
149+
return undefined;
150+
}
162151
}
163152

164153
return launchConfig;
165154
}
166155

156+
private async promptForCodeLldbSettings(): Promise<void> {
157+
const libLldbPathResult = await getLLDBLibPath(this.toolchain);
158+
if (!libLldbPathResult.success) {
159+
const errorMessage = `Error: ${getErrorDescription(libLldbPathResult.failure)}`;
160+
vscode.window.showWarningMessage(
161+
`Failed to setup CodeLLDB for debugging of Swift code. Debugging may produce unexpected results. ${errorMessage}`
162+
);
163+
this.outputChannel.log(`Failed to setup CodeLLDB: ${errorMessage}`);
164+
return;
165+
}
166+
const libLldbPath = libLldbPathResult.success;
167+
const lldbConfig = vscode.workspace.getConfiguration("lldb");
168+
if (
169+
lldbConfig.get<string>("library") === libLldbPath &&
170+
lldbConfig.get<string>("launch.expressions") === "native"
171+
) {
172+
return;
173+
}
174+
const userSelection = await vscode.window.showInformationMessage(
175+
"The Swift extension needs to update some CodeLLDB settings to enable debugging features. Do you want to set this up in your global settings or workspace settings?",
176+
{ modal: true },
177+
"Global",
178+
"Workspace",
179+
"Run Anyway"
180+
);
181+
switch (userSelection) {
182+
case "Global":
183+
lldbConfig.update("library", libLldbPath, vscode.ConfigurationTarget.Global);
184+
lldbConfig.update(
185+
"launch.expressions",
186+
"native",
187+
vscode.ConfigurationTarget.Global
188+
);
189+
// clear workspace setting
190+
lldbConfig.update("library", undefined, vscode.ConfigurationTarget.Workspace);
191+
// clear workspace setting
192+
lldbConfig.update(
193+
"launch.expressions",
194+
undefined,
195+
vscode.ConfigurationTarget.Workspace
196+
);
197+
break;
198+
case "Workspace":
199+
lldbConfig.update("library", libLldbPath, vscode.ConfigurationTarget.Workspace);
200+
lldbConfig.update(
201+
"launch.expressions",
202+
"native",
203+
vscode.ConfigurationTarget.Workspace
204+
);
205+
break;
206+
}
207+
return;
208+
}
209+
167210
private convertEnvironmentVariables(map: { [key: string]: string }): string[] {
168211
return Object.entries(map).map(([key, value]) => `${key}=${value}`);
169212
}

src/debugger/lldb.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ export const CI_DISABLE_ASLR =
3333
: {};
3434

3535
/**
36-
* Get LLDB library for given LLDB executable
37-
* @param executable LLDB executable
36+
* Get the path to the LLDB library.
37+
*
3838
* @returns Library path for LLDB
3939
*/
4040
export async function getLLDBLibPath(toolchain: SwiftToolchain): Promise<Result<string>> {

0 commit comments

Comments
 (0)