Skip to content

Commit aea36b8

Browse files
committed
Add problemMatcher override and finish the implementation
1 parent f80cde9 commit aea36b8

4 files changed

Lines changed: 128 additions & 54 deletions

File tree

Extension/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,13 @@
619619
"description": "%c_cpp.taskDefinitions.options.cwd.description%"
620620
}
621621
}
622+
},
623+
"problemMatcher": {
624+
"type": "array",
625+
"description": "%c_cpp.taskDefinitions.problemMatcher.description%",
626+
"items": {
627+
"type": "string"
628+
}
622629
}
623630
}
624631
},
@@ -708,6 +715,13 @@
708715
"description": "%c_cpp.taskDefinitions.options.cwd.description%"
709716
}
710717
}
718+
},
719+
"problemMatcher": {
720+
"type": "array",
721+
"description": "%c_cpp.taskDefinitions.problemMatcher.description%",
722+
"items": {
723+
"type": "string"
724+
}
711725
}
712726
}
713727
},
@@ -797,6 +811,13 @@
797811
"description": "%c_cpp.taskDefinitions.options.cwd.description%"
798812
}
799813
}
814+
},
815+
"problemMatcher": {
816+
"type": "array",
817+
"description": "%c_cpp.taskDefinitions.problemMatcher.description%",
818+
"items": {
819+
"type": "string"
820+
}
800821
}
801822
}
802823
}

Extension/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,7 @@
10051005
"c_cpp.taskDefinitions.args.quoting.weak.description": "Quotes the argument using the shell's weak quote character (e.g. \" under bash).",
10061006
"c_cpp.taskDefinitions.options.description": "Additional command options.",
10071007
"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.",
1008+
"c_cpp.taskDefinitions.problemMatcher.description": "One or more problem matchers to use to detect compiler errors and warnings in task output.",
10081009
"c_cpp.taskDefinitions.detail.description": "Additional details of the task.",
10091010
"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.",
10101011
"c_cpp.debuggers.sourceFileMap.sourceFileMapEntry.editorPath.description": "The path to the source tree the editor will use.",

Extension/src/LanguageServer/cppBuildTaskProvider.ts

Lines changed: 99 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,17 @@ export interface CppBuildTaskDefinition extends TaskDefinition {
2525
label: string; // The label appears in tasks.json file.
2626
command: string | util.IQuotedString;
2727
args: (string | util.IQuotedString)[];
28-
options: cp.ExecOptions | cp.SpawnOptions | undefined;
28+
options: cp.ExecOptions | undefined;
29+
windows?: CppBuildTaskPlatformOverride;
30+
linux?: CppBuildTaskPlatformOverride;
31+
osx?: CppBuildTaskPlatformOverride;
32+
}
33+
34+
interface CppBuildTaskPlatformOverride {
35+
command?: string | util.IQuotedString;
36+
args?: (string | util.IQuotedString)[];
37+
options?: cp.ExecOptions | undefined;
38+
problemMatcher?: string | string[];
2939
}
3040

