Skip to content

Commit 7d61e66

Browse files
CopilotOmotola
andauthored
Fix cmake.buildTask build failures not aborting debug launches (#4785)
* Initial plan * Fix build diagnostics and exit code not propagating when using cmake.buildTask (#4489) When cmake.buildTask is true, the CustomBuildTaskTerminal captures build output but never forwards it to the CMakeBuildConsumer for diagnostic parsing. This means compiler errors never appear in the Problems tab. Additionally, setBuildProcessForTask always resolved with exit code 0 regardless of the actual build result, causing failed builds to appear successful (related to #3389). Changes: - Forward build output from CustomBuildTaskTerminal to an external OutputConsumer so diagnostics are parsed during task builds - Pass the consumer through resolveInternalTask to the terminal - Use onDidEndTaskProcess instead of onDidEndTask to capture the actual exit code from the build task Co-authored-by: Omotola <[email protected]> * Revert #4489 changes, keep only #3389 fix (build task exit code propagation) Co-authored-by: Omotola <[email protected]> * Fix stale target path on build failure by using direct exit code promise from CustomBuildTaskTerminal (#3389) Co-authored-by: Omotola <[email protected]> * Dispose onDidClose listener after resolving exit code Co-authored-by: Omotola <[email protected]> * Merge main into copilot/fix-build-task-error-reporting, resolve CHANGELOG conflict Co-authored-by: Omotola <[email protected]> * Fix brace-style lint error in cmakeTaskProvider.ts Co-authored-by: Omotola <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Omotola <[email protected]> Co-authored-by: Omotola <[email protected]>
1 parent acbdeb2 commit 7d61e66

4 files changed

Lines changed: 38 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Improvements:
2727
- Honor `debugger.workingDirectory` from the CMake File API when debugging a target, so that the `DEBUGGER_WORKING_DIRECTORY` target property is used as the debugger working directory. [#4595](https://github.com/microsoft/vscode-cmake-tools/issues/4595)
2828

2929
Bug Fixes:
30+
- Fix `cmake.buildTask` build failures not aborting debug launches. When `${command:cmake.launchTargetPath}` is used in `launch.json`, a failed build now correctly prevents the stale executable from being launched. [#3389](https://github.com/microsoft/vscode-cmake-tools/issues/3389)
3031
- Fix `cmake.compileFile` command truncating long compile commands at ~1024 characters on macOS. The command is now sent to the terminal in chunks to avoid VS Code terminal buffer limitations. [#4470](https://github.com/microsoft/vscode-cmake-tools/issues/4470)
3132
- Fix configure/build sometimes using stale preset values when unsaved changes to included preset files are auto-saved before configure. The extension now explicitly refreshes presets from disk after saving, instead of relying solely on the asynchronous file watcher. [#4502](https://github.com/microsoft/vscode-cmake-tools/issues/4502)
3233
- Reduce overly verbose logging when CMake configure or build fails. The Output panel no longer floods with duplicated output, and the channel is only revealed on error rather than unconditionally. [#4749](https://github.com/microsoft/vscode-cmake-tools/issues/4749)

src/cmakeBuildRunner.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,25 @@ export class CMakeBuildRunner {
3232
this.setBuildInProgress(true);
3333
}
3434

35-
public async setBuildProcessForTask(taskExecutor: vscode.TaskExecution): Promise<void> {
35+
public async setBuildProcessForTask(taskExecutor: vscode.TaskExecution, exitCodePromise?: Promise<number | null>): Promise<void> {
3636
this.taskExecutor = taskExecutor;
37-
this.currentBuildProcess = { child: undefined, result: new Promise<proc.ExecutionResult>(resolve => {
38-
const disposable: vscode.Disposable = vscode.tasks.onDidEndTask((endEvent: vscode.TaskEndEvent) => {
39-
if (endEvent.execution === this.taskExecutor) {
40-
this.taskExecutor = undefined;
41-
disposable.dispose();
42-
resolve({ retc: 0, stdout: '', stderr: '' });
43-
}
44-
});
45-
})};
37+
if (exitCodePromise) {
38+
// Use the direct exit code promise from the CustomBuildTaskTerminal (most reliable).
39+
this.currentBuildProcess = { child: undefined, result: exitCodePromise.then(exitCode => ({
40+
retc: exitCode, stdout: '', stderr: ''
41+
}))};
42+
} else {
43+
// Fallback: listen for task process end event.
44+
this.currentBuildProcess = { child: undefined, result: new Promise<proc.ExecutionResult>(resolve => {
45+
const disposable: vscode.Disposable = vscode.tasks.onDidEndTaskProcess((endEvent: vscode.TaskProcessEndEvent) => {
46+
if (endEvent.execution === this.taskExecutor) {
47+
this.taskExecutor = undefined;
48+
disposable.dispose();
49+
resolve({ retc: endEvent.exitCode ?? null, stdout: '', stderr: '' });
50+
}
51+
});
52+
})};
53+
}
4654
this.setBuildInProgress(true);
4755
}
4856

src/cmakeTaskProvider.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -240,20 +240,29 @@ export class CMakeTaskProvider implements vscode.TaskProvider {
240240
return undefined;
241241
}
242242

243-
public static async resolveInternalTask(task: CMakeTask): Promise<CMakeTask | undefined> {
243+
public static async resolveInternalTask(task: CMakeTask): Promise<{ task: CMakeTask; exitCodePromise?: Promise<number | null> } | undefined> {
244244
const execution: any = task.execution;
245245
if (!execution) {
246246
const definition: CMakeTaskDefinition = <any>task.definition;
247247
// task.scope can be a WorkspaceFolder, TaskScope.Global, or TaskScope.Workspace.
248248
// Only use it as a WorkspaceFolder if it's an object (not a number or null).
249249
const workspaceFolder: vscode.WorkspaceFolder | undefined = (task.scope && typeof task.scope === 'object') ? task.scope as vscode.WorkspaceFolder : undefined;
250+
let exitCodeResolve!: (exitCode: number | null) => void;
251+
const exitCodePromise = new Promise<number | null>(resolve => {
252+
exitCodeResolve = resolve;
253+
});
250254
const resolvedTask: CMakeTask = new vscode.Task(definition, workspaceFolder ?? vscode.TaskScope.Workspace, definition.label, CMakeTaskProvider.CMakeSourceStr,
251-
new vscode.CustomExecution(async (resolvedDefinition: vscode.TaskDefinition): Promise<vscode.Pseudoterminal> =>
252-
new CustomBuildTaskTerminal(resolvedDefinition.command, resolvedDefinition.targets, workspaceFolder, resolvedDefinition.preset, resolvedDefinition.options)
253-
), []);
254-
return resolvedTask;
255-
}
256-
return task;
255+
new vscode.CustomExecution(async (resolvedDefinition: vscode.TaskDefinition): Promise<vscode.Pseudoterminal> => {
256+
const terminal = new CustomBuildTaskTerminal(resolvedDefinition.command, resolvedDefinition.targets, workspaceFolder, resolvedDefinition.preset, resolvedDefinition.options);
257+
const listener = terminal.onDidClose((exitCode) => {
258+
listener.dispose();
259+
exitCodeResolve(exitCode);
260+
});
261+
return terminal;
262+
}), []);
263+
return { task: resolvedTask, exitCodePromise };
264+
}
265+
return { task };
257266
}
258267

259268
public static async findBuildTask(workspaceFolder: string, presetName?: string, targets?: string[], expansionOptions?: expand.ExpansionOptions): Promise<CMakeTask | undefined> {

src/drivers/cmakeDriver.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,9 +2082,9 @@ export abstract class CMakeDriver implements vscode.Disposable {
20822082
if (useBuildTask) {
20832083
const task: CMakeTask | undefined = await CMakeTaskProvider.findBuildTask(this.workspaceFolder, this._buildPreset?.name, targets, this.expansionOptions);
20842084
if (task) {
2085-
const resolvedTask: CMakeTask | undefined = await CMakeTaskProvider.resolveInternalTask(task);
2086-
if (resolvedTask) {
2087-
await this.cmakeBuildRunner.setBuildProcessForTask(await vscode.tasks.executeTask(resolvedTask));
2085+
const resolved = await CMakeTaskProvider.resolveInternalTask(task);
2086+
if (resolved) {
2087+
await this.cmakeBuildRunner.setBuildProcessForTask(await vscode.tasks.executeTask(resolved.task), resolved.exitCodePromise);
20882088
}
20892089
}
20902090
} else {

0 commit comments

Comments
 (0)