Skip to content

Commit

Permalink
Task Provider for Cmake build tasks (#1880)
Browse files Browse the repository at this point in the history
  • Loading branch information
elahehrashedi authored Jun 25, 2021
1 parent f00255b commit b71fb38
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 99 deletions.
21 changes: 20 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,28 @@
"command"
],
"properties": {
"label": {
"type": "string",
"description": "The name of the task"
},
"command": {
"type": "string",
"description": "CMake command"
"enum": [ "build" ],
"description": "CMake build command"
},
"options": {
"type": "object",
"description": "Additional command options",
"properties": {
"cwd": {
"type": "string",
"description": "The current working directory of the executed program or script. If omitted Code's current workspace root is used."
}
}
},
"detail": {
"type": "string",
"description": "Additional details of the task"
}
}
}
Expand Down
19 changes: 9 additions & 10 deletions src/cmake-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import * as nls from 'vscode-nls';
import paths from './paths';
import {CMakeToolsFolder} from './folders';
import {ConfigurationWebview} from './cache-view';
import {updateFullFeatureSetForFolder, registerTaskProvider, enableFullFeatureSet, isActiveFolder} from './extension';
import { updateFullFeatureSetForFolder, updateCMakeDriverInTaskProvider, enableFullFeatureSet, isActiveFolder, updateDefaultTargetInTaskProvider } from './extension';
import { ConfigurationReader } from './config';
import * as preset from '@cmt/preset';
import * as util from '@cmt/util';
Expand Down Expand Up @@ -245,7 +245,7 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
try {
this._statusMessage.set(localize('reloading.status', 'Reloading...'));
await drv.setBuildPreset(expandedBuildPreset);
await this.reRegisterTaskProviderforNewTarget(drv, undefined);
this.updateDriverAndTargetInTaskProvider(drv);
this.workspaceContext.state.buildPresetName = buildPreset;
this._statusMessage.set(localize('ready.status', 'Ready'));
} catch (error) {
Expand Down Expand Up @@ -656,8 +656,8 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
this._targetName.set(this.defaultBuildTarget || drv.allTargetName);
await this._ctestController.reloadTests(drv);

// Make sure to re-register the task provider when a new driver is created
await registerTaskProvider(await this.tasksBuildCommandDrv(drv));
// Update the task provider when a new driver is created
updateCMakeDriverInTaskProvider(drv);

// All set up. Fulfill the driver promise.
return drv;
Expand Down Expand Up @@ -1255,7 +1255,7 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
target = target || this.workspaceContext.state.defaultBuildTarget || await this.allTargetName;
targetName = target;
}
await this.reRegisterTaskProviderforNewTarget(drv, target);
this.updateDriverAndTargetInTaskProvider(drv, target);
const consumer = new CMakeBuildConsumer(BUILD_LOGGER);
const IS_BUILDING_KEY = 'cmake:isBuilding';
try {
Expand Down Expand Up @@ -1509,14 +1509,13 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
}
await this._setDefaultBuildTarget(target);
const drv = await this._cmakeDriver;
await this.reRegisterTaskProviderforNewTarget(drv, target);
this.updateDriverAndTargetInTaskProvider(drv, target);
}

