@@ -8,6 +8,10 @@ import { FileSystem } from '../file_system/FileSystem';
8
8
9
9
import ChildLogger from '../logging/child_logger' ;
10
10
11
+ namespace Constants {
12
+ export const DEFAULT_TOOLCHAIN_SUFFIX = '(default)' ;
13
+ }
14
+
11
15
/**
12
16
* Configuration of Rust installed via Rustup
13
17
*/
@@ -21,7 +25,7 @@ export class Rustup {
21
25
* A path to Rust's installation root.
22
26
* It is what `rustc --print=sysroot` returns.
23
27
*/
24
- private pathToRustcSysRoot : string ;
28
+ private pathToRustcSysRoot : string | undefined ;
25
29
26
30
/**
27
31
* A path to Rust's source code.
@@ -41,44 +45,51 @@ export class Rustup {
41
45
private components : string [ ] ;
42
46
43
47
/**
44
- * Checks if Rustup manages a specified Rust's installation root
45
- * @param rustcSysRoot a path to Rust's installation root to check
46
- * @returns true if Rustup manages it otherwire false
48
+ * Toolchains received by invoking rustup
47
49
*/
48
- public static doesManageRustcSysRoot ( pathToRustcSysRoot : string ) : boolean {
49
- // Usually rustup installs itself to the directory `.rustup` so if the sysroot is in the directory `.rustup`, then it is controlled by rustup.
50
- // Also a user can specify a directory to install rustup to by specifying the environment variable `RUSTUP_HOME`
51
- const rustupHome : string | undefined = process . env . RUSTUP_HOME ;
52
- if ( rustupHome ) {
53
- return pathToRustcSysRoot . startsWith ( rustupHome ) ;
54
- } else {
55
- // It can be inaccurate since nobody can stop a user from installing Rust not via Rustup, but to `.rustup` directory
56
- return pathToRustcSysRoot . includes ( '.rustup' ) ;
57
- }
58
- }
50
+ private toolchains : string [ ] ;
59
51
60
52
/**
61
53
* Creates a new instance of the class.
62
54
* The method is asynchronous because it tries to find Rust's source code
63
55
* @param pathToRustcSysRoot A path to Rust's installation root
64
56
*/
65
57
public static async create ( logger : ChildLogger ) : Promise < Rustup | undefined > {
66
- const sysrootPath : string | undefined = await this . invokeGettingSysrootPath ( 'nightly' , logger ) ;
67
- if ( ! sysrootPath ) {
58
+ const rustupExe = await FileSystem . findExecutablePath ( Rustup . getRustupExecutable ( ) ) ;
59
+ if ( ! rustupExe ) {
68
60
return undefined ;
69
61
}
70
- const rustup = new Rustup ( logger , sysrootPath , undefined , undefined ) ;
71
- await rustup . updatePathToRustSourceCodePath ( ) ;
62
+ const rustup = new Rustup ( logger ) ;
63
+ await rustup . updateToolchains ( ) ;
72
64
await rustup . updateComponents ( ) ;
65
+ await rustup . updateSysrootPath ( 'nightly' ) ;
66
+ await rustup . updatePathToRustSourceCodePath ( ) ;
73
67
await rustup . updatePathToRlsExecutable ( ) ;
74
68
return rustup ;
75
69
}
76
70
77
71
/**
78
- * Returns the path to Rust's installation root
72
+ * Returns whether the nightly toolchain is installed or not
73
+ */
74
+ public isNightlyToolchainInstalled ( ) : boolean {
75
+ const nightlyToolchain = this . toolchains . find ( t => t . startsWith ( 'nightly' ) ) ;
76
+ if ( nightlyToolchain ) {
77
+ return true ;
78
+ } else {
79
+ return false ;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Returns either the default toolchain or undefined if there are no installed toolchains
79
85
*/
80
- public getPathToRustcSysRoot ( ) : string {
81
- return this . pathToRustcSysRoot ;
86
+ public getDefaultToolchain ( ) : string | undefined {
87
+ const logger = this . logger . createChildLogger ( 'getDefaultToolchain: ' ) ;
88
+ const toolchain = this . toolchains . find ( t => t . endsWith ( Constants . DEFAULT_TOOLCHAIN_SUFFIX ) ) ;
89
+ if ( ! toolchain && this . toolchains . length !== 0 ) {
90
+ logger . error ( `no default toolchain; this.toolchains=${ this . toolchains } ` ) ;
91
+ }
92
+ return toolchain ;
82
93
}
83
94
84
95
/**
@@ -95,6 +106,28 @@ export class Rustup {
95
106
return this . pathToRlsExecutable ;
96
107
}
97
108
109
+ /**
110
+ * Requests rustup to install the specified toolchain
111
+ * @param toolchain The toolchain to install
112
+ * @return true if no error occurred and the toolchain has been installed otherwise false
113
+ */
114
+ public async installToolchain ( toolchain : string ) : Promise < boolean > {
115
+ const logger = this . logger . createChildLogger ( `installToolchain: toolchain=${ toolchain } ` ) ;
116
+ const output = await Rustup . invoke ( [ 'toolchain' , 'install' , toolchain ] , logger ) ;
117
+ if ( output ) {
118
+ logger . debug ( `output=${ output } ` ) ;
119
+ } else {
120
+ logger . error ( `output=${ output } ` ) ;
121
+ return false ;
122
+ }
123
+ await this . updateToolchains ( ) ;
124
+ if ( this . toolchains . length === 0 ) {
125
+ logger . error ( 'this.toolchains.length === 0' ) ;
126
+ return false ;
127
+ }
128
+ return true ;
129
+ }
130
+
98
131
/**
99
132
* Requests Rustup install RLS
100
133
* @return true if no error occurred and RLS has been installed otherwise false
@@ -127,26 +160,61 @@ export class Rustup {
127
160
* Requests rustup to give components list and saves them in the field `components`
128
161
*/
129
162
public async updateComponents ( ) : Promise < void > {
163
+ this . components = [ ] ;
130
164
const logger = this . logger . createChildLogger ( 'updateComponents: ' ) ;
165
+ if ( ! this . isNightlyToolchainInstalled ( ) ) {
166
+ logger . error ( 'nightly toolchain is not installed' ) ;
167
+ return ;
168
+ }
131
169
const stdoutData : string | undefined = await Rustup . invoke ( [ 'component' , 'list' , '--toolchain' , 'nightly' ] , logger ) ;
132
170
if ( ! stdoutData ) {
133
171
logger . error ( `stdoutData=${ stdoutData } ` ) ;
134
- return undefined ;
172
+ return ;
135
173
}
136
174
this . components = stdoutData . split ( '\n' ) ;
137
175
logger . debug ( `this.components=${ JSON . stringify ( this . components ) } ` ) ;
138
176
}
139
177
178
+ /**
179
+ * Requests rustup to give toolchains list and saves it in the field `toolchains`
180
+ */
181
+ public async updateToolchains ( ) : Promise < void > {
182
+ const logger = this . logger . createChildLogger ( 'updateToolchains: ' ) ;
183
+ this . toolchains = await Rustup . invokeGettingToolchains ( logger ) ;
184
+ logger . debug ( `this.toolchains=${ JSON . stringify ( this . toolchains ) } ` ) ;
185
+ }
186
+
187
+ /**
188
+ * Requests rustup to give the path to the sysroot of the specified toolchain
189
+ * @param toolchain The toolchain to get the path to the sysroot for
190
+ */
191
+ public async updateSysrootPath ( toolchain : string ) : Promise < void > {
192
+ this . pathToRustcSysRoot = undefined ;
193
+ const logger = this . logger . createChildLogger ( `updateSysrootPath: toolchain=${ toolchain } : ` ) ;
194
+ if ( ! this . toolchains . find ( t => t . startsWith ( toolchain ) ) ) {
195
+ logger . error ( 'toolchain is not installed' ) ;
196
+ return ;
197
+ }
198
+ this . pathToRustcSysRoot = await Rustup . invokeGettingSysrootPath ( toolchain , logger ) ;
199
+ if ( ! this . pathToRustcSysRoot ) {
200
+ logger . error ( `this.pathToRustcSysRoot=${ this . pathToRustcSysRoot } ` ) ;
201
+ }
202
+ }
203
+
140
204
/**
141
205
* Checks if Rust's source code is installed at the expected path.
142
206
* This method assigns either the expected path or undefined to the field `pathToRustSourceCode`, depending on if the expected path exists.
143
207
* The method is asynchronous because it checks if the expected path exists
144
208
*/
145
209
public async updatePathToRustSourceCodePath ( ) : Promise < void > {
210
+ const logger = this . logger . createChildLogger ( 'updatePathToRustSourceCodePath: ' ) ;
211
+ this . pathToRustSourceCode = undefined ;
212
+ if ( ! this . pathToRustcSysRoot ) {
213
+ logger . error ( `this.pathToRustcSysRoot=${ this . pathToRustcSysRoot } ` ) ;
214
+ return ;
215
+ }
146
216
const pathToRustSourceCode = join ( this . pathToRustcSysRoot , 'lib' , 'rustlib' , 'src' , 'rust' , 'src' ) ;
147
-
148
217
const isRustSourceCodeInstalled : boolean = await FileSystem . doesPathExist ( pathToRustSourceCode ) ;
149
-
150
218
if ( isRustSourceCodeInstalled ) {
151
219
this . pathToRustSourceCode = pathToRustSourceCode ;
152
220
} else {
@@ -259,6 +327,16 @@ export class Rustup {
259
327
return output . trim ( ) ;
260
328
}
261
329
330
+ private static async invokeGettingToolchains ( logger : ChildLogger ) : Promise < string [ ] > {
331
+ const functionLogger = logger . createChildLogger ( 'invokeGettingToolchains: ' ) ;
332
+ const output = await this . invoke ( [ 'toolchain' , 'list' ] , functionLogger ) ;
333
+ if ( ! output ) {
334
+ functionLogger . error ( `output=${ output } ` ) ;
335
+ return [ ] ;
336
+ }
337
+ return output . trim ( ) . split ( '\n' ) ;
338
+ }
339
+
262
340
/**
263
341
* Invokes `rustup run...` with the specified toolchain and arguments, checks if it exited successfully and returns its output
264
342
* @param toolchain The toolchain to invoke rustup with
@@ -298,21 +376,13 @@ export class Rustup {
298
376
* @param pathToRustSourceCode A value for the field `pathToRustSourceCode`
299
377
* @param pathToRlsExecutable A value fo the field `pathToRlsExecutable`
300
378
*/
301
- private constructor (
302
- logger : ChildLogger ,
303
- pathToRustcSysRoot : string ,
304
- pathToRustSourceCode : string | undefined ,
305
- pathToRlsExecutable : string | undefined
306
- ) {
379
+ private constructor ( logger : ChildLogger ) {
307
380
this . logger = logger ;
308
-
309
- this . pathToRustcSysRoot = pathToRustcSysRoot ;
310
-
311
- this . pathToRustSourceCode = pathToRustSourceCode ;
312
-
313
- this . pathToRlsExecutable = pathToRlsExecutable ;
314
-
381
+ this . pathToRustcSysRoot = undefined ;
382
+ this . pathToRustSourceCode = undefined ;
383
+ this . pathToRlsExecutable = undefined ;
315
384
this . components = [ ] ;
385
+ this . toolchains = [ ] ;
316
386
}
317
387
318
388
/**
0 commit comments