diff --git a/src/parser.ts b/src/parser.ts index 39495209..f6f46367 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1624,6 +1624,12 @@ export async function parseLaunchConfigurations( ); } } else { + // If the binary has no extension, append .exe + // This ensures VS Code can properly link to the file in the output + // Note: No platform check needed here - this entire block is Windows-specific (see line 1553) + if (!path.extname(compilerTargetBinary)) { + compilerTargetBinary += ".exe"; + } logger.message( localize( "producing.target.binary", @@ -1707,6 +1713,15 @@ export async function parseLaunchConfigurations( linkerTool.arguments, ["out", "o"] ); + // On Windows, if the binary has no extension, append .exe + // This ensures VS Code can properly link to the file in the output + if ( + linkerTargetBinary && + process.platform === "win32" && + !path.extname(linkerTargetBinary) + ) { + linkerTargetBinary += ".exe"; + } logger.message( localize( "found.linker.command", diff --git a/src/test/fakeSuite/extension.test.ts b/src/test/fakeSuite/extension.test.ts index 5dfcb038..ef89c305 100644 --- a/src/test/fakeSuite/extension.test.ts +++ b/src/test/fakeSuite/extension.test.ts @@ -130,6 +130,49 @@ suite("Unit testing replacing characters in and outside of quotes", () => { }); }); +suite("Unit testing binary path extension handling", () => { + // Tests for the logic that appends .exe extension on Windows + // when binary paths have no extension (fixes issue #698) + + test("path.extname returns empty string for paths without extension", () => { + expect(path.extname("app")).to.be.equal(""); + expect(path.extname("/path/to/app")).to.be.equal(""); + expect(path.extname("C:\\path\\to\\app")).to.be.equal(""); + }); + + test("path.extname returns extension for paths with extension", () => { + expect(path.extname("app.exe")).to.be.equal(".exe"); + expect(path.extname("/path/to/app.exe")).to.be.equal(".exe"); + expect(path.extname("C:\\path\\to\\app.exe")).to.be.equal(".exe"); + expect(path.extname("lib.dll")).to.be.equal(".dll"); + expect(path.extname("file.out")).to.be.equal(".out"); + }); + + test("Appending .exe only when no extension present", () => { + // Simulates the logic in parser.ts for appending .exe on Windows + const appendExeIfNoExtension = (binaryPath: string): string => { + if (!path.extname(binaryPath)) { + return binaryPath + ".exe"; + } + return binaryPath; + }; + + // Paths without extension should get .exe appended + expect(appendExeIfNoExtension("app")).to.be.equal("app.exe"); + expect(appendExeIfNoExtension("/path/to/app")).to.be.equal( + "/path/to/app.exe" + ); + expect(appendExeIfNoExtension("C:\\path\\to\\app")).to.be.equal( + "C:\\path\\to\\app.exe" + ); + + // Paths with extension should remain unchanged + expect(appendExeIfNoExtension("app.exe")).to.be.equal("app.exe"); + expect(appendExeIfNoExtension("lib.dll")).to.be.equal("lib.dll"); + expect(appendExeIfNoExtension("file.out")).to.be.equal("file.out"); + }); +}); + // TODO: refactor initialization and cleanup of each test suite("Fake dryrun parsing", () => { suiteSetup(async function (this: Mocha.Context) {