taskConfiguration.ts 62.2 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import * as nls from 'vs/nls';
E
Erich Gamma 已提交
7 8 9

import * as Objects from 'vs/base/common/objects';
import { IStringDictionary } from 'vs/base/common/collections';
10
import { Platform } from 'vs/base/common/platform';
E
Erich Gamma 已提交
11 12 13
import * as Types from 'vs/base/common/types';
import * as UUID from 'vs/base/common/uuid';

M
Matt Bierner 已提交
14
import { ValidationStatus, IProblemReporter as IProblemReporterBase } from 'vs/base/common/parsers';
15 16
import {
	NamedProblemMatcher, ProblemMatcher, ProblemMatcherParser, Config as ProblemMatcherConfig,
17
	isNamedProblemMatcher, ProblemMatcherRegistry
18
} from 'vs/workbench/parts/tasks/common/problemMatcher';
19

S
Sandeep Somavarapu 已提交
20
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
21

22
import * as Tasks from '../common/tasks';
D
Dirk Baeumer 已提交
23
import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry';
E
Erich Gamma 已提交
24

25
import { TaskDefinition } from 'vs/workbench/parts/tasks/node/tasks';
A
Alex Ross 已提交
26
import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
27

28
export const enum ShellQuoting {
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
	/**
	 * Default is character escaping.
	 */
	escape = 1,

	/**
	 * Default is strong quoting
	 */
	strong = 2,

	/**
	 * Default is weak quoting.
	 */
	weak = 3
}

export interface ShellQuotingOptions {
	/**
	 * The character used to do character escaping.
	 */
	escape?: string | {
		escapeChar: string;
		charsToEscape: string;
	};

	/**
	 * The character used for string quoting.
	 */
	strong?: string;

	/**
	 * The character used for weak quoting.
	 */
	weak?: string;
}

D
Dirk Baeumer 已提交
65
export interface ShellConfiguration {
66
	executable?: string;
D
Dirk Baeumer 已提交
67
	args?: string[];
68
	quoting?: ShellQuotingOptions;
D
Dirk Baeumer 已提交
69 70
}

71
export interface CommandOptionsConfig {
D
Dirk Baeumer 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
	/**
	 * The current working directory of the executed program or shell.
	 * If omitted VSCode's current workspace root is used.
	 */
	cwd?: string;

	/**
	 * The additional environment of the executed program or shell. If omitted
	 * the parent process' environment is used.
	 */
	env?: IStringDictionary<string>;

	/**
	 * The shell configuration;
	 */
	shell?: ShellConfiguration;
}

90
export interface PresentationOptionsConfig {
91 92 93 94 95
	/**
	 * Controls whether the terminal executing a task is brought to front or not.
	 * Defaults to `RevealKind.Always`.
	 */
	reveal?: string;
D
Dirk Baeumer 已提交
96 97

	/**
98 99 100 101 102 103 104 105 106 107 108
	 * Controls whether the executed command is printed to the output window or terminal as well.
	 */
	echo?: boolean;

	/**
	 * Controls whether the terminal is focus when this task is executed
	 */
	focus?: boolean;

	/**
	 * Controls whether the task runs in a new terminal
D
Dirk Baeumer 已提交
109
	 */
110
	panel?: string;
111 112 113 114

	/**
	 * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message.
	 */
D
Dirk Baeumer 已提交
115
	showReuseMessage?: boolean;
116 117 118 119

	/**
	 * Controls whether the terminal should be cleared before running the task.
	 */
120
	clear?: boolean;
121 122
}

123
export interface RunOptionsConfig {
124
	reevaluateOnRerun?: boolean;
A
Alex Ross 已提交
125
	runOn?: string;
A
Alex Ross 已提交
126 127
}

128
export interface TaskIdentifier {
D
Dirk Baeumer 已提交
129
	type?: string;
130 131 132 133 134 135 136 137
	[name: string]: any;
}

export namespace TaskIdentifier {
	export function is(value: any): value is TaskIdentifier {
		let candidate: TaskIdentifier = value;
		return candidate !== void 0 && Types.isString(value.type);
	}
138
}
D
Dirk Baeumer 已提交
139

140
export interface LegacyTaskProperties {
E
Erich Gamma 已提交
141
	/**
142 143
	 * @deprecated Use `isBackground` instead.
	 * Whether the executed command is kept alive and is watching the file system.
144
	 */
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
	isWatching?: boolean;

	/**
	 * @deprecated Use `group` instead.
	 * Whether this task maps to the default build command.
	 */
	isBuildCommand?: boolean;

	/**
	 * @deprecated Use `group` instead.
	 * Whether this task maps to the default test command.
	 */
	isTestCommand?: boolean;
}

export interface LegacyCommandProperties {

	/**
	 * Whether this is a shell or process
	 */
	type?: string;

	/**
	 * @deprecated Use presentation options
	 * Controls whether the output view of the running tasks is brought to front or not.
	 * See BaseTaskRunnerConfiguration#showOutput for details.
	 */
	showOutput?: string;

	/**
	 * @deprecated Use presentation options
	 * Controls whether the executed command is printed to the output windows as well.
	 */
	echoCommand?: boolean;

180 181 182
	/**
	 * @deprecated Use presentation instead
	 */
183
	terminal?: PresentationOptionsConfig;
184

185 186 187 188 189 190 191 192 193 194 195 196
	/**
	 * @deprecated Use inline commands.
	 * See BaseTaskRunnerConfiguration#suppressTaskName for details.
	 */
	suppressTaskName?: boolean;

	/**
	 * Some commands require that the task argument is highlighted with a special
	 * prefix (e.g. /t: for msbuild). This property can be used to control such
	 * a prefix.
	 */
	taskSelector?: string;
197 198

	/**
D
Dirk Baeumer 已提交
199
	 * @deprecated use the task type instead.
200 201 202 203 204
	 * Specifies whether the command is a shell command and therefore must
	 * be executed in a shell interpreter (e.g. cmd.exe, bash, ...).
	 *
	 * Defaults to false if omitted.
	 */
D
Dirk Baeumer 已提交
205
	isShellCommand?: boolean | ShellConfiguration;
206 207
}

208
export type CommandString = string | string[] | { value: string | string[], quoting: 'escape' | 'strong' | 'weak' };
209

210 211 212 213
export namespace CommandString {
	export function value(value: CommandString): string {
		if (Types.isString(value)) {
			return value;
214 215
		} else if (Types.isStringArray(value)) {
			return value.join(' ');
216
		} else {
217 218 219 220 221
			if (Types.isString(value.value)) {
				return value.value;
			} else {
				return value.value.join(' ');
			}
222 223 224 225 226
		}
	}
}

export interface BaseCommandProperties {
227 228 229 230 231

	/**
	 * The command to be executed. Can be an external program or a shell
	 * command.
	 */
232
	command?: CommandString;
233 234 235 236

	/**
	 * The command options used when the command is executed. Can be omitted.
	 */
237
	options?: CommandOptionsConfig;
238 239 240 241

	/**
	 * The arguments passed to the command or additional arguments passed to the
	 * command when using a global command.
E
Erich Gamma 已提交
242
	 */
243
	args?: CommandString[];
244 245 246
}


247
export interface CommandProperties extends BaseCommandProperties {
D
Dirk Baeumer 已提交
248

249
	/**
250
	 * Windows specific command properties
251
	 */
252
	windows?: BaseCommandProperties;
253

254
	/**
255
	 * OSX specific command properties
256
	 */
257
	osx?: BaseCommandProperties;
258 259

	/**
260
	 * linux specific command properties
261
	 */
262 263
	linux?: BaseCommandProperties;
}
264

D
Dirk Baeumer 已提交
265 266
export interface GroupKind {
	kind?: string;
267
	isDefault?: boolean;
D
Dirk Baeumer 已提交
268 269
}

270
export interface ConfigurationProperties {
271
	/**
272
	 * The task's name
273
	 */
274
	taskName?: string;
E
Erich Gamma 已提交
275

276 277 278 279 280
	/**
	 * The UI label used for the task.
	 */
	label?: string;

E
Erich Gamma 已提交
281
	/**
282 283
	 * An optional indentifier which can be used to reference a task
	 * in a dependsOn or other attributes.
E
Erich Gamma 已提交
284
	 */
285
	identifier?: string;
E
Erich Gamma 已提交
286

287 288 289 290 291
	/**
	 * Whether the executed command is kept alive and runs in the background.
	 */
	isBackground?: boolean;

D
Dirk Baeumer 已提交
292 293 294 295 296
	/**
	 * Whether the task should prompt on close for confirmation if running.
	 */
	promptOnClose?: boolean;

E
Erich Gamma 已提交
297
	/**
298 299
	 * Defines the group the task belongs too.
	 */
D
Dirk Baeumer 已提交
300
	group?: string | GroupKind;
301 302

	/**
303
	 * The other tasks the task depend on
E
Erich Gamma 已提交
304
	 */
305
	dependsOn?: string | TaskIdentifier | (string | TaskIdentifier)[];
E
Erich Gamma 已提交
306 307

	/**
308
	 * Controls the behavior of the used terminal
E
Erich Gamma 已提交
309
	 */
310
	presentation?: PresentationOptionsConfig;
E
Erich Gamma 已提交
311

312 313 314
	/**
	 * Controls shell options.
	 */
315
	options?: CommandOptionsConfig;
316

E
Erich Gamma 已提交
317
	/**
318 319
	 * The problem matcher(s) to use to capture problems in the tasks
	 * output.
E
Erich Gamma 已提交
320
	 */
321
	problemMatcher?: ProblemMatcherConfig.ProblemMatcherType;
A
Alex Ross 已提交
322 323 324 325

	/**
	 * Task run options. Control run related properties.
	 */
326
	runOptions?: RunOptionsConfig;
327
}
E
Erich Gamma 已提交
328

