Skip to content

Explore future direction of the task runner system. #15179

@dbaeumer

Description

@dbaeumer

THIS IS WORK IN PROGRESS

This issue will be used to discuss the future direction of the task runner system in VS Code.

History

The task runner system in VS Code got introduce in one of the first public releases of VS Code to allow calling out to an external program to do some work (building, compiling, running tests, upload, publish). Besides running the external program the task running system parsed the output to produce problem markers (errors, warnings and info messages).

When introduced there was no extension API available nor a terminal.

Current Limitations

The task running framework has currently the following major limitations:

  • can only execute one task at a time
  • can only handle one command definition (all tasks share the same command (e.g. program)). This is being worked around using a script to execute different programs.
  • no contribution mechanism for problem matchers
  • no contribution mechanism for build tasks. There can only be exactly one.
  • no input. If the external program reads from stdin the task simply hangs.
  • no support for output encoding (this is currently hard coded to utf8)
  • no support for ANSI control characters
  • minimal status UI (spinning progress)

Open Questions

Do we need more than one build task?

There are two major reasons for this

  • problem matcher must be very specific (for example match on the file name extension) to not create false problems. Consider an example where a compiler A and B produce the same error format. If there are executed in one task with both problem matchers for MA and MB attached then matcher MB might produce a marker although the error was generated by compiler B. Since problem formats don't vary much this in not a uncommon case. Making the problem matcher file extension specific requires quite some customization (users need to define the problem matcher in the task.json or we support variables in problem matchers)
  • watch task in gulp run in parallel which can result in interleaved output from different compilers if two files are saved using a Save All action. Consider the following gulp file as a demonstration:
var gulp = require('gulp');
var watch = require('gulp-watch');

gulp.task('watch-less', function () {
    return watch('**/*.less', undefined, function(vinyl) {
		let counter = 0;
		let interval = setInterval(function() {
			console.log('Compiling less file ' + counter);
			if (++counter === 5) {
				clearInterval(interval);
			}
		}, 10);
	});
});

gulp.task('watch-ts', function () {
    return watch('**/*.ts', undefined, function(vinyl) {
		let counter = 0;
		let interval = setInterval(function() {
			console.log('Compiling ts file ' + counter);
			if (++counter === 5) {
				clearInterval(interval);
			}
		}, 10);
	});
});
gulp.task('watch', ['watch-less', 'watch-ts']);

Having one build task which trigger gulp watch and then saving changes to a ts and less file with Save All produces the following single output.

[20:51:36] Using gulpfile P:\mseng\VSCode\Playgrounds\gulp\gulpfile.js  
[20:51:36] Starting 'watch-less'...                                     
[20:51:36] Starting 'watch-ts'...                                       
Compiling less file 0                                                   
Compiling ts file 0                                                     
Compiling less file 1                                                   
Compiling ts file 1                                                     
Compiling less file 2                                                   
Compiling ts file 2                                                     
Compiling less file 3                                                   
Compiling ts file 3                                                     
Compiling less file 4                                                   
Compiling ts file 4                                                     

Assigning errors to the correct problem matcher is again problematic may be even impossible if a problem matcher is a multi line matcher (e.g matcher for example the lines ["Compiling less file 0","Compiling less file 1"]

To handle this the watch-less and the watch-ts task are best execute as two separate tasks. Without an integration in VS Code dev would usually start the two gulp task in two different terminals to keep the output separate for readability as well.

Proposal

Build and Co.

First we should decouple task executing from build, rebuild, clean, run tests. The task runner should be able to participate in these commands however it shouldn't be the main driver of these commands. Using the Build command as an example this should work as follows:

  1. Users presses execute the Build command
  2. VS Code activates all extensions that want to participate in build
  3. These extensions register a build participant like they register a completion item provider today.
  4. The task framework is a build participant as well
  5. The Build command calls all participants to execute a build. When all participants are done the build is complete.
  6. To help parsing the output of an external program into problem markers VS Codes internal problem matcher library will be made available as an npm module.

Dependency management

If we have more than one build participant or even two independent build tasks the question raises if we need to define the order of the executed participants or build tasks. We have the following options:

  • we execute them in random order. Usually extension participating in a build don't depend on other extensions. So this might work from an extension perspective. However for external build tools this might be problematic and creating a single build task that does the sequencing might raise problem matcher challenges as described above.
  • we allow users to script the order. If done in a generic way this will require quite some development effort since VS Code has no scripting interface right now. We could support a way to allow the scripting via a JS file in the extension host however this might be confusing for dev that don't know JS at all (for example php or C# devs)
  • we allow users to define the order using a json structure. This could be done using a comparable approach to gulp task dependency. Something like:
{
   "main": ["minify-js", "compile-less"],
   "minify-js": ["compile-ts"]
}

Open question:

  • do we need to do something special for 'watching builds' ?
  • how do we collect output. Will we have one build output that participants can write two ?

Executing an external tool

External tool tasks should be executed in a terminal instead of being executed hidden with its output made available in an output channel. This will allow us to support ANSI control characters, input, ...

Metadata

Metadata

Assignees

Labels

plan-itemVS Code - planned item for upcoming

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions