diff --git a/src/configuration.ts b/src/configuration.ts index bad3a282e..5a7dc39ce 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -50,7 +50,7 @@ export interface DebuggerConfiguration { /** Are we using debug adapter provided with Toolchain */ readonly useDebugAdapterFromToolchain: boolean; /** Return path to debug adapter */ - readonly debugAdapterPath: string; + readonly customDebugAdapterPath: string; } /** workspace folder configuration */ @@ -149,7 +149,7 @@ const configuration = { .getConfiguration("swift.debugger") .get("useDebugAdapterFromToolchain", false); }, - get debugAdapterPath(): string { + get customDebugAdapterPath(): string { return vscode.workspace.getConfiguration("swift.debugger").get("path", ""); }, }; diff --git a/src/debugger/debugAdapter.ts b/src/debugger/debugAdapter.ts index c2c565cde..bc255ce15 100644 --- a/src/debugger/debugAdapter.ts +++ b/src/debugger/debugAdapter.ts @@ -18,6 +18,7 @@ import contextKeys from "../contextKeys"; import { fileExists } from "../utilities/filesystem"; import { Version } from "../utilities/version"; import { WorkspaceContext } from "../WorkspaceContext"; +import { SwiftToolchain } from "../toolchain/toolchain"; /** * Class managing which debug adapter we are using. Will only setup lldb-vscode/lldb-dap if it is available. @@ -33,10 +34,25 @@ export class DebugAdapter { } /** Return debug adapter for toolchain */ - public static getDebugAdapter(swiftVersion: Version): string { + public static getDebugAdapter(swiftVersion: Version): "lldb-vscode" | "lldb-dap" { return swiftVersion.isLessThan(new Version(6, 0, 0)) ? "lldb-vscode" : "lldb-dap"; } + /** Return the path to the debug adapter */ + public static async debugAdapterPath(toolchain: SwiftToolchain): Promise { + const customDebugAdapterPath = configuration.debugger.customDebugAdapterPath; + if (customDebugAdapterPath.length > 0) { + return customDebugAdapterPath; + } + + const debugAdapter = this.getDebugAdapter(toolchain.swiftVersion); + if (process.platform === "darwin" && debugAdapter === "lldb-dap") { + return await toolchain.getLLDBDebugAdapter(); + } else { + return toolchain.getToolchainExecutable(debugAdapter); + } + } + /** * Verify that the toolchain debug adapter exists * @param workspace WorkspaceContext @@ -47,18 +63,15 @@ export class DebugAdapter { workspace: WorkspaceContext, quiet = false ): Promise { - const useCustom = configuration.debugger.debugAdapterPath.length > 0; - const debugAdapter = DebugAdapter.getDebugAdapter(workspace.toolchain.swiftVersion); - const lldbDebugAdapterPath = useCustom - ? configuration.debugger.debugAdapterPath - : workspace.toolchain.getToolchainExecutable(debugAdapter); + const lldbDebugAdapterPath = await this.debugAdapterPath(workspace.toolchain); if (!(await fileExists(lldbDebugAdapterPath))) { if (!quiet) { + const debugAdapterName = this.getDebugAdapter(workspace.toolchain.swiftVersion); vscode.window.showErrorMessage( - useCustom - ? `Cannot find ${debugAdapter} debug adapter specified in setting Swift.Debugger.Path.` - : `Cannot find ${debugAdapter} debug adapter in your Swift toolchain.` + configuration.debugger.customDebugAdapterPath.length > 0 + ? `Cannot find ${debugAdapterName} debug adapter specified in setting Swift.Debugger.Path.` + : `Cannot find ${debugAdapterName} debug adapter in your Swift toolchain.` ); } workspace.outputChannel.log(`Failed to find ${lldbDebugAdapterPath}`); diff --git a/src/debugger/debugAdapterFactory.ts b/src/debugger/debugAdapterFactory.ts index 48a9d2e29..7a8472a2a 100644 --- a/src/debugger/debugAdapterFactory.ts +++ b/src/debugger/debugAdapterFactory.ts @@ -14,7 +14,6 @@ import * as vscode from "vscode"; import { WorkspaceContext } from "../WorkspaceContext"; -import configuration from "../configuration"; import { DebugAdapter } from "./debugAdapter"; export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vscode.Disposable { @@ -23,23 +22,16 @@ export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vs _session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined ): vscode.ProviderResult { - // use the executable specified in the settings or use version in toolchain - const debugAdapter = DebugAdapter.getDebugAdapter( - workspaceContext.toolchain.swiftVersion - ); - if (!executable) { - const lldbDebugAdapterPath = - configuration.debugger.debugAdapterPath.length > 0 - ? configuration.debugger.debugAdapterPath - : workspaceContext.toolchain.getToolchainExecutable(debugAdapter); - DebugAdapter.verifyDebugAdapterExists(workspaceContext).then(() => { - /** Ignore */ - }); - executable = new vscode.DebugAdapterExecutable(lldbDebugAdapterPath, [], {}); + if (executable) { + // make VS Code launch the debug adapter executable + return executable; } - // make VS Code launch the debug adapter executable - return executable; + return DebugAdapter.debugAdapterPath(workspaceContext.toolchain) + .then(path => + DebugAdapter.verifyDebugAdapterExists(workspaceContext).then(() => path) + ) + .then(path => new vscode.DebugAdapterExecutable(path, [], {})); } } diff --git a/src/toolchain/toolchain.ts b/src/toolchain/toolchain.ts index 81fecc0ad..f37728f5a 100644 --- a/src/toolchain/toolchain.ts +++ b/src/toolchain/toolchain.ts @@ -354,28 +354,57 @@ export class SwiftToolchain { } /** - * Cannot use `getToolchainExecutable` to get the LLDB executable as LLDB - * is not in macOS toolchain path + * Returns the path to the LLDB executable inside the selected toolchain. + * If the user is on macOS and has no OSS toolchain selected, also search + * inside Xcode. + * @returns The path to the `lldb` executable + * @throws Throws an error if the executable cannot be found */ public async getLLDB(): Promise { - let lldbPath = path.join( + return this.findToolchainOrXcodeExecutable("lldb"); + } + + /** + * Returns the path to the LLDB debug adapter executable inside the selected + * toolchain. If the user is on macOS and has no OSS toolchain selected, also + * search inside Xcode. + * @returns The path to the `lldb-dap` executable + * @throws Throws an error if the executable cannot be found + */ + public async getLLDBDebugAdapter(): Promise { + return this.findToolchainOrXcodeExecutable("lldb-dap"); + } + + /** + * Search for the supplied executable in the toolchain. + * If the user is on macOS and has no OSS toolchain selected, also + * search inside Xcode. + */ + private async findToolchainOrXcodeExecutable(executable: string): Promise { + const toolchainExecutablePath = path.join( this.swiftFolderPath, - process.platform === "win32" ? "lldb.exe" : "lldb" + process.platform === "win32" ? `${executable}.exe` : executable ); - if (!(await pathExists(lldbPath))) { - if (process.platform !== "darwin") { - throw new Error("Failed to find LLDB in swift toolchain"); - } - const xcodeDirectory = SwiftToolchain.getXcodeDirectory(this.swiftFolderPath); - if (!xcodeDirectory) { - throw new Error("Failed to find LLDB in swift toolchain"); - } - const { stdout } = await execFile("xcrun", ["-find", "lldb"], { - env: { ...process.env, DEVELOPER_DIR: xcodeDirectory }, - }); - lldbPath = stdout.trimEnd(); + + if (await pathExists(toolchainExecutablePath)) { + return toolchainExecutablePath; } - return lldbPath; + + if (process.platform !== "darwin") { + throw new Error(`Failed to find ${executable} in swift toolchain`); + } + return this.findXcodeExecutable(executable); + } + + private async findXcodeExecutable(executable: string): Promise { + const xcodeDirectory = SwiftToolchain.getXcodeDirectory(this.toolchainPath); + if (!xcodeDirectory) { + throw new Error(`Failed to find ${executable} in Swift toolchain`); + } + const { stdout } = await execFile("xcrun", ["-find", executable], { + env: { ...process.env, DEVELOPER_DIR: xcodeDirectory }, + }); + return stdout.trimEnd(); } private basePlatformDeveloperPath(): string | undefined {