329
export interface CustomTask extends CommandProperties, ConfigurationProperties {
E
Erich Gamma 已提交
330
	/**
331
	 * Custom tasks have the type CUSTOMIZED_TASK_TYPE
E
Erich Gamma 已提交
332
	 */
333
	type?: string;
E
Erich Gamma 已提交
334

335
}
336

337
export interface ConfiguringTask extends ConfigurationProperties {
E
Erich Gamma 已提交
338
	/**
339
	 * The contributed type of the task
E
Erich Gamma 已提交
340
	 */
341
	type?: string;
E
Erich Gamma 已提交
342 343 344 345 346
}

/**
 * The base task runner configuration
 */
347
export interface BaseTaskRunnerConfiguration {
E
Erich Gamma 已提交
348 349 350 351 352

	/**
	 * The command to be executed. Can be an external program or a shell
	 * command.
	 */
353
	command?: CommandString;
E
Erich Gamma 已提交
354 355

	/**
356 357
	 * @deprecated Use type instead
	 *
E
Erich Gamma 已提交
358 359 360 361 362 363 364
	 * Specifies whether the command is a shell command and therefore must
	 * be executed in a shell interpreter (e.g. cmd.exe, bash, ...).
	 *
	 * Defaults to false if omitted.
	 */
	isShellCommand?: boolean;

365 366 367 368 369
	/**
	 * The task type
	 */
	type?: string;

E
Erich Gamma 已提交
370 371 372
	/**
	 * The command options used when the command is executed. Can be omitted.
	 */
373
	options?: CommandOptionsConfig;
E
Erich Gamma 已提交
374 375 376 377

	/**
	 * The arguments passed to the command. Can be omitted.
	 */
378
	args?: CommandString[];
E
Erich Gamma 已提交
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395

	/**
	 * Controls whether the output view of the running tasks is brought to front or not.
	 * Valid values are:
	 *   "always": bring the output window always to front when a task is executed.
	 *   "silent": only bring it to front if no problem matcher is defined for the task executed.
	 *   "never": never bring the output window to front.
	 *
	 * If omitted "always" is used.
	 */
	showOutput?: string;

	/**
	 * Controls whether the executed command is printed to the output windows as well.
	 */
	echoCommand?: boolean;

396 397 398 399
	/**
	 * The group
	 */
	group?: string | GroupKind;
D
Dirk Baeumer 已提交
400 401 402
	/**
	 * Controls the behavior of the used terminal
	 */
403
	presentation?: PresentationOptionsConfig;
D
Dirk Baeumer 已提交
404

E
Erich Gamma 已提交
405 406 407 408 409 410 411 412 413 414 415 416
	/**
	 * If set to false the task name is added as an additional argument to the
	 * command when executed. If set to true the task name is suppressed. If
	 * omitted false is used.
	 */
	suppressTaskName?: boolean;

	/**
	 * Some commands require that the task argument is highlighted with a special
	 * prefix (e.g. /t: for msbuild). This property can be used to control such
	 * a prefix.
	 */
J
Johannes Rieken 已提交
417
	taskSelector?: string;
E
Erich Gamma 已提交
418 419 420 421 422 423 424 425 426

	/**
	 * The problem matcher(s) to used if a global command is exucuted (e.g. no tasks
	 * are defined). A tasks.json file can either contain a global problemMatcher
	 * property or a tasks property but not both.
	 */
	problemMatcher?: ProblemMatcherConfig.ProblemMatcherType;

	/**
427 428
	 * @deprecated Use `isBackground` instead.
	 *
E
Erich Gamma 已提交
429
	 * Specifies whether a global command is a watching the filesystem. A task.json
430
	 * file can either contain a global isWatching property or a tasks property
E
Erich Gamma 已提交
431 432 433 434
	 * but not both.
	 */
	isWatching?: boolean;

435 436 437 438 439
	/**
	 * Specifies whether a global command is a background task.
	 */
	isBackground?: boolean;

D
Dirk Baeumer 已提交
440 441 442 443 444
	/**
	 * Whether the task should prompt on close for confirmation if running.
	 */
	promptOnClose?: boolean;

E
Erich Gamma 已提交
445 446 447 448
	/**
	 * The configuration of the available tasks. A tasks.json file can either
	 * contain a global problemMatcher property or a tasks property but not both.
	 */
449
	tasks?: (CustomTask | ConfiguringTask)[];
E
Erich Gamma 已提交
450 451 452 453 454

	/**
	 * Problem matcher declarations
	 */
	declares?: ProblemMatcherConfig.NamedProblemMatcher[];
A
Alex Ross 已提交
455 456 457 458 459

	/**
	 * Optional user input varaibles.
	 */
	inputs?: ConfiguredInput[];
E
Erich Gamma 已提交
460 461 462 463 464 465 466 467
}

/**
 * A configuration of an external build system. BuildConfiguration.buildSystem
 * must be set to 'program'
 */
export interface ExternalTaskRunnerConfiguration extends BaseTaskRunnerConfiguration {

468 469
	_runner?: string;

470 471 472 473 474
	/**
	 * Determines the runner to use
	 */
	runner?: string;

E
Erich Gamma 已提交
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
	/**
	 * The config's version number
	 */
	version: string;

	/**
	 * Windows specific task configuration
	 */
	windows?: BaseTaskRunnerConfiguration;

	/**
	 * Mac specific task configuration
	 */
	osx?: BaseTaskRunnerConfiguration;

	/**
	 * Linux speciif task configuration
	 */
	linux?: BaseTaskRunnerConfiguration;
}

enum ProblemMatcherKind {
	Unknown,
	String,
	ProblemMatcher,
	Array
}

503 504
const EMPTY_ARRAY: any[] = [];
Object.freeze(EMPTY_ARRAY);
E
Erich Gamma 已提交
505

506
function assignProperty<T, K extends keyof T>(target: T, source: Partial<T>, key: K) {
507 508 509
	if (source[key] !== void 0) {
		target[key] = source[key];
	}
E
Erich Gamma 已提交
510 511
}

512
function fillProperty<T, K extends keyof T>(target: T, source: Partial<T>, key: K) {
513 514 515 516 517 518
	if (target[key] === void 0 && source[key] !== void 0) {
		target[key] = source[key];
	}
}


519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
interface ParserType<T> {
	isEmpty(value: T): boolean;
	assignProperties(target: T, source: T): T;
	fillProperties(target: T, source: T): T;
	fillDefaults(value: T, context: ParseContext): T;
	freeze(value: T): Readonly<T>;
}

interface MetaData<T, U> {
	property: keyof T;
	type?: ParserType<U>;
}


function _isEmpty<T>(this: void, value: T, properties: MetaData<T, any>[]): boolean {
	if (value === void 0 || value === null) {
		return true;
	}
	for (let meta of properties) {
		let property = value[meta.property];
		if (property !== void 0 && property !== null) {
			if (meta.type !== void 0 && !meta.type.isEmpty(property)) {
				return false;
			} else if (!Array.isArray(property) || property.length > 0) {
				return false;
			}
		}
	}
	return true;
}

function _assignProperties<T>(this: void, target: T, source: T, properties: MetaData<T, any>[]): T {
	if (_isEmpty(source, properties)) {
		return target;
	}
	if (_isEmpty(target, properties)) {
		return source;
	}
	for (let meta of properties) {
		let property = meta.property;
		let value: any;
		if (meta.type !== void 0) {
			value = meta.type.assignProperties(target[property], source[property]);
		} else {
			value = source[property];
		}
		if (value !== void 0 && value !== null) {
			target[property] = value;
		}
	}
	return target;
}

function _fillProperties<T>(this: void, target: T, source: T, properties: MetaData<T, any>[]): T {
	if (_isEmpty(source, properties)) {
		return target;
	}
	if (_isEmpty(target, properties)) {
		return source;
	}
	for (let meta of properties) {
		let property = meta.property;
		let value: any;
		if (meta.type) {
			value = meta.type.fillProperties(target[property], source[property]);
584
		} else if (target[property] === void 0) {
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
			value = source[property];
		}
		if (value !== void 0 && value !== null) {
			target[property] = value;
		}
	}
	return target;
}

function _fillDefaults<T>(this: void, target: T, defaults: T, properties: MetaData<T, any>[], context: ParseContext): T {
	if (target && Object.isFrozen(target)) {
		return target;
	}
	if (target === void 0 || target === null) {
		if (defaults !== void 0 && defaults !== null) {
J
Johannes Rieken 已提交
600
			return Objects.deepClone(defaults);
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
		} else {
			return undefined;
		}
	}
	for (let meta of properties) {
		let property = meta.property;
		if (target[property] !== void 0) {
			continue;
		}
		let value: any;
		if (meta.type) {
			value = meta.type.fillDefaults(target[property], context);
		} else {
			value = defaults[property];
		}

		if (value !== void 0 && value !== null) {
			target[property] = value;
		}
	}
	return target;
}

function _freeze<T>(this: void, target: T, properties: MetaData<T, any>[]): Readonly<T> {
	if (target === void 0 || target === null) {
		return undefined;
	}
	if (Object.isFrozen(target)) {
		return target;
	}
	for (let meta of properties) {
		if (meta.type) {
			let value = target[meta.property];
			if (value) {
				meta.type.freeze(value);
			}
		}
	}
	Object.freeze(target);
	return target;
}

A
Alex Ross 已提交
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
export namespace RunOnOptions {
	export function fromString(value: string | undefined): Tasks.RunOnOptions {
		if (!value) {
			return Tasks.RunOnOptions.default;
		}
		switch (value.toLowerCase()) {
			case 'folderopen':
				return Tasks.RunOnOptions.folderOpen;
			case 'default':
			default:
				return Tasks.RunOnOptions.default;
		}
	}
}

