@@ -2,17 +2,26 @@ import * as vscode from "vscode";
2
2
import * as toolchain from "./toolchain" ;
3
3
import type { Config } from "./config" ;
4
4
import { log } from "./util" ;
5
+ import { unwrapUndefinable } from "./undefinable" ;
5
6
6
7
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
7
8
// our configuration should be compatible with it so use the same key.
8
9
export const TASK_TYPE = "cargo" ;
10
+
9
11
export const TASK_SOURCE = "rust" ;
10
12
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.
14
19
cwd ?: string ;
20
+ // The shell environment.
15
21
env ?: { [ key : string ] : string } ;
22
+ // Override the cargo executable name, such as
23
+ // "my_custom_cargo_bin".
24
+ overrideCargo ?: string ;
16
25
}
17
26
18
27
class RustTaskProvider implements vscode . TaskProvider {
@@ -37,14 +46,12 @@ class RustTaskProvider implements vscode.TaskProvider {
37
46
{ command : "run" , group : undefined } ,
38
47
] ;
39
48
40
- const cargoPath = await toolchain . cargoPath ( ) ;
41
-
42
49
const tasks : vscode . Task [ ] = [ ] ;
43
50
for ( const workspaceTarget of vscode . workspace . workspaceFolders || [ ] ) {
44
51
for ( const def of defs ) {
45
52
const vscodeTask = await buildRustTask (
46
53
workspaceTarget ,
47
- { type : TASK_TYPE , program : cargoPath , args : [ def . command ] } ,
54
+ { type : TASK_TYPE , command : def . command } ,
48
55
`cargo ${ def . command } ` ,
49
56
this . config . problemMatcher ,
50
57
this . config . cargoRunner ,
@@ -62,7 +69,7 @@ class RustTaskProvider implements vscode.TaskProvider {
62
69
// we need to inform VSCode how to execute that command by creating
63
70
// a ShellExecution for it.
64
71
65
- const definition = task . definition as RustTargetDefinition ;
72
+ const definition = task . definition as CargoTaskDefinition ;
66
73
67
74
if ( definition . type === TASK_TYPE ) {
68
75
return await buildRustTask (
@@ -80,16 +87,34 @@ class RustTaskProvider implements vscode.TaskProvider {
80
87
81
88
export async function buildRustTask (
82
89
scope : vscode . WorkspaceFolder | vscode . TaskScope | undefined ,
83
- definition : RustTargetDefinition ,
90
+ definition : CargoTaskDefinition ,
84
91
name : string ,
85
92
problemMatcher : string [ ] ,
86
93
customRunner ?: string ,
87
94
throwOnError : boolean = false ,
88
95
) : 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
+ }
90
109
110
+ async function cargoToExecution (
111
+ definition : CargoTaskDefinition ,
112
+ customRunner : string | undefined ,
113
+ throwOnError : boolean ,
114
+ ) : Promise < vscode . ProcessExecution | vscode . ShellExecution > {
91
115
if ( customRunner ) {
92
116
const runnerCommand = `${ customRunner } .buildShellExecution` ;
117
+
93
118
try {
94
119
const runnerArgs = {
95
120
kind : TASK_TYPE ,
@@ -100,7 +125,7 @@ export async function buildRustTask(
100
125
const customExec = await vscode . commands . executeCommand ( runnerCommand , runnerArgs ) ;
101
126
if ( customExec ) {
102
127
if ( customExec instanceof vscode . ShellExecution ) {
103
- exec = customExec ;
128
+ return customExec ;
104
129
} else {
105
130
log . debug ( "Invalid cargo ShellExecution" , customExec ) ;
106
131
throw "Invalid cargo ShellExecution." ;
@@ -113,20 +138,20 @@ export async function buildRustTask(
113
138
}
114
139
}
115
140
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 ] ;
119
145
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
+ } ) ;
130
155
}
131
156
132
157
export function activateTaskProvider ( config : Config ) : vscode . Disposable {
0 commit comments