From f80cde91da58ddbd6e2feca214bf3d717f19a5ba Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Fri, 19 Jun 2026 16:47:09 -0700 Subject: [PATCH 1/3] add platform override properties --- Extension/.scripts/generateOptionsSchema.ts | 14 +- Extension/package.json | 269 +++++++++++++++++++- Extension/tools/TaskDefinitionsSchema.json | 118 +++++++++ 3 files changed, 398 insertions(+), 3 deletions(-) create mode 100644 Extension/tools/TaskDefinitionsSchema.json diff --git a/Extension/.scripts/generateOptionsSchema.ts b/Extension/.scripts/generateOptionsSchema.ts index 346b21007..237945ed3 100644 --- a/Extension/.scripts/generateOptionsSchema.ts +++ b/Extension/.scripts/generateOptionsSchema.ts @@ -85,8 +85,13 @@ function replaceReferences(definitions: any, objects: any): any { objects[key].anyOf = replaceReferences(definitions, objects[key].anyOf); } - // Recursively replace references if this object has properties. - if (objects[key].hasOwnProperty('type') && objects[key].type === 'object' && objects[key].properties !== null) { + // Handle 'oneOf' with references + if (objects[key].hasOwnProperty('oneOf')) { + objects[key].oneOf = replaceReferences(definitions, objects[key].oneOf); + } + + // Recursively replace references if this schema node has properties. + if (objects[key].hasOwnProperty('properties') && objects[key].properties !== null) { objects[key].properties = replaceReferences(definitions, objects[key].properties); objects[key].properties = updateDefaults(objects[key].properties, objects[key].default); } @@ -117,11 +122,13 @@ function mergeReferences(baseDefinitions: any, additionalDefinitions: any): void export async function main() { const packageJSON: any = JSON.parse(await read(resolve($root, 'package.json'))); const schemaJSON: any = JSON.parse(await read(resolve($root, 'tools/OptionsSchema.json'))); + const taskDefinitionsJSON: any = JSON.parse(await read(resolve($root, 'tools/TaskDefinitionsSchema.json'))); const symbolSettingsJSON: any = JSON.parse(await read(resolve($root, 'tools/VSSymbolSettings.json'))); mergeReferences(schemaJSON.definitions, symbolSettingsJSON.definitions); schemaJSON.definitions = replaceReferences(schemaJSON.definitions, schemaJSON.definitions); + taskDefinitionsJSON.definitions = replaceReferences(taskDefinitionsJSON.definitions, taskDefinitionsJSON.definitions); // Hard Code adding in configurationAttributes launch and attach. // cppdbg @@ -132,6 +139,9 @@ export async function main() { packageJSON.contributes.debuggers[1].configurationAttributes.launch = schemaJSON.definitions.CppvsdbgLaunchOptions; packageJSON.contributes.debuggers[1].configurationAttributes.attach = schemaJSON.definitions.CppvsdbgAttachOptions; + // task definitions + packageJSON.contributes.taskDefinitions = [taskDefinitionsJSON.definitions.CppBuildTaskDefinition]; + let content: string = JSON.stringify(packageJSON, null, 4); // We use '\u200b' (unicode zero-length space character) to break VS Code's URL detection regex for URLs that are examples. This process will diff --git a/Extension/package.json b/Extension/package.json index f6761096f..d9ecbe3a1 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -532,6 +532,273 @@ "detail": { "type": "string", "description": "%c_cpp.taskDefinitions.detail.description%" + }, + "windows": { + "type": "object", + "properties": { + "command": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "value", + "quoting" + ], + "properties": { + "value": { + "type": "string", + "description": "%c_cpp.taskDefinitions.args.value.description%" + }, + "quoting": { + "type": "string", + "enum": [ + "escape", + "strong", + "weak" + ], + "enumDescriptions": [ + "%c_cpp.taskDefinitions.args.quoting.escape.description%", + "%c_cpp.taskDefinitions.args.quoting.strong.description%", + "%c_cpp.taskDefinitions.args.quoting.weak.description%" + ], + "default": "strong", + "description": "%c_cpp.taskDefinitions.args.quoting.description%" + } + } + } + ] + }, + "args": { + "type": "array", + "description": "%c_cpp.taskDefinitions.args.description%", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "value", + "quoting" + ], + "properties": { + "value": { + "type": "string", + "description": "%c_cpp.taskDefinitions.args.value.description%" + }, + "quoting": { + "type": "string", + "enum": [ + "escape", + "strong", + "weak" + ], + "enumDescriptions": [ + "%c_cpp.taskDefinitions.args.quoting.escape.description%", + "%c_cpp.taskDefinitions.args.quoting.strong.description%", + "%c_cpp.taskDefinitions.args.quoting.weak.description%" + ], + "default": "strong", + "description": "%c_cpp.taskDefinitions.args.quoting.description%" + } + } + } + ] + } + }, + "options": { + "type": "object", + "description": "%c_cpp.taskDefinitions.options.description%", + "properties": { + "cwd": { + "type": "string", + "description": "%c_cpp.taskDefinitions.options.cwd.description%" + } + } + } + } + }, + "linux": { + "type": "object", + "properties": { + "command": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "value", + "quoting" + ], + "properties": { + "value": { + "type": "string", + "description": "%c_cpp.taskDefinitions.args.value.description%" + }, + "quoting": { + "type": "string", + "enum": [ + "escape", + "strong", + "weak" + ], + "enumDescriptions": [ + "%c_cpp.taskDefinitions.args.quoting.escape.description%", + "%c_cpp.taskDefinitions.args.quoting.strong.description%", + "%c_cpp.taskDefinitions.args.quoting.weak.description%" + ], + "default": "strong", + "description": "%c_cpp.taskDefinitions.args.quoting.description%" + } + } + } + ] + }, + "args": { + "type": "array", + "description": "%c_cpp.taskDefinitions.args.description%", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "value", + "quoting" + ], + "properties": { + "value": { + "type": "string", + "description": "%c_cpp.taskDefinitions.args.value.description%" + }, + "quoting": { + "type": "string", + "enum": [ + "escape", + "strong", + "weak" + ], + "enumDescriptions": [ + "%c_cpp.taskDefinitions.args.quoting.escape.description%", + "%c_cpp.taskDefinitions.args.quoting.strong.description%", + "%c_cpp.taskDefinitions.args.quoting.weak.description%" + ], + "default": "strong", + "description": "%c_cpp.taskDefinitions.args.quoting.description%" + } + } + } + ] + } + }, + "options": { + "type": "object", + "description": "%c_cpp.taskDefinitions.options.description%", + "properties": { + "cwd": { + "type": "string", + "description": "%c_cpp.taskDefinitions.options.cwd.description%" + } + } + } + } + }, + "osx": { + "type": "object", + "properties": { + "command": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "value", + "quoting" + ], + "properties": { + "value": { + "type": "string", + "description": "%c_cpp.taskDefinitions.args.value.description%" + }, + "quoting": { + "type": "string", + "enum": [ + "escape", + "strong", + "weak" + ], + "enumDescriptions": [ + "%c_cpp.taskDefinitions.args.quoting.escape.description%", + "%c_cpp.taskDefinitions.args.quoting.strong.description%", + "%c_cpp.taskDefinitions.args.quoting.weak.description%" + ], + "default": "strong", + "description": "%c_cpp.taskDefinitions.args.quoting.description%" + } + } + } + ] + }, + "args": { + "type": "array", + "description": "%c_cpp.taskDefinitions.args.description%", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "value", + "quoting" + ], + "properties": { + "value": { + "type": "string", + "description": "%c_cpp.taskDefinitions.args.value.description%" + }, + "quoting": { + "type": "string", + "enum": [ + "escape", + "strong", + "weak" + ], + "enumDescriptions": [ + "%c_cpp.taskDefinitions.args.quoting.escape.description%", + "%c_cpp.taskDefinitions.args.quoting.strong.description%", + "%c_cpp.taskDefinitions.args.quoting.weak.description%" + ], + "default": "strong", + "description": "%c_cpp.taskDefinitions.args.quoting.description%" + } + } + } + ] + } + }, + "options": { + "type": "object", + "description": "%c_cpp.taskDefinitions.options.description%", + "properties": { + "cwd": { + "type": "string", + "description": "%c_cpp.taskDefinitions.options.cwd.description%" + } + } + } + } } } } @@ -6944,4 +7211,4 @@ "uuid": "^11.1.1", "undici": "^7.28.0" } -} \ No newline at end of file +} diff --git a/Extension/tools/TaskDefinitionsSchema.json b/Extension/tools/TaskDefinitionsSchema.json new file mode 100644 index 000000000..ec67c7db7 --- /dev/null +++ b/Extension/tools/TaskDefinitionsSchema.json @@ -0,0 +1,118 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VS Code task definitions", + "description": "A json schema for VS Code task definitions contributed by the C/C++ extension", + "type": "object", + "definitions": { + "TaskStringWithQuoting": { + "type": "object", + "required": [ + "value", + "quoting" + ], + "properties": { + "value": { + "type": "string", + "description": "%c_cpp.taskDefinitions.args.value.description%" + }, + "quoting": { + "type": "string", + "enum": [ + "escape", + "strong", + "weak" + ], + "enumDescriptions": [ + "%c_cpp.taskDefinitions.args.quoting.escape.description%", + "%c_cpp.taskDefinitions.args.quoting.strong.description%", + "%c_cpp.taskDefinitions.args.quoting.weak.description%" + ], + "default": "strong", + "description": "%c_cpp.taskDefinitions.args.quoting.description%" + } + } + }, + "TaskStringOrQuotedString": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/TaskStringWithQuoting" + } + ] + }, + "CppBuildTaskOptions": { + "type": "object", + "description": "%c_cpp.taskDefinitions.options.description%", + "properties": { + "cwd": { + "type": "string", + "description": "%c_cpp.taskDefinitions.options.cwd.description%" + } + } + }, + "CppBuildTaskPlatformOverride": { + "type": "object", + "properties": { + "command": { + "$ref": "#/definitions/TaskStringOrQuotedString" + }, + "args": { + "type": "array", + "description": "%c_cpp.taskDefinitions.args.description%", + "items": { + "$ref": "#/definitions/TaskStringOrQuotedString" + } + }, + "options": { + "$ref": "#/definitions/CppBuildTaskOptions" + } + } + }, + "CppBuildTaskDefinition": { + "type": "cppbuild", + "required": [ + "command", + "label" + ], + "properties": { + "label": { + "type": "string", + "description": "%c_cpp.taskDefinitions.name.description%" + }, + "command": { + "$ref": "#/definitions/TaskStringOrQuotedString" + }, + "args": { + "type": "array", + "description": "%c_cpp.taskDefinitions.args.description%", + "items": { + "$ref": "#/definitions/TaskStringOrQuotedString" + } + }, + "options": { + "$ref": "#/definitions/CppBuildTaskOptions" + }, + "detail": { + "type": "string", + "description": "%c_cpp.taskDefinitions.detail.description%" + }, + "windows": { + "$ref": "#/definitions/CppBuildTaskPlatformOverride" + }, + "linux": { + "$ref": "#/definitions/CppBuildTaskPlatformOverride" + }, + "osx": { + "$ref": "#/definitions/CppBuildTaskPlatformOverride" + } + } + }, + "TaskDefinitions": [ + { + "$ref": "#/definitions/CppBuildTaskDefinition" + } + ] + } +} \ No newline at end of file From aea36b86ebd8feacd74aa23cbef9894e2f3ab423 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Wed, 1 Jul 2026 10:30:52 -0700 Subject: [PATCH 2/3] Add problemMatcher override and finish the implementation --- Extension/package.json | 21 +++ Extension/package.nls.json | 1 + .../LanguageServer/cppBuildTaskProvider.ts | 153 +++++++++++------- Extension/tools/TaskDefinitionsSchema.json | 7 + 4 files changed, 128 insertions(+), 54 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index d9ecbe3a1..da9df5664 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -619,6 +619,13 @@ "description": "%c_cpp.taskDefinitions.options.cwd.description%" } } + }, + "problemMatcher": { + "type": "array", + "description": "%c_cpp.taskDefinitions.problemMatcher.description%", + "items": { + "type": "string" + } } } }, @@ -708,6 +715,13 @@ "description": "%c_cpp.taskDefinitions.options.cwd.description%" } } + }, + "problemMatcher": { + "type": "array", + "description": "%c_cpp.taskDefinitions.problemMatcher.description%", + "items": { + "type": "string" + } } } }, @@ -797,6 +811,13 @@ "description": "%c_cpp.taskDefinitions.options.cwd.description%" } } + }, + "problemMatcher": { + "type": "array", + "description": "%c_cpp.taskDefinitions.problemMatcher.description%", + "items": { + "type": "string" + } } } } diff --git a/Extension/package.nls.json b/Extension/package.nls.json index 17df9bcdb..68a7266a1 100644 --- a/Extension/package.nls.json +++ b/Extension/package.nls.json @@ -1005,6 +1005,7 @@ "c_cpp.taskDefinitions.args.quoting.weak.description": "Quotes the argument using the shell's weak quote character (e.g. \" under bash).", "c_cpp.taskDefinitions.options.description": "Additional command options.", "c_cpp.taskDefinitions.options.cwd.description": "The current working directory of the executed program or script. If omitted Code's current workspace root is used.", + "c_cpp.taskDefinitions.problemMatcher.description": "One or more problem matchers to use to detect compiler errors and warnings in task output.", "c_cpp.taskDefinitions.detail.description": "Additional details of the task.", "c_cpp.debuggers.sourceFileMap.sourceFileMapEntry.description": "Current and compile-time paths to the same source trees. Files found under the EditorPath are mapped to the CompileTimePath path for breakpoint matching and mapped from CompileTimePath to EditorPath when displaying stacktrace locations.", "c_cpp.debuggers.sourceFileMap.sourceFileMapEntry.editorPath.description": "The path to the source tree the editor will use.", diff --git a/Extension/src/LanguageServer/cppBuildTaskProvider.ts b/Extension/src/LanguageServer/cppBuildTaskProvider.ts index e42b429c6..bcc15d623 100644 --- a/Extension/src/LanguageServer/cppBuildTaskProvider.ts +++ b/Extension/src/LanguageServer/cppBuildTaskProvider.ts @@ -25,7 +25,17 @@ export interface CppBuildTaskDefinition extends TaskDefinition { label: string; // The label appears in tasks.json file. command: string | util.IQuotedString; args: (string | util.IQuotedString)[]; - options: cp.ExecOptions | cp.SpawnOptions | undefined; + options: cp.ExecOptions | undefined; + windows?: CppBuildTaskPlatformOverride; + linux?: CppBuildTaskPlatformOverride; + osx?: CppBuildTaskPlatformOverride; +} + +interface CppBuildTaskPlatformOverride { + command?: string | util.IQuotedString; + args?: (string | util.IQuotedString)[]; + options?: cp.ExecOptions | undefined; + problemMatcher?: string | string[]; } export class CppBuildTask extends Task { @@ -50,7 +60,7 @@ export class CppBuildTaskProvider implements TaskProvider { const execution: ProcessExecution | ShellExecution | CustomExecution | undefined = _task.execution; if (!execution) { const definition: CppBuildTaskDefinition = _task.definition; - _task = this.getTask(definition.command, false, definition.args ? definition.args : [], definition, _task.detail); + _task = this.getTask(definition, _task.detail); return _task; } return undefined; @@ -59,7 +69,7 @@ export class CppBuildTaskProvider implements TaskProvider { public resolveInsiderTask(_task: CppBuildTask): CppBuildTask | undefined { const definition: CppBuildTaskDefinition = _task.definition; definition.label = definition.label.replace(ext.configPrefix, ""); - _task = this.getTask(definition.command, false, definition.args ? definition.args : [], definition, _task.detail); + _task = this.getTask(definition, _task.detail); return _task; } @@ -152,83 +162,118 @@ export class CppBuildTaskProvider implements TaskProvider { return emptyTasks; } - // Create a build task per compiler path + // Create a build task per compiler path. const result: CppBuildTask[] = []; - // Task for valid user compiler path setting + // Task for valid user compiler path setting. if (isCompilerValid && userCompilerPath) { - result.push(this.getTask(userCompilerPath, appendSourceToName, userCompilerPathAndArgs?.allCompilerArgs)); + result.push(this.generateTask(userCompilerPath, appendSourceToName, userCompilerPathAndArgs?.allCompilerArgs)); } - // Tasks for known compiler paths + // Tasks for known compiler paths. if (knownCompilerPaths) { - result.push(...knownCompilerPaths.map(compilerPath => this.getTask(compilerPath, appendSourceToName, undefined))); + result.push(...knownCompilerPaths.map(compilerPath => this.generateTask(compilerPath, appendSourceToName, undefined))); } return result; } - private getTask: (compilerPath: string | util.IQuotedString, appendSourceToName: boolean, compilerArgs?: (string | util.IQuotedString)[], definition?: CppBuildTaskDefinition, detail?: string) => Task = (compilerPath: string | util.IQuotedString, appendSourceToName: boolean, compilerArgs?: (string | util.IQuotedString)[], definition?: CppBuildTaskDefinition, detail?: string) => { + private generateTask(compilerPath: string | util.IQuotedString, appendSourceToName: boolean, compilerArgs?: (string | util.IQuotedString)[]): CppBuildTask { const compilerPathString: string = util.isString(compilerPath) ? compilerPath : compilerPath.value; - const compilerPathBase: string = path.basename(compilerPathString); - const isCl: boolean = compilerPathBase.toLowerCase() === "cl.exe"; - const isClang: boolean = !isCl && compilerPathBase.toLowerCase().includes("clang"); - // Double-quote the command if needed. - const resolvedCompilerPathString: string = isCl ? compilerPathBase : compilerPathString; - let resolvedCompilerPath: string | util.IQuotedString = compilerPath; - if (isCl) { - resolvedCompilerPath = compilerPathBase; - } - - if (!definition) { - const isWindows: boolean = os.platform() === 'win32'; - const taskLabel: string = ((appendSourceToName && !compilerPathBase.startsWith(ext.configPrefix)) ? - ext.configPrefix : "") + compilerPathBase + " " + localize("build.active.file", "build active file"); - const programName: string = util.defaultExePath(); - let args: (string | util.IQuotedString)[] = isCl ? - ['/Zi', '/EHsc', '/nologo', `/Fe${programName}`, '${file}'] : - isClang ? - ['-fcolor-diagnostics', '-fansi-escape-codes', '-g', '${file}', '-o', programName] : - ['-fdiagnostics-color=always', '-g', '${file}', '-o', programName]; - - if (compilerArgs && compilerArgs.length > 0) { - args = args.concat(compilerArgs); - } - const cwd: string = isWindows && !isCl && !process.env.PATH?.includes(path.dirname(compilerPathString)) ? path.dirname(compilerPathString) : "${fileDirname}"; - const options: cp.ExecOptions | cp.SpawnOptions | undefined = { cwd: cwd }; - definition = { - type: CppBuildTaskProvider.CppBuildScriptType, - label: taskLabel, - command: compilerPath, - args: args, - options: options - }; - if (isCl) { - definition.command = compilerPathBase; - } + const compilerName: string = path.basename(compilerPathString); + const isCl: boolean = compilerName.toLowerCase() === "cl.exe"; + const isClang: boolean = !isCl && compilerName.toLowerCase().includes("clang"); + + const isWindows: boolean = os.platform() === 'win32'; + const taskLabel: string = ((appendSourceToName && !compilerName.startsWith(ext.configPrefix)) ? + ext.configPrefix : "") + compilerName + " " + localize("build.active.file", "build active file"); + const programName: string = util.defaultExePath(); + let args: (string | util.IQuotedString)[] = isCl ? + ['/Zi', '/EHsc', '/nologo', `/Fe${programName}`, '${file}'] : + isClang ? + ['-fcolor-diagnostics', '-fansi-escape-codes', '-g', '${file}', '-o', programName] : + ['-fdiagnostics-color=always', '-g', '${file}', '-o', programName]; + + if (compilerArgs && compilerArgs.length > 0) { + args = args.concat(compilerArgs); } + const cwd: string = isWindows && !isCl && !process.env.PATH?.includes(path.dirname(compilerPathString)) ? path.dirname(compilerPathString) : "${fileDirname}"; + const options: cp.ExecOptions | undefined = { cwd: cwd }; + const definition: CppBuildTaskDefinition = { + type: CppBuildTaskProvider.CppBuildScriptType, + label: taskLabel, + command: isCl ? compilerName : compilerPath, + args: args, + options: options + }; + + return this.getTask(definition); + } + + private getTask(definition: CppBuildTaskDefinition, detail?: string): CppBuildTask { + const platformDefinition: CppBuildTaskDefinition = this.applyPlatformOverrides(definition); + const command: string = util.isString(platformDefinition.command) ? platformDefinition.command : platformDefinition.command.value; + const compilerName: string = path.basename(command); + const isCl: boolean = compilerName.toLowerCase() === "cl.exe"; + const isClang: boolean = !isCl && compilerName.toLowerCase().includes("clang"); const editor: TextEditor | undefined = window.activeTextEditor; const folder: WorkspaceFolder | undefined = editor ? workspace.getWorkspaceFolder(editor.document.uri) : undefined; - const taskUsesActiveFile: boolean = definition.args.some(arg => { + const taskUsesActiveFile: boolean = platformDefinition.args.some(arg => { if (util.isString(arg)) { return arg.indexOf('${file}') >= 0; } return arg.value.indexOf('${file}') >= 0; }); // Need to check this before ${file} is resolved const scope: WorkspaceFolder | TaskScope = folder ? folder : TaskScope.Workspace; - const task: CppBuildTask = new Task(definition, scope, definition.label, ext.CppSourceStr, - new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise => - // When the task is executed, this callback will run. Here, we setup for running the task. - new CustomBuildTaskTerminal(resolvedCompilerPath, resolvedDefinition.args, resolvedDefinition.options, { taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin' }) - ), isCl ? '$msCompile' : '$gcc'); + const customExecution: CustomExecution = new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise => { + // When the task is executed, this callback will run. Here, we setup for running the task. + // Apply platform-specific overrides (windows/linux/osx) at execution time so that VS Code + // can still match the task definition by its original shape during the resolve phase. + const effectiveDefinition: CppBuildTaskDefinition = this.applyPlatformOverrides(resolvedDefinition as CppBuildTaskDefinition); + const effectiveArgs: (string | util.IQuotedString)[] = effectiveDefinition.args ? effectiveDefinition.args : []; + return new CustomBuildTaskTerminal( + effectiveDefinition.command, + effectiveArgs, + effectiveDefinition.options, + { taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin' } + ); + }); + const task: CppBuildTask = new CppBuildTask(definition, scope, definition.label, ext.CppSourceStr, customExecution, isCl ? '$msCompile' : '$gcc'); task.group = TaskGroup.Build; - task.detail = detail ? detail : localize("compiler.details", "compiler:") + " " + resolvedCompilerPathString; + task.detail = detail ? detail : localize("compiler.details", "compiler:") + " " + (isCl ? compilerName : command); return task; - }; + } + + private applyPlatformOverrides(definition: CppBuildTaskDefinition): CppBuildTaskDefinition { + const platform: NodeJS.Platform = os.platform(); + let platformOverride: CppBuildTaskPlatformOverride | undefined; + + if (platform === 'win32') { + platformOverride = definition.windows; + } else if (platform === 'linux') { + platformOverride = definition.linux; + } else if (platform === 'darwin') { + platformOverride = definition.osx; + } + + if (!platformOverride) { + return definition; + } + + const mergedDefinition: CppBuildTaskDefinition = { + ...definition, + command: platformOverride.command ?? definition.command, + args: platformOverride.args ?? definition.args, + options: platformOverride.options ?? definition.options, + problemMatcher: platformOverride.problemMatcher ?? definition.problemMatcher + }; + + return mergedDefinition; + } public async getJsonTasks(): Promise { const rawJson: any = await this.getRawTasksJson(); @@ -244,7 +289,7 @@ export class CppBuildTaskProvider implements TaskProvider { args: task.args, options: task.options }; - const cppBuildTask: CppBuildTask = new Task(definition, TaskScope.Workspace, task.label, ext.CppSourceStr); + const cppBuildTask: CppBuildTask = new CppBuildTask(definition, TaskScope.Workspace, task.label, ext.CppSourceStr); cppBuildTask.detail = task.detail; cppBuildTask.existing = true; if (util.isObject(task.group) && task.group.isDefault) { diff --git a/Extension/tools/TaskDefinitionsSchema.json b/Extension/tools/TaskDefinitionsSchema.json index ec67c7db7..592ef848b 100644 --- a/Extension/tools/TaskDefinitionsSchema.json +++ b/Extension/tools/TaskDefinitionsSchema.json @@ -67,6 +67,13 @@ }, "options": { "$ref": "#/definitions/CppBuildTaskOptions" + }, + "problemMatcher": { + "type": "array", + "description": "%c_cpp.taskDefinitions.problemMatcher.description%", + "items": { + "type": "string" + } } } }, From 7f14d770101955c0e4d2dc6113028aff5aa2467b Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Wed, 1 Jul 2026 13:01:11 -0700 Subject: [PATCH 3/3] address PR feedback --- Extension/package.json | 65 ++++++++++++++----- .../LanguageServer/cppBuildTaskProvider.ts | 16 +++-- Extension/tools/TaskDefinitionsSchema.json | 23 +++++-- 3 files changed, 78 insertions(+), 26 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index da9df5664..35ae46c25 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -529,6 +529,20 @@ } } }, + "problemMatcher": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "%c_cpp.taskDefinitions.problemMatcher.description%" + }, "detail": { "type": "string", "description": "%c_cpp.taskDefinitions.detail.description%" @@ -621,11 +635,18 @@ } }, "problemMatcher": { - "type": "array", - "description": "%c_cpp.taskDefinitions.problemMatcher.description%", - "items": { - "type": "string" - } + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "%c_cpp.taskDefinitions.problemMatcher.description%" } } }, @@ -717,11 +738,18 @@ } }, "problemMatcher": { - "type": "array", - "description": "%c_cpp.taskDefinitions.problemMatcher.description%", - "items": { - "type": "string" - } + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "%c_cpp.taskDefinitions.problemMatcher.description%" } } }, @@ -813,11 +841,18 @@ } }, "problemMatcher": { - "type": "array", - "description": "%c_cpp.taskDefinitions.problemMatcher.description%", - "items": { - "type": "string" - } + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "%c_cpp.taskDefinitions.problemMatcher.description%" } } } diff --git a/Extension/src/LanguageServer/cppBuildTaskProvider.ts b/Extension/src/LanguageServer/cppBuildTaskProvider.ts index bcc15d623..486e3df64 100644 --- a/Extension/src/LanguageServer/cppBuildTaskProvider.ts +++ b/Extension/src/LanguageServer/cppBuildTaskProvider.ts @@ -24,8 +24,8 @@ export interface CppBuildTaskDefinition extends TaskDefinition { type: string; label: string; // The label appears in tasks.json file. command: string | util.IQuotedString; - args: (string | util.IQuotedString)[]; - options: cp.ExecOptions | undefined; + args?: (string | util.IQuotedString)[]; + options?: cp.ExecOptions | undefined; windows?: CppBuildTaskPlatformOverride; linux?: CppBuildTaskPlatformOverride; osx?: CppBuildTaskPlatformOverride; @@ -220,12 +220,12 @@ export class CppBuildTaskProvider implements TaskProvider { const editor: TextEditor | undefined = window.activeTextEditor; const folder: WorkspaceFolder | undefined = editor ? workspace.getWorkspaceFolder(editor.document.uri) : undefined; - const taskUsesActiveFile: boolean = platformDefinition.args.some(arg => { + const taskUsesActiveFile: boolean = platformDefinition.args?.some(arg => { if (util.isString(arg)) { return arg.indexOf('${file}') >= 0; } return arg.value.indexOf('${file}') >= 0; - }); // Need to check this before ${file} is resolved + }) || false; // Need to check this before ${file} is resolved const scope: WorkspaceFolder | TaskScope = folder ? folder : TaskScope.Workspace; const customExecution: CustomExecution = new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise => { // When the task is executed, this callback will run. Here, we setup for running the task. @@ -240,7 +240,7 @@ export class CppBuildTaskProvider implements TaskProvider { { taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin' } ); }); - const task: CppBuildTask = new CppBuildTask(definition, scope, definition.label, ext.CppSourceStr, customExecution, isCl ? '$msCompile' : '$gcc'); + const task: CppBuildTask = new CppBuildTask(definition, scope, definition.label, ext.CppSourceStr, customExecution, platformDefinition.problemMatcher ?? (isCl ? '$msCompile' : '$gcc')); task.group = TaskGroup.Build; task.detail = detail ? detail : localize("compiler.details", "compiler:") + " " + (isCl ? compilerName : command); @@ -287,7 +287,11 @@ export class CppBuildTaskProvider implements TaskProvider { label: task.label, command: task.command, args: task.args, - options: task.options + options: task.options, + windows: task.windows, + linux: task.linux, + osx: task.osx, + problemMatcher: task.problemMatcher }; const cppBuildTask: CppBuildTask = new CppBuildTask(definition, TaskScope.Workspace, task.label, ext.CppSourceStr); cppBuildTask.detail = task.detail; diff --git a/Extension/tools/TaskDefinitionsSchema.json b/Extension/tools/TaskDefinitionsSchema.json index 592ef848b..95da3f2e3 100644 --- a/Extension/tools/TaskDefinitionsSchema.json +++ b/Extension/tools/TaskDefinitionsSchema.json @@ -52,6 +52,20 @@ } } }, + "CppBuildTaskProblemMatcher": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "%c_cpp.taskDefinitions.problemMatcher.description%" + }, "CppBuildTaskPlatformOverride": { "type": "object", "properties": { @@ -69,11 +83,7 @@ "$ref": "#/definitions/CppBuildTaskOptions" }, "problemMatcher": { - "type": "array", - "description": "%c_cpp.taskDefinitions.problemMatcher.description%", - "items": { - "type": "string" - } + "$ref": "#/definitions/CppBuildTaskProblemMatcher" } } }, @@ -101,6 +111,9 @@ "options": { "$ref": "#/definitions/CppBuildTaskOptions" }, + "problemMatcher": { + "$ref": "#/definitions/CppBuildTaskProblemMatcher" + }, "detail": { "type": "string", "description": "%c_cpp.taskDefinitions.detail.description%"