mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-13 15:35:20 +01:00
Fixes #24073: Improve task API and adopt gulp extension to it.
This commit is contained in:
@@ -26,5 +26,23 @@
|
||||
"onCommand:workbench.action.tasks.runTask",
|
||||
"onCommand:workbench.action.tasks.build",
|
||||
"onCommand:workbench.action.tasks.test"
|
||||
]
|
||||
],
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"id": "gulp",
|
||||
"type": "object",
|
||||
"title": "Gulp",
|
||||
"properties": {
|
||||
"gulp.autoDetect": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"off",
|
||||
"on"
|
||||
],
|
||||
"default": "on",
|
||||
"description": "%config.gulp.autoDetect%"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"config.gulp.autoDetect": "Controls whether auto detection of gulp tasks in on or off. Default is on."
|
||||
}
|
||||
+117
-72
@@ -8,93 +8,138 @@ import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.config(process.env.VSCODE_NLS_CONFIG)();
|
||||
|
||||
type AutoDetect = 'on' | 'off';
|
||||
let taskProvider: vscode.Disposable | undefined;
|
||||
|
||||
export function activate(_context: vscode.ExtensionContext): void {
|
||||
let workspaceRoot = vscode.workspace.rootPath;
|
||||
if (!workspaceRoot) {
|
||||
return;
|
||||
}
|
||||
let gulpfile = path.join(workspaceRoot, 'gulpfile.js');
|
||||
let gulpPromise: Thenable<vscode.TaskSet> | undefined = undefined;
|
||||
let fileWatcher = vscode.workspace.createFileSystemWatcher(gulpfile);
|
||||
let pattern = path.join(workspaceRoot, 'gulpfile{.babel.js,.js}');
|
||||
let gulpPromise: Thenable<vscode.Task[]> | undefined = undefined;
|
||||
let fileWatcher = vscode.workspace.createFileSystemWatcher(pattern);
|
||||
fileWatcher.onDidChange(() => gulpPromise = undefined);
|
||||
fileWatcher.onDidCreate(() => gulpPromise = undefined);
|
||||
fileWatcher.onDidDelete(() => gulpPromise = undefined);
|
||||
|
||||
vscode.workspace.registerTaskProvider({
|
||||
provideTasks: () => {
|
||||
if (!gulpPromise) {
|
||||
gulpPromise = getGulpTasks();
|
||||
}
|
||||
return gulpPromise;
|
||||
function onConfigurationChanged() {
|
||||
let autoDetect = vscode.workspace.getConfiguration('gulp').get<AutoDetect>('autoDetect');
|
||||
if (taskProvider && autoDetect === 'off') {
|
||||
gulpPromise = undefined;
|
||||
taskProvider.dispose();
|
||||
taskProvider = undefined;
|
||||
} else if (!taskProvider && autoDetect === 'on') {
|
||||
taskProvider = vscode.workspace.registerTaskProvider({
|
||||
provideTasks: () => {
|
||||
if (!gulpPromise) {
|
||||
gulpPromise = getGulpTasks();
|
||||
}
|
||||
return gulpPromise;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
vscode.workspace.onDidChangeConfiguration(onConfigurationChanged);
|
||||
onConfigurationChanged();
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
if (taskProvider) {
|
||||
taskProvider.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function exists(file: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, _reject) => {
|
||||
fs.exists(file, (value) => {
|
||||
resolve(value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getGulpTasks(): Thenable<vscode.TaskSet> {
|
||||
return new Promise<vscode.TaskSet>((resolve, _reject) => {
|
||||
let workspaceRoot = vscode.workspace.rootPath;
|
||||
let emptyTaskSet = { tasks: [] };
|
||||
if (!workspaceRoot) {
|
||||
return resolve(emptyTaskSet);
|
||||
}
|
||||
let gulpfile = path.join(workspaceRoot, 'gulpfile.js');
|
||||
fs.exists(gulpfile, (value) => {
|
||||
if (!value) {
|
||||
resolve(emptyTaskSet);
|
||||
return;
|
||||
function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: string; stderr: string }> {
|
||||
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
|
||||
cp.exec(command, options, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject({ error, stdout, stderr });
|
||||
}
|
||||
|
||||
let commandLine: string;
|
||||
let platform = process.platform;
|
||||
if (platform === 'win32' && fs.existsSync(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp.cmd'))) {
|
||||
commandLine = `${path.join('.', 'node_modules', '.bin', 'gulp.cmd')} --tasks-simple --no-color`;
|
||||
} else if ((platform === 'linux' || platform === 'darwin') && fs.existsSync(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp.cmd'))) {
|
||||
commandLine = `${path.join('.', 'node_modules', '.bin', 'gulp')} --tasks-simple --no-color`;
|
||||
} else {
|
||||
commandLine = 'gulp --tasks-simple --no-color';
|
||||
}
|
||||
cp.exec(commandLine, { cwd: workspaceRoot }, (error, stdout, stderr) => {
|
||||
let channel = vscode.window.createOutputChannel('tasks');
|
||||
if (stderr) {
|
||||
channel.appendLine(stderr);
|
||||
}
|
||||
if (error) {
|
||||
channel.appendLine(`Auto detecting gulp failed with error: ${error ? error.toString() : 'unknown'}`);
|
||||
resolve(emptyTaskSet);
|
||||
return;
|
||||
}
|
||||
let result: vscode.TaskSet = { tasks: [], buildTasks: [], testTasks: [] };
|
||||
if (stdout) {
|
||||
let buildTask: { id: string | undefined, rank: number } = { id: undefined, rank: 0 };
|
||||
let testTask: { id: string | undefined, rank: number } = { id: undefined, rank: 0 };
|
||||
let lines = stdout.split(/\r{0,1}\n/);
|
||||
for (let line of lines) {
|
||||
if (line.length === 0) {
|
||||
continue;
|
||||
}
|
||||
let task = new vscode.ShellTask(`gulp ${line}`, `gulp ${line}`);
|
||||
result.tasks.push(task);
|
||||
let lowerCaseLine = line.toLowerCase();
|
||||
if (lowerCaseLine === 'build') {
|
||||
buildTask = { id: line, rank: 2 };
|
||||
} else if (lowerCaseLine.indexOf('build') !== -1 && buildTask.rank < 1) {
|
||||
buildTask = { id: line, rank: 1 };
|
||||
} else if (lowerCaseLine === 'test') {
|
||||
testTask = { id: line, rank: 2 };
|
||||
} else if (lowerCaseLine.indexOf('test') !== -1 && testTask.rank < 1) {
|
||||
testTask = { id: line, rank: 1 };
|
||||
}
|
||||
}
|
||||
if (buildTask.id) {
|
||||
result.buildTasks!.push(buildTask.id);
|
||||
}
|
||||
if (testTask.id) {
|
||||
result.testTasks!.push(testTask.id);
|
||||
}
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
resolve({ stdout, stderr });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getGulpTasks(): Promise<vscode.Task[]> {
|
||||
let workspaceRoot = vscode.workspace.rootPath;
|
||||
let emptyTasks: vscode.Task[] = [];
|
||||
if (!workspaceRoot) {
|
||||
return emptyTasks;
|
||||
}
|
||||
let gulpfile = path.join(workspaceRoot, 'gulpfile.js');
|
||||
if (!await exists(gulpfile)) {
|
||||
gulpfile = path.join(workspaceRoot, 'gulpfile.babel.js');
|
||||
if (! await exists(gulpfile)) {
|
||||
return emptyTasks;
|
||||
}
|
||||
}
|
||||
|
||||
let gulpCommand: string;
|
||||
let platform = process.platform;
|
||||
if (platform === 'win32' && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp.cmd'))) {
|
||||
gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp.cmd');
|
||||
} else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp'))) {
|
||||
gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp');
|
||||
} else {
|
||||
gulpCommand = 'gulp';
|
||||
}
|
||||
|
||||
let commandLine = `${gulpCommand} --tasks-simple --no-color`;
|
||||
let channel = vscode.window.createOutputChannel('tasks');
|
||||
try {
|
||||
let { stdout, stderr } = await exec(commandLine, { cwd: workspaceRoot });
|
||||
if (stderr) {
|
||||
channel.appendLine(stderr);
|
||||
}
|
||||
let result: vscode.Task[] = [];
|
||||
if (stdout) {
|
||||
let buildTask: { task: vscode.Task | undefined, rank: number } = { task: undefined, rank: 0 };
|
||||
let testTask: { task: vscode.Task | undefined, rank: number } = { task: undefined, rank: 0 };
|
||||
let lines = stdout.split(/\r{0,1}\n/);
|
||||
for (let line of lines) {
|
||||
if (line.length === 0) {
|
||||
continue;
|
||||
}
|
||||
let task = new vscode.ShellTask(`gulp: ${line}`, `${gulpCommand} ${line}`);
|
||||
task.identifier = `gulp.${line}`;
|
||||
result.push(task);
|
||||
let lowerCaseLine = line.toLowerCase();
|
||||
if (lowerCaseLine === 'build') {
|
||||
buildTask = { task, rank: 2 };
|
||||
} else if (lowerCaseLine.indexOf('build') !== -1 && buildTask.rank < 1) {
|
||||
buildTask = { task, rank: 1 };
|
||||
} else if (lowerCaseLine === 'test') {
|
||||
testTask = { task, rank: 2 };
|
||||
} else if (lowerCaseLine.indexOf('test') !== -1 && testTask.rank < 1) {
|
||||
testTask = { task, rank: 1 };
|
||||
}
|
||||
}
|
||||
if (buildTask.task) {
|
||||
buildTask.task.group = vscode.TaskGroup.Build;
|
||||
}
|
||||
if (testTask.task) {
|
||||
testTask.task.group = vscode.TaskGroup.Test;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (err) {
|
||||
if (err.stderr) {
|
||||
channel.appendLine(err.stderr);
|
||||
}
|
||||
channel.appendLine(localize('execFailed', 'Auto detecting gulp failed with error: {0}', err.error ? err.error.toString() : 'unknown'));
|
||||
return emptyTasks;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,7 @@
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6",
|
||||
"es2015.promise"
|
||||
"es2016"
|
||||
],
|
||||
"outDir": "./out",
|
||||
"strictNullChecks": true,
|
||||
|
||||
Vendored
+51
-25
@@ -267,6 +267,30 @@ declare module 'vscode' {
|
||||
env?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export namespace TaskGroup {
|
||||
/**
|
||||
* The clean task group
|
||||
*/
|
||||
export const Clean: 'clean';
|
||||
/**
|
||||
* The build task group
|
||||
*/
|
||||
export const Build: 'build';
|
||||
/**
|
||||
* The rebuild all task group
|
||||
*/
|
||||
export const RebuildAll: 'rebuildAll';
|
||||
/**
|
||||
* The test task group
|
||||
*/
|
||||
export const Test: 'test';
|
||||
}
|
||||
|
||||
/**
|
||||
* The supported task groups.
|
||||
*/
|
||||
export type TaskGroup = 'clean' | 'build' | 'rebuildAll' | 'test';
|
||||
|
||||
/**
|
||||
* A task that starts an external process.
|
||||
*/
|
||||
@@ -328,6 +352,12 @@ declare module 'vscode' {
|
||||
*/
|
||||
args: string[];
|
||||
|
||||
/**
|
||||
* The task group this tasks belongs to. Defaults to undefined meaning
|
||||
* that the task doesn't belong to any special group.
|
||||
*/
|
||||
group?: TaskGroup;
|
||||
|
||||
/**
|
||||
* The process options used when the process is executed.
|
||||
* Defaults to an empty object literal.
|
||||
@@ -374,7 +404,7 @@ declare module 'vscode' {
|
||||
* The current working directory of the executed shell.
|
||||
* If omitted VSCode's current workspace root is used.
|
||||
*/
|
||||
cwd?: string;
|
||||
cwd: string;
|
||||
|
||||
/**
|
||||
* The additional environment of the executed shell. If omitted
|
||||
@@ -382,6 +412,19 @@ declare module 'vscode' {
|
||||
* the parent process' environment.
|
||||
*/
|
||||
env?: { [key: string]: string };
|
||||
} | {
|
||||
/**
|
||||
* The current working directory of the executed shell.
|
||||
* If omitted VSCode's current workspace root is used.
|
||||
*/
|
||||
cwd?: string;
|
||||
|
||||
/**
|
||||
* The additional environment of the executed shell. If omitted
|
||||
* the parent process' environment is used. If provided it is merged with
|
||||
* the parent process' environment.
|
||||
*/
|
||||
env: { [key: string]: string };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -429,6 +472,12 @@ declare module 'vscode' {
|
||||
*/
|
||||
readonly commandLine: string;
|
||||
|
||||
/**
|
||||
* The task group this tasks belongs to. Defaults to undefined meaning
|
||||
* that the task doesn't belong to any special group.
|
||||
*/
|
||||
group?: TaskGroup;
|
||||
|
||||
/**
|
||||
* The shell options used when the shell is executed. Defaults to an
|
||||
* empty object literal.
|
||||
@@ -449,29 +498,6 @@ declare module 'vscode' {
|
||||
|
||||
export type Task = ProcessTask | ShellTask;
|
||||
|
||||
/**
|
||||
* Result return from a task provider.
|
||||
*/
|
||||
export interface TaskSet {
|
||||
/**
|
||||
* The actual tasks returned.
|
||||
*/
|
||||
tasks: Task[];
|
||||
|
||||
/**
|
||||
* The build tasks provided. Tasks must be identified using
|
||||
* `Task.identifier`
|
||||
*/
|
||||
buildTasks?: string[];
|
||||
|
||||
/**
|
||||
* The test tasks provided. Tasks must be identified using
|
||||
* `Task.identifier`
|
||||
*/
|
||||
testTasks?: string[];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A task provider allows to add tasks to the task service.
|
||||
* A task provider is registerd via #workspace.registerTaskProvider.
|
||||
@@ -482,7 +508,7 @@ declare module 'vscode' {
|
||||
* @param token A cancellation token.
|
||||
* @return a #TaskSet
|
||||
*/
|
||||
provideTasks(token: CancellationToken): ProviderResult<TaskSet>;
|
||||
provideTasks(token: CancellationToken): ProviderResult<Task[]>;
|
||||
}
|
||||
|
||||
export namespace workspace {
|
||||
|
||||
@@ -529,6 +529,7 @@ export function createApiFactory(
|
||||
FileLocationKind: extHostTypes.FileLocationKind,
|
||||
ApplyToKind: extHostTypes.ApplyToKind,
|
||||
RevealKind: extHostTypes.RevealKind,
|
||||
TaskGroup: extHostTypes.TaskGroup,
|
||||
ShellTask: extHostTypes.ShellTask,
|
||||
ProcessTask: extHostTypes.ProcessTask
|
||||
};
|
||||
|
||||
@@ -309,6 +309,7 @@ namespace Tasks {
|
||||
_id: uuidMap.getUUID(task.identifier),
|
||||
name: task.name,
|
||||
identifier: task.identifier,
|
||||
group: types.TaskGroup.is(task.group) ? task.group : undefined,
|
||||
command: command,
|
||||
showOutput: behaviour.showOutput,
|
||||
isBackground: !!task.isBackground,
|
||||
@@ -381,57 +382,6 @@ class UUIDMap {
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskSet {
|
||||
|
||||
const idMaps: Map<string, UUIDMap> = new Map<string, UUIDMap>();
|
||||
|
||||
function getUUIDMap(extensionId: string): UUIDMap {
|
||||
let result = idMaps.get(extensionId);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = new UUIDMap();
|
||||
idMaps.set(extensionId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function from(extension: IExtensionDescription, value: vscode.TaskSet): TaskSystem.TaskSet {
|
||||
if (value === void 0 || value === null || !Array.isArray(value.tasks)) {
|
||||
return { tasks: Object.create(null) };
|
||||
}
|
||||
|
||||
let tasks = Tasks.from(value.tasks, getUUIDMap(extension.id));
|
||||
let buildTasks: string[];
|
||||
let testTasks: string[];
|
||||
if (value.buildTasks || value.testTasks) {
|
||||
let map: Map<string, TaskSystem.Task> = new Map<string, TaskSystem.Task>();
|
||||
tasks.forEach(task => map.set(task.identifier, task));
|
||||
if (Array.isArray(value.buildTasks)) {
|
||||
buildTasks = [];
|
||||
for (let elem of value.buildTasks) {
|
||||
if (typeof elem === 'string' && map.has(elem)) {
|
||||
buildTasks.push(map.get(elem)._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(value.testTasks)) {
|
||||
testTasks = [];
|
||||
for (let elem of value.testTasks) {
|
||||
if (typeof elem === 'string' && map.has(elem)) {
|
||||
testTasks.push(map.get(elem)._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tasks,
|
||||
buildTasks,
|
||||
testTasks
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface HandlerData {
|
||||
provider: vscode.TaskProvider;
|
||||
extension: IExtensionDescription;
|
||||
@@ -442,12 +392,14 @@ export class ExtHostTask extends ExtHostTaskShape {
|
||||
private _proxy: MainThreadTaskShape;
|
||||
private _handleCounter: number;
|
||||
private _handlers: Map<number, HandlerData>;
|
||||
private _idMaps: Map<string, UUIDMap>;
|
||||
|
||||
constructor(threadService: IThreadService) {
|
||||
super();
|
||||
this._proxy = threadService.get(MainContext.MainThreadTask);
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
this._idMaps = new Map<string, UUIDMap>();
|
||||
};
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
@@ -469,11 +421,24 @@ export class ExtHostTask extends ExtHostTaskShape {
|
||||
return TPromise.wrapError<TaskSystem.TaskSet>(new Error('no handler found'));
|
||||
}
|
||||
return asWinJsPromise(token => handler.provider.provideTasks(token)).then(value => {
|
||||
return TaskSet.from(handler.extension, value);
|
||||
return {
|
||||
tasks: Tasks.from(value, this.getUUIDMap(handler.extension.id)),
|
||||
extension: handler.extension
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private nextHandle(): number {
|
||||
return this._handleCounter++;
|
||||
}
|
||||
|
||||
private getUUIDMap(extensionId: string): UUIDMap {
|
||||
let result = this._idMaps.get(extensionId);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = new UUIDMap();
|
||||
this._idMaps.set(extensionId, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1033,9 +1033,12 @@ export class BaseTask {
|
||||
}
|
||||
|
||||
set identifier(value: string) {
|
||||
if (typeof name !== 'string') {
|
||||
if (typeof value !== 'string') {
|
||||
throw illegalArgument('identifier');
|
||||
}
|
||||
if (value.indexOf(':') !== -1) {
|
||||
throw illegalArgument('identifier must not contain \':\'');
|
||||
}
|
||||
this._identifier = value;
|
||||
}
|
||||
|
||||
@@ -1084,11 +1087,37 @@ namespace ProblemMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskGroup {
|
||||
/**
|
||||
* The clean task group
|
||||
*/
|
||||
export const Clean: 'clean' = 'clean';
|
||||
|
||||
/**
|
||||
* The build task group
|
||||
*/
|
||||
export const Build: 'build' = 'build';
|
||||
|
||||
/**
|
||||
* The rebuild all task group
|
||||
*/
|
||||
export const RebuildAll: 'rebuildAll' = 'rebuildAll';
|
||||
|
||||
/**
|
||||
* The test task group
|
||||
*/
|
||||
export const Test: 'test' = 'test';
|
||||
|
||||
export function is(value: string): value is vscode.TaskGroup {
|
||||
return value === Clean || value === Build || value === RebuildAll || value === Test;
|
||||
}
|
||||
}
|
||||
|
||||
export class ProcessTask extends BaseTask {
|
||||
|
||||
private _process: string;
|
||||
private _args: string[];
|
||||
private _group: vscode.TaskGroup;
|
||||
private _options: vscode.ProcessOptions;
|
||||
|
||||
private static parseArguments(restArgs: any[]): { args: string[]; options: vscode.ProcessOptions; problemMatchers: vscode.ProblemMatcher[] } {
|
||||
@@ -1145,6 +1174,17 @@ export class ProcessTask extends BaseTask {
|
||||
this._args = value;
|
||||
}
|
||||
|
||||
get group(): vscode.TaskGroup {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
set group(value: vscode.TaskGroup) {
|
||||
if (!TaskGroup.is(value)) {
|
||||
throw illegalArgument('group');
|
||||
}
|
||||
this._group = value;
|
||||
}
|
||||
|
||||
get options(): vscode.ProcessOptions {
|
||||
return this._options;
|
||||
}
|
||||
@@ -1160,6 +1200,7 @@ export class ProcessTask extends BaseTask {
|
||||
export class ShellTask extends BaseTask {
|
||||
|
||||
private _commandLine: string;
|
||||
private _group: vscode.TaskGroup;
|
||||
private _options: vscode.ShellOptions;
|
||||
|
||||
private static parseArguments(restArgs: any[]): { options: vscode.ShellOptions; problemMatchers: vscode.ProblemMatcher[] } {
|
||||
@@ -1197,6 +1238,17 @@ export class ShellTask extends BaseTask {
|
||||
return this._commandLine;
|
||||
}
|
||||
|
||||
get group(): vscode.TaskGroup {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
set group(value: vscode.TaskGroup) {
|
||||
if (!TaskGroup.is(value)) {
|
||||
throw illegalArgument('group');
|
||||
}
|
||||
this._group = value;
|
||||
}
|
||||
|
||||
get options(): vscode.ShellOptions {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
@@ -286,12 +286,6 @@ function mergeProperty<T, K extends keyof T>(target: T, source: T, key: K) {
|
||||
}
|
||||
}
|
||||
|
||||
function fillProperty<T, K extends keyof T>(target: T, source: T, key: K) {
|
||||
if (target[key] === void 0 && source[key] !== void 0) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
|
||||
interface ParseContext {
|
||||
problemReporter: IProblemReporter;
|
||||
namedProblemMatchers: IStringDictionary<NamedProblemMatcher>;
|
||||
@@ -639,23 +633,13 @@ namespace ProblemMatcherConverter {
|
||||
|
||||
namespace TaskDescription {
|
||||
|
||||
export interface TaskConfiguration {
|
||||
tasks: Tasks.Task[];
|
||||
buildTask?: string;
|
||||
testTask?: string;
|
||||
}
|
||||
|
||||
export function isEmpty(value: TaskConfiguration): boolean {
|
||||
return !value || !value.tasks || Object.keys(value.tasks).length === 0;
|
||||
}
|
||||
|
||||
export function from(this: void, tasks: TaskDescription[], globals: Globals, context: ParseContext): TaskConfiguration {
|
||||
export function from(this: void, tasks: TaskDescription[], globals: Globals, context: ParseContext): Tasks.Task[] {
|
||||
if (!tasks) {
|
||||
return undefined;
|
||||
}
|
||||
let parsedTasks: Tasks.Task[] = [];
|
||||
let defaultBuildTask: { id: string; exact: number; } = { id: null, exact: -1 };
|
||||
let defaultTestTask: { id: string; exact: number; } = { id: null, exact: -1 };
|
||||
let defaultBuildTask: { task: Tasks.Task; rank: number; } = { task: null, rank: -1 };
|
||||
let defaultTestTask: { task: Tasks.Task; rank: number; } = { task: null, rank: -1 };
|
||||
tasks.forEach((externalTask) => {
|
||||
let taskName = externalTask.taskName;
|
||||
if (!taskName) {
|
||||
@@ -733,68 +717,57 @@ namespace TaskDescription {
|
||||
}
|
||||
if (addTask) {
|
||||
parsedTasks.push(task);
|
||||
if (!Types.isUndefined(externalTask.isBuildCommand) && externalTask.isBuildCommand && defaultBuildTask.exact < 2) {
|
||||
defaultBuildTask.id = task._id;
|
||||
defaultBuildTask.exact = 2;
|
||||
} else if (taskName === 'build' && defaultBuildTask.exact < 2) {
|
||||
defaultBuildTask.id = task._id;
|
||||
defaultBuildTask.exact = 1;
|
||||
if (!Types.isUndefined(externalTask.isBuildCommand) && externalTask.isBuildCommand && defaultBuildTask.rank < 2) {
|
||||
defaultBuildTask.task = task;
|
||||
defaultBuildTask.rank = 2;
|
||||
} else if (taskName === 'build' && defaultBuildTask.rank < 2) {
|
||||
defaultBuildTask.task = task;
|
||||
defaultBuildTask.rank = 1;
|
||||
}
|
||||
if (!Types.isUndefined(externalTask.isTestCommand) && externalTask.isTestCommand && defaultTestTask.exact < 2) {
|
||||
defaultTestTask.id = task._id;
|
||||
defaultTestTask.exact = 2;
|
||||
} else if (taskName === 'test' && defaultTestTask.exact < 2) {
|
||||
defaultTestTask.id = task._id;
|
||||
defaultTestTask.exact = 1;
|
||||
if (!Types.isUndefined(externalTask.isTestCommand) && externalTask.isTestCommand && defaultTestTask.rank < 2) {
|
||||
defaultTestTask.task = task;
|
||||
defaultTestTask.rank = 2;
|
||||
} else if (taskName === 'test' && defaultTestTask.rank < 2) {
|
||||
defaultTestTask.task = task;
|
||||
defaultTestTask.rank = 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
let buildTask: string;
|
||||
if (defaultBuildTask.exact > 0) {
|
||||
buildTask = defaultBuildTask.id;
|
||||
if (defaultBuildTask.task) {
|
||||
defaultBuildTask.task.group = Tasks.TaskGroup.Build;
|
||||
}
|
||||
let testTask: string;
|
||||
if (defaultTestTask.exact > 0) {
|
||||
testTask = defaultTestTask.id;
|
||||
if (defaultTestTask.task) {
|
||||
defaultTestTask.task.group = Tasks.TaskGroup.Test;
|
||||
}
|
||||
let result = { tasks: parsedTasks, buildTask, testTask };
|
||||
return isEmpty(result) ? undefined : result;
|
||||
return parsedTasks.length === 0 ? undefined : parsedTasks;
|
||||
}
|
||||
|
||||
export function merge(target: TaskConfiguration, source: TaskConfiguration): TaskConfiguration {
|
||||
if (isEmpty(source)) {
|
||||
export function merge(target: Tasks.Task[], source: Tasks.Task[]): Tasks.Task[] {
|
||||
if (source === void 0 || source.length === 0) {
|
||||
return target;
|
||||
}
|
||||
if (isEmpty(target)) {
|
||||
if (target === void 0 || target.length === 0) {
|
||||
return source;
|
||||
}
|
||||
|
||||
if (source.tasks) {
|
||||
if (source) {
|
||||
// Tasks are keyed by ID but we need to merge by name
|
||||
let targetNames: IStringDictionary<string> = Object.create(null);
|
||||
Object.keys(target.tasks).forEach(key => {
|
||||
let task = target.tasks[key];
|
||||
targetNames[task.name] = task.id;
|
||||
let map: IStringDictionary<Tasks.Task> = Object.create(null);
|
||||
target.forEach((task) => {
|
||||
map[task.name] = task;
|
||||
});
|
||||
|
||||
let sourceNames: IStringDictionary<string> = Object.create(null);
|
||||
Object.keys(source.tasks).forEach(key => {
|
||||
let task = source.tasks[key];
|
||||
sourceNames[task.name] = task.id;
|
||||
source.forEach((task) => {
|
||||
map[task.name] = task;
|
||||
});
|
||||
|
||||
Object.keys(sourceNames).forEach(taskName => {
|
||||
let targetId = targetNames[taskName];
|
||||
let sourceId = sourceNames[taskName];
|
||||
// Same name exists globally
|
||||
if (targetId) {
|
||||
delete target.tasks[targetId];
|
||||
}
|
||||
target.tasks[sourceId] = source.tasks[sourceId];
|
||||
let newTarget: Tasks.Task[] = [];
|
||||
target.forEach(task => {
|
||||
newTarget.push(map[task.name]);
|
||||
delete map[task.name];
|
||||
});
|
||||
Object.keys(map).forEach(key => newTarget.push(map[key]));
|
||||
target = newTarget;
|
||||
}
|
||||
fillProperty(target, source, 'buildTask');
|
||||
fillProperty(target, source, 'testTask');
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -976,7 +949,7 @@ export namespace ExecutionEngine {
|
||||
|
||||
export interface ParseResult {
|
||||
validationStatus: ValidationStatus;
|
||||
taskSet: Tasks.TaskSet;
|
||||
tasks: Tasks.Task[];
|
||||
engine: Tasks.ExecutionEngine;
|
||||
}
|
||||
|
||||
@@ -1000,18 +973,18 @@ class ConfigurationParser {
|
||||
let context: ParseContext = { problemReporter: this.problemReporter, namedProblemMatchers: undefined, isTermnial: engine === Tasks.ExecutionEngine.Terminal };
|
||||
return {
|
||||
validationStatus: this.problemReporter.status,
|
||||
taskSet: this.createTaskRunnerConfiguration(fileConfig, context),
|
||||
tasks: this.createTaskRunnerConfiguration(fileConfig, context),
|
||||
engine
|
||||
};
|
||||
}
|
||||
|
||||
private createTaskRunnerConfiguration(fileConfig: ExternalTaskRunnerConfiguration, context: ParseContext): Tasks.TaskSet {
|
||||
private createTaskRunnerConfiguration(fileConfig: ExternalTaskRunnerConfiguration, context: ParseContext): Tasks.Task[] {
|
||||
let globals = Globals.from(fileConfig, context);
|
||||
if (this.problemReporter.status.isFatal()) {
|
||||
return undefined;
|
||||
}
|
||||
context.namedProblemMatchers = ProblemMatcherConverter.namedFrom(fileConfig.declares, context);
|
||||
let globalTasks: TaskDescription.TaskConfiguration;
|
||||
let globalTasks: Tasks.Task[];
|
||||
if (fileConfig.windows && Platform.platform === Platform.Platform.Windows) {
|
||||
globalTasks = TaskDescription.from(fileConfig.windows.tasks, globals, context);
|
||||
} else if (fileConfig.osx && Platform.platform === Platform.Platform.Mac) {
|
||||
@@ -1020,15 +993,13 @@ class ConfigurationParser {
|
||||
globalTasks = TaskDescription.from(fileConfig.linux.tasks, globals, context);
|
||||
}
|
||||
|
||||
let taskConfig: TaskDescription.TaskConfiguration;
|
||||
let tasks: Tasks.Task[];
|
||||
if (fileConfig.tasks) {
|
||||
taskConfig = TaskDescription.from(fileConfig.tasks, globals, context);
|
||||
tasks = TaskDescription.from(fileConfig.tasks, globals, context);
|
||||
}
|
||||
taskConfig = TaskDescription.merge(taskConfig, globalTasks);
|
||||
tasks = TaskDescription.merge(tasks, globalTasks);
|
||||
|
||||
if (TaskDescription.isEmpty(taskConfig)) {
|
||||
let tasks: Tasks.Task[] = [];
|
||||
let buildTask: string;
|
||||
if (!tasks || tasks.length === 0) {
|
||||
if (globals.command && globals.command.name) {
|
||||
let matchers: ProblemMatcher[] = ProblemMatcherConverter.from(fileConfig.problemMatcher, context);;
|
||||
let isBackground = fileConfig.isBackground ? !!fileConfig.isBackground : fileConfig.isWatching ? !!fileConfig.isWatching : undefined;
|
||||
@@ -1036,6 +1007,7 @@ class ConfigurationParser {
|
||||
_id: UUID.generateUuid(),
|
||||
name: globals.command.name,
|
||||
identifier: UUID.generateUuid(),
|
||||
group: Tasks.TaskGroup.Build,
|
||||
command: undefined,
|
||||
isBackground: isBackground,
|
||||
showOutput: undefined,
|
||||
@@ -1044,21 +1016,10 @@ class ConfigurationParser {
|
||||
};
|
||||
TaskDescription.mergeGlobals(task, globals);
|
||||
TaskDescription.fillDefaults(task);
|
||||
tasks.push(task);
|
||||
buildTask = task._id;
|
||||
tasks = [task];
|
||||
}
|
||||
|
||||
taskConfig = {
|
||||
tasks: tasks,
|
||||
buildTask
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
tasks: taskConfig.tasks,
|
||||
buildTasks: taskConfig.buildTask ? [taskConfig.buildTask] : [],
|
||||
testTasks: taskConfig.testTask ? [taskConfig.testTask] : []
|
||||
};
|
||||
return tasks || [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import * as Types from 'vs/base/common/types';
|
||||
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ProblemMatcher } from 'vs/platform/markers/common/problemMatcher';
|
||||
|
||||
export interface CommandOptions {
|
||||
@@ -93,6 +94,22 @@ export namespace ShowOutput {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskGroup {
|
||||
export const Clean: 'clean' = 'clean';
|
||||
|
||||
export const Build: 'build' = 'build';
|
||||
|
||||
export const RebuildAll: 'rebuildAll' = 'rebuildAll';
|
||||
|
||||
export const Test: 'test' = 'test';
|
||||
|
||||
export function is(value: string): value is TaskGroup {
|
||||
return value === Clean || value === Build || value === RebuildAll || value === Test;
|
||||
}
|
||||
}
|
||||
|
||||
export type TaskGroup = 'clean' | 'build' | 'rebuildAll' | 'test';
|
||||
|
||||
/**
|
||||
* A task description
|
||||
*/
|
||||
@@ -113,6 +130,11 @@ export interface Task {
|
||||
*/
|
||||
identifier: string;
|
||||
|
||||
/**
|
||||
* the task's group;
|
||||
*/
|
||||
group?: TaskGroup;
|
||||
|
||||
/**
|
||||
* The command configuration
|
||||
*/
|
||||
@@ -156,28 +178,13 @@ export interface Task {
|
||||
problemMatchers?: ProblemMatcher[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a task set.
|
||||
*/
|
||||
export interface TaskSet {
|
||||
/**
|
||||
* The inferred build tasks
|
||||
*/
|
||||
buildTasks?: string[];
|
||||
|
||||
/**
|
||||
* The inferred test tasks;
|
||||
*/
|
||||
testTasks?: string[];
|
||||
|
||||
/**
|
||||
* The configured tasks
|
||||
*/
|
||||
tasks: Task[];
|
||||
}
|
||||
|
||||
export enum ExecutionEngine {
|
||||
Unknown = 0,
|
||||
Terminal = 1,
|
||||
Process = 2
|
||||
}
|
||||
|
||||
export interface TaskSet {
|
||||
tasks: Task[];
|
||||
extension?: IExtensionDescription;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ import { IOutputService, IOutputChannelRegistry, Extensions as OutputExt, IOutpu
|
||||
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
|
||||
import { ITaskSystem, ITaskResolver, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents } from 'vs/workbench/parts/tasks/common/taskSystem';
|
||||
import { Task, TaskSet, ExecutionEngine, ShowOutput } from 'vs/workbench/parts/tasks/common/tasks';
|
||||
import { Task, TaskSet, TaskGroup, ExecutionEngine, ShowOutput } from 'vs/workbench/parts/tasks/common/tasks';
|
||||
import { ITaskService, TaskServiceEvents, ITaskProvider } from 'vs/workbench/parts/tasks/common/taskService';
|
||||
import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
|
||||
|
||||
@@ -467,7 +467,7 @@ class ProblemReporter implements TaskConfig.IProblemReporter {
|
||||
}
|
||||
|
||||
interface WorkspaceTaskResult {
|
||||
taskSet: TaskSet;
|
||||
set: TaskSet;
|
||||
hasErrors: boolean;
|
||||
}
|
||||
|
||||
@@ -646,7 +646,7 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
|
||||
public build(): TPromise<ITaskSummary> {
|
||||
return this.getTaskSets().then((values) => {
|
||||
let runnable = this.createRunnableTask(values, (set) => set.buildTasks);
|
||||
let runnable = this.createRunnableTask(values, TaskGroup.Build);
|
||||
if (!runnable || !runnable.task) {
|
||||
throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask', 'No build task defined. Mark a task with \'isBuildCommand\' in the tasks.json file.'), TaskErrors.NoBuildTask);
|
||||
}
|
||||
@@ -667,7 +667,7 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
|
||||
public runTest(): TPromise<ITaskSummary> {
|
||||
return this.getTaskSets().then((values) => {
|
||||
let runnable = this.createRunnableTask(values, (set) => set.testTasks);
|
||||
let runnable = this.createRunnableTask(values, TaskGroup.Test);
|
||||
if (!runnable || !runnable.task) {
|
||||
throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask', 'No test task defined. Mark a task with \'isTestCommand\' in the tasks.json file.'), TaskErrors.NoTestTask);
|
||||
}
|
||||
@@ -701,22 +701,21 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
});
|
||||
}
|
||||
|
||||
private createRunnableTask(sets: TaskSet[], idFetcher: (set: TaskSet) => string[]): { task: Task; resolver: ITaskResolver } {
|
||||
private createRunnableTask(sets: TaskSet[], group: TaskGroup): { task: Task; resolver: ITaskResolver } {
|
||||
let uuidMap: IStringDictionary<Task> = Object.create(null);
|
||||
let identifierMap: IStringDictionary<Task> = Object.create(null);
|
||||
|
||||
let taskIds: string[] = [];
|
||||
let primaryTasks: Task[] = [];
|
||||
sets.forEach((set) => {
|
||||
set.tasks.forEach((task) => {
|
||||
uuidMap[task._id] = task;
|
||||
identifierMap[task.identifier] = task;
|
||||
if (group && task.group === group) {
|
||||
primaryTasks.push(task);
|
||||
}
|
||||
});
|
||||
let ids: string[] = idFetcher(set);
|
||||
if (ids) {
|
||||
taskIds.push(...ids);
|
||||
}
|
||||
});
|
||||
if (taskIds.length === 0) {
|
||||
if (primaryTasks.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
let resolver: ITaskResolver = {
|
||||
@@ -728,15 +727,15 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
return identifierMap[id];
|
||||
}
|
||||
};
|
||||
if (taskIds.length === 1) {
|
||||
return { task: resolver.resolve(taskIds[0]), resolver };
|
||||
if (primaryTasks.length === 1) {
|
||||
return { task: primaryTasks[0], resolver };
|
||||
} else {
|
||||
let id: string = UUID.generateUuid();
|
||||
let task: Task = {
|
||||
_id: id,
|
||||
name: id,
|
||||
identifier: id,
|
||||
dependsOn: taskIds,
|
||||
dependsOn: primaryTasks.map(task => task._id),
|
||||
command: undefined,
|
||||
showOutput: ShowOutput.Never
|
||||
};
|
||||
@@ -880,7 +879,7 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
if (this._taskSystem instanceof ProcessTaskSystem) {
|
||||
this._taskSystem.hasErrors(this._configHasErrors);
|
||||
}
|
||||
return value.taskSet;
|
||||
return value.set;
|
||||
});
|
||||
return this._workspaceTasksPromise;
|
||||
}
|
||||
@@ -888,7 +887,7 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
private updateWorkspaceTasks(): void {
|
||||
this._workspaceTasksPromise = this.computeWorkspaceTasks().then(value => {
|
||||
this._configHasErrors = value.hasErrors;
|
||||
return value.taskSet;
|
||||
return value.set;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -897,7 +896,7 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
{
|
||||
let { config, hasParseErrors } = this.getConfiguration();
|
||||
if (hasParseErrors) {
|
||||
return TPromise.as({ taskSet: undefined, hasErrors: true });
|
||||
return TPromise.as({ set: undefined, hasErrors: true });
|
||||
}
|
||||
if (config) {
|
||||
let engine = TaskConfig.ExecutionEngine.from(config);
|
||||
@@ -950,7 +949,7 @@ class TaskService extends EventEmitter implements ITaskService {
|
||||
problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.'));
|
||||
return { taskSet: undefined, hasErrors };
|
||||
}
|
||||
return { taskSet: parseResult.taskSet, hasErrors };
|
||||
return { set: { tasks: parseResult.tasks }, hasErrors };
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,31 +55,17 @@ class ProblemReporter implements IProblemReporter {
|
||||
|
||||
class ConfiguationBuilder {
|
||||
|
||||
public result: Tasks.TaskSet;
|
||||
public result: Tasks.Task[];
|
||||
|
||||
constructor() {
|
||||
this.result = {
|
||||
tasks: [],
|
||||
buildTasks: [],
|
||||
testTasks: []
|
||||
};
|
||||
this.result = [];
|
||||
}
|
||||
|
||||
public task(name: string, command: string): TaskBuilder {
|
||||
let builder = new TaskBuilder(this, name, command);
|
||||
this.result.tasks.push(builder.result);
|
||||
this.result.push(builder.result);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public buildTask(id: string): ConfiguationBuilder {
|
||||
this.result.buildTasks.push(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public testTask(id: string): ConfiguationBuilder {
|
||||
this.result.testTasks.push(id);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class CommandConfigurationBuilder {
|
||||
@@ -153,6 +139,11 @@ class TaskBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public group(value: Tasks.TaskGroup): TaskBuilder {
|
||||
this.result.group = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public args(value: string[]): TaskBuilder {
|
||||
this.result.args = value;
|
||||
return this;
|
||||
@@ -308,12 +299,10 @@ function testDefaultProblemMatcher(external: ExternalTaskRunnerConfiguration, re
|
||||
let reporter = new ProblemReporter();
|
||||
let result = parse(external, reporter);
|
||||
assert.ok(!reporter.receivedMessage);
|
||||
let taskSet = result.taskSet;
|
||||
assert.strictEqual(taskSet.tasks.length, 1);
|
||||
let task = taskSet.tasks[0];
|
||||
assert.strictEqual(result.tasks.length, 1);
|
||||
let task = result.tasks[0];
|
||||
assert.ok(task);
|
||||
assert.strictEqual(task.problemMatchers.length, resolved);
|
||||
|
||||
}
|
||||
|
||||
function testConfiguration(external: ExternalTaskRunnerConfiguration, builder: ConfiguationBuilder): void {
|
||||
@@ -325,52 +314,86 @@ function testConfiguration(external: ExternalTaskRunnerConfiguration, builder: C
|
||||
assertConfiguration(result, builder.result);
|
||||
}
|
||||
|
||||
function assertConfiguration(result: ParseResult, expected: Tasks.TaskSet) {
|
||||
assert.ok(result.validationStatus.isOK());
|
||||
let actual = result.taskSet;
|
||||
assert.strictEqual(typeof actual.tasks, typeof expected.tasks);
|
||||
let actualBuildTasks: string[] = [];
|
||||
let actualTestTasks: string[] = [];
|
||||
if (actual.tasks && expected.tasks) {
|
||||
// We can't compare Ids since the parser uses UUID which are random
|
||||
// So create a new map using the name.
|
||||
let actualTasks: { [key: string]: Tasks.Task; } = Object.create(null);
|
||||
let actualId2Name: { [key: string]: string; } = Object.create(null);
|
||||
actual.tasks.forEach(task => {
|
||||
assert.ok(!actualTasks[task.name]);
|
||||
actualTasks[task.name] = task;
|
||||
actualId2Name[task._id] = task.name;
|
||||
});
|
||||
let expectedTasks: { [key: string]: Tasks.Task; } = Object.create(null);
|
||||
expected.tasks.forEach(task => {
|
||||
assert.ok(!expectedTasks[task.name]);
|
||||
expectedTasks[task.name] = task;
|
||||
});
|
||||
let actualKeys = Object.keys(actualTasks);
|
||||
assert.strictEqual(actualKeys.length, Object.keys(expected.tasks).length);
|
||||
class TaskGroupMap {
|
||||
private _store: { [key: string]: Tasks.Task[] };
|
||||
|
||||
constructor() {
|
||||
this._store = Object.create(null);
|
||||
}
|
||||
|
||||
public add(group: string, task: Tasks.Task): void {
|
||||
let tasks = this._store[group];
|
||||
if (!tasks) {
|
||||
tasks = [];
|
||||
this._store[group] = tasks;
|
||||
}
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
public static assert(actual: TaskGroupMap, expected: TaskGroupMap): void {
|
||||
let actualKeys = Object.keys(actual._store);
|
||||
let expectedKeys = Object.keys(expected._store);
|
||||
if (actualKeys.length === 0 && expectedKeys.length === 0) {
|
||||
return;
|
||||
}
|
||||
assert.strictEqual(actualKeys.length, expectedKeys.length);
|
||||
actualKeys.forEach(key => assert.ok(expected._store[key]));
|
||||
expectedKeys.forEach(key => actual._store[key]);
|
||||
actualKeys.forEach((key) => {
|
||||
let actualTask = actualTasks[key];
|
||||
let expectedTask = expectedTasks[key];
|
||||
assert.ok(expectedTask);
|
||||
assertTask(actualTask, expectedTask);
|
||||
});
|
||||
actual.buildTasks.forEach((id) => {
|
||||
actualBuildTasks.push(actualId2Name[id]);
|
||||
});
|
||||
actual.testTasks.forEach((id) => {
|
||||
actualTestTasks.push(actualId2Name[id]);
|
||||
let actualTasks = actual._store[key];
|
||||
let expectedTasks = expected._store[key];
|
||||
assert.strictEqual(actualTasks.length, expectedTasks.length);
|
||||
if (actualTasks.length === 1) {
|
||||
assert.strictEqual(actualTasks[0].name, expectedTasks[0].name);
|
||||
return;
|
||||
}
|
||||
let expectedTaskMap: { [key: string]: boolean } = Object.create(null);
|
||||
expectedTasks.forEach(task => expectedTaskMap[task.name] = true);
|
||||
actualTasks.forEach(task => delete expectedTaskMap[task.name]);
|
||||
assert.strictEqual(Object.keys(expectedTaskMap).length, 0);
|
||||
});
|
||||
}
|
||||
assertTaskConfig(actualBuildTasks, expected.buildTasks);
|
||||
assertTaskConfig(actualTestTasks, expected.testTasks);
|
||||
}
|
||||
|
||||
function assertTaskConfig(actual: string[], expected: string[]): void {
|
||||
function assertConfiguration(result: ParseResult, expected: Tasks.Task[]): void {
|
||||
assert.ok(result.validationStatus.isOK());
|
||||
let actual = result.tasks;
|
||||
assert.strictEqual(typeof actual, typeof expected);
|
||||
if (actual && expected) {
|
||||
assert.strictEqual(actual.length, expected.length);
|
||||
assert.deepEqual(actual, expected);
|
||||
if (!actual) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't compare Ids since the parser uses UUID which are random
|
||||
// So create a new map using the name.
|
||||
let actualTasks: { [key: string]: Tasks.Task; } = Object.create(null);
|
||||
let actualId2Name: { [key: string]: string; } = Object.create(null);
|
||||
let actualTaskGroups = new TaskGroupMap();
|
||||
actual.forEach(task => {
|
||||
assert.ok(!actualTasks[task.name]);
|
||||
actualTasks[task.name] = task;
|
||||
actualId2Name[task._id] = task.name;
|
||||
if (task.group) {
|
||||
actualTaskGroups.add(task.group, task);
|
||||
}
|
||||
});
|
||||
let expectedTasks: { [key: string]: Tasks.Task; } = Object.create(null);
|
||||
let expectedTaskGroup = new TaskGroupMap();
|
||||
expected.forEach(task => {
|
||||
assert.ok(!expectedTasks[task.name]);
|
||||
expectedTasks[task.name] = task;
|
||||
if (task.group) {
|
||||
expectedTaskGroup.add(task.group, task);
|
||||
}
|
||||
});
|
||||
let actualKeys = Object.keys(actualTasks);
|
||||
assert.strictEqual(actualKeys.length, expected.length);
|
||||
actualKeys.forEach((key) => {
|
||||
let actualTask = actualTasks[key];
|
||||
let expectedTask = expectedTasks[key];
|
||||
assert.ok(expectedTask);
|
||||
assertTask(actualTask, expectedTask);
|
||||
});
|
||||
TaskGroupMap.assert(actualTaskGroups, expectedTaskGroup);
|
||||
}
|
||||
|
||||
function assertTask(actual: Tasks.Task, expected: Tasks.Task) {
|
||||
@@ -461,8 +484,9 @@ function assertProblemPattern(actual: ProblemPattern, expected: ProblemPattern)
|
||||
|
||||
suite('Tasks Configuration parsing tests', () => {
|
||||
test('tasks: all default', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true);
|
||||
testConfiguration(
|
||||
{
|
||||
@@ -472,8 +496,9 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global isShellCommand', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
command().
|
||||
shell(true);
|
||||
@@ -487,9 +512,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global show output silent', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
showOutput(Tasks.ShowOutput.Silent);
|
||||
testConfiguration(
|
||||
@@ -503,8 +529,9 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global promptOnClose default', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true);
|
||||
testConfiguration(
|
||||
{
|
||||
@@ -517,9 +544,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global promptOnClose', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('tsc', 'tsc').
|
||||
suppressTaskName(true).
|
||||
group(Tasks.TaskGroup.Build).
|
||||
promptOnClose(false);
|
||||
testConfiguration(
|
||||
{
|
||||
@@ -532,9 +560,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global promptOnClose default watching', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('tsc', 'tsc').
|
||||
suppressTaskName(true).
|
||||
group(Tasks.TaskGroup.Build).
|
||||
isBackground(true).
|
||||
promptOnClose(false);
|
||||
testConfiguration(
|
||||
@@ -548,9 +577,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global show output never', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
showOutput(Tasks.ShowOutput.Never);
|
||||
testConfiguration(
|
||||
@@ -564,9 +594,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global echo Command', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
command().
|
||||
echo(true);
|
||||
@@ -581,9 +612,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: global args', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
command().
|
||||
args(['--p']);
|
||||
@@ -600,9 +632,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: options - cwd', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
command().
|
||||
options({
|
||||
@@ -621,9 +654,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: options - env', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
command().
|
||||
options({ cwd: '${workspaceRoot}', env: { key: 'value' } });
|
||||
@@ -643,9 +677,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
|
||||
test('tasks: os windows', () => {
|
||||
let name: string = Platform.isWindows ? 'tsc.win' : 'tsc';
|
||||
let builder = new ConfiguationBuilder().buildTask(name);
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task(name, name).
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true);
|
||||
let external: ExternalTaskRunnerConfiguration = {
|
||||
version: '0.1.0',
|
||||
@@ -659,9 +694,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
|
||||
test('tasks: os windows & global isShellCommand', () => {
|
||||
let name: string = Platform.isWindows ? 'tsc.win' : 'tsc';
|
||||
let builder = new ConfiguationBuilder().buildTask(name);
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task(name, name).
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
command().
|
||||
shell(true);
|
||||
@@ -678,9 +714,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
|
||||
test('tasks: os mac', () => {
|
||||
let name: string = Platform.isMacintosh ? 'tsc.osx' : 'tsc';
|
||||
let builder = new ConfiguationBuilder().buildTask(name);
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task(name, name).
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true);
|
||||
let external: ExternalTaskRunnerConfiguration = {
|
||||
version: '0.1.0',
|
||||
@@ -694,9 +731,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
|
||||
test('tasks: os linux', () => {
|
||||
let name: string = Platform.isLinux ? 'tsc.linux' : 'tsc';
|
||||
let builder = new ConfiguationBuilder().buildTask(name);
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task(name, name).
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true);
|
||||
let external: ExternalTaskRunnerConfiguration = {
|
||||
version: '0.1.0',
|
||||
@@ -709,9 +747,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: overwrite showOutput', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
showOutput(Platform.isWindows ? Tasks.ShowOutput.Always : Tasks.ShowOutput.Never).
|
||||
suppressTaskName(true);
|
||||
let external: ExternalTaskRunnerConfiguration = {
|
||||
@@ -726,9 +765,10 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
});
|
||||
|
||||
test('tasks: overwrite echo Command', () => {
|
||||
let builder = new ConfiguationBuilder().buildTask('tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.
|
||||
task('tsc', 'tsc').
|
||||
group(Tasks.TaskGroup.Build).
|
||||
suppressTaskName(true).
|
||||
command().
|
||||
echo(Platform.isWindows ? false : true);
|
||||
@@ -787,8 +827,8 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
}
|
||||
]
|
||||
};
|
||||
let builder = new ConfiguationBuilder().buildTask('taskName');
|
||||
builder.task('taskName', 'tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('taskName', 'tsc').group(Tasks.TaskGroup.Build);
|
||||
testConfiguration(external, builder);
|
||||
});
|
||||
|
||||
@@ -802,8 +842,8 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
}
|
||||
]
|
||||
};
|
||||
let builder = new ConfiguationBuilder().buildTask('build');
|
||||
builder.task('build', 'tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('build', 'tsc').group(Tasks.TaskGroup.Build);
|
||||
testConfiguration(external, builder);
|
||||
});
|
||||
|
||||
@@ -818,8 +858,8 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
}
|
||||
]
|
||||
};
|
||||
let builder = new ConfiguationBuilder().testTask('taskName');
|
||||
builder.task('taskName', 'tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('taskName', 'tsc').group(Tasks.TaskGroup.Test);
|
||||
testConfiguration(external, builder);
|
||||
});
|
||||
|
||||
@@ -833,8 +873,8 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
}
|
||||
]
|
||||
};
|
||||
let builder = new ConfiguationBuilder().testTask('test');
|
||||
builder.task('test', 'tsc');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('test', 'tsc').group(Tasks.TaskGroup.Test);
|
||||
testConfiguration(external, builder);
|
||||
});
|
||||
|
||||
@@ -852,8 +892,9 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
}
|
||||
]
|
||||
};
|
||||
let builder = new ConfiguationBuilder().testTask('test');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('test', 'tsc').
|
||||
group(Tasks.TaskGroup.Test).
|
||||
showOutput(Tasks.ShowOutput.Never).
|
||||
args(['--p']).
|
||||
isBackground(true).
|
||||
@@ -876,8 +917,9 @@ suite('Tasks Configuration parsing tests', () => {
|
||||
}
|
||||
]
|
||||
};
|
||||
let builder = new ConfiguationBuilder().testTask('test');
|
||||
let builder = new ConfiguationBuilder();
|
||||
builder.task('test', 'tsc').
|
||||
group(Tasks.TaskGroup.Test).
|
||||
showOutput(Tasks.ShowOutput.Never).
|
||||
command().
|
||||
echo(true);
|
||||
|
||||
Reference in New Issue
Block a user