3141
export class CppBuildTask extends Task {
@@ -50,7 +60,7 @@ export class CppBuildTaskProvider implements TaskProvider {
5060
const execution: ProcessExecution | ShellExecution | CustomExecution | undefined = _task.execution;
5161
if (!execution) {
5262
const definition: CppBuildTaskDefinition = <any>_task.definition;
53-
_task = this.getTask(definition.command, false, definition.args ? definition.args : [], definition, _task.detail);
63+
_task = this.getTask(definition, _task.detail);
5464
return _task;
5565
}
5666
return undefined;
@@ -59,7 +69,7 @@ export class CppBuildTaskProvider implements TaskProvider {
5969
public resolveInsiderTask(_task: CppBuildTask): CppBuildTask | undefined {
6070
const definition: CppBuildTaskDefinition = <any>_task.definition;
6171
definition.label = definition.label.replace(ext.configPrefix, "");
62-
_task = this.getTask(definition.command, false, definition.args ? definition.args : [], definition, _task.detail);
72+
_task = this.getTask(definition, _task.detail);
6373
return _task;
6474
}
6575

@@ -152,83 +162,118 @@ export class CppBuildTaskProvider implements TaskProvider {
152162
return emptyTasks;
153163
}
154164

155-
// Create a build task per compiler path
165+
// Create a build task per compiler path.
156166
const result: CppBuildTask[] = [];
157167

158-
// Task for valid user compiler path setting
168+
// Task for valid user compiler path setting.
159169
if (isCompilerValid && userCompilerPath) {
160-
result.push(this.getTask(userCompilerPath, appendSourceToName, userCompilerPathAndArgs?.allCompilerArgs));
170+
result.push(this.generateTask(userCompilerPath, appendSourceToName, userCompilerPathAndArgs?.allCompilerArgs));
161171
}
162172

163-
// Tasks for known compiler paths
173+
// Tasks for known compiler paths.
164174
if (knownCompilerPaths) {
165-
result.push(...knownCompilerPaths.map<Task>(compilerPath => this.getTask(compilerPath, appendSourceToName, undefined)));
175+
result.push(...knownCompilerPaths.map<CppBuildTask>(compilerPath => this.generateTask(compilerPath, appendSourceToName, undefined)));
166176
}
167177

168178
return result;
169179
}
170180

171-
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) => {
181+
private generateTask(compilerPath: string | util.IQuotedString, appendSourceToName: boolean, compilerArgs?: (string | util.IQuotedString)[]): CppBuildTask {
172182
const compilerPathString: string = util.isString(compilerPath) ? compilerPath : compilerPath.value;
173-
const compilerPathBase: string = path.basename(compilerPathString);
174-
const isCl: boolean = compilerPathBase.toLowerCase() === "cl.exe";
175-
const isClang: boolean = !isCl && compilerPathBase.toLowerCase().includes("clang");
176-
// Double-quote the command if needed.
177-
const resolvedCompilerPathString: string = isCl ? compilerPathBase : compilerPathString;
178-
let resolvedCompilerPath: string | util.IQuotedString = compilerPath;
179-
if (isCl) {
180-
resolvedCompilerPath = compilerPathBase;
181-
}
182-
183-
if (!definition) {
184-
const isWindows: boolean = os.platform() === 'win32';
185-
const taskLabel: string = ((appendSourceToName && !compilerPathBase.startsWith(ext.configPrefix)) ?
186-
ext.configPrefix : "") + compilerPathBase + " " + localize("build.active.file", "build active file");
187-
const programName: string = util.defaultExePath();
188-
let args: (string | util.IQuotedString)[] = isCl ?
189-
['/Zi', '/EHsc', '/nologo', `/Fe${programName}`, '${file}'] :
190-
isClang ?
191-
['-fcolor-diagnostics', '-fansi-escape-codes', '-g', '${file}', '-o', programName] :
192-
['-fdiagnostics-color=always', '-g', '${file}', '-o', programName];
193-
194-
if (compilerArgs && compilerArgs.length > 0) {
195-
args = args.concat(compilerArgs);
196-
}
197-
const cwd: string = isWindows && !isCl && !process.env.PATH?.includes(path.dirname(compilerPathString)) ? path.dirname(compilerPathString) : "${fileDirname}";
198-
const options: cp.ExecOptions | cp.SpawnOptions | undefined = { cwd: cwd };
199-
definition = {
200-
type: CppBuildTaskProvider.CppBuildScriptType,
201-
label: taskLabel,
202-
command: compilerPath,
203-
args: args,
204-
options: options
205-
};
206-
if (isCl) {
207-
definition.command = compilerPathBase;
208-
}
183+
const compilerName: string = path.basename(compilerPathString);
184+
const isCl: boolean = compilerName.toLowerCase() === "cl.exe";
185+
const isClang: boolean = !isCl && compilerName.toLowerCase().includes("clang");
186+
187+
const isWindows: boolean = os.platform() === 'win32';
188+
const taskLabel: string = ((appendSourceToName && !compilerName.startsWith(ext.configPrefix)) ?
189+
ext.configPrefix : "") + compilerName + " " + localize("build.active.file", "build active file");
190+
const programName: string = util.defaultExePath();
191+
let args: (string | util.IQuotedString)[] = isCl ?
192+
['/Zi', '/EHsc', '/nologo', `/Fe${programName}`, '${file}'] :
193+
isClang ?
194+
['-fcolor-diagnostics', '-fansi-escape-codes', '-g', '${file}', '-o', programName] :
195+
['-fdiagnostics-color=always', '-g', '${file}', '-o', programName];
196+
197+
if (compilerArgs && compilerArgs.length > 0) {
198+
args = args.concat(compilerArgs);
209199
}
200+
const cwd: string = isWindows && !isCl && !process.env.PATH?.includes(path.dirname(compilerPathString)) ? path.dirname(compilerPathString) : "${fileDirname}";
201+
const options: cp.ExecOptions | undefined = { cwd: cwd };
202+
const definition: CppBuildTaskDefinition = {
203+
type: CppBuildTaskProvider.CppBuildScriptType,
204+
label: taskLabel,
205+
command: isCl ? compilerName : compilerPath,
206+
args: args,
207+
options: options
208+
};
209+
210+
return this.getTask(definition);
211+
}
212+
213+
private getTask(definition: CppBuildTaskDefinition, detail?: string): CppBuildTask {
214+
const platformDefinition: CppBuildTaskDefinition = this.applyPlatformOverrides(definition);
215+
const command: string = util.isString(platformDefinition.command) ? platformDefinition.command : platformDefinition.command.value;
216+
const compilerName: string = path.basename(command);
217+
const isCl: boolean = compilerName.toLowerCase() === "cl.exe";
218+
const isClang: boolean = !isCl && compilerName.toLowerCase().includes("clang");
210219

211220
const editor: TextEditor | undefined = window.activeTextEditor;
212221
const folder: WorkspaceFolder | undefined = editor ? workspace.getWorkspaceFolder(editor.document.uri) : undefined;
213222

214-
const taskUsesActiveFile: boolean = definition.args.some(arg => {
223+
const taskUsesActiveFile: boolean = platformDefinition.args.some(arg => {
215224
if (util.isString(arg)) {
216225
return arg.indexOf('${file}') >= 0;
217226
}
218227
return arg.value.indexOf('${file}') >= 0;
219228
}); // Need to check this before ${file} is resolved
220229
const scope: WorkspaceFolder | TaskScope = folder ? folder : TaskScope.Workspace;
221-
const task: CppBuildTask = new Task(definition, scope, definition.label, ext.CppSourceStr,
222-
new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> =>
223-
// When the task is executed, this callback will run. Here, we setup for running the task.
224-
new CustomBuildTaskTerminal(resolvedCompilerPath, resolvedDefinition.args, resolvedDefinition.options, { taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin' })
225-
), isCl ? '$msCompile' : '$gcc');
230+
const customExecution: CustomExecution = new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> => {
231+
// When the task is executed, this callback will run. Here, we setup for running the task.
232+
// Apply platform-specific overrides (windows/linux/osx) at execution time so that VS Code
233+
// can still match the task definition by its original shape during the resolve phase.
234+
const effectiveDefinition: CppBuildTaskDefinition = this.applyPlatformOverrides(resolvedDefinition as CppBuildTaskDefinition);
235+
const effectiveArgs: (string | util.IQuotedString)[] = effectiveDefinition.args ? effectiveDefinition.args : [];
236+
return new CustomBuildTaskTerminal(
237+
effectiveDefinition.command,
238+
effectiveArgs,
239+
effectiveDefinition.options,
240+
{ taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin' }
241+
);
242+
});
243+
const task: CppBuildTask = new CppBuildTask(definition, scope, definition.label, ext.CppSourceStr, customExecution, isCl ? '$msCompile' : '$gcc');
226244

227245
task.group = TaskGroup.Build;
228-
task.detail = detail ? detail : localize("compiler.details", "compiler:") + " " + resolvedCompilerPathString;
246+
task.detail = detail ? detail : localize("compiler.details", "compiler:") + " " + (isCl ? compilerName : command);
229247

230248
return task;
231-
};
249+
}
250+
251+
private applyPlatformOverrides(definition: CppBuildTaskDefinition): CppBuildTaskDefinition {
252+
const platform: NodeJS.Platform = os.platform();
253+
let platformOverride: CppBuildTaskPlatformOverride | undefined;
254+
255+
if (platform === 'win32') {
256+
platformOverride = definition.windows;
257+
} else if (platform === 'linux') {
258+
platformOverride = definition.linux;
259+
} else if (platform === 'darwin') {
260+
platformOverride = definition.osx;
261+
}
262+
263+
if (!platformOverride) {
264+
return definition;
265+
}
266+
267+
const mergedDefinition: CppBuildTaskDefinition = {
268+
...definition,
269+
command: platformOverride.command ?? definition.command,
270+
args: platformOverride.args ?? definition.args,
271+
options: platformOverride.options ?? definition.options,
272+
problemMatcher: platformOverride.problemMatcher ?? definition.problemMatcher
273+
};
274+
275+
return mergedDefinition;
276+
}
232277

233278
public async getJsonTasks(): Promise<CppBuildTask[]> {
234279
const rawJson: any = await this.getRawTasksJson();
@@ -244,7 +289,7 @@ export class CppBuildTaskProvider implements TaskProvider {
244289
args: task.args,
245290
options: task.options
246291
};
247-
const cppBuildTask: CppBuildTask = new Task(definition, TaskScope.Workspace, task.label, ext.CppSourceStr);
292+
const cppBuildTask: CppBuildTask = new CppBuildTask(definition, TaskScope.Workspace, task.label, ext.CppSourceStr);
248293
cppBuildTask.detail = task.detail;
249294
cppBuildTask.existing = true;
250295
if (util.isObject(task.group) && task.group.isDefault) {

Extension/tools/TaskDefinitionsSchema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@
6767
},
6868
"options": {
6969
"$ref": "#/definitions/CppBuildTaskOptions"
70+
},
71+
"problemMatcher": {
72+
"type": "array",
73+
"description": "%c_cpp.taskDefinitions.problemMatcher.description%",
74+
"items": {
75+
"type": "string"
76+
}
7077
}
7178
}
7279
},

0 commit comments

Comments
 (0)