Skip to content

Commit 3725797

Browse files
Use one launch configuration type for all debug adapters (#1362)
1 parent 4fa67ac commit 3725797

File tree

11 files changed

+293
-258
lines changed

11 files changed

+293
-258
lines changed

package.json

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,9 +658,25 @@
658658
{
659659
"title": "Debugger",
660660
"properties": {
661+
"swift.debugger.debugAdapter": {
662+
"type": "string",
663+
"default": "auto",
664+
"enum": [
665+
"auto",
666+
"lldb-dap",
667+
"CodeLLDB"
668+
],
669+
"enumDescriptions": [
670+
"Automatically select which debug adapter to use based on your Swift toolchain version.",
671+
"Use the `lldb-dap` executable from the toolchain. Requires Swift 6 or later.",
672+
"Use the CodeLLDB extension's debug adapter."
673+
],
674+
"order": 1
675+
},
661676
"swift.debugger.useDebugAdapterFromToolchain": {
662677
"type": "boolean",
663678
"default": false,
679+
"markdownDeprecationMessage": "**Deprecated**: Use the `swift.debugger.debugAdapter` setting instead. This will be removed in future versions of the Swift extension.",
664680
"markdownDescription": "Use the LLDB debug adapter packaged with the Swift toolchain as your debug adapter. Note: this is only available starting with Swift 6. The CodeLLDB extension will be used if your Swift toolchain does not contain lldb-dap.",
665681
"order": 1
666682
},
@@ -1196,8 +1212,8 @@
11961212
],
11971213
"debuggers": [
11981214
{
1199-
"type": "swift-lldb",
1200-
"label": "Swift LLDB Debugger",
1215+
"type": "swift",
1216+
"label": "Swift Debugger",
12011217
"configurationAttributes": {
12021218
"launch": {
12031219
"required": [

src/commands/attachDebugger.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import * as vscode from "vscode";
1616
import { WorkspaceContext } from "../WorkspaceContext";
1717
import { getLldbProcess } from "../debugger/lldb";
18-
import { LaunchConfigType } from "../debugger/debugAdapter";
18+
import { SWIFT_LAUNCH_CONFIG_TYPE } from "../debugger/debugAdapter";
1919

2020
/**
2121
* Attaches the LLDB debugger to a running process selected by the user.
@@ -37,7 +37,7 @@ export async function attachDebugger(ctx: WorkspaceContext) {
3737
});
3838
if (picked) {
3939
const debugConfig: vscode.DebugConfiguration = {
40-
type: LaunchConfigType.SWIFT_EXTENSION,
40+
type: SWIFT_LAUNCH_CONFIG_TYPE,
4141
request: "attach",
4242
name: "Attach",
4343
pid: picked.pid,

src/configuration.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414

1515
import * as vscode from "vscode";
1616

17-
type CFamilySupportOptions = "enable" | "disable" | "cpptools-inactive";
18-
type ActionAfterBuildError = "Focus Problems" | "Focus Terminal" | "Do Nothing";
19-
type OpenAfterCreateNewProjectOptions =
17+
export type DebugAdapters = "auto" | "lldb-dap" | "CodeLLDB";
18+
export type CFamilySupportOptions = "enable" | "disable" | "cpptools-inactive";
19+
export type ActionAfterBuildError = "Focus Problems" | "Focus Terminal" | "Do Nothing";
20+
export type OpenAfterCreateNewProjectOptions =
2021
| "always"
2122
| "alwaysNewWindow"
2223
| "whenNoFolderOpen"
@@ -47,8 +48,8 @@ export interface LSPConfiguration {
4748

4849
/** debugger configuration */
4950
export interface DebuggerConfiguration {
50-
/** Whether or not to use CodeLLDB for debugging instead of lldb-dap */
51-
readonly useDebugAdapterFromToolchain: boolean;
51+
/** Get the underlying debug adapter type requested by the user. */
52+
readonly debugAdapter: DebugAdapters;
5253
/** Return path to debug adapter */
5354
readonly customDebugAdapterPath: string;
5455
}
@@ -182,19 +183,31 @@ const configuration = {
182183
/** debugger configuration */
183184
get debugger(): DebuggerConfiguration {
184185
return {
185-
get useDebugAdapterFromToolchain(): boolean {
186-
// Enabled by default only when we're on Windows arm64 since CodeLLDB does not support
187-
// this platform and gives an awful error message.
186+
get debugAdapter(): DebugAdapters {
187+
// Use inspect to determine if the user has explicitly set swift.debugger.useDebugAdapterFromToolchain
188+
const inspectUseDebugAdapterFromToolchain = vscode.workspace
189+
.getConfiguration("swift.debugger")
190+
.inspect<boolean>("useDebugAdapterFromToolchain");
191+
let useDebugAdapterFromToolchain =
192+
inspectUseDebugAdapterFromToolchain?.workspaceValue ??
193+
inspectUseDebugAdapterFromToolchain?.globalValue;
194+
// On Windows arm64 we enable swift.debugger.useDebugAdapterFromToolchain by default since CodeLLDB does
195+
// not support this platform and gives an awful error message.
188196
if (process.platform === "win32" && process.arch === "arm64") {
189-
// We need to use inspect to find out if the value is explicitly set.
190-
const inspect = vscode.workspace
191-
.getConfiguration("swift.debugger")
192-
.inspect<boolean>("useDebugAdapterFromToolchain");
193-
return inspect?.workspaceValue ?? inspect?.globalValue ?? true;
197+
useDebugAdapterFromToolchain = useDebugAdapterFromToolchain ?? true;
194198
}
195-
return vscode.workspace
199+
const selectedAdapter = vscode.workspace
196200
.getConfiguration("swift.debugger")
197-
.get<boolean>("useDebugAdapterFromToolchain", false);
201+
.get<DebugAdapters>("debugAdapter", "auto");
202+
switch (selectedAdapter) {
203+
case "auto":
204+
if (useDebugAdapterFromToolchain !== undefined) {
205+
return useDebugAdapterFromToolchain ? "lldb-dap" : "CodeLLDB";
206+
}
207+
return "auto";
208+
default:
209+
return selectedAdapter;
210+
}
198211
},
199212
get customDebugAdapterPath(): string {
200213
return vscode.workspace.getConfiguration("swift.debugger").get<string>("path", "");

src/debugger/buildConfig.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import configuration from "../configuration";
2020
import { FolderContext } from "../FolderContext";
2121
import { BuildFlags } from "../toolchain/BuildFlags";
2222
import { regexEscapedString, swiftRuntimeEnv } from "../utilities/utilities";
23-
import { DebugAdapter } from "./debugAdapter";
23+
import { SWIFT_LAUNCH_CONFIG_TYPE } from "./debugAdapter";
2424
import { TargetType } from "../SwiftPackage";
2525
import { Version } from "../utilities/version";
2626
import { TestLibrary } from "../TestExplorer/TestRunner";
@@ -515,7 +515,7 @@ export class TestingConfigurationFactory {
515515
}).map(([key, value]) => `settings set target.env-vars ${key}="${value}"`);
516516

517517
return {
518-
type: DebugAdapter.getLaunchConfigType(this.ctx.workspaceContext.swiftVersion),
518+
type: SWIFT_LAUNCH_CONFIG_TYPE,
519519
request: "custom",
520520
name: `Test ${this.ctx.swiftPackage.name}`,
521521
targetCreateCommands: [`file -a ${arch} ${xctestPath}/xctest`],
@@ -738,7 +738,7 @@ export class TestingConfigurationFactory {
738738
function getBaseConfig(ctx: FolderContext, expandEnvVariables: boolean) {
739739
const { folder, nameSuffix } = getFolderAndNameSuffix(ctx, expandEnvVariables);
740740
return {
741-
type: DebugAdapter.getLaunchConfigType(ctx.workspaceContext.swiftVersion),
741+
type: SWIFT_LAUNCH_CONFIG_TYPE,
742742
request: "launch",
743743
sourceLanguages: ["swift"],
744744
name: `Test ${ctx.swiftPackage.name}`,

src/debugger/debugAdapter.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ import { SwiftToolchain } from "../toolchain/toolchain";
2121
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
2222

2323
/**
24-
* The supported {@link vscode.DebugConfiguration.type Debug Configuration Type} for auto-generation of launch configurations
24+
* The launch configuration type added by the Swift extension that will delegate to the appropriate
25+
* LLDB debug adapter when launched.
26+
*/
27+
export const SWIFT_LAUNCH_CONFIG_TYPE = "swift";
28+
29+
/**
30+
* The supported {@link vscode.DebugConfiguration.type Debug Configuration Types} that can handle
31+
* LLDB launch requests.
2532
*/
2633
export const enum LaunchConfigType {
27-
SWIFT_EXTENSION = "swift-lldb",
34+
LLDB_DAP = "swift",
2835
CODE_LLDB = "lldb",
2936
}
3037

@@ -40,10 +47,12 @@ export class DebugAdapter {
4047
* @returns the type of launch configuration used by the given Swift toolchain version
4148
*/
4249
public static getLaunchConfigType(swiftVersion: Version): LaunchConfigType {
43-
return swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0)) &&
44-
configuration.debugger.useDebugAdapterFromToolchain
45-
? LaunchConfigType.SWIFT_EXTENSION
46-
: LaunchConfigType.CODE_LLDB;
50+
const lldbDapIsAvailable = swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0));
51+
if (lldbDapIsAvailable && configuration.debugger.debugAdapter === "lldb-dap") {
52+
return LaunchConfigType.LLDB_DAP;
53+
} else {
54+
return LaunchConfigType.CODE_LLDB;
55+
}
4756
}
4857

4958
/**
@@ -60,7 +69,7 @@ export class DebugAdapter {
6069

6170
const debugAdapter = this.getLaunchConfigType(toolchain.swiftVersion);
6271
switch (debugAdapter) {
63-
case LaunchConfigType.SWIFT_EXTENSION:
72+
case LaunchConfigType.LLDB_DAP:
6473
return toolchain.getLLDBDebugAdapter();
6574
case LaunchConfigType.CODE_LLDB:
6675
return toolchain.getLLDB();

src/debugger/debugAdapterFactory.ts

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
import * as vscode from "vscode";
1616
import * as path from "path";
1717
import { WorkspaceContext } from "../WorkspaceContext";
18-
import { DebugAdapter, LaunchConfigType } from "./debugAdapter";
19-
import { Version } from "../utilities/version";
18+
import { DebugAdapter, LaunchConfigType, SWIFT_LAUNCH_CONFIG_TYPE } from "./debugAdapter";
2019
import { registerLoggingDebugAdapterTracker } from "./logTracker";
2120
import { SwiftToolchain } from "../toolchain/toolchain";
2221
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
@@ -28,14 +27,7 @@ import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
2827
* @returns A disposable to be disposed when the extension is deactivated
2928
*/
3029
export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Disposable {
31-
let subscriptions: vscode.Disposable[] = [];
32-
const register = async () => {
33-
subscriptions.map(sub => sub.dispose());
34-
subscriptions = [
35-
registerLoggingDebugAdapterTracker(workspaceContext.toolchain.swiftVersion),
36-
registerLLDBDebugAdapter(workspaceContext.toolchain, workspaceContext.outputChannel),
37-
];
38-
30+
async function updateDebugAdapter() {
3931
await workspaceContext.setLLDBVersion();
4032

4133
// Verify that the adapter exists, but only after registration. This async method
@@ -49,20 +41,23 @@ export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Dis
4941
).catch(error => {
5042
workspaceContext.outputChannel.log(error);
5143
});
52-
};
44+
}
5345

54-
const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
55-
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
56-
register();
57-
}
58-
});
46+
const subscriptions: vscode.Disposable[] = [
47+
registerLoggingDebugAdapterTracker(),
48+
registerLLDBDebugAdapter(workspaceContext.toolchain, workspaceContext.outputChannel),
49+
vscode.workspace.onDidChangeConfiguration(event => {
50+
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
51+
updateDebugAdapter();
52+
}
53+
}),
54+
];
5955

6056
// Perform the initial registration, then reregister every time the settings change.
61-
register();
57+
updateDebugAdapter();
6258

6359
return {
6460
dispose: () => {
65-
changeMonitor.dispose();
6661
subscriptions.map(sub => sub.dispose());
6762
},
6863
};
@@ -78,13 +73,13 @@ function registerLLDBDebugAdapter(
7873
outputChannel: SwiftOutputChannel
7974
): vscode.Disposable {
8075
const debugAdpaterFactory = vscode.debug.registerDebugAdapterDescriptorFactory(
81-
LaunchConfigType.SWIFT_EXTENSION,
76+
SWIFT_LAUNCH_CONFIG_TYPE,
8277
new LLDBDebugAdapterExecutableFactory(toolchain, outputChannel)
8378
);
8479

8580
const debugConfigProvider = vscode.debug.registerDebugConfigurationProvider(
86-
LaunchConfigType.SWIFT_EXTENSION,
87-
new LLDBDebugConfigurationProvider(process.platform, toolchain.swiftVersion)
81+
SWIFT_LAUNCH_CONFIG_TYPE,
82+
new LLDBDebugConfigurationProvider(process.platform, toolchain)
8883
);
8984

9085
return {
@@ -140,14 +135,13 @@ export class LLDBDebugAdapterExecutableFactory implements vscode.DebugAdapterDes
140135
export class LLDBDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
141136
constructor(
142137
private platform: NodeJS.Platform,
143-
private swiftVersion: Version
138+
private toolchain: SwiftToolchain
144139
) {}
145140

146141
async resolveDebugConfiguration(
147142
_folder: vscode.WorkspaceFolder | undefined,
148143
launchConfig: vscode.DebugConfiguration
149-
): Promise<vscode.DebugConfiguration> {
150-
launchConfig.env = this.convertEnvironmentVariables(launchConfig.env);
144+
): Promise<vscode.DebugConfiguration | undefined | null> {
151145
// Fix the program path on Windows to include the ".exe" extension
152146
if (
153147
this.platform === "win32" &&
@@ -157,20 +151,20 @@ export class LLDBDebugConfigurationProvider implements vscode.DebugConfiguration
157151
launchConfig.program += ".exe";
158152
}
159153

160-
// Delegate to CodeLLDB if that's the debug adapter we have selected
161-
if (DebugAdapter.getLaunchConfigType(this.swiftVersion) === LaunchConfigType.CODE_LLDB) {
162-
launchConfig.type = LaunchConfigType.CODE_LLDB;
154+
// Delegate to the appropriate debug adapter extension
155+
launchConfig.type = DebugAdapter.getLaunchConfigType(this.toolchain.swiftVersion);
156+
if (launchConfig.type === LaunchConfigType.CODE_LLDB) {
163157
launchConfig.sourceLanguages = ["swift"];
158+
} else if (launchConfig.type === LaunchConfigType.LLDB_DAP) {
159+
if (launchConfig.env) {
160+
launchConfig.env = this.convertEnvironmentVariables(launchConfig.env);
161+
}
164162
}
163+
165164
return launchConfig;
166165
}
167166

168-
convertEnvironmentVariables(
169-
map: { [key: string]: string } | undefined
170-
): { [key: string]: string } | string[] | undefined {
171-
if (map === undefined) {
172-
return undefined;
173-
}
167+
private convertEnvironmentVariables(map: { [key: string]: string }): string[] {
174168
return Object.entries(map).map(([key, value]) => `${key}=${value}`);
175169
}
176170
}

src/debugger/launch.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import * as vscode from "vscode";
1717
import { FolderContext } from "../FolderContext";
1818
import { BuildFlags } from "../toolchain/BuildFlags";
1919
import { stringArrayInEnglish, swiftLibraryPathKey, swiftRuntimeEnv } from "../utilities/utilities";
20-
import { DebugAdapter } from "./debugAdapter";
20+
import { SWIFT_LAUNCH_CONFIG_TYPE } from "./debugAdapter";
2121
import { getFolderAndNameSuffix } from "./buildConfig";
2222
import configuration from "../configuration";
2323
import { CI_DISABLE_ASLR } from "./lldb";
@@ -136,7 +136,7 @@ function createExecutableConfigurations(ctx: FolderContext): vscode.DebugConfigu
136136

137137
return executableProducts.flatMap(product => {
138138
const baseConfig = {
139-
type: DebugAdapter.getLaunchConfigType(ctx.workspaceContext.swiftVersion),
139+
type: SWIFT_LAUNCH_CONFIG_TYPE,
140140
request: "launch",
141141
args: [],
142142
cwd: folder,
@@ -174,7 +174,7 @@ export function createSnippetConfiguration(
174174
const buildDirectory = BuildFlags.buildDirectoryFromWorkspacePath(folder, true);
175175

176176
return {
177-
type: DebugAdapter.getLaunchConfigType(ctx.workspaceContext.swiftVersion),
177+
type: SWIFT_LAUNCH_CONFIG_TYPE,
178178
request: "launch",
179179
name: `Run ${snippetName}`,
180180
program: path.posix.join(buildDirectory, "debug", snippetName),

src/debugger/logTracker.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import * as vscode from "vscode";
16-
import { DebugAdapter } from "./debugAdapter";
17-
import { Version } from "../utilities/version";
16+
import { LaunchConfigType } from "./debugAdapter";
1817
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
1918

2019
/**
@@ -44,28 +43,19 @@ interface DebugMessage {
4443
* Register the LoggingDebugAdapterTrackerFactory with the VS Code debug adapter tracker
4544
* @returns A disposable to be disposed when the extension is deactivated
4645
*/
47-
export function registerLoggingDebugAdapterTracker(swiftVersion: Version): vscode.Disposable {
48-
const register = () =>
49-
vscode.debug.registerDebugAdapterTrackerFactory(
50-
DebugAdapter.getLaunchConfigType(swiftVersion),
51-
new LoggingDebugAdapterTrackerFactory()
52-
);
53-
54-
// Maintains the disposable for the last registered debug adapter.
55-
let debugAdapterDisposable = register();
56-
const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
57-
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
58-
// Dispose the old adapter and reconfigure with the new settings.
59-
debugAdapterDisposable.dispose();
60-
debugAdapterDisposable = register();
61-
}
62-
});
46+
export function registerLoggingDebugAdapterTracker(): vscode.Disposable {
47+
// Register the factory for both lldb-dap and CodeLLDB since either could be used when
48+
// resolving a Swift launch configuration.
49+
const trackerFactory = new LoggingDebugAdapterTrackerFactory();
50+
const subscriptions: vscode.Disposable[] = [
51+
vscode.debug.registerDebugAdapterTrackerFactory(LaunchConfigType.CODE_LLDB, trackerFactory),
52+
vscode.debug.registerDebugAdapterTrackerFactory(LaunchConfigType.LLDB_DAP, trackerFactory),
53+
];
6354

6455
// Return a disposable that cleans everything up.
6556
return {
6657
dispose() {
67-
changeMonitor.dispose();
68-
debugAdapterDisposable.dispose();
58+
subscriptions.forEach(sub => sub.dispose());
6959
},
7060
};
7161
}

0 commit comments

Comments
 (0)