@@ -2,17 +2,26 @@ import * as vscode from "vscode";
22import * as toolchain from "./toolchain" ;
33import type { Config } from "./config" ;
44import { log } from "./util" ;
5+ import { unwrapUndefinable } from "./undefinable" ;
56
67// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
78// our configuration should be compatible with it so use the same key.
89export const TASK_TYPE = "cargo" ;
10+
911export const TASK_SOURCE = "rust" ;
1012
11- export interface RustTargetDefinition extends vscode . TaskDefinition {
12- program : string ;
13- args : string [ ] ;
13+ export interface CargoTaskDefinition extends vscode . TaskDefinition {
14+ // The cargo command, such as "run" or "check".
15+ command : string ;
16+ // Additional arguments passed to the cargo command.
17+ args ?: string [ ] ;
18+ // The working directory to run the cargo command in.
1419 cwd ?: string ;
20+ // The shell environment.
1521 env ?: { [ key : string ] : string } ;
22+ // Override the cargo executable name, such as
23+ // "my_custom_cargo_bin".
24+ overrideCargo ?: string ;
1625}
1726
1827class RustTaskProvider implements vscode . TaskProvider {
@@ -37,14 +46,12 @@ class RustTaskProvider implements vscode.TaskProvider {
3746 { command : "run" , group : undefined } ,
3847 ] ;
3948
40- const cargoPath = await toolchain . cargoPath ( ) ;
41-
4249 const tasks : vscode . Task [ ] = [ ] ;
4350 for ( const workspaceTarget of vscode . workspace . workspaceFolders || [ ] ) {
4451 for ( const def of defs ) {
4552 const vscodeTask = await buildRustTask (
4653 workspaceTarget ,
47- { type : TASK_TYPE , program : cargoPath , args : [ def . command ] } ,
54+ { type : TASK_TYPE , command : def . command } ,
4855 `cargo ${ def . command } ` ,
4956 this . config . problemMatcher ,
5057 this . config . cargoRunner ,
@@ -62,7 +69,7 @@ class RustTaskProvider implements vscode.TaskProvider {
6269 // we need to inform VSCode how to execute that command by creating
6370 // a ShellExecution for it.
6471
65- const definition = task . definition as RustTargetDefinition ;
72+ const definition = task . definition as CargoTaskDefinition ;
6673
6774 if ( definition . type === TASK_TYPE ) {
6875 return await buildRustTask (
@@ -80,16 +87,34 @@ class RustTaskProvider implements vscode.TaskProvider {
8087
8188export async function buildRustTask (
8289 scope : vscode . WorkspaceFolder | vscode . TaskScope | undefined ,
83- definition : RustTargetDefinition ,
90+ definition : CargoTaskDefinition ,
8491 name : string ,
8592 problemMatcher : string [ ] ,
8693 customRunner ?: string ,
8794 throwOnError : boolean = false ,
8895) : Promise < vscode . Task > {
89- let exec : vscode . ProcessExecution | vscode . ShellExecution | undefined = undefined ;
96+ const exec = await cargoToExecution ( definition , customRunner , throwOnError ) ;
97+
98+ return new vscode . Task (
99+ definition ,
100+ // scope can sometimes be undefined. in these situations we default to the workspace taskscope as
101+ // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider)
102+ scope ?? vscode . TaskScope . Workspace ,
103+ name ,
104+ TASK_SOURCE ,
105+ exec ,
106+ problemMatcher ,
107+ ) ;
108+ }
90109
110+ async function cargoToExecution (
111+ definition : CargoTaskDefinition ,
112+ customRunner : string | undefined ,
113+ throwOnError : boolean ,
114+ ) : Promise < vscode . ProcessExecution | vscode . ShellExecution > {
91115 if ( customRunner ) {
92116 const runnerCommand = `${ customRunner } .buildShellExecution` ;
117+
93118 try {
94119 const runnerArgs = {
95120 kind : TASK_TYPE ,
@@ -100,7 +125,7 @@ export async function buildRustTask(
100125 const customExec = await vscode . commands . executeCommand ( runnerCommand , runnerArgs ) ;
101126 if ( customExec ) {
102127 if ( customExec instanceof vscode . ShellExecution ) {
103- exec = customExec ;
128+ return customExec ;
104129 } else {
105130 log . debug ( "Invalid cargo ShellExecution" , customExec ) ;
106131 throw "Invalid cargo ShellExecution." ;
@@ -113,20 +138,20 @@ export async function buildRustTask(
113138 }
114139 }
115140
116- if ( ! exec ) {
117- exec = new vscode . ProcessExecution ( definition . program , definition . args , definition ) ;
118- }
141+ // Check whether we must use a user-defined substitute for cargo.
142+ // Split on spaces to allow overrides like "wrapper cargo".
143+ const cargoPath = await toolchain . cargoPath ( ) ;
144+ const cargoCommand = definition . overrideCargo ?. split ( " " ) ?? [ cargoPath ] ;
119145
120- return new vscode . Task (
121- definition ,
122- // scope can sometimes be undefined. in these situations we default to the workspace taskscope as
123- // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider)
124- scope ?? vscode . TaskScope . Workspace ,
125- name ,
126- TASK_SOURCE ,
127- exec ,
128- problemMatcher ,
129- ) ;
146+ const args = [ definition . command ] . concat ( definition . args ?? [ ] ) ;
147+ const fullCommand = [ ...cargoCommand , ...args ] ;
148+
149+ const processName = unwrapUndefinable ( fullCommand [ 0 ] ) ;
150+
151+ return new vscode . ProcessExecution ( processName , fullCommand . slice ( 1 ) , {
152+ cwd : definition . cwd ,
153+ env : definition . env ,
154+ } ) ;
130155}
131156
132157export function activateTaskProvider ( config : Config ) : vscode . Disposable {
0 commit comments