diff --git a/src/parser.ts b/src/parser.ts index 39495209..0e33fcba 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -385,9 +385,9 @@ async function parseLineAsTool( // - any spaces/tabs before the tool invocation // - with or without path (relative -to the makefile location- or full) // - with or without extension (windows only) - // - with or without quotes + // - with or without quotes (single or double, to support mingw-make) // - must have at least one space or tab after the tool invocation - let regexpStr: string = '^[\\s\\"]*(.*?)('; + let regexpStr: string = "^[\\s\\\"']*(.*?)("; if (process.platform === "win32") { regexpStr += versionedToolNames.join("\\.exe|"); @@ -399,7 +399,7 @@ async function parseLineAsTool( regexpStr += "|"; } - regexpStr += versionedToolNames.join("|") + ')(\\s|\\"\\s)(.*)$'; + regexpStr += versionedToolNames.join("|") + ")(\\s|[\\\"']\\s)(.*)$"; let regexp: RegExp = RegExp(regexpStr, "mg"); let match: RegExpExecArray | null = regexp.exec(line); diff --git a/src/test/fakeSuite/extension.test.ts b/src/test/fakeSuite/extension.test.ts index 5dfcb038..0f4044aa 100644 --- a/src/test/fakeSuite/extension.test.ts +++ b/src/test/fakeSuite/extension.test.ts @@ -130,6 +130,112 @@ suite("Unit testing replacing characters in and outside of quotes", () => { }); }); +// Unit test for the regex pattern used in parseLineAsTool to support +// both single and double quotes in tool invocations (issue #739: mingw-make support) +suite("Unit testing tool invocation regex pattern for single and double quotes", () => { + suiteSetup(async function (this: Mocha.Context) { + this.timeout(100000); + }); + + setup(async function (this: Mocha.Context) { + this.timeout(100000); + }); + + // Helper to build the regex pattern similar to parseLineAsTool in parser.ts + function buildToolInvocationRegex(toolNames: string[]): RegExp { + const prefixRegex = "(([a-zA-Z0-9-_.]*-)*"; + const suffixRegex = "(-[a-zA-Z0-9-_.]*)*)"; + const versionedToolNames = toolNames.map( + (t) => `${prefixRegex}${t}${suffixRegex}` + ); + + // Pattern from parser.ts that supports both single and double quotes + let regexpStr = "^[\\s\\\"']*(.*?)("; + regexpStr += versionedToolNames.join("|") + ")(\\s|[\\\"']\\s)(.*)$"; + + return new RegExp(regexpStr, "mg"); + } + + test("Test tool invocation with double quotes (original behavior)", () => { + const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]); + const testCases = [ + { input: 'gcc -c main.c', shouldMatch: true }, + { input: '"gcc" -c main.c', shouldMatch: true }, + { input: '"/usr/bin/gcc" -c main.c', shouldMatch: true }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match !== null).to.equal( + testCase.shouldMatch, + `Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}` + ); + } + }); + + test("Test tool invocation with single quotes (mingw-make style)", () => { + const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]); + const testCases = [ + { input: "'gcc' -c main.c", shouldMatch: true }, + { input: "'/usr/bin/gcc' -c main.c", shouldMatch: true }, + { input: "'clang' -c main.cpp", shouldMatch: true }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match !== null).to.equal( + testCase.shouldMatch, + `Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}` + ); + } + }); + + test("Test tool invocation without quotes", () => { + const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]); + const testCases = [ + { input: "gcc -c main.c", shouldMatch: true }, + { input: "/usr/bin/gcc -c main.c", shouldMatch: true }, + { input: "clang -c main.cpp", shouldMatch: true }, + { input: "cc -c test.c", shouldMatch: true }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match !== null).to.equal( + testCase.shouldMatch, + `Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}` + ); + } + }); + + test("Test tool path extraction with various quote styles", () => { + const regex = buildToolInvocationRegex(["gcc"]); + const testCases = [ + { input: "/usr/bin/gcc -c main.c", expectedPath: "/usr/bin/" }, + { input: '"/usr/bin/gcc" -c main.c', expectedPath: "/usr/bin/" }, + { input: "'/usr/bin/gcc' -c main.c", expectedPath: "/usr/bin/" }, + { input: "gcc -c main.c", expectedPath: "" }, + { input: "'gcc' -c main.c", expectedPath: "" }, + { input: '"gcc" -c main.c', expectedPath: "" }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match).to.not.be.null; + if (match) { + expect(match[1]).to.equal( + testCase.expectedPath, + `Expected path "${testCase.expectedPath}" for input "${testCase.input}", got "${match[1]}"` + ); + } + } + }); +}); + // TODO: refactor initialization and cleanup of each test suite("Fake dryrun parsing", () => { suiteSetup(async function (this: Mocha.Context) {