export namespace RunOptions {
	export function fromConfiguration(value: RunOptionsConfig | undefined): Tasks.RunOptions {
		return {
661
			reevaluateOnRerun: value ? value.reevaluateOnRerun : true,
A
Alex Ross 已提交
662 663 664 665 666
			runOn: value ? RunOnOptions.fromString(value.runOn) : Tasks.RunOnOptions.default
		};
	}
}

667
interface ParseContext {
S
Sandeep Somavarapu 已提交
668
	workspaceFolder: IWorkspaceFolder;
669
	problemReporter: IProblemReporter;
670
	namedProblemMatchers: IStringDictionary<NamedProblemMatcher>;
671
	uuidMap: UUIDMap;
672 673
	engine: Tasks.ExecutionEngine;
	schemaVersion: Tasks.JsonSchemaVersion;
674
	platform: Platform;
E
Erich Gamma 已提交
675 676
}

677

678
namespace ShellConfiguration {
679

680
	const properties: MetaData<Tasks.ShellConfiguration, void>[] = [{ property: 'executable' }, { property: 'args' }, { property: 'quoting' }];
681

682 683
	export function is(value: any): value is ShellConfiguration {
		let candidate: ShellConfiguration = value;
D
Dirk Baeumer 已提交
684
		return candidate && (Types.isString(candidate.executable) || Types.isStringArray(candidate.args));
685 686 687 688 689 690
	}

	export function from(this: void, config: ShellConfiguration, context: ParseContext): Tasks.ShellConfiguration {
		if (!is(config)) {
			return undefined;
		}
D
Dirk Baeumer 已提交
691 692 693 694
		let result: ShellConfiguration = {};
		if (config.executable !== void 0) {
			result.executable = config.executable;
		}
695 696 697
		if (config.args !== void 0) {
			result.args = config.args.slice();
		}
698 699 700 701
		if (config.quoting !== void 0) {
			result.quoting = Objects.deepClone(config.quoting);
		}

702 703 704
		return result;
	}

705 706
	export function isEmpty(this: void, value: Tasks.ShellConfiguration): boolean {
		return _isEmpty(value, properties);
707 708
	}

709 710
	export function assignProperties(this: void, target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration {
		return _assignProperties(target, source, properties);
711 712
	}

713 714
	export function fillProperties(this: void, target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration {
		return _fillProperties(target, source, properties);
715 716
	}

717 718
	export function fillDefaults(this: void, value: Tasks.ShellConfiguration, context: ParseContext): Tasks.ShellConfiguration {
		return value;
719 720
	}

721
	export function freeze(this: void, value: Tasks.ShellConfiguration): Readonly<Tasks.ShellConfiguration> {
722
		if (!value) {
723
			return undefined;
724
		}
725
		return Object.freeze(value);
726 727 728
	}
}

729
namespace CommandOptions {
730 731

	const properties: MetaData<Tasks.CommandOptions, Tasks.ShellConfiguration>[] = [{ property: 'cwd' }, { property: 'env' }, { property: 'shell', type: ShellConfiguration }];
732
	const defaults: CommandOptionsConfig = { cwd: '${workspaceFolder}' };
733

734
	export function from(this: void, options: CommandOptionsConfig, context: ParseContext): Tasks.CommandOptions {
735
		let result: Tasks.CommandOptions = {};
736 737 738 739
		if (options.cwd !== void 0) {
			if (Types.isString(options.cwd)) {
				result.cwd = options.cwd;
			} else {
740
				context.problemReporter.warn(nls.localize('ConfigurationParser.invalidCWD', 'Warning: options.cwd must be of type string. Ignoring value {0}\n', options.cwd));
741 742 743
			}
		}
		if (options.env !== void 0) {
J
Johannes Rieken 已提交
744
			result.env = Objects.deepClone(options.env);
745
		}
D
Dirk Baeumer 已提交
746
		result.shell = ShellConfiguration.from(options.shell, context);
747
		return isEmpty(result) ? undefined : result;
E
Erich Gamma 已提交
748 749
	}

750
	export function isEmpty(value: Tasks.CommandOptions): boolean {
751
		return _isEmpty(value, properties);
E
Erich Gamma 已提交
752 753
	}

754
	export function assignProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions {
755 756
		if (isEmpty(source)) {
			return target;
E
Erich Gamma 已提交
757
		}
758 759
		if (isEmpty(target)) {
			return source;
E
Erich Gamma 已提交
760
		}
761
		assignProperty(target, source, 'cwd');
762 763 764 765 766
		if (target.env === void 0) {
			target.env = source.env;
		} else if (source.env !== void 0) {
			let env: { [key: string]: string; } = Object.create(null);
			Object.keys(target.env).forEach(key => env[key] = target.env[key]);
767
			Object.keys(source.env).forEach(key => env[key] = source.env[key]);
768 769
			target.env = env;
		}
770 771 772 773 774
		target.shell = ShellConfiguration.assignProperties(target.shell, source.shell);
		return target;
	}

	export function fillProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions {
775
		return _fillProperties(target, source, properties);
E
Erich Gamma 已提交
776 777
	}

778 779
	export function fillDefaults(value: Tasks.CommandOptions, context: ParseContext): Tasks.CommandOptions {
		return _fillDefaults(value, defaults, properties, context);
780 781
	}

782 783
	export function freeze(value: Tasks.CommandOptions): Readonly<Tasks.CommandOptions> {
		return _freeze(value, properties);
E
Erich Gamma 已提交
784
	}
785
}
E
Erich Gamma 已提交
786

787
namespace CommandConfiguration {
788

789
	export namespace PresentationOptions {
790
		const properties: MetaData<Tasks.PresentationOptions, void>[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }, { property: 'clear' }];
791

792
		interface PresentationOptionsShape extends LegacyCommandProperties {
793
			presentation?: PresentationOptionsConfig;
794 795 796
		}

		export function from(this: void, config: PresentationOptionsShape, context: ParseContext): Tasks.PresentationOptions {
797 798 799
			let echo: boolean;
			let reveal: Tasks.RevealKind;
			let focus: boolean;
800
			let panel: Tasks.PanelKind;
D
Dirk Baeumer 已提交
801
			let showReuseMessage: boolean;
802
			let clear: boolean;
D
Dirk Baeumer 已提交
803 804 805 806 807 808
			if (Types.isBoolean(config.echoCommand)) {
				echo = config.echoCommand;
			}
			if (Types.isString(config.showOutput)) {
				reveal = Tasks.RevealKind.fromString(config.showOutput);
			}
809 810 811 812
			let presentation = config.presentation || config.terminal;
			if (presentation) {
				if (Types.isBoolean(presentation.echo)) {
					echo = presentation.echo;
D
Dirk Baeumer 已提交
813
				}
814 815
				if (Types.isString(presentation.reveal)) {
					reveal = Tasks.RevealKind.fromString(presentation.reveal);
D
Dirk Baeumer 已提交
816
				}
817 818
				if (Types.isBoolean(presentation.focus)) {
					focus = presentation.focus;
819
				}
820 821
				if (Types.isString(presentation.panel)) {
					panel = Tasks.PanelKind.fromString(presentation.panel);
822
				}
D
Dirk Baeumer 已提交
823 824
				if (Types.isBoolean(presentation.showReuseMessage)) {
					showReuseMessage = presentation.showReuseMessage;
825
				}
826 827
				if (Types.isBoolean(presentation.clear)) {
					clear = presentation.clear;
828
				}
D
Dirk Baeumer 已提交
829
			}
830
			if (echo === void 0 && reveal === void 0 && focus === void 0 && panel === void 0 && showReuseMessage === void 0 && clear === void 0) {
D
Dirk Baeumer 已提交
831 832
				return undefined;
			}
833
			return { echo, reveal, focus, panel, showReuseMessage, clear };
D
Dirk Baeumer 已提交
834 835
		}

836
		export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions {
837
			return _assignProperties(target, source, properties);
838 839
		}

840
		export function fillProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions {
841
			return _fillProperties(target, source, properties);
D
Dirk Baeumer 已提交
842 843
		}

844
		export function fillDefaults(value: Tasks.PresentationOptions, context: ParseContext): Tasks.PresentationOptions {
845
			let defaultEcho = context.engine === Tasks.ExecutionEngine.Terminal ? true : false;
846
			return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false }, properties, context);
D
Dirk Baeumer 已提交
847 848
		}

849
		export function freeze(value: Tasks.PresentationOptions): Readonly<Tasks.PresentationOptions> {
850
			return _freeze(value, properties);
D
Dirk Baeumer 已提交
851 852
		}

853
		export function isEmpty(this: void, value: Tasks.PresentationOptions): boolean {
854
			return _isEmpty(value, properties);
D
Dirk Baeumer 已提交
855 856 857
		}
	}

858 859 860 861 862 863 864
	namespace ShellString {
		export function from(this: void, value: CommandString): Tasks.CommandString {
			if (value === void 0 || value === null) {
				return undefined;
			}
			if (Types.isString(value)) {
				return value;
865 866 867 868 869 870 871 872 873 874 875 876 877
			} else if (Types.isStringArray(value)) {
				return value.join(' ');
			} else {
				let quoting = Tasks.ShellQuoting.from(value.quoting);
				let result = Types.isString(value.value) ? value.value : Types.isStringArray(value.value) ? value.value.join(' ') : undefined;
				if (result) {
					return {
						value: result,
						quoting: quoting
					};
				} else {
					return undefined;
				}
878 879 880 881
			}
		}
	}