async reRegisterTaskProviderforNewTarget(drv: CMakeDriver | null, target: string | undefined) {
updateDriverAndTargetInTaskProvider(drv: CMakeDriver | null, target?: string) {
if (drv && (this.useCMakePresets || target)) {
const buildargs = await drv.getCMakeBuildCommand(target);
const command = (buildargs) ? buildCmdStr(buildargs.command, buildargs.args) : null;
await registerTaskProvider(command);
updateCMakeDriverInTaskProvider(drv);
updateDefaultTargetInTaskProvider(target);
}
}

Expand Down
141 changes: 141 additions & 0 deletions src/cmakeTaskProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { CMakeDriver } from './drivers/driver';
import * as proc from './proc';
import * as nls from 'vscode-nls';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();

interface CMakeTaskDefinition extends vscode.TaskDefinition {
type: string;
label: string;
command: string; // Command is either "build", "install", or "test".
options?: { cwd?: string };
}

export class CMakeTask extends vscode.Task {
detail?: string;
}

export class CMakeTaskProvider implements vscode.TaskProvider {
static CMakeScriptType: string = 'cmake';
static CMakeSourceStr: string = "CMake";
static allTargetName: string = "all";
private cmakeDriver?: CMakeDriver;
private defaultTarget: string = CMakeTaskProvider.allTargetName;

constructor() {
}

public updateCMakeDriver(cmakeDriver: CMakeDriver) {
this.cmakeDriver = cmakeDriver;
if (CMakeTaskProvider.allTargetName === "all") {
CMakeTaskProvider.allTargetName = this.cmakeDriver.allTargetName;
}
}

public updateDefaultTarget(defaultTarget: string | undefined) {
this.defaultTarget = defaultTarget ? defaultTarget :
this.cmakeDriver ? this.cmakeDriver.allTargetName : CMakeTaskProvider.allTargetName;
}

public async provideTasks(): Promise<CMakeTask[]> {
// Create a CMake build task
const result: CMakeTask[] = [];
const taskName: string = "CMake: build";
const definition: CMakeTaskDefinition = {
type: CMakeTaskProvider.CMakeScriptType,
label: taskName,
command: "build"
};
const task = new vscode.Task(definition, vscode.TaskScope.Workspace, taskName, CMakeTaskProvider.CMakeSourceStr);
task.group = vscode.TaskGroup.Build;
task.detail = "CMake template build task";
result.push(task);
return result;
}

public async resolveTask(task: CMakeTask): Promise<CMakeTask | undefined> {
const execution: any = task.execution;
if (!execution) {
const definition: CMakeTaskDefinition = <any>task.definition;
const scope: vscode.WorkspaceFolder | vscode.TaskScope = vscode.TaskScope.Workspace;
const resolvedTask: CMakeTask = new vscode.Task(definition, scope, definition.label, CMakeTaskProvider.CMakeSourceStr,
new vscode.CustomExecution(async (resolvedDefinition: vscode.TaskDefinition): Promise<vscode.Pseudoterminal> =>
new CustomBuildTaskTerminal(resolvedDefinition.command, this.defaultTarget, resolvedDefinition.options, this.cmakeDriver)
), []); // TODO: add problem matcher
return resolvedTask;
}
return undefined;
}
}

class CustomBuildTaskTerminal implements vscode.Pseudoterminal , proc.OutputConsumer {
private writeEmitter = new vscode.EventEmitter<string>();
private closeEmitter = new vscode.EventEmitter<number>();
public get onDidWrite(): vscode.Event<string> { return this.writeEmitter.event; }
public get onDidClose(): vscode.Event<number> { return this.closeEmitter.event; }
private endOfLine: string = "\r\n";

constructor(private command: string, private defaultTarget: string, private options?: { cwd?: string }, private cmakeDriver?: CMakeDriver) {
}

output(line: string): void {
this.writeEmitter.fire(line + this.endOfLine);
}

error(error: string): void {
this.writeEmitter.fire(error + this.endOfLine);
}

async open(_initialDimensions: vscode.TerminalDimensions | undefined): Promise<void> {
// At this point we can start using the terminal.
this.writeEmitter.fire(localize("starting.build", "Starting build...") + this.endOfLine);
await this.doBuild();
}

close(): void {
// The terminal has been closed. Shutdown the build.
}

private async doBuild(): Promise<any> {
if (this.command !== "build") {
this.writeEmitter.fire(localize("not.a.build.command", "\"{0}\" is not a recognized build command.", this.command) + this.endOfLine);
this.closeEmitter.fire(-1);
return;
}
let buildCommand: proc.BuildCommand | null;
let cmakePath: string = "CMake.EXE";
let args: string[] = [];

if (this.cmakeDriver) {
buildCommand = await this.cmakeDriver.getCMakeBuildCommand(this.defaultTarget);
if (buildCommand) {
cmakePath = buildCommand.command;
args = buildCommand.args ? buildCommand.args : [];
}
}

this.writeEmitter.fire(proc.buildCmdStr(cmakePath, args) + this.endOfLine);
try {
const result: proc.ExecutionResult = await proc.execute(cmakePath, args, this, this.options).result;
const dot: string = ".";
if (result.retc) {
this.writeEmitter.fire(localize("build.finished.with.error", "Build finished with error(s)") + dot + this.endOfLine);
} else if (result.stderr && !result.stdout) {
this.writeEmitter.fire(localize("build.finished.with.warnings", "Build finished with warning(s)") + dot + this.endOfLine);
} else if (result.stdout && result.stdout.includes("warning")) {
this.writeEmitter.fire(localize("build.finished.with.warnings", "Build finished with warning(s)") + dot + this.endOfLine);
} else {
this.writeEmitter.fire(localize("build.finished.successfully", "Build finished successfully.") + this.endOfLine);
}
this.closeEmitter.fire(0);
} catch {
this.closeEmitter.fire(-1);
}
}
}
48 changes: 25 additions & 23 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {FireNow, FireLate} from '@cmt/prop';
import rollbar from '@cmt/rollbar';
import {StateManager} from './state';
import {StatusBar} from '@cmt/status';
import {CMakeTaskProvider} from '@cmt/taskprovider';
import {CMakeTaskProvider} from '@cmt/cmakeTaskProvider';
import * as telemetry from '@cmt/telemetry';
import {ProjectOutlineProvider, TargetNode, SourceFileNode, WorkspaceFolderNode} from '@cmt/tree';
import * as util from '@cmt/util';
Expand All @@ -41,6 +41,8 @@ import { CMakeDriver } from './drivers/driver';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
export const cmakeTaskProvider: CMakeTaskProvider = new CMakeTaskProvider();
let taskProvider: vscode.Disposable;

const log = logging.createLogger('extension');

Expand All @@ -49,6 +51,12 @@ const HIDE_LAUNCH_COMMAND_KEY = 'cmake:hideLaunchCommand';
const HIDE_DEBUG_COMMAND_KEY = 'cmake:hideDebugCommand';
const HIDE_BUILD_COMMAND_KEY = 'cmake:hideBuildCommand';

/**
* The global extension manager. There is only one of these, even if multiple
* backends.
*/
let _EXT_MANAGER: ExtensionManager|null = null;

type CMakeToolsMapFn = (cmt: CMakeTools) => Thenable<any>;
type CMakeToolsQueryMapFn = (cmt: CMakeTools) => Thenable<string | string[] | null>;

Expand Down Expand Up @@ -1480,25 +1488,6 @@ class ExtensionManager implements vscode.Disposable {
}
}

