Skip to content

Use one launch configuration type for all debug adapters #1362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -638,9 +638,25 @@
{
"title": "Debugger",
"properties": {
"swift.debugger.debugAdapter": {
"type": "string",
"default": "auto",
"enum": [
"auto",
"lldb-dap",
"CodeLLDB"
],
"enumDescriptions": [
"Automatically select which debug adapter to use based on your Swift toolchain version.",
"Use the `lldb-dap` executable from the toolchain. Requires Swift 6 or later.",
"Use the CodeLLDB extension's debug adapter."
],
"order": 1
},
"swift.debugger.useDebugAdapterFromToolchain": {
"type": "boolean",
"default": false,
"markdownDeprecationMessage": "**Deprecated**: Use the `swift.debugger.debugAdapter` setting instead. This will be removed in future versions of the Swift extension.",
"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.",
"order": 1
},
Expand Down Expand Up @@ -1158,8 +1174,8 @@
],
"debuggers": [
{
"type": "swift-lldb",
"label": "Swift LLDB Debugger",
"type": "swift",
"label": "Swift Debugger",
"configurationAttributes": {
"launch": {
"required": [
Expand Down
4 changes: 2 additions & 2 deletions src/commands/attachDebugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import * as vscode from "vscode";
import { WorkspaceContext } from "../WorkspaceContext";
import { getLldbProcess } from "../debugger/lldb";
import { LaunchConfigType } from "../debugger/debugAdapter";
import { SWIFT_LAUNCH_CONFIG_TYPE } from "../debugger/debugAdapter";

/**
* Attaches the LLDB debugger to a running process selected by the user.
Expand All @@ -37,7 +37,7 @@ export async function attachDebugger(ctx: WorkspaceContext) {
});
if (picked) {
const debugConfig: vscode.DebugConfiguration = {
type: LaunchConfigType.SWIFT_EXTENSION,
type: SWIFT_LAUNCH_CONFIG_TYPE,
request: "attach",
name: "Attach",
pid: picked.pid,
Expand Down
43 changes: 28 additions & 15 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@

import * as vscode from "vscode";

type CFamilySupportOptions = "enable" | "disable" | "cpptools-inactive";
type ActionAfterBuildError = "Focus Problems" | "Focus Terminal" | "Do Nothing";
type OpenAfterCreateNewProjectOptions =
export type DebugAdapters = "auto" | "lldb-dap" | "CodeLLDB";
export type CFamilySupportOptions = "enable" | "disable" | "cpptools-inactive";
export type ActionAfterBuildError = "Focus Problems" | "Focus Terminal" | "Do Nothing";
export type OpenAfterCreateNewProjectOptions =
| "always"
| "alwaysNewWindow"
| "whenNoFolderOpen"
Expand Down Expand Up @@ -47,8 +48,8 @@ export interface LSPConfiguration {

/** debugger configuration */
export interface DebuggerConfiguration {
/** Whether or not to use CodeLLDB for debugging instead of lldb-dap */
readonly useDebugAdapterFromToolchain: boolean;
/** Get the underlying debug adapter type requested by the user. */
readonly debugAdapter: DebugAdapters;
/** Return path to debug adapter */
readonly customDebugAdapterPath: string;
}
Expand Down Expand Up @@ -182,19 +183,31 @@ const configuration = {
/** debugger configuration */
get debugger(): DebuggerConfiguration {
return {
get useDebugAdapterFromToolchain(): boolean {
// Enabled by default only when we're on Windows arm64 since CodeLLDB does not support
// this platform and gives an awful error message.
get debugAdapter(): DebugAdapters {
// Use inspect to determine if the user has explicitly set swift.debugger.useDebugAdapterFromToolchain
const inspectUseDebugAdapterFromToolchain = vscode.workspace
.getConfiguration("swift.debugger")
.inspect<boolean>("useDebugAdapterFromToolchain");
let useDebugAdapterFromToolchain =
inspectUseDebugAdapterFromToolchain?.workspaceValue ??
inspectUseDebugAdapterFromToolchain?.globalValue;
// On Windows arm64 we enable swift.debugger.useDebugAdapterFromToolchain by default since CodeLLDB does
// not support this platform and gives an awful error message.
if (process.platform === "win32" && process.arch === "arm64") {
// We need to use inspect to find out if the value is explicitly set.
const inspect = vscode.workspace
.getConfiguration("swift.debugger")
.inspect<boolean>("useDebugAdapterFromToolchain");
return inspect?.workspaceValue ?? inspect?.globalValue ?? true;
useDebugAdapterFromToolchain = useDebugAdapterFromToolchain ?? true;
}
return vscode.workspace
const selectedAdapter = vscode.workspace
.getConfiguration("swift.debugger")
.get<boolean>("useDebugAdapterFromToolchain", false);
.get<DebugAdapters>("debugAdapter", "auto");
switch (selectedAdapter) {
case "auto":
if (useDebugAdapterFromToolchain !== undefined) {
return useDebugAdapterFromToolchain ? "lldb-dap" : "CodeLLDB";
}
return "auto";
default:
return selectedAdapter;
}
},
get customDebugAdapterPath(): string {
return vscode.workspace.getConfiguration("swift.debugger").get<string>("path", "");
Expand Down
6 changes: 3 additions & 3 deletions src/debugger/buildConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import configuration from "../configuration";
import { FolderContext } from "../FolderContext";
import { BuildFlags } from "../toolchain/BuildFlags";
import { regexEscapedString, swiftRuntimeEnv } from "../utilities/utilities";
import { DebugAdapter } from "./debugAdapter";
import { SWIFT_LAUNCH_CONFIG_TYPE } from "./debugAdapter";
import { TargetType } from "../SwiftPackage";
import { Version } from "../utilities/version";
import { TestLibrary } from "../TestExplorer/TestRunner";
Expand Down Expand Up @@ -515,7 +515,7 @@ export class TestingConfigurationFactory {
}).map(([key, value]) => `settings set target.env-vars ${key}="${value}"`);

return {
type: DebugAdapter.getLaunchConfigType(this.ctx.workspaceContext.swiftVersion),
type: SWIFT_LAUNCH_CONFIG_TYPE,
request: "custom",
name: `Test ${this.ctx.swiftPackage.name}`,
targetCreateCommands: [`file -a ${arch} ${xctestPath}/xctest`],
Expand Down Expand Up @@ -738,7 +738,7 @@ export class TestingConfigurationFactory {
function getBaseConfig(ctx: FolderContext, expandEnvVariables: boolean) {
const { folder, nameSuffix } = getFolderAndNameSuffix(ctx, expandEnvVariables);
return {
type: DebugAdapter.getLaunchConfigType(ctx.workspaceContext.swiftVersion),
type: SWIFT_LAUNCH_CONFIG_TYPE,
request: "launch",
sourceLanguages: ["swift"],
name: `Test ${ctx.swiftPackage.name}`,
Expand Down
23 changes: 16 additions & 7 deletions src/debugger/debugAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@ import { SwiftToolchain } from "../toolchain/toolchain";
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";

/**
* The supported {@link vscode.DebugConfiguration.type Debug Configuration Type} for auto-generation of launch configurations
* The launch configuration type added by the Swift extension that will delegate to the appropriate
* LLDB debug adapter when launched.
*/
export const SWIFT_LAUNCH_CONFIG_TYPE = "swift";

/**
* The supported {@link vscode.DebugConfiguration.type Debug Configuration Types} that can handle
* LLDB launch requests.
*/
export const enum LaunchConfigType {
SWIFT_EXTENSION = "swift-lldb",
LLDB_DAP = "swift",
CODE_LLDB = "lldb",
}

Expand All @@ -40,10 +47,12 @@ export class DebugAdapter {
* @returns the type of launch configuration used by the given Swift toolchain version
*/
public static getLaunchConfigType(swiftVersion: Version): LaunchConfigType {
return swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0)) &&
configuration.debugger.useDebugAdapterFromToolchain
? LaunchConfigType.SWIFT_EXTENSION
: LaunchConfigType.CODE_LLDB;
const lldbDapIsAvailable = swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0));
if (lldbDapIsAvailable && configuration.debugger.debugAdapter === "lldb-dap") {
return LaunchConfigType.LLDB_DAP;
} else {
return LaunchConfigType.CODE_LLDB;
}
}

/**
Expand All @@ -60,7 +69,7 @@ export class DebugAdapter {

const debugAdapter = this.getLaunchConfigType(toolchain.swiftVersion);
switch (debugAdapter) {
case LaunchConfigType.SWIFT_EXTENSION:
case LaunchConfigType.LLDB_DAP:
return toolchain.getLLDBDebugAdapter();
case LaunchConfigType.CODE_LLDB:
return toolchain.getLLDB();
Expand Down
60 changes: 27 additions & 33 deletions src/debugger/debugAdapterFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
import * as vscode from "vscode";
import * as path from "path";
import { WorkspaceContext } from "../WorkspaceContext";
import { DebugAdapter, LaunchConfigType } from "./debugAdapter";
import { Version } from "../utilities/version";
import { DebugAdapter, LaunchConfigType, SWIFT_LAUNCH_CONFIG_TYPE } from "./debugAdapter";
import { registerLoggingDebugAdapterTracker } from "./logTracker";
import { SwiftToolchain } from "../toolchain/toolchain";
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
Expand All @@ -28,14 +27,7 @@ import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
* @returns A disposable to be disposed when the extension is deactivated
*/
export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Disposable {
let subscriptions: vscode.Disposable[] = [];
const register = async () => {
subscriptions.map(sub => sub.dispose());
subscriptions = [
registerLoggingDebugAdapterTracker(workspaceContext.toolchain.swiftVersion),
registerLLDBDebugAdapter(workspaceContext.toolchain, workspaceContext.outputChannel),
];

async function updateDebugAdapter() {
await workspaceContext.setLLDBVersion();

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

const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
register();
}
});
const subscriptions: vscode.Disposable[] = [
registerLoggingDebugAdapterTracker(),
registerLLDBDebugAdapter(workspaceContext.toolchain, workspaceContext.outputChannel),
vscode.workspace.onDidChangeConfiguration(event => {
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
updateDebugAdapter();
}
}),
];

// Perform the initial registration, then reregister every time the settings change.
register();
updateDebugAdapter();

return {
dispose: () => {
changeMonitor.dispose();
subscriptions.map(sub => sub.dispose());
},
};
Expand All @@ -78,13 +73,13 @@ function registerLLDBDebugAdapter(
outputChannel: SwiftOutputChannel
): vscode.Disposable {
const debugAdpaterFactory = vscode.debug.registerDebugAdapterDescriptorFactory(
LaunchConfigType.SWIFT_EXTENSION,
SWIFT_LAUNCH_CONFIG_TYPE,
new LLDBDebugAdapterExecutableFactory(toolchain, outputChannel)
);

const debugConfigProvider = vscode.debug.registerDebugConfigurationProvider(
LaunchConfigType.SWIFT_EXTENSION,
new LLDBDebugConfigurationProvider(process.platform, toolchain.swiftVersion)
SWIFT_LAUNCH_CONFIG_TYPE,
new LLDBDebugConfigurationProvider(process.platform, toolchain)
);

return {
Expand Down Expand Up @@ -140,14 +135,13 @@ export class LLDBDebugAdapterExecutableFactory implements vscode.DebugAdapterDes
export class LLDBDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
constructor(
private platform: NodeJS.Platform,
private swiftVersion: Version
private toolchain: SwiftToolchain
) {}

async resolveDebugConfiguration(
_folder: vscode.WorkspaceFolder | undefined,
launchConfig: vscode.DebugConfiguration
): Promise<vscode.DebugConfiguration> {
launchConfig.env = this.convertEnvironmentVariables(launchConfig.env);
): Promise<vscode.DebugConfiguration | undefined | null> {
// Fix the program path on Windows to include the ".exe" extension
if (
this.platform === "win32" &&
Expand All @@ -157,20 +151,20 @@ export class LLDBDebugConfigurationProvider implements vscode.DebugConfiguration
launchConfig.program += ".exe";
}

// Delegate to CodeLLDB if that's the debug adapter we have selected
if (DebugAdapter.getLaunchConfigType(this.swiftVersion) === LaunchConfigType.CODE_LLDB) {
launchConfig.type = LaunchConfigType.CODE_LLDB;
// Delegate to the appropriate debug adapter extension
launchConfig.type = DebugAdapter.getLaunchConfigType(this.toolchain.swiftVersion);
if (launchConfig.type === LaunchConfigType.CODE_LLDB) {
launchConfig.sourceLanguages = ["swift"];
} else if (launchConfig.type === LaunchConfigType.LLDB_DAP) {
if (launchConfig.env) {
launchConfig.env = this.convertEnvironmentVariables(launchConfig.env);
}
}

return launchConfig;
}

convertEnvironmentVariables(
map: { [key: string]: string } | undefined
): { [key: string]: string } | string[] | undefined {
if (map === undefined) {
return undefined;
}
private convertEnvironmentVariables(map: { [key: string]: string }): string[] {
return Object.entries(map).map(([key, value]) => `${key}=${value}`);
}
}
6 changes: 3 additions & 3 deletions src/debugger/launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import * as vscode from "vscode";
import { FolderContext } from "../FolderContext";
import { BuildFlags } from "../toolchain/BuildFlags";
import { stringArrayInEnglish, swiftLibraryPathKey, swiftRuntimeEnv } from "../utilities/utilities";
import { DebugAdapter } from "./debugAdapter";
import { SWIFT_LAUNCH_CONFIG_TYPE } from "./debugAdapter";
import { getFolderAndNameSuffix } from "./buildConfig";
import configuration from "../configuration";
import { CI_DISABLE_ASLR } from "./lldb";
Expand Down Expand Up @@ -136,7 +136,7 @@ function createExecutableConfigurations(ctx: FolderContext): vscode.DebugConfigu

return executableProducts.flatMap(product => {
const baseConfig = {
type: DebugAdapter.getLaunchConfigType(ctx.workspaceContext.swiftVersion),
type: SWIFT_LAUNCH_CONFIG_TYPE,
request: "launch",
args: [],
cwd: folder,
Expand Down Expand Up @@ -174,7 +174,7 @@ export function createSnippetConfiguration(
const buildDirectory = BuildFlags.buildDirectoryFromWorkspacePath(folder, true);

return {
type: DebugAdapter.getLaunchConfigType(ctx.workspaceContext.swiftVersion),
type: SWIFT_LAUNCH_CONFIG_TYPE,
request: "launch",
name: `Run ${snippetName}`,
program: path.posix.join(buildDirectory, "debug", snippetName),
Expand Down
30 changes: 10 additions & 20 deletions src/debugger/logTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
//===----------------------------------------------------------------------===//

import * as vscode from "vscode";
import { DebugAdapter } from "./debugAdapter";
import { Version } from "../utilities/version";
import { LaunchConfigType } from "./debugAdapter";
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";

/**
Expand Down Expand Up @@ -44,28 +43,19 @@ interface DebugMessage {
* Register the LoggingDebugAdapterTrackerFactory with the VS Code debug adapter tracker
* @returns A disposable to be disposed when the extension is deactivated
*/
export function registerLoggingDebugAdapterTracker(swiftVersion: Version): vscode.Disposable {
const register = () =>
vscode.debug.registerDebugAdapterTrackerFactory(
DebugAdapter.getLaunchConfigType(swiftVersion),
new LoggingDebugAdapterTrackerFactory()
);

// Maintains the disposable for the last registered debug adapter.
let debugAdapterDisposable = register();
const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
// Dispose the old adapter and reconfigure with the new settings.
debugAdapterDisposable.dispose();
debugAdapterDisposable = register();
}
});
export function registerLoggingDebugAdapterTracker(): vscode.Disposable {
// Register the factory for both lldb-dap and CodeLLDB since either could be used when
// resolving a Swift launch configuration.
const trackerFactory = new LoggingDebugAdapterTrackerFactory();
const subscriptions: vscode.Disposable[] = [
vscode.debug.registerDebugAdapterTrackerFactory(LaunchConfigType.CODE_LLDB, trackerFactory),
vscode.debug.registerDebugAdapterTrackerFactory(LaunchConfigType.LLDB_DAP, trackerFactory),
];

// Return a disposable that cleans everything up.
return {
dispose() {
changeMonitor.dispose();
debugAdapterDisposable.dispose();
subscriptions.forEach(sub => sub.dispose());
},
};
}
Expand Down
Loading