882 883 884 885 886 887 888 889 890 891 892
	interface BaseCommandConfiguationShape extends BaseCommandProperties, LegacyCommandProperties {
	}

	interface CommandConfiguationShape extends BaseCommandConfiguationShape {
		windows?: BaseCommandConfiguationShape;
		osx?: BaseCommandConfiguationShape;
		linux?: BaseCommandConfiguationShape;
	}

	const properties: MetaData<Tasks.CommandConfiguration, any>[] = [
		{ property: 'runtime' }, { property: 'name' }, { property: 'options', type: CommandOptions },
893
		{ property: 'args' }, { property: 'taskSelector' }, { property: 'suppressTaskName' },
894
		{ property: 'presentation', type: PresentationOptions }
895 896
	];

897 898
	export function from(this: void, config: CommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration {
		let result: Tasks.CommandConfiguration = fromBase(config, context);
899

900
		let osConfig: Tasks.CommandConfiguration = undefined;
901
		if (config.windows && context.platform === Platform.Windows) {
902
			osConfig = fromBase(config.windows, context);
903
		} else if (config.osx && context.platform === Platform.Mac) {
904
			osConfig = fromBase(config.osx, context);
905
		} else if (config.linux && context.platform === Platform.Linux) {
906 907 908
			osConfig = fromBase(config.linux, context);
		}
		if (osConfig) {
909
			result = assignProperties(result, osConfig, context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0);
910 911 912 913
		}
		return isEmpty(result) ? undefined : result;
	}

914 915 916
	function fromBase(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration {
		let result: Tasks.CommandConfiguration = {
			name: undefined,
917
			runtime: undefined,
918
			presentation: undefined
919
		};
920 921

		result.name = ShellString.from(config.command);
D
Dirk Baeumer 已提交
922
		if (Types.isString(config.type)) {
923 924 925
			if (config.type === 'shell' || config.type === 'process') {
				result.runtime = Tasks.RuntimeType.fromString(config.type);
			}
D
Dirk Baeumer 已提交
926 927 928
		}
		let isShellConfiguration = ShellConfiguration.is(config.isShellCommand);
		if (Types.isBoolean(config.isShellCommand) || isShellConfiguration) {
929
			result.runtime = Tasks.RuntimeType.Shell;
930
		} else if (config.isShellCommand !== void 0) {
931 932
			result.runtime = !!config.isShellCommand ? Tasks.RuntimeType.Shell : Tasks.RuntimeType.Process;
		}
933

934
		if (config.args !== void 0) {
935 936 937
			result.args = [];
			for (let arg of config.args) {
				let converted = ShellString.from(arg);
D
Dirk Baeumer 已提交
938
				if (converted !== void 0) {
939 940
					result.args.push(converted);
				} else {
941 942 943 944 945 946
					context.problemReporter.error(
						nls.localize(
							'ConfigurationParser.inValidArg',
							'Error: command argument must either be a string or a quoted string. Provided value is:\n{0}',
							arg ? JSON.stringify(arg, undefined, 4) : 'undefined'
						));
947
				}
948
			}
E
Erich Gamma 已提交
949
		}
950 951
		if (config.options !== void 0) {
			result.options = CommandOptions.from(config.options, context);
D
Dirk Baeumer 已提交
952 953
			if (result.options && result.options.shell === void 0 && isShellConfiguration) {
				result.options.shell = ShellConfiguration.from(config.isShellCommand as ShellConfiguration, context);
954
				if (context.engine !== Tasks.ExecutionEngine.Terminal) {
D
Dirk Baeumer 已提交
955 956 957
					context.problemReporter.warn(nls.localize('ConfigurationParser.noShell', 'Warning: shell configuration is only supported when executing tasks in the terminal.'));
				}
			}
E
Erich Gamma 已提交
958
		}
959 960 961
		let panel = PresentationOptions.from(config, context);
		if (panel) {
			result.presentation = panel;
E
Erich Gamma 已提交
962
		}
963 964
		if (Types.isString(config.taskSelector)) {
			result.taskSelector = config.taskSelector;
E
Erich Gamma 已提交
965
		}
966 967 968
		if (Types.isBoolean(config.suppressTaskName)) {
			result.suppressTaskName = config.suppressTaskName;
		}
969
		return isEmpty(result) ? undefined : result;
E
Erich Gamma 已提交
970 971
	}

972 973 974 975
	export function hasCommand(value: Tasks.CommandConfiguration): boolean {
		return value && !!value.name;
	}

976
	export function isEmpty(value: Tasks.CommandConfiguration): boolean {
977
		return _isEmpty(value, properties);
978 979
	}

980
	export function assignProperties(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration, overwriteArgs: boolean): Tasks.CommandConfiguration {
981 982
		if (isEmpty(source)) {
			return target;
E
Erich Gamma 已提交
983
		}
984 985
		if (isEmpty(target)) {
			return source;
E
Erich Gamma 已提交
986
		}
987
		assignProperty(target, source, 'name');
988
		assignProperty(target, source, 'runtime');
989 990
		assignProperty(target, source, 'taskSelector');
		assignProperty(target, source, 'suppressTaskName');
991
		if (source.args !== void 0) {
992
			if (target.args === void 0 || overwriteArgs) {
993 994 995
				target.args = source.args;
			} else {
				target.args = target.args.concat(source.args);
E
Erich Gamma 已提交
996 997
			}
		}
998
		target.presentation = PresentationOptions.assignProperties(target.presentation, source.presentation);
999 1000 1001 1002
		target.options = CommandOptions.assignProperties(target.options, source.options);
		return target;
	}

1003 1004 1005 1006
	export function fillProperties(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration): Tasks.CommandConfiguration {
		return _fillProperties(target, source, properties);
	}

1007 1008 1009 1010 1011 1012
	export function fillGlobals(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration, taskName: string): Tasks.CommandConfiguration {
		if (isEmpty(source)) {
			return target;
		}
		target = target || {
			name: undefined,
1013
			runtime: undefined,
1014
			presentation: undefined
1015
		};
1016 1017 1018 1019
		if (target.name === void 0) {
			fillProperty(target, source, 'name');
			fillProperty(target, source, 'taskSelector');
			fillProperty(target, source, 'suppressTaskName');
1020
			let args: Tasks.CommandString[] = source.args ? source.args.slice() : [];
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
			if (!target.suppressTaskName) {
				if (target.taskSelector !== void 0) {
					args.push(target.taskSelector + taskName);
				} else {
					args.push(taskName);
				}
			}
			if (target.args) {
				args = args.concat(target.args);
			}
			target.args = args;
		}
1033
		fillProperty(target, source, 'runtime');
1034

1035
		target.presentation = PresentationOptions.fillProperties(target.presentation, source.presentation);
1036 1037
		target.options = CommandOptions.fillProperties(target.options, source.options);

1038
		return target;
E
Erich Gamma 已提交
1039 1040
	}

1041
	export function fillDefaults(value: Tasks.CommandConfiguration, context: ParseContext): void {
1042 1043 1044
		if (!value || Object.isFrozen(value)) {
			return;
		}
1045 1046
		if (value.name !== void 0 && value.runtime === void 0) {
			value.runtime = Tasks.RuntimeType.Process;
1047
		}
1048
		value.presentation = PresentationOptions.fillDefaults(value.presentation, context);
1049
		if (!isEmpty(value)) {
1050
			value.options = CommandOptions.fillDefaults(value.options, context);
1051
		}
1052 1053 1054
		if (value.args === void 0) {
			value.args = EMPTY_ARRAY;
		}
1055 1056
		if (value.suppressTaskName === void 0) {
			value.suppressTaskName = false;
E
Erich Gamma 已提交
1057 1058 1059
		}
	}

1060 1061
	export function freeze(value: Tasks.CommandConfiguration): Readonly<Tasks.CommandConfiguration> {
		return _freeze(value, properties);
E
Erich Gamma 已提交
1062
	}
1063
}
E
Erich Gamma 已提交
1064

1065 1066 1067
namespace ProblemMatcherConverter {

	export function namedFrom(this: void, declares: ProblemMatcherConfig.NamedProblemMatcher[], context: ParseContext): IStringDictionary<NamedProblemMatcher> {
J
Johannes Rieken 已提交
1068
		let result: IStringDictionary<NamedProblemMatcher> = Object.create(null);
1069 1070

		if (!Types.isArray(declares)) {
E
Erich Gamma 已提交
1071 1072
			return result;
		}
1073
		(<ProblemMatcherConfig.NamedProblemMatcher[]>declares).forEach((value) => {
1074
			let namedProblemMatcher = (new ProblemMatcherParser(context.problemReporter)).parse(value);
1075
			if (isNamedProblemMatcher(namedProblemMatcher)) {
E
Erich Gamma 已提交
1076
				result[namedProblemMatcher.name] = namedProblemMatcher;
1077
			} else {
1078
				context.problemReporter.error(nls.localize('ConfigurationParser.noName', 'Error: Problem Matcher in declare scope must have a name:\n{0}\n', JSON.stringify(value, undefined, 4)));
E
Erich Gamma 已提交
1079 1080 1081 1082 1083
			}
		});
		return result;
	}

1084 1085 1086 1087 1088 1089 1090
	export function from(this: void, config: ProblemMatcherConfig.ProblemMatcherType, context: ParseContext): ProblemMatcher[] {
		let result: ProblemMatcher[] = [];
		if (config === void 0) {
			return result;
		}
		let kind = getProblemMatcherKind(config);
		if (kind === ProblemMatcherKind.Unknown) {
1091
			context.problemReporter.warn(nls.localize(
1092 1093 1094
				'ConfigurationParser.unknownMatcherKind',
				'Warning: the defined problem matcher is unknown. Supported types are string | ProblemMatcher | (string | ProblemMatcher)[].\n{0}\n',
				JSON.stringify(config, null, 4)));
E
Erich Gamma 已提交
1095
			return result;
1096
		} else if (kind === ProblemMatcherKind.String || kind === ProblemMatcherKind.ProblemMatcher) {
1097
			let matcher = resolveProblemMatcher(config as ProblemMatcherConfig.ProblemMatcher, context);
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
			if (matcher) {
				result.push(matcher);
			}
		} else if (kind === ProblemMatcherKind.Array) {
			let problemMatchers = <(string | ProblemMatcherConfig.ProblemMatcher)[]>config;
			problemMatchers.forEach(problemMatcher => {
				let matcher = resolveProblemMatcher(problemMatcher, context);
				if (matcher) {
					result.push(matcher);
				}
			});
		}
		return result;
	}

	function getProblemMatcherKind(this: void, value: ProblemMatcherConfig.ProblemMatcherType): ProblemMatcherKind {
		if (Types.isString(value)) {
			return ProblemMatcherKind.String;
		} else if (Types.isArray(value)) {
			return ProblemMatcherKind.Array;
		} else if (!Types.isUndefined(value)) {
			return ProblemMatcherKind.ProblemMatcher;
E
Erich Gamma 已提交
1120
		} else {
1121
			return ProblemMatcherKind.Unknown;
E
Erich Gamma 已提交
1122 1123 1124
		}
	}

1125 1126 1127 1128 1129 1130 1131
	function resolveProblemMatcher(this: void, value: string | ProblemMatcherConfig.ProblemMatcher, context: ParseContext): ProblemMatcher {
		if (Types.isString(value)) {
			let variableName = <string>value;
			if (variableName.length > 1 && variableName[0] === '$') {
				variableName = variableName.substring(1);
				let global = ProblemMatcherRegistry.get(variableName);
				if (global) {
J
Johannes Rieken 已提交
1132
					return Objects.deepClone(global);
1133 1134 1135
				}
				let localProblemMatcher = context.namedProblemMatchers[variableName];
				if (localProblemMatcher) {
J
Johannes Rieken 已提交
1136
					localProblemMatcher = Objects.deepClone(localProblemMatcher);
1137 1138 1139 1140 1141
					// remove the name
					delete localProblemMatcher.name;
					return localProblemMatcher;
				}
			}
1142
			context.problemReporter.error(nls.localize('ConfigurationParser.invalidVaraibleReference', 'Error: Invalid problemMatcher reference: {0}\n', value));
1143 1144 1145
			return undefined;
		} else {
			let json = <ProblemMatcherConfig.ProblemMatcher>value;
1146
			return new ProblemMatcherParser(context.problemReporter).parse(json);
1147 1148 1149 1150
		}
	}
}

1151 1152 1153
const source: Tasks.TaskSource = {
	kind: Tasks.TaskSourceKind.Workspace,
	label: 'Workspace',
1154
	config: undefined
1155
};
1156

1157
namespace GroupKind {
1158
	export function from(this: void, external: string | GroupKind): [string, Tasks.GroupType] {
1159 1160 1161 1162 1163
		if (external === void 0) {
			return undefined;
		}
		if (Types.isString(external)) {
			if (Tasks.TaskGroup.is(external)) {
1164
				return [external, Tasks.GroupType.user];
1165
			} else {
D
Dirk Baeumer 已提交
1166 1167 1168
				return undefined;
			}
		}
1169 1170 1171 1172
		if (!Types.isString(external.kind) || !Tasks.TaskGroup.is(external.kind)) {
			return undefined;
		}
		let group: string = external.kind;
1173
		let isDefault: boolean = !!external.isDefault;
1174

1175
		return [group, isDefault ? Tasks.GroupType.default : Tasks.GroupType.user];
D
Dirk Baeumer 已提交
1176
	}
1177 1178
}

1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
namespace TaskDependency {
	export function from(this: void, external: string | TaskIdentifier, context: ParseContext): Tasks.TaskDependency {
		if (Types.isString(external)) {
			return { workspaceFolder: context.workspaceFolder, task: external };
		} else if (TaskIdentifier.is(external)) {
			return { workspaceFolder: context.workspaceFolder, task: TaskDefinition.createTaskIdentifier(external as Tasks.TaskIdentifier, context.problemReporter) };
		} else {
			return undefined;
		}
	}
}

1191
namespace ConfigurationProperties {
D
Dirk Baeumer 已提交
1192

1193
	const properties: MetaData<Tasks.ConfigurationProperties, any>[] = [
1194

1195 1196 1197 1198
		{ property: 'name' }, { property: 'identifier' }, { property: 'group' }, { property: 'isBackground' },
		{ property: 'promptOnClose' }, { property: 'dependsOn' },
		{ property: 'presentation', type: CommandConfiguration.PresentationOptions }, { property: 'problemMatchers' }
	];
1199

1200
	export function from(this: void, external: ConfigurationProperties, context: ParseContext, includeCommandOptions: boolean): Tasks.ConfigurationProperties {
1201
		if (!external) {
1202
			return undefined;
E
Erich Gamma 已提交
1203
		}
1204 1205 1206 1207
		let result: Tasks.ConfigurationProperties = {};
		if (Types.isString(external.taskName)) {
			result.name = external.taskName;
		}
1208 1209 1210
		if (Types.isString(external.label) && context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0) {
			result.name = external.label;
		}
1211 1212 1213 1214 1215 1216 1217 1218 1219
		if (Types.isString(external.identifier)) {
			result.identifier = external.identifier;
		}
		if (external.isBackground !== void 0) {
			result.isBackground = !!external.isBackground;
		}
		if (external.promptOnClose !== void 0) {
			result.promptOnClose = !!external.promptOnClose;
		}
D
Dirk Baeumer 已提交
1220 1221 1222
		if (external.group !== void 0) {
			if (Types.isString(external.group) && Tasks.TaskGroup.is(external.group)) {
				result.group = external.group;
1223
				result.groupType = Tasks.GroupType.user;
D
Dirk Baeumer 已提交
1224 1225 1226 1227
			} else {
				let values = GroupKind.from(external.group);
				if (values) {
					result.group = values[0];
1228
					result.groupType = values[1];
D
Dirk Baeumer 已提交
1229 1230
				}
			}
1231 1232
		}
		if (external.dependsOn !== void 0) {
1233 1234 1235
			if (Types.isArray(external.dependsOn)) {
				result.dependsOn = external.dependsOn.map(item => TaskDependency.from(item, context));
			} else {
1236
				result.dependsOn = [TaskDependency.from(external.dependsOn, context)];
1237
			}
1238
		}
1239
		if (includeCommandOptions && (external.presentation !== void 0 || (external as LegacyCommandProperties).terminal !== void 0)) {
1240 1241
			result.presentation = CommandConfiguration.PresentationOptions.from(external, context);
		}
1242 1243 1244
		if (includeCommandOptions && (external.options !== void 0)) {
			result.options = CommandOptions.from(external.options, context);
		}
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
		if (external.problemMatcher) {
			result.problemMatchers = ProblemMatcherConverter.from(external.problemMatcher, context);
		}
		return isEmpty(result) ? undefined : result;
	}

	export function isEmpty(this: void, value: Tasks.ConfigurationProperties): boolean {
		return _isEmpty(value, properties);
	}
}

namespace ConfiguringTask {

1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
	const grunt = 'grunt.';
	const jake = 'jake.';
	const gulp = 'gulp.';
	const npm = 'vscode.npm.';
	const typescript = 'vscode.typescript.';

	interface CustomizeShape {
		customize: string;
	}

1268
	export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number): Tasks.ConfiguringTask {
1269 1270 1271 1272
		if (!external) {
			return undefined;
		}
		let type = external.type;
1273 1274
		let customize = (external as CustomizeShape).customize;
		if (!type && !customize) {
1275
			context.problemReporter.error(nls.localize('ConfigurationParser.noTaskType', 'Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4)));
1276 1277
			return undefined;
		}
D
Dirk Baeumer 已提交
1278
		let typeDeclaration = TaskDefinitionRegistry.get(type);
1279
		if (!typeDeclaration) {
1280
			let message = nls.localize('ConfigurationParser.noTypeDefinition', 'Error: there is no registered task type \'{0}\'. Did you miss to install an extension that provides a corresponding task provider?', type);
1281 1282 1283
			context.problemReporter.error(message);
			return undefined;
		}
1284
		let identifier: Tasks.TaskIdentifier;
1285 1286
		if (Types.isString(customize)) {
			if (customize.indexOf(grunt) === 0) {
1287
				identifier = { type: 'grunt', task: customize.substring(grunt.length) };
1288
			} else if (customize.indexOf(jake) === 0) {
1289
				identifier = { type: 'jake', task: customize.substring(jake.length) };
1290
			} else if (customize.indexOf(gulp) === 0) {
1291
				identifier = { type: 'gulp', task: customize.substring(gulp.length) };
1292
			} else if (customize.indexOf(npm) === 0) {
1293
				identifier = { type: 'npm', script: customize.substring(npm.length + 4) };
1294
			} else if (customize.indexOf(typescript) === 0) {
1295
				identifier = { type: 'typescript', tsconfig: customize.substring(typescript.length + 6) };
1296
			}
1297
		} else {
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
			if (Types.isString(external.type)) {
				identifier = external as Tasks.TaskIdentifier;
			}
		}
		if (identifier === void 0) {
			context.problemReporter.error(nls.localize(
				'ConfigurationParser.missingType',
				'Error: the task configuration \'{0}\' is missing the required property \'type\'. The task configuration will be ignored.', JSON.stringify(external, undefined, 0)
			));
			return undefined;
1308
		}
1309
		let taskIdentifier: Tasks.KeyedTaskIdentifier = TaskDefinition.createTaskIdentifier(identifier, context.problemReporter);
1310
		if (taskIdentifier === void 0) {
1311 1312
			context.problemReporter.error(nls.localize(
				'ConfigurationParser.incorrectType',
D
Dániel Tar 已提交
1313
				'Error: the task configuration \'{0}\' is using an unknown type. The task configuration will be ignored.', JSON.stringify(external, undefined, 0)
1314
			));
1315
			return undefined;
1316
		}
1317
		let configElement: Tasks.TaskSourceConfigElement = {
1318
			workspaceFolder: context.workspaceFolder,
1319 1320 1321 1322
			file: '.vscode\\tasks.json',
			index,
			element: external
		};
1323 1324 1325
		let result: Tasks.ConfiguringTask = {
			type: type,
			configures: taskIdentifier,
1326
			_id: `${typeDeclaration.extensionId}.${taskIdentifier._key}`,
M
Matt Bierner 已提交
1327
			_source: Objects.assign({} as Tasks.WorkspaceTaskSource, source, { config: configElement }),
A
Alex Ross 已提交
1328
			_label: undefined,
A
Alex Ross 已提交
1329
			runOptions: RunOptions.fromConfiguration(external.runOptions)
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
		};
		let configuration = ConfigurationProperties.from(external, context, true);
		if (configuration) {
			result = Objects.assign(result, configuration);
			if (result.name) {
				result._label = result.name;
			} else {
				let label = result.configures.type;
				if (typeDeclaration.required && typeDeclaration.required.length > 0) {
					for (let required of typeDeclaration.required) {
						let value = result.configures[required];
						if (value) {
							label = label + ' ' + value;
							break;
						}
					}
D
Dirk Baeumer 已提交
1346
				}
1347
				result._label = label;
1348
			}
1349 1350
			if (!result.identifier) {
				result.identifier = taskIdentifier._key;
E
Erich Gamma 已提交
1351
			}
1352 1353 1354 1355 1356 1357 1358
		}
		return result;
	}
}

namespace CustomTask {

1359
	export function from(this: void, external: CustomTask, context: ParseContext, index: number): Tasks.CustomTask {
1360 1361 1362 1363 1364
		if (!external) {
			return undefined;
		}
		let type = external.type;
		if (type === void 0 || type === null) {
1365
			type = Tasks.CUSTOMIZED_TASK_TYPE;
1366
		}
1367
		if (type !== Tasks.CUSTOMIZED_TASK_TYPE && type !== 'shell' && type !== 'process') {
1368
			context.problemReporter.error(nls.localize('ConfigurationParser.notCustom', 'Error: tasks is not declared as a custom task. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4)));
1369 1370 1371
			return undefined;
		}
		let taskName = external.taskName;
1372 1373 1374
		if (Types.isString(external.label) && context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0) {
			taskName = external.label;
		}
1375
		if (!taskName) {
D
Dirk Baeumer 已提交
1376
			context.problemReporter.error(nls.localize('ConfigurationParser.noTaskName', 'Error: a task must provide a label property. The task will be ignored.\n{0}\n', JSON.stringify(external, null, 4)));
1377 1378 1379 1380
			return undefined;
		}

		let result: Tasks.CustomTask = {
1381
			type: Tasks.CUSTOMIZED_TASK_TYPE,
1382
			_id: context.uuidMap.getUUID(taskName),
M
Matt Bierner 已提交
1383
			_source: Objects.assign({} as Tasks.WorkspaceTaskSource, source, { config: { index, element: external, file: '.vscode\\tasks.json', workspaceFolder: context.workspaceFolder } }),
1384 1385 1386
			_label: taskName,
			name: taskName,
			identifier: taskName,
1387
			hasDefinedMatchers: false,
A
Alex Ross 已提交
1388
			command: undefined,
A
Alex Ross 已提交
1389
			runOptions: RunOptions.fromConfiguration(external.runOptions)
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
		};
		let configuration = ConfigurationProperties.from(external, context, false);
		if (configuration) {
			result = Objects.assign(result, configuration);
		}
		let supportLegacy: boolean = true; //context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0;
		if (supportLegacy) {
			let legacy: LegacyTaskProperties = external as LegacyTaskProperties;
			if (result.isBackground === void 0 && legacy.isWatching !== void 0) {
				result.isBackground = !!legacy.isWatching;
1400
			}
1401 1402 1403 1404 1405
			if (result.group === void 0) {
				if (legacy.isBuildCommand === true) {
					result.group = Tasks.TaskGroup.Build;
				} else if (legacy.isTestCommand === true) {
					result.group = Tasks.TaskGroup.Test;
1406
				}
1407
			}
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422
		}
		let command: Tasks.CommandConfiguration = CommandConfiguration.from(external, context);
		if (command) {
			result.command = command;
		}
		if (external.command !== void 0) {
			// if the task has its own command then we suppress the
			// task name by default.
			command.suppressTaskName = true;
		}
		return result;
	}

	export function fillGlobals(task: Tasks.CustomTask, globals: Globals): void {
		// We only merge a command from a global definition if there is no dependsOn
1423 1424
		// or there is a dependsOn and a defined command.
		if (CommandConfiguration.hasCommand(task.command) || task.dependsOn === void 0) {
1425 1426
			task.command = CommandConfiguration.fillGlobals(task.command, globals.command, task.name);
		}
1427 1428 1429 1430
		if (task.problemMatchers === void 0 && globals.problemMatcher !== void 0) {
			task.problemMatchers = Objects.deepClone(globals.problemMatcher);
			task.hasDefinedMatchers = true;
		}
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
		// promptOnClose is inferred from isBackground if available
		if (task.promptOnClose === void 0 && task.isBackground === void 0 && globals.promptOnClose !== void 0) {
			task.promptOnClose = globals.promptOnClose;
		}
	}

	export function fillDefaults(task: Tasks.CustomTask, context: ParseContext): void {
		CommandConfiguration.fillDefaults(task.command, context);
		if (task.promptOnClose === void 0) {
			task.promptOnClose = task.isBackground !== void 0 ? !task.isBackground : true;
		}
		if (task.isBackground === void 0) {
			task.isBackground = false;
		}
		if (task.problemMatchers === void 0) {
			task.problemMatchers = EMPTY_ARRAY;
		}
1448 1449
		if (task.group !== void 0 && task.groupType === void 0) {
			task.groupType = Tasks.GroupType.user;
1450
		}
1451 1452
	}

1453
	export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfigurationProperties & { _id: string, _source: Tasks.WorkspaceTaskSource }): Tasks.CustomTask {
1454
		let result: Tasks.CustomTask = {
1455
			_id: configuredProps._id,
1456
			_source: Objects.assign({}, configuredProps._source, { customizes: contributedTask.defines }),
1457
			_label: configuredProps.name || contributedTask._label,
1458
			type: Tasks.CUSTOMIZED_TASK_TYPE,
1459 1460
			command: contributedTask.command,
			name: configuredProps.name || contributedTask.name,
1461
			identifier: configuredProps.identifier || contributedTask.identifier,
A
Alex Ross 已提交
1462 1463
			hasDefinedMatchers: false,
			runOptions: contributedTask.runOptions,
1464 1465 1466 1467
		};
		let resultConfigProps: Tasks.ConfigurationProperties = result;

		assignProperty(resultConfigProps, configuredProps, 'group');
1468
		assignProperty(resultConfigProps, configuredProps, 'groupType');
1469 1470 1471 1472 1473 1474
		assignProperty(resultConfigProps, configuredProps, 'isBackground');
		assignProperty(resultConfigProps, configuredProps, 'dependsOn');
		assignProperty(resultConfigProps, configuredProps, 'problemMatchers');
		assignProperty(resultConfigProps, configuredProps, 'promptOnClose');
		result.command.presentation = CommandConfiguration.PresentationOptions.assignProperties(
			result.command.presentation, configuredProps.presentation);
1475
		result.command.options = CommandOptions.assignProperties(result.command.options, configuredProps.options);
1476 1477 1478

		let contributedConfigProps: Tasks.ConfigurationProperties = contributedTask;
		fillProperty(resultConfigProps, contributedConfigProps, 'group');
1479
		fillProperty(resultConfigProps, contributedConfigProps, 'groupType');
1480 1481 1482 1483 1484 1485
		fillProperty(resultConfigProps, contributedConfigProps, 'isBackground');
		fillProperty(resultConfigProps, contributedConfigProps, 'dependsOn');
		fillProperty(resultConfigProps, contributedConfigProps, 'problemMatchers');
		fillProperty(resultConfigProps, contributedConfigProps, 'promptOnClose');
		result.command.presentation = CommandConfiguration.PresentationOptions.fillProperties(
			result.command.presentation, contributedConfigProps.presentation);
1486
		result.command.options = CommandOptions.fillProperties(result.command.options, contributedConfigProps.options);
1487

1488 1489 1490 1491
		if (contributedTask.hasDefinedMatchers === true) {
			result.hasDefinedMatchers = true;
		}

1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
		return result;
	}
}

interface TaskParseResult {
	custom: Tasks.CustomTask[];
	configured: Tasks.ConfiguringTask[];
}

namespace TaskParser {

	function isCustomTask(value: CustomTask | ConfiguringTask): value is CustomTask {
		let type = value.type;
1505
		let customize = (value as any).customize;
1506
		return customize === void 0 && (type === void 0 || type === null || type === Tasks.CUSTOMIZED_TASK_TYPE || type === 'shell' || type === 'process');
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517
	}

	export function from(this: void, externals: (CustomTask | ConfiguringTask)[], globals: Globals, context: ParseContext): TaskParseResult {
		let result: TaskParseResult = { custom: [], configured: [] };
		if (!externals) {
			return result;
		}
		let defaultBuildTask: { task: Tasks.Task; rank: number; } = { task: undefined, rank: -1 };
		let defaultTestTask: { task: Tasks.Task; rank: number; } = { task: undefined, rank: -1 };
		let schema2_0_0: boolean = context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0;

1518 1519
		for (let index = 0; index < externals.length; index++) {
			let external = externals[index];
1520
			if (isCustomTask(external)) {
1521
				let customTask = CustomTask.from(external, context, index);
1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
				if (customTask) {
					CustomTask.fillGlobals(customTask, globals);
					CustomTask.fillDefaults(customTask, context);
					if (schema2_0_0) {
						if ((customTask.command === void 0 || customTask.command.name === void 0) && (customTask.dependsOn === void 0 || customTask.dependsOn.length === 0)) {
							context.problemReporter.error(nls.localize(
								'taskConfiguration.noCommandOrDependsOn', 'Error: the task \'{0}\' neither specifies a command nor a dependsOn property. The task will be ignored. Its definition is:\n{1}',
								customTask.name, JSON.stringify(external, undefined, 4)
							));
							continue;
						}
					} else {
						if (customTask.command === void 0 || customTask.command.name === void 0) {
							context.problemReporter.warn(nls.localize(
								'taskConfiguration.noCommand', 'Error: the task \'{0}\' doesn\'t define a command. The task will be ignored. Its definition is:\n{1}',
								customTask.name, JSON.stringify(external, undefined, 4)
							));
							continue;
						}
					}
					if (customTask.group === Tasks.TaskGroup.Build && defaultBuildTask.rank < 2) {
						defaultBuildTask.task = customTask;
						defaultBuildTask.rank = 2;
					} else if (customTask.group === Tasks.TaskGroup.Test && defaultTestTask.rank < 2) {
						defaultTestTask.task = customTask;
						defaultTestTask.rank = 2;
					} else if (customTask.name === 'build' && defaultBuildTask.rank < 1) {
						defaultBuildTask.task = customTask;
						defaultBuildTask.rank = 1;
					} else if (customTask.name === 'test' && defaultTestTask.rank < 1) {
						defaultTestTask.task = customTask;
						defaultTestTask.rank = 1;
					}
					result.custom.push(customTask);
D
Dirk Baeumer 已提交
1556 1557
				}
			} else {
1558
				let configuredTask = ConfiguringTask.from(external, context, index);
1559 1560
				if (configuredTask) {
					result.configured.push(configuredTask);
D
Dirk Baeumer 已提交
1561
				}
E
Erich Gamma 已提交
1562
			}
1563
		}
1564
		if (defaultBuildTask.rank > -1 && defaultBuildTask.rank < 2) {
1565
			defaultBuildTask.task.group = Tasks.TaskGroup.Build;
1566
			defaultBuildTask.task.groupType = Tasks.GroupType.user;
1567
		} else if (defaultTestTask.rank > -1 && defaultTestTask.rank < 2) {
1568
			defaultTestTask.task.group = Tasks.TaskGroup.Test;
1569
			defaultTestTask.task.groupType = Tasks.GroupType.user;
E
Erich Gamma 已提交
1570
		}
1571 1572

		return result;
E
Erich Gamma 已提交
1573 1574
	}

1575
	export function assignTasks(target: Tasks.CustomTask[], source: Tasks.CustomTask[]): Tasks.CustomTask[] {
1576
		if (source === void 0 || source.length === 0) {
1577 1578
			return target;
		}
1579
		if (target === void 0 || target.length === 0) {
1580 1581 1582
			return source;
		}

1583
		if (source) {
1584
			// Tasks are keyed by ID but we need to merge by name
1585
			let map: IStringDictionary<Tasks.CustomTask> = Object.create(null);
1586 1587
			target.forEach((task) => {
				map[task.name] = task;
1588 1589
			});

1590 1591
			source.forEach((task) => {
				map[task.name] = task;
1592
			});
1593
			let newTarget: Tasks.CustomTask[] = [];
1594 1595 1596
			target.forEach(task => {
				newTarget.push(map[task.name]);
				delete map[task.name];
E
Erich Gamma 已提交
1597
			});
1598 1599
			Object.keys(map).forEach(key => newTarget.push(map[key]));
			target = newTarget;
E
Erich Gamma 已提交
1600
		}
1601 1602 1603 1604 1605
		return target;
	}
}

interface Globals {
1606
	command?: Tasks.CommandConfiguration;
1607
	problemMatcher?: ProblemMatcher[];
1608 1609 1610 1611 1612
	promptOnClose?: boolean;
	suppressTaskName?: boolean;
}

namespace Globals {
1613 1614 1615

	export function from(config: ExternalTaskRunnerConfiguration, context: ParseContext): Globals {
		let result = fromBase(config, context);
1616
		let osGlobals: Globals | undefined = undefined;
1617
		if (config.windows && context.platform === Platform.Windows) {
1618
			osGlobals = fromBase(config.windows, context);
1619
		} else if (config.osx && context.platform === Platform.Mac) {
1620
			osGlobals = fromBase(config.osx, context);
1621
		} else if (config.linux && context.platform === Platform.Linux) {
1622 1623 1624
			osGlobals = fromBase(config.linux, context);
		}
		if (osGlobals) {
1625
			result = Globals.assignProperties(result, osGlobals);
1626 1627 1628 1629 1630
		}
		let command = CommandConfiguration.from(config, context);
		if (command) {
			result.command = command;
		}
1631
		Globals.fillDefaults(result, context);
1632 1633 1634 1635 1636
		Globals.freeze(result);
		return result;
	}

	export function fromBase(this: void, config: BaseTaskRunnerConfiguration, context: ParseContext): Globals {
1637 1638 1639 1640 1641 1642 1643
		let result: Globals = {};
		if (config.suppressTaskName !== void 0) {
			result.suppressTaskName = !!config.suppressTaskName;
		}
		if (config.promptOnClose !== void 0) {
			result.promptOnClose = !!config.promptOnClose;
		}
1644 1645 1646
		if (config.problemMatcher) {
			result.problemMatcher = ProblemMatcherConverter.from(config.problemMatcher, context);
		}
E
Erich Gamma 已提交
1647 1648 1649
		return result;
	}

1650
	export function isEmpty(value: Globals): boolean {
D
Dirk Baeumer 已提交
1651
		return !value || value.command === void 0 && value.promptOnClose === void 0 && value.suppressTaskName === void 0;
1652 1653
	}

1654
	export function assignProperties(target: Globals, source: Globals): Globals {
1655 1656
		if (isEmpty(source)) {
			return target;
E
Erich Gamma 已提交
1657
		}
1658 1659 1660
		if (isEmpty(target)) {
			return source;
		}
1661 1662
		assignProperty(target, source, 'promptOnClose');
		assignProperty(target, source, 'suppressTaskName');
1663
		return target;
E
Erich Gamma 已提交
1664 1665
	}

1666
	export function fillDefaults(value: Globals, context: ParseContext): void {
1667 1668 1669
		if (!value) {
			return;
		}
1670
		CommandConfiguration.fillDefaults(value.command, context);
1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686
		if (value.suppressTaskName === void 0) {
			value.suppressTaskName = false;
		}
		if (value.promptOnClose === void 0) {
			value.promptOnClose = true;
		}
	}

	export function freeze(value: Globals): void {
		Object.freeze(value);
		if (value.command) {
			CommandConfiguration.freeze(value.command);
		}
	}
}

1687 1688
export namespace ExecutionEngine {

1689
	export function from(config: ExternalTaskRunnerConfiguration): Tasks.ExecutionEngine {
1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
		let runner = config.runner || config._runner;
		let result: Tasks.ExecutionEngine;
		if (runner) {
			switch (runner) {
				case 'terminal':
					result = Tasks.ExecutionEngine.Terminal;
					break;
				case 'process':
					result = Tasks.ExecutionEngine.Process;
					break;
			}
		}
		let schemaVersion = JsonSchemaVersion.from(config);
		if (schemaVersion === Tasks.JsonSchemaVersion.V0_1_0) {
			return result || Tasks.ExecutionEngine.Process;
		} else if (schemaVersion === Tasks.JsonSchemaVersion.V2_0_0) {
			return Tasks.ExecutionEngine.Terminal;
		} else {
			throw new Error('Shouldn\'t happen.');
		}
1710
	}
1711
}
1712

1713 1714
export namespace JsonSchemaVersion {

1715
	const _default: Tasks.JsonSchemaVersion = Tasks.JsonSchemaVersion.V2_0_0;
1716

1717 1718 1719
	export function from(config: ExternalTaskRunnerConfiguration): Tasks.JsonSchemaVersion {
		let version = config.version;
		if (!version) {
1720
			return _default;
1721 1722 1723 1724
		}
		switch (version) {
			case '0.1.0':
				return Tasks.JsonSchemaVersion.V0_1_0;
1725
			case '2.0.0':
1726
				return Tasks.JsonSchemaVersion.V2_0_0;
1727 1728
			default:
				return _default;
1729
		}
1730 1731 1732
	}
}

1733 1734
export interface ParseResult {
	validationStatus: ValidationStatus;
1735 1736
	custom: Tasks.CustomTask[];
	configured: Tasks.ConfiguringTask[];
1737
	engine: Tasks.ExecutionEngine;
1738 1739
}

1740
export interface IProblemReporter extends IProblemReporterBase {
1741 1742
}

1743 1744 1745 1746 1747
class UUIDMap {

	private last: IStringDictionary<string | string[]>;
	private current: IStringDictionary<string | string[]>;

1748
	constructor(other?: UUIDMap) {
1749
		this.current = Object.create(null);
1750 1751 1752 1753 1754 1755 1756 1757 1758 1759
		if (other) {
			for (let key of Object.keys(other.current)) {
				let value = other.current[key];
				if (Array.isArray(value)) {
					this.current[key] = value.slice();
				} else {
					this.current[key] = value;
				}
			}
		}
1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
	}

	public start(): void {
		this.last = this.current;
		this.current = Object.create(null);
	}

	public getUUID(identifier: string): string {
		let lastValue = this.last[identifier];
		let result: string;
		if (lastValue !== void 0) {
			if (Array.isArray(lastValue)) {
				result = lastValue.shift();
				if (lastValue.length === 0) {
					delete this.last[identifier];
				}
			} else {
				result = lastValue;
				delete this.last[identifier];
			}
		}
		if (result === void 0) {
			result = UUID.generateUuid();
		}
		let currentValue = this.current[identifier];
		if (currentValue === void 0) {
			this.current[identifier] = result;
		} else {
			if (Array.isArray(currentValue)) {
				currentValue.push(result);
			} else {
				let arrayValue: string[] = [currentValue];
				arrayValue.push(result);
				this.current[identifier] = arrayValue;
			}
		}
		return result;
	}

	public finish(): void {
		this.last = undefined;
	}
}

1804 1805
class ConfigurationParser {

S
Sandeep Somavarapu 已提交
1806
	private workspaceFolder: IWorkspaceFolder;
1807
	private problemReporter: IProblemReporter;
1808
	private uuidMap: UUIDMap;
1809
	private platform: Platform;
1810

1811
	constructor(workspaceFolder: IWorkspaceFolder, platform: Platform, problemReporter: IProblemReporter, uuidMap: UUIDMap) {
1812
		this.workspaceFolder = workspaceFolder;
1813
		this.platform = platform;
1814
		this.problemReporter = problemReporter;
1815
		this.uuidMap = uuidMap;
1816 1817 1818
	}

	public run(fileConfig: ExternalTaskRunnerConfiguration): ParseResult {
1819
		let engine = ExecutionEngine.from(fileConfig);
1820
		let schemaVersion = JsonSchemaVersion.from(fileConfig);
1821
		let context: ParseContext = {
1822
			workspaceFolder: this.workspaceFolder,
1823
			problemReporter: this.problemReporter,
1824
			uuidMap: this.uuidMap,
1825
			namedProblemMatchers: undefined,
1826
			engine,
1827 1828
			schemaVersion,
			platform: this.platform
1829 1830
		};
		let taskParseResult = this.createTaskRunnerConfiguration(fileConfig, context);
1831
		return {
1832
			validationStatus: this.problemReporter.status,
1833 1834
			custom: taskParseResult.custom,
			configured: taskParseResult.configured,
1835
			engine
1836 1837 1838
		};
	}

1839
	private createTaskRunnerConfiguration(fileConfig: ExternalTaskRunnerConfiguration, context: ParseContext): TaskParseResult {
1840
		let globals = Globals.from(fileConfig, context);
1841
		if (this.problemReporter.status.isFatal()) {
1842
			return { custom: [], configured: [] };
1843 1844
		}
		context.namedProblemMatchers = ProblemMatcherConverter.namedFrom(fileConfig.declares, context);
1845
		let globalTasks: Tasks.CustomTask[];
1846
		let externalGlobalTasks: (ConfiguringTask | CustomTask)[];
1847
		if (fileConfig.windows && context.platform === Platform.Windows) {
1848
			globalTasks = TaskParser.from(fileConfig.windows.tasks, globals, context).custom;
1849
			externalGlobalTasks = fileConfig.windows.tasks;
1850
		} else if (fileConfig.osx && context.platform === Platform.Mac) {
1851
			globalTasks = TaskParser.from(fileConfig.osx.tasks, globals, context).custom;
1852
			externalGlobalTasks = fileConfig.osx.tasks;
1853
		} else if (fileConfig.linux && context.platform === Platform.Linux) {
1854
			globalTasks = TaskParser.from(fileConfig.linux.tasks, globals, context).custom;
1855 1856 1857 1858 1859 1860 1861 1862 1863 1864
			externalGlobalTasks = fileConfig.linux.tasks;
		}
		if (context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0 && globalTasks && globalTasks.length > 0 && externalGlobalTasks && externalGlobalTasks.length > 0) {
			let taskContent: string[] = [];
			for (let task of externalGlobalTasks) {
				taskContent.push(JSON.stringify(task, null, 4));
			}
			context.problemReporter.error(
				nls.localize(
					'TaskParse.noOsSpecificGlobalTasks',
1865
					'Task version 2.0.0 doesn\'t support global OS specific tasks. Convert them to a task with a OS specific command. Affected tasks are:\n{0}', taskContent.join('\n'))
1866
			);
1867 1868
		}

1869
		let result: TaskParseResult = { custom: undefined, configured: undefined };
1870
		if (fileConfig.tasks) {
1871
			result = TaskParser.from(fileConfig.tasks, globals, context);
1872
		}
1873
		if (globalTasks) {
1874
			result.custom = TaskParser.assignTasks(result.custom, globalTasks);
1875 1876
		}

1877
		if ((!result.custom || result.custom.length === 0) && (globals.command && globals.command.name)) {
1878
			let matchers: ProblemMatcher[] = ProblemMatcherConverter.from(fileConfig.problemMatcher, context);
1879
			let isBackground = fileConfig.isBackground ? !!fileConfig.isBackground : fileConfig.isWatching ? !!fileConfig.isWatching : undefined;
1880
			let name = Tasks.CommandString.value(globals.command.name);
1881
			let task: Tasks.CustomTask = {
1882
				_id: context.uuidMap.getUUID(name),
M
Matt Bierner 已提交
1883
				_source: Objects.assign({} as Tasks.WorkspaceTaskSource, source, { config: { index: -1, element: fileConfig, workspaceFolder: context.workspaceFolder } }),
1884
				_label: name,
1885
				type: Tasks.CUSTOMIZED_TASK_TYPE,
1886 1887
				name: name,
				identifier: name,
1888
				group: Tasks.TaskGroup.Build,
1889 1890
				command: {
					name: undefined,
1891
					runtime: undefined,
1892
					presentation: undefined,
1893 1894
					suppressTaskName: true
				},
1895
				isBackground: isBackground,
1896 1897
				problemMatchers: matchers,
				hasDefinedMatchers: false,
1898
				runOptions: { reevaluateOnRerun: true },
1899
			};
1900 1901 1902
			let value = GroupKind.from(fileConfig.group);
			if (value) {
				task.group = value[0];
1903
				task.groupType = value[1];
1904 1905 1906
			} else if (fileConfig.group === 'none') {
				task.group = undefined;
			}
1907 1908 1909
			CustomTask.fillGlobals(task, globals);
			CustomTask.fillDefaults(task, context);
			result.custom = [task];
1910
		}
1911 1912
		result.custom = result.custom || [];
		result.configured = result.configured || [];
1913
		return result;
1914
	}
E
Erich Gamma 已提交
1915 1916
}

1917
let uuidMaps: Map<string, UUIDMap> = new Map();
1918
export function parse(workspaceFolder: IWorkspaceFolder, platform: Platform, configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter): ParseResult {
1919 1920 1921 1922 1923
	let uuidMap = uuidMaps.get(workspaceFolder.uri.toString());
	if (!uuidMap) {
		uuidMap = new UUIDMap();
		uuidMaps.set(workspaceFolder.uri.toString(), uuidMap);
	}
1924 1925
	try {
		uuidMap.start();
1926
		return (new ConfigurationParser(workspaceFolder, platform, logger, uuidMap)).run(configuration);
1927 1928 1929
	} finally {
		uuidMap.finish();
	}
1930 1931
}

1932
export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfigurationProperties & { _id: string; _source: Tasks.WorkspaceTaskSource }): Tasks.CustomTask {
1933 1934 1935
	return CustomTask.createCustomTask(contributedTask, configuredProps);
}

D
Dirk Baeumer 已提交
1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
/*
class VersionConverter {
	constructor(private problemReporter: IProblemReporter) {
	}

	public convert(fromConfig: ExternalTaskRunnerConfiguration): ExternalTaskRunnerConfiguration {
		let result: ExternalTaskRunnerConfiguration;
		result.version = '2.0.0';
		if (Array.isArray(fromConfig.tasks)) {

		} else {
			result.tasks = [];
		}


		return result;
	}

	private convertGlobalTask(fromConfig: ExternalTaskRunnerConfiguration): TaskDescription {
		let command: string = this.getGlobalCommand(fromConfig);
		if (!command) {
			this.problemReporter.error(nls.localize('Converter.noGlobalName', 'No global command specified. Can\'t convert to 2.0.0 version.'));
			return undefined;
		}
		let result: TaskDescription = {
			taskName: command
		};
		if (fromConfig.isShellCommand) {
			result.type = 'shell';
		} else {
			result.type = 'process';
			result.args = fromConfig.args;
		}
		if (fromConfig.)

		return result;
	}

	private getGlobalCommand(fromConfig: ExternalTaskRunnerConfiguration): string {
		if (fromConfig.command) {
			return fromConfig.command;
		} else if (fromConfig.windows && fromConfig.windows.command) {
			return fromConfig.windows.command;
		} else if (fromConfig.osx && fromConfig.osx.command) {
			return fromConfig.osx.command;
		} else if (fromConfig.linux && fromConfig.linux.command) {
			return fromConfig.linux.command;
		} else {
			return undefined;
		}
	}

	private createCommandLine(command: string, args: string[], isWindows: boolean): string {
		let result: string[];
		let commandHasSpace = false;
		let argHasSpace = false;
		if (TaskDescription.hasUnescapedSpaces(command)) {
			result.push(`"${command}"`);
			commandHasSpace = true;
		} else {
			result.push(command);
		}
		if (args) {
			for (let arg of args) {
				if (TaskDescription.hasUnescapedSpaces(arg)) {
					result.push(`"${arg}"`);
					argHasSpace= true;
				} else {
					result.push(arg);
				}
			}
		}
		return result.join(' ');
	}

}
J
Johannes Rieken 已提交
2012
*/