diff --git a/assets/test/.vscode/launch.json b/assets/test/.vscode/launch.json index 930f8660b..54fd2f838 100644 --- a/assets/test/.vscode/launch.json +++ b/assets/test/.vscode/launch.json @@ -1,7 +1,7 @@ { "configurations": [ { - "type": "lldb", + "type": "swift-lldb", "request": "launch", "sourceLanguages": [ "swift" @@ -13,7 +13,7 @@ "preLaunchTask": "swift: Build Debug PackageExe (defaultPackage)" }, { - "type": "lldb", + "type": "swift-lldb", "request": "launch", "sourceLanguages": [ "swift" diff --git a/assets/test/defaultPackage/.vscode/launch.json b/assets/test/defaultPackage/.vscode/launch.json index 9cc2754e9..6be4b9430 100644 --- a/assets/test/defaultPackage/.vscode/launch.json +++ b/assets/test/defaultPackage/.vscode/launch.json @@ -1,11 +1,8 @@ { "configurations": [ { - "type": "lldb", + "type": "swift-lldb", "request": "launch", - "sourceLanguages": [ - "swift" - ], "name": "Debug package1", "program": "${workspaceFolder:defaultPackage}/.build/debug/package1", "args": [], @@ -13,11 +10,8 @@ "preLaunchTask": "swift: Build Debug package1" }, { - "type": "lldb", + "type": "swift-lldb", "request": "launch", - "sourceLanguages": [ - "swift" - ], "name": "Release package1", "program": "${workspaceFolder:defaultPackage}/.build/release/package1", "args": [], diff --git a/assets/test/diagnostics/.vscode/launch.json b/assets/test/diagnostics/.vscode/launch.json index 3bdf85c50..aca14e524 100644 --- a/assets/test/diagnostics/.vscode/launch.json +++ b/assets/test/diagnostics/.vscode/launch.json @@ -1,11 +1,8 @@ { "configurations": [ { - "type": "lldb", + "type": "swift-lldb", "request": "launch", - "sourceLanguages": [ - "swift" - ], "args": [], "cwd": "${workspaceFolder:diagnostics}", "name": "Debug diagnostics", @@ -13,11 +10,8 @@ "preLaunchTask": "swift: Build Debug diagnostics" }, { - "type": "lldb", + "type": "swift-lldb", "request": "launch", - "sourceLanguages": [ - "swift" - ], "args": [], "cwd": "${workspaceFolder:diagnostics}", "name": "Release diagnostics", @@ -25,11 +19,8 @@ "preLaunchTask": "swift: Build Release diagnostics" }, { - "type": "lldb", + "type": "swift-lldb", "request": "launch", - "sourceLanguages": [ - "swift" - ], "args": [], "cwd": "${workspaceFolder:diagnostics}", "name": "Debug diagnostics", @@ -37,11 +28,8 @@ "preLaunchTask": "swift: Build Debug diagnostics" }, { - "type": "lldb", + "type": "swift-lldb", "request": "launch", - "sourceLanguages": [ - "swift" - ], "args": [], "cwd": "${workspaceFolder:diagnostics}", "name": "Release diagnostics", diff --git a/package.json b/package.json index e0e93cd07..8eac6b485 100644 --- a/package.json +++ b/package.json @@ -558,7 +558,7 @@ "swift.debugger.useDebugAdapterFromToolchain": { "type": "boolean", "default": false, - "markdownDescription": "Use lldb debug adapter packaged with Swift toolchain as your debug adapter. This is currently only available on Windows or platforms using Swift 6", + "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 }, "swift.debugger.path": { @@ -1075,12 +1075,12 @@ "array", "string" ], - "description": "Program arguments.", + "description": "Arguments to provide to the program.", "default": [] }, "cwd": { "type": "string", - "description": "Program working directory.", + "description": "The working directory that the program will be launched within.", "default": "${workspaceRoot}" }, "env": { @@ -1277,9 +1277,9 @@ "format": "prettier --check *.json src test", "pretest": "npm run compile && find ./assets/test -type d -name '.build' -exec rm -rf {} + && find . -type d -name 'Package.resolved' -exec rm -rf {} + && tsc -p ./", "test": "vscode-test", - "integration-test": "vscode-test --label integrationTests", - "unit-test": "vscode-test --label unitTests", - "coverage": "npm run compile-tests && vscode-test --coverage", + "integration-test": "npm test -- --label integrationTests", + "unit-test": "npm test -- --label unitTests", + "coverage": "npm test -- --coverage", "compile-tests": "find ./assets/test -type d -name '.build' -exec rm -rf {} + && npm run compile && npm run esbuild", "package": "vsce package", "dev-package": "vsce package --no-update-package-json 1.11.1-dev", diff --git a/src/WorkspaceContext.ts b/src/WorkspaceContext.ts index 2eeb1201f..f8dfc2442 100644 --- a/src/WorkspaceContext.ts +++ b/src/WorkspaceContext.ts @@ -104,24 +104,6 @@ export class WorkspaceContext implements vscode.Disposable { } }); } - // on change of swift debugger type - if ( - event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain") || - event.affectsConfiguration("swift.debugger.path") - ) { - if (configuration.debugger.useDebugAdapterFromToolchain) { - if (!(await DebugAdapter.verifyDebugAdapterExists(this))) { - return; - } - } - this.folders.forEach( - async ctx => - await makeDebugConfigurations( - ctx, - "Launch configurations need to be updated after changing the debug adapter." - ) - ); - } }); const backgroundCompilationOnDidSave = BackgroundCompilation.start(this); const contextKeysUpdate = this.observeFolders((folder, event) => { @@ -443,7 +425,7 @@ export class WorkspaceContext implements vscode.Disposable { /** find LLDB version and setup path in CodeLLDB */ async setLLDBVersion() { // check we are using CodeLLDB - if (DebugAdapter.adapterName !== "lldb") { + if (DebugAdapter.getDebugAdapterType(this.swiftVersion) !== "lldb-vscode") { return; } const libPathResult = await getLLDBLibPath(this.toolchain); diff --git a/src/configuration.ts b/src/configuration.ts index d52194848..f44051492 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -47,7 +47,7 @@ export interface LSPConfiguration { /** debugger configuration */ export interface DebuggerConfiguration { - /** Are we using debug adapter provided with Toolchain */ + /** Whether or not to use CodeLLDB for debugging instead of lldb-dap */ readonly useDebugAdapterFromToolchain: boolean; /** Return path to debug adapter */ readonly customDebugAdapterPath: string; @@ -151,8 +151,16 @@ const configuration = { /** debugger configuration */ get debugger(): DebuggerConfiguration { return { - /** Should we use the debug adapter included in the Toolchain or CodeLLDB */ 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. + 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("useDebugAdapterFromToolchain"); + return inspect?.workspaceValue ?? inspect?.globalValue ?? true; + } return vscode.workspace .getConfiguration("swift.debugger") .get("useDebugAdapterFromToolchain", false); diff --git a/src/debugger/buildConfig.ts b/src/debugger/buildConfig.ts index d5c48d576..d09e59293 100644 --- a/src/debugger/buildConfig.ts +++ b/src/debugger/buildConfig.ts @@ -427,7 +427,6 @@ export class TestingConfigurationFactory { return { type: DebugAdapter.adapterName, request: "custom", - sourceLanguages: ["swift"], name: `Test ${this.ctx.swiftPackage.name}`, targetCreateCommands: [`file -a ${arch} ${xctestPath}/xctest`], processCreateCommands: [ @@ -639,8 +638,10 @@ function getBaseConfig(ctx: FolderContext, expandEnvVariables: boolean) { export function getFolderAndNameSuffix( ctx: FolderContext, - expandEnvVariables = false + expandEnvVariables = false, + platform?: "posix" | "win32" ): { folder: string; nameSuffix: string } { + const nodePath = platform === "posix" ? path.posix : platform === "win32" ? path.win32 : path; const workspaceFolder = expandEnvVariables ? ctx.workspaceFolder.uri.fsPath : `\${workspaceFolder:${ctx.workspaceFolder.name}}`; @@ -650,7 +651,7 @@ export function getFolderAndNameSuffix( folder = workspaceFolder; nameSuffix = ""; } else { - folder = path.join(workspaceFolder, ctx.relativePath); + folder = nodePath.join(workspaceFolder, ctx.relativePath); nameSuffix = ` (${ctx.relativePath})`; } return { folder: folder, nameSuffix: nameSuffix }; diff --git a/src/debugger/debugAdapter.ts b/src/debugger/debugAdapter.ts index bc255ce15..f67fc7c4b 100644 --- a/src/debugger/debugAdapter.ts +++ b/src/debugger/debugAdapter.ts @@ -24,18 +24,17 @@ import { SwiftToolchain } from "../toolchain/toolchain"; * Class managing which debug adapter we are using. Will only setup lldb-vscode/lldb-dap if it is available. */ export class DebugAdapter { - private static debugAdapaterExists = false; - /** Debug adapter name */ public static get adapterName(): string { - return configuration.debugger.useDebugAdapterFromToolchain && this.debugAdapaterExists - ? "swift-lldb" - : "lldb"; + return "swift-lldb"; } /** Return debug adapter for toolchain */ - public static getDebugAdapter(swiftVersion: Version): "lldb-vscode" | "lldb-dap" { - return swiftVersion.isLessThan(new Version(6, 0, 0)) ? "lldb-vscode" : "lldb-dap"; + public static getDebugAdapterType(swiftVersion: Version): "lldb-vscode" | "lldb-dap" { + return swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0)) && + configuration.debugger.useDebugAdapterFromToolchain + ? "lldb-dap" + : "lldb-vscode"; } /** Return the path to the debug adapter */ @@ -45,7 +44,7 @@ export class DebugAdapter { return customDebugAdapterPath; } - const debugAdapter = this.getDebugAdapter(toolchain.swiftVersion); + const debugAdapter = this.getDebugAdapterType(toolchain.swiftVersion); if (process.platform === "darwin" && debugAdapter === "lldb-dap") { return await toolchain.getLLDBDebugAdapter(); } else { @@ -67,7 +66,7 @@ export class DebugAdapter { if (!(await fileExists(lldbDebugAdapterPath))) { if (!quiet) { - const debugAdapterName = this.getDebugAdapter(workspace.toolchain.swiftVersion); + const debugAdapterName = this.getDebugAdapterType(workspace.toolchain.swiftVersion); vscode.window.showErrorMessage( configuration.debugger.customDebugAdapterPath.length > 0 ? `Cannot find ${debugAdapterName} debug adapter specified in setting Swift.Debugger.Path.` @@ -75,12 +74,10 @@ export class DebugAdapter { ); } workspace.outputChannel.log(`Failed to find ${lldbDebugAdapterPath}`); - this.debugAdapaterExists = false; contextKeys.lldbVSCodeAvailable = false; return false; } - this.debugAdapaterExists = true; contextKeys.lldbVSCodeAvailable = true; return true; } diff --git a/src/debugger/debugAdapterFactory.ts b/src/debugger/debugAdapterFactory.ts index 7a8472a2a..d5ab3f415 100644 --- a/src/debugger/debugAdapterFactory.ts +++ b/src/debugger/debugAdapterFactory.ts @@ -13,25 +13,25 @@ //===----------------------------------------------------------------------===// import * as vscode from "vscode"; +import * as path from "path"; import { WorkspaceContext } from "../WorkspaceContext"; import { DebugAdapter } from "./debugAdapter"; +import { Version } from "../utilities/version"; export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vscode.Disposable { class LLDBDebugAdapterExecutableFactory implements vscode.DebugAdapterDescriptorFactory { - createDebugAdapterDescriptor( + async createDebugAdapterDescriptor( _session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined - ): vscode.ProviderResult { + ): Promise { if (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, [], {})); + const adapterPath = await DebugAdapter.debugAdapterPath(workspaceContext.toolchain); + await DebugAdapter.verifyDebugAdapterExists(workspaceContext); + return new vscode.DebugAdapterExecutable(adapterPath, [], {}); } } @@ -41,7 +41,10 @@ export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vs ); const debugConfigProvider = vscode.debug.registerDebugConfigurationProvider( "swift-lldb", - new LLDBDebugConfigurationProvider() + new LLDBDebugConfigurationProvider( + process.platform, + workspaceContext.toolchain.swiftVersion + ) ); return { dispose: () => { @@ -60,7 +63,12 @@ export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vs * This could also be used to augment the configuration with values from the settings * althought it isn't at the moment. */ -class LLDBDebugConfigurationProvider implements vscode.DebugConfigurationProvider { +export class LLDBDebugConfigurationProvider implements vscode.DebugConfigurationProvider { + constructor( + private platform: NodeJS.Platform, + private swiftVersion: Version + ) {} + async resolveDebugConfiguration( folder: vscode.WorkspaceFolder | undefined, launchConfig: vscode.DebugConfiguration, @@ -68,6 +76,15 @@ class LLDBDebugConfigurationProvider implements vscode.DebugConfigurationProvide cancellation?: vscode.CancellationToken ): Promise { launchConfig.env = this.convertEnvironmentVariables(launchConfig.env); + // Fix the program path on Windows to include the ".exe" extension + if (this.platform === "win32" && path.extname(launchConfig.program) !== ".exe") { + launchConfig.program += ".exe"; + } + // Delegate to CodeLLDB if that's the debug adapter we have selected + if (DebugAdapter.getDebugAdapterType(this.swiftVersion) === "lldb-vscode") { + launchConfig.type = "lldb"; + launchConfig.sourceLanguages = ["swift"]; + } return launchConfig; } diff --git a/src/debugger/launch.ts b/src/debugger/launch.ts index f6c3330b3..742a853c0 100644 --- a/src/debugger/launch.ts +++ b/src/debugger/launch.ts @@ -14,12 +14,12 @@ import * as path from "path"; import * as vscode from "vscode"; -import configuration from "../configuration"; import { FolderContext } from "../FolderContext"; import { BuildFlags } from "../toolchain/BuildFlags"; import { stringArrayInEnglish, swiftLibraryPathKey, swiftRuntimeEnv } from "../utilities/utilities"; import { DebugAdapter } from "./debugAdapter"; import { getFolderAndNameSuffix } from "./buildConfig"; +import configuration from "../configuration"; /** * Edit launch.json based on contents of Swift Package. @@ -102,7 +102,7 @@ export function getLaunchConfiguration( target: string, folderCtx: FolderContext ): vscode.DebugConfiguration | undefined { - const wsLaunchSection = vscode.workspace.getConfiguration("launch", folderCtx.folder); + const wsLaunchSection = vscode.workspace.getConfiguration("launch", folderCtx.workspaceFolder); const launchConfigs = wsLaunchSection.get("configurations") || []; const { folder } = getFolderAndNameSuffix(folderCtx); const buildDirectory = BuildFlags.buildDirectoryFromWorkspacePath(folder, true); @@ -114,14 +114,12 @@ export function getLaunchConfiguration( // Return array of DebugConfigurations for executables based on what is in Package.swift function createExecutableConfigurations(ctx: FolderContext): vscode.DebugConfiguration[] { const executableProducts = ctx.swiftPackage.executableProducts; - const { folder, nameSuffix } = getFolderAndNameSuffix(ctx); - const buildDirectory = BuildFlags.buildDirectoryFromWorkspacePath(folder, true); - const binaryExtension = process.platform === "win32" ? ".exe" : ""; + const { folder, nameSuffix } = getFolderAndNameSuffix(ctx, undefined, "posix"); + const buildDirectory = BuildFlags.buildDirectoryFromWorkspacePath(folder, true, "posix"); return executableProducts.flatMap(product => { const baseConfig = { type: DebugAdapter.adapterName, request: "launch", - sourceLanguages: ["swift"], args: [], cwd: folder, env: swiftRuntimeEnv(true), @@ -130,13 +128,13 @@ function createExecutableConfigurations(ctx: FolderContext): vscode.DebugConfigu { ...baseConfig, name: `Debug ${product.name}${nameSuffix}`, - program: path.join(buildDirectory, "debug", product.name + binaryExtension), + program: path.posix.join(buildDirectory, "debug", product.name), preLaunchTask: `swift: Build Debug ${product.name}${nameSuffix}`, }, { ...baseConfig, name: `Release ${product.name}${nameSuffix}`, - program: path.join(buildDirectory, "release", product.name + binaryExtension), + program: path.posix.join(buildDirectory, "release", product.name), preLaunchTask: `swift: Build Release ${product.name}${nameSuffix}`, }, ]; @@ -159,9 +157,8 @@ export function createSnippetConfiguration( return { type: DebugAdapter.adapterName, request: "launch", - sourceLanguages: ["swift"], name: `Run ${snippetName}`, - program: path.join(buildDirectory, "debug", snippetName), + program: path.posix.join(buildDirectory, "debug", snippetName), args: [], cwd: folder, env: swiftRuntimeEnv(true), diff --git a/src/toolchain/BuildFlags.ts b/src/toolchain/BuildFlags.ts index a6ca65e05..7b06ff6a2 100644 --- a/src/toolchain/BuildFlags.ts +++ b/src/toolchain/BuildFlags.ts @@ -98,10 +98,16 @@ export class BuildFlags { * Get build path from configuration if exists or return a fallback .build directory in given workspace * @param filesystem path to workspace that will be used as a fallback loacation with .build directory */ - static buildDirectoryFromWorkspacePath(workspacePath: string, absolute = false): string { + static buildDirectoryFromWorkspacePath( + workspacePath: string, + absolute = false, + platform?: "posix" | "win32" + ): string { + const nodePath = + platform === "posix" ? path.posix : platform === "win32" ? path.win32 : path; const buildPath = configuration.buildPath.length > 0 ? configuration.buildPath : ".build"; - if (!path.isAbsolute(buildPath) && absolute) { - return path.join(workspacePath, buildPath); + if (!nodePath.isAbsolute(buildPath) && absolute) { + return nodePath.join(workspacePath, buildPath); } else { return buildPath; } diff --git a/src/toolchain/toolchain.ts b/src/toolchain/toolchain.ts index f37728f5a..6811a97d4 100644 --- a/src/toolchain/toolchain.ts +++ b/src/toolchain/toolchain.ts @@ -336,10 +336,10 @@ export class SwiftToolchain { /** * Return fullpath for toolchain executable */ - public getToolchainExecutable(exe: string): string { + public getToolchainExecutable(executable: string): string { // should we add `.exe` at the end of the executable name - const windowsExeSuffix = process.platform === "win32" ? ".exe" : ""; - return `${this.toolchainPath}/bin/${exe}${windowsExeSuffix}`; + const executableSuffix = process.platform === "win32" ? ".exe" : ""; + return path.join(this.toolchainPath, "bin", executable + executableSuffix); } private static getXcodeDirectory(toolchainPath: string): string | undefined { diff --git a/test/unit-tests/debugger/debugAdapterFactory.test.ts b/test/unit-tests/debugger/debugAdapterFactory.test.ts new file mode 100644 index 000000000..0f810a2c1 --- /dev/null +++ b/test/unit-tests/debugger/debugAdapterFactory.test.ts @@ -0,0 +1,119 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the VS Code Swift open source project +// +// Copyright (c) 2024 the VS Code Swift project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of VS Code Swift project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import * as assert from "assert"; +import { DebugAdapter } from "../../../src/debugger/debugAdapter"; +import { LLDBDebugConfigurationProvider } from "../../../src/debugger/debugAdapterFactory"; +import { Version } from "../../../src/utilities/version"; +import { mockNamespace } from "../MockUtils"; +import configuration from "../../../src/configuration"; +import { when } from "ts-mockito"; + +suite("Debug Adapter Factory Test Suite", () => { + const swift6 = new Version(6, 0, 0); + const swift510 = new Version(5, 10, 1); + const mockDebugConfig = mockNamespace(configuration, "debugger"); + + suite("LLDBDebugConfigurationProvider Test Suite", () => { + setup(() => { + when(mockDebugConfig.useDebugAdapterFromToolchain).thenReturn(true); + }); + + test("uses lldb-dap for swift versions >=6.0.0", async () => { + const configProvider = new LLDBDebugConfigurationProvider("darwin", swift6); + const launchConfig = await configProvider.resolveDebugConfiguration(undefined, { + name: "Test Launch Config", + type: DebugAdapter.adapterName, + request: "launch", + program: "${workspaceFolder}/.build/debug/executable", + }); + assert.strictEqual(launchConfig.type, DebugAdapter.adapterName); + }); + + test("delegates to CodeLLDB for swift versions <6.0.0", async () => { + const configProvider = new LLDBDebugConfigurationProvider("darwin", swift510); + const launchConfig = await configProvider.resolveDebugConfiguration(undefined, { + name: "Test Launch Config", + type: DebugAdapter.adapterName, + request: "launch", + program: "${workspaceFolder}/.build/debug/executable", + }); + assert.strictEqual(launchConfig.type, "lldb"); + assert.deepStrictEqual(launchConfig.sourceLanguages, ["swift"]); + }); + + test("delegates to CodeLLDB on Swift 6.0.0 if setting swift.debugger.useDebugAdapterFromToolchain is explicitly disabled", async () => { + when(mockDebugConfig.useDebugAdapterFromToolchain).thenReturn(false); + const configProvider = new LLDBDebugConfigurationProvider("darwin", swift6); + const launchConfig = await configProvider.resolveDebugConfiguration(undefined, { + name: "Test Launch Config", + type: DebugAdapter.adapterName, + request: "launch", + program: "${workspaceFolder}/.build/debug/executable", + }); + assert.strictEqual(launchConfig.type, "lldb"); + assert.deepStrictEqual(launchConfig.sourceLanguages, ["swift"]); + }); + + test("modifies program to add file extension on Windows", async () => { + const configProvider = new LLDBDebugConfigurationProvider("win32", swift6); + const launchConfig = await configProvider.resolveDebugConfiguration(undefined, { + name: "Test Launch Config", + type: DebugAdapter.adapterName, + request: "launch", + program: "${workspaceFolder}/.build/debug/executable", + }); + assert.strictEqual( + launchConfig.program, + "${workspaceFolder}/.build/debug/executable.exe" + ); + }); + + test("does not modify program on Windows if file extension is already present", async () => { + const configProvider = new LLDBDebugConfigurationProvider("win32", swift6); + const launchConfig = await configProvider.resolveDebugConfiguration(undefined, { + name: "Test Launch Config", + type: DebugAdapter.adapterName, + request: "launch", + program: "${workspaceFolder}/.build/debug/executable.exe", + }); + assert.strictEqual( + launchConfig.program, + "${workspaceFolder}/.build/debug/executable.exe" + ); + }); + + test("does not modify program on macOS", async () => { + const configProvider = new LLDBDebugConfigurationProvider("darwin", swift6); + const launchConfig = await configProvider.resolveDebugConfiguration(undefined, { + name: "Test Launch Config", + type: DebugAdapter.adapterName, + request: "launch", + program: "${workspaceFolder}/.build/debug/executable", + }); + assert.strictEqual(launchConfig.program, "${workspaceFolder}/.build/debug/executable"); + }); + + test("does not modify program on Linux", async () => { + const configProvider = new LLDBDebugConfigurationProvider("linux", swift6); + const launchConfig = await configProvider.resolveDebugConfiguration(undefined, { + name: "Test Launch Config", + type: DebugAdapter.adapterName, + request: "launch", + program: "${workspaceFolder}/.build/debug/executable", + }); + assert.strictEqual(launchConfig.program, "${workspaceFolder}/.build/debug/executable"); + }); + }); +});