/**
* The global extension manager. There is only one of these, even if multiple
* backends.
*/
let _EXT_MANAGER: ExtensionManager|null = null;
let cmakeTaskProvider: vscode.Disposable | undefined;

export async function registerTaskProvider(command: string | null) {
if (command) {
rollbar.invokeAsync(localize('registerTaskProvider', 'Register the task provider.'), async () => {
if (cmakeTaskProvider) {
cmakeTaskProvider.dispose();
}

cmakeTaskProvider = vscode.tasks.registerTaskProvider(CMakeTaskProvider.CMakeType, new CMakeTaskProvider({ build: command }));
});
}
}

async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle) {
reportProgress(localize('initial.setup', 'Initial setup'), progress);

Expand Down Expand Up @@ -1686,10 +1675,13 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.workspace.registerTextDocumentContentProvider('cmake-tools-schema', new SchemaProvider());
vscode.commands.executeCommand("setContext", "inCMakeProject", true);

taskProvider = vscode.tasks.registerTaskProvider(CMakeTaskProvider.CMakeScriptType, cmakeTaskProvider);

return setup(context);

// TODO: Return the extension API
// context.subscriptions.push(vscode.commands.registerCommand('cmake._extensionInstance', () => cmt));

}

// Enable all or part of the CMake Tools palette commands
Expand Down Expand Up @@ -1743,13 +1735,23 @@ export async function updateFullFeatureSetForFolder(folder: vscode.WorkspaceFold
enableFullFeatureSet(true);
}

// this method is called when your extension is deactivated
// update CMakeDriver in taskProvider
export function updateCMakeDriverInTaskProvider(cmakeDriver: CMakeDriver) {
cmakeTaskProvider.updateCMakeDriver(cmakeDriver);
}

// update default target in taskProvider
export function updateDefaultTargetInTaskProvider(defaultTarget?: string) {
cmakeTaskProvider.updateDefaultTarget(defaultTarget);
}

// this method is called when your extension is deactivated.
export async function deactivate() {
log.debug(localize('deactivate.cmaketools', 'Deactivate CMakeTools'));
if (_EXT_MANAGER) {
await _EXT_MANAGER.asyncDispose();
}
if (cmakeTaskProvider) {
cmakeTaskProvider.dispose();
if (taskProvider) {
taskProvider.dispose();
}
}
64 changes: 0 additions & 64 deletions src/taskprovider.ts

This file was deleted.

Loading

0 comments on commit b71fb38

Please sign in to comment.