Path: blob/main/src/vs/workbench/api/node/extHostTask.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as path from '../../../base/common/path.js';67import { URI, UriComponents } from '../../../base/common/uri.js';8import { findExecutable } from '../../../base/node/processes.js';9import * as types from '../common/extHostTypes.js';10import { IExtHostWorkspace } from '../common/extHostWorkspace.js';11import type * as vscode from 'vscode';12import * as tasks from '../common/shared/tasks.js';13import { IExtHostDocumentsAndEditors } from '../common/extHostDocumentsAndEditors.js';14import { IExtHostConfiguration } from '../common/extHostConfiguration.js';15import { IWorkspaceFolder, WorkspaceFolder } from '../../../platform/workspace/common/workspace.js';16import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';17import { IExtHostTerminalService } from '../common/extHostTerminalService.js';18import { IExtHostRpcService } from '../common/extHostRpcService.js';19import { IExtHostInitDataService } from '../common/extHostInitDataService.js';20import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerData } from '../common/extHostTask.js';21import { Schemas } from '../../../base/common/network.js';22import { ILogService } from '../../../platform/log/common/log.js';23import { IExtHostApiDeprecationService } from '../common/extHostApiDeprecationService.js';24import * as resources from '../../../base/common/resources.js';25import { homedir } from 'os';26import { IExtHostVariableResolverProvider } from '../common/extHostVariableResolverService.js';2728export class ExtHostTask extends ExtHostTaskBase {29constructor(30@IExtHostRpcService extHostRpc: IExtHostRpcService,31@IExtHostInitDataService initData: IExtHostInitDataService,32@IExtHostWorkspace private readonly workspaceService: IExtHostWorkspace,33@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,34@IExtHostConfiguration configurationService: IExtHostConfiguration,35@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,36@ILogService logService: ILogService,37@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService,38@IExtHostVariableResolverProvider private readonly variableResolver: IExtHostVariableResolverProvider,39) {40super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService);41if (initData.remote.isRemote && initData.remote.authority) {42this.registerTaskSystem(Schemas.vscodeRemote, {43scheme: Schemas.vscodeRemote,44authority: initData.remote.authority,45platform: process.platform46});47} else {48this.registerTaskSystem(Schemas.file, {49scheme: Schemas.file,50authority: '',51platform: process.platform52});53}54this._proxy.$registerSupportedExecutions(true, true, true);55}5657public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {58const tTask = (task as types.Task);5960if (!task.execution && (tTask._id === undefined)) {61throw new Error('Tasks to execute must include an execution');62}6364// We have a preserved ID. So the task didn't change.65if (tTask._id !== undefined) {66// Always get the task execution first to prevent timing issues when retrieving it later67const handleDto = TaskHandleDTO.from(tTask, this.workspaceService);68const executionDTO = await this._proxy.$getTaskExecution(handleDto);69if (executionDTO.task === undefined) {70throw new Error('Task from execution DTO is undefined');71}72const execution = await this.getTaskExecution(executionDTO, task);73this._proxy.$executeTask(handleDto).catch(() => { /* The error here isn't actionable. */ });74return execution;75} else {76const dto = TaskDTO.from(task, extension);77if (dto === undefined) {78return Promise.reject(new Error('Task is not valid'));79}8081// If this task is a custom execution, then we need to save it away82// in the provided custom execution map that is cleaned up after the83// task is executed.84if (CustomExecutionDTO.is(dto.execution)) {85await this.addCustomExecution(dto, task, false);86}87// Always get the task execution first to prevent timing issues when retrieving it later88const execution = await this.getTaskExecution(await this._proxy.$getTaskExecution(dto), task);89this._proxy.$executeTask(dto).catch(() => { /* The error here isn't actionable. */ });90return execution;91}92}9394protected provideTasksInternal(validTypes: { [key: string]: boolean }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.ITaskDTO[]; extension: IExtensionDescription } {95const taskDTOs: tasks.ITaskDTO[] = [];96if (value) {97for (const task of value) {98this.checkDeprecation(task, handler);99100if (!task.definition || !validTypes[task.definition.type]) {101this._logService.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);102}103104const taskDTO: tasks.ITaskDTO | undefined = TaskDTO.from(task, handler.extension);105if (taskDTO) {106taskDTOs.push(taskDTO);107108if (CustomExecutionDTO.is(taskDTO.execution)) {109// The ID is calculated on the main thread task side, so, let's call into it here.110// We need the task id's pre-computed for custom task executions because when OnDidStartTask111// is invoked, we have to be able to map it back to our data.112taskIdPromises.push(this.addCustomExecution(taskDTO, task, true));113}114}115}116}117return {118tasks: taskDTOs,119extension: handler.extension120};121}122123protected async resolveTaskInternal(resolvedTaskDTO: tasks.ITaskDTO): Promise<tasks.ITaskDTO | undefined> {124return resolvedTaskDTO;125}126127private async getAFolder(workspaceFolders: vscode.WorkspaceFolder[] | undefined): Promise<IWorkspaceFolder> {128let folder = (workspaceFolders && workspaceFolders.length > 0) ? workspaceFolders[0] : undefined;129if (!folder) {130const userhome = URI.file(homedir());131folder = new WorkspaceFolder({ uri: userhome, name: resources.basename(userhome), index: 0 });132}133return {134uri: folder.uri,135name: folder.name,136index: folder.index,137toResource: () => {138throw new Error('Not implemented');139}140};141}142143public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }; variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }> {144const uri: URI = URI.revive(uriComponents);145const result = {146process: undefined as string | undefined,147variables: Object.create(null)148};149const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri);150const workspaceFolders = (await this._workspaceProvider.getWorkspaceFolders2()) ?? [];151152const resolver = await this.variableResolver.getResolver();153const ws: IWorkspaceFolder = workspaceFolder ? {154uri: workspaceFolder.uri,155name: workspaceFolder.name,156index: workspaceFolder.index,157toResource: () => {158throw new Error('Not implemented');159}160} : await this.getAFolder(workspaceFolders);161162for (const variable of toResolve.variables) {163result.variables[variable] = await resolver.resolveAsync(ws, variable);164}165if (toResolve.process !== undefined) {166let paths: string[] | undefined = undefined;167if (toResolve.process.path !== undefined) {168paths = toResolve.process.path.split(path.delimiter);169for (let i = 0; i < paths.length; i++) {170paths[i] = await resolver.resolveAsync(ws, paths[i]);171}172}173const processName = await resolver.resolveAsync(ws, toResolve.process.name);174const cwd = toResolve.process.cwd !== undefined ? await resolver.resolveAsync(ws, toResolve.process.cwd) : undefined;175const foundExecutable = await findExecutable(processName, cwd, paths);176if (foundExecutable) {177result.process = foundExecutable;178} else if (path.isAbsolute(processName)) {179result.process = processName;180} else {181result.process = path.join(cwd ?? '', processName);182}183}184return result;185}186187public async $jsonTasksSupported(): Promise<boolean> {188return true;189}190191public async $findExecutable(command: string, cwd?: string, paths?: string[]): Promise<string | undefined> {192return findExecutable(command, cwd, paths);193}194}195196197