1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
- import * as child_process from "child_process" ;
5
4
import * as fs from "fs" ;
6
5
import * as os from "os" ;
7
6
import * as path from "path" ;
8
7
import * as process from "process" ;
9
8
import { IPowerShellAdditionalExePathSettings } from "./settings" ;
9
+ import { checkIfFileExists , checkIfDirectoryExists } from "./utils" ;
10
10
11
11
const WindowsPowerShell64BitLabel = "Windows PowerShell (x64)" ;
12
12
const WindowsPowerShell32BitLabel = "Windows PowerShell (x86)" ;
13
13
14
- const LinuxExePath = "/usr/bin/pwsh" ;
14
+ const LinuxExePath = "/usr/bin/pwsh" ;
15
15
const LinuxPreviewExePath = "/usr/bin/pwsh-preview" ;
16
16
17
- const SnapExePath = "/snap/bin/pwsh" ;
18
- const SnapPreviewExePath = "/snap/bin/pwsh-preview" ;
17
+ const SnapExePath = "/snap/bin/pwsh" ;
18
+ const SnapPreviewExePath = "/snap/bin/pwsh-preview" ;
19
19
20
- const MacOSExePath = "/usr/local/bin/pwsh" ;
20
+ const MacOSExePath = "/usr/local/bin/pwsh" ;
21
21
const MacOSPreviewExePath = "/usr/local/bin/pwsh-preview" ;
22
22
23
23
export enum OperatingSystem {
@@ -97,17 +97,21 @@ export class PowerShellExeFinder {
97
97
/**
98
98
* Returns the first available PowerShell executable found in the search order.
99
99
*/
100
- public getFirstAvailablePowerShellInstallation ( ) : IPowerShellExeDetails {
101
- for ( const pwsh of this . enumeratePowerShellInstallations ( ) ) {
100
+ public async getFirstAvailablePowerShellInstallation ( ) : Promise < IPowerShellExeDetails > {
101
+ for await ( const pwsh of this . enumeratePowerShellInstallations ( ) ) {
102
102
return pwsh ;
103
103
}
104
104
}
105
105
106
106
/**
107
107
* Get an array of all PowerShell executables found when searching for PowerShell installations.
108
108
*/
109
- public getAllAvailablePowerShellInstallations ( ) : IPowerShellExeDetails [ ] {
110
- return Array . from ( this . enumeratePowerShellInstallations ( ) ) ;
109
+ public async getAllAvailablePowerShellInstallations ( ) : Promise < IPowerShellExeDetails [ ] > {
110
+ const array : IPowerShellExeDetails [ ] = [ ] ;
111
+ for await ( const pwsh of this . enumeratePowerShellInstallations ( ) ) {
112
+ array . push ( pwsh ) ;
113
+ }
114
+ return array ;
111
115
}
112
116
113
117
/**
@@ -137,18 +141,18 @@ export class PowerShellExeFinder {
137
141
* PowerShell items returned by this object are verified
138
142
* to exist on the filesystem.
139
143
*/
140
- public * enumeratePowerShellInstallations ( ) : Iterable < IPowerShellExeDetails > {
144
+ public async * enumeratePowerShellInstallations ( ) : AsyncIterable < IPowerShellExeDetails > {
141
145
// Get the default PowerShell installations first
142
- for ( const defaultPwsh of this . enumerateDefaultPowerShellInstallations ( ) ) {
143
- if ( defaultPwsh && defaultPwsh . exists ( ) ) {
146
+ for await ( const defaultPwsh of this . enumerateDefaultPowerShellInstallations ( ) ) {
147
+ if ( defaultPwsh && await defaultPwsh . exists ( ) ) {
144
148
yield defaultPwsh ;
145
149
}
146
150
}
147
151
148
152
// Also show any additionally configured PowerShells
149
153
// These may be duplicates of the default installations, but given a different name.
150
154
for ( const additionalPwsh of this . enumerateAdditionalPowerShellInstallations ( ) ) {
151
- if ( additionalPwsh && additionalPwsh . exists ( ) ) {
155
+ if ( additionalPwsh && await additionalPwsh . exists ( ) ) {
152
156
yield additionalPwsh ;
153
157
}
154
158
}
@@ -159,7 +163,7 @@ export class PowerShellExeFinder {
159
163
* Returned values may not exist, but come with an .exists property
160
164
* which will check whether the executable exists.
161
165
*/
162
- private * enumerateDefaultPowerShellInstallations ( ) : Iterable < IPossiblePowerShellExe > {
166
+ private async * enumerateDefaultPowerShellInstallations ( ) : AsyncIterable < IPossiblePowerShellExe > {
163
167
// Find PSCore stable first
164
168
yield this . findPSCoreStable ( ) ;
165
169
@@ -174,7 +178,7 @@ export class PowerShellExeFinder {
174
178
yield this . findPSCoreWindowsInstallation ( { useAlternateBitness : true } ) ;
175
179
176
180
// Also look for the MSIX/UWP installation
177
- yield this . findPSCoreMsix ( ) ;
181
+ yield await this . findPSCoreMsix ( ) ;
178
182
179
183
break ;
180
184
}
@@ -213,7 +217,7 @@ export class PowerShellExeFinder {
213
217
}
214
218
215
219
/**
216
- * Iterates through the configured additonal PowerShell executable locations,
220
+ * Iterates through the configured additional PowerShell executable locations,
217
221
* without checking for their existence.
218
222
*/
219
223
private * enumerateAdditionalPowerShellInstallations ( ) : Iterable < IPossiblePowerShellExe > {
@@ -227,7 +231,7 @@ export class PowerShellExeFinder {
227
231
}
228
232
}
229
233
230
- private findPSCoreStable ( ) : IPossiblePowerShellExe {
234
+ private async findPSCoreStable ( ) : Promise < IPossiblePowerShellExe > {
231
235
switch ( this . platformDetails . operatingSystem ) {
232
236
case OperatingSystem . Linux :
233
237
return new PossiblePowerShellExe ( LinuxExePath , "PowerShell" ) ;
@@ -236,11 +240,11 @@ export class PowerShellExeFinder {
236
240
return new PossiblePowerShellExe ( MacOSExePath , "PowerShell" ) ;
237
241
238
242
case OperatingSystem . Windows :
239
- return this . findPSCoreWindowsInstallation ( ) ;
243
+ return await this . findPSCoreWindowsInstallation ( ) ;
240
244
}
241
245
}
242
246
243
- private findPSCorePreview ( ) : IPossiblePowerShellExe {
247
+ private async findPSCorePreview ( ) : Promise < IPossiblePowerShellExe > {
244
248
switch ( this . platformDetails . operatingSystem ) {
245
249
case OperatingSystem . Linux :
246
250
return new PossiblePowerShellExe ( LinuxPreviewExePath , "PowerShell Preview" ) ;
@@ -249,7 +253,7 @@ export class PowerShellExeFinder {
249
253
return new PossiblePowerShellExe ( MacOSPreviewExePath , "PowerShell Preview" ) ;
250
254
251
255
case OperatingSystem . Windows :
252
- return this . findPSCoreWindowsInstallation ( { findPreview : true } ) ;
256
+ return await this . findPSCoreWindowsInstallation ( { findPreview : true } ) ;
253
257
}
254
258
}
255
259
@@ -260,10 +264,10 @@ export class PowerShellExeFinder {
260
264
261
265
const dotnetGlobalToolExePath : string = path . join ( os . homedir ( ) , ".dotnet" , "tools" , exeName ) ;
262
266
263
- return new PossiblePowerShellExe ( dotnetGlobalToolExePath , ".NET Core PowerShell Global Tool" ) ;
267
+ return new PossiblePowerShellExe ( dotnetGlobalToolExePath , ".NET Core PowerShell Global Tool" , undefined , false ) ;
264
268
}
265
269
266
- private findPSCoreMsix ( { findPreview } : { findPreview ?: boolean } = { } ) : IPossiblePowerShellExe {
270
+ private async findPSCoreMsix ( { findPreview } : { findPreview ?: boolean } = { } ) : Promise < IPossiblePowerShellExe > {
267
271
// We can't proceed if there's no LOCALAPPDATA path
268
272
if ( ! process . env . LOCALAPPDATA ) {
269
273
return null ;
@@ -272,7 +276,7 @@ export class PowerShellExeFinder {
272
276
// Find the base directory for MSIX application exe shortcuts
273
277
const msixAppDir = path . join ( process . env . LOCALAPPDATA , "Microsoft" , "WindowsApps" ) ;
274
278
275
- if ( ! fileExistsSync ( msixAppDir ) ) {
279
+ if ( ! await checkIfDirectoryExists ( msixAppDir ) ) {
276
280
return null ;
277
281
}
278
282
@@ -282,6 +286,7 @@ export class PowerShellExeFinder {
282
286
: { pwshMsixDirRegex : PowerShellExeFinder . PwshMsixRegex , pwshMsixName : "PowerShell (Store)" } ;
283
287
284
288
// We should find only one such application, so return on the first one
289
+ // TODO: Use VS Code async fs API for this.
285
290
for ( const subdir of fs . readdirSync ( msixAppDir ) ) {
286
291
if ( pwshMsixDirRegex . test ( subdir ) ) {
287
292
const pwshMsixPath = path . join ( msixAppDir , subdir , "pwsh.exe" ) ;
@@ -301,9 +306,9 @@ export class PowerShellExeFinder {
301
306
return new PossiblePowerShellExe ( SnapPreviewExePath , "PowerShell Preview Snap" ) ;
302
307
}
303
308
304
- private findPSCoreWindowsInstallation (
309
+ private async findPSCoreWindowsInstallation (
305
310
{ useAlternateBitness = false , findPreview = false } :
306
- { useAlternateBitness ?: boolean ; findPreview ?: boolean } = { } ) : IPossiblePowerShellExe {
311
+ { useAlternateBitness ?: boolean ; findPreview ?: boolean } = { } ) : Promise < IPossiblePowerShellExe > {
307
312
308
313
const programFilesPath : string = this . getProgramFilesPath ( { useAlternateBitness } ) ;
309
314
@@ -314,13 +319,7 @@ export class PowerShellExeFinder {
314
319
const powerShellInstallBaseDir = path . join ( programFilesPath , "PowerShell" ) ;
315
320
316
321
// Ensure the base directory exists
317
- try {
318
- const powerShellInstallBaseDirLStat = fs . lstatSync ( powerShellInstallBaseDir ) ;
319
- if ( ! powerShellInstallBaseDirLStat . isDirectory ( ) )
320
- {
321
- return null ;
322
- }
323
- } catch {
322
+ if ( ! await checkIfDirectoryExists ( powerShellInstallBaseDir ) ) {
324
323
return null ;
325
324
}
326
325
@@ -366,7 +365,7 @@ export class PowerShellExeFinder {
366
365
367
366
// Now look for the file
368
367
const exePath = path . join ( powerShellInstallBaseDir , item , "pwsh.exe" ) ;
369
- if ( ! fs . existsSync ( exePath ) ) {
368
+ if ( ! await checkIfFileExists ( exePath ) ) {
370
369
continue ;
371
370
}
372
371
@@ -413,7 +412,7 @@ export class PowerShellExeFinder {
413
412
displayName = WindowsPowerShell32BitLabel ;
414
413
}
415
414
416
- winPS = new PossiblePowerShellExe ( winPSPath , displayName , { knownToExist : true } ) ;
415
+ winPS = new PossiblePowerShellExe ( winPSPath , displayName , true ) ;
417
416
418
417
if ( useAlternateBitness ) {
419
418
this . alternateBitnessWinPS = winPS ;
@@ -479,40 +478,20 @@ export function getWindowsSystemPowerShellPath(systemFolderName: string) {
479
478
"powershell.exe" ) ;
480
479
}
481
480
482
- function fileExistsSync ( filePath : string ) : boolean {
483
- try {
484
- // This will throw if the path does not exist,
485
- // and otherwise returns a value that we don't care about
486
- fs . lstatSync ( filePath ) ;
487
- return true ;
488
- } catch {
489
- return false ;
490
- }
491
- }
492
-
493
481
interface IPossiblePowerShellExe extends IPowerShellExeDetails {
494
- exists ( ) : boolean ;
482
+ exists ( ) : Promise < boolean > ;
495
483
}
496
484
497
485
class PossiblePowerShellExe implements IPossiblePowerShellExe {
498
- public readonly exePath : string ;
499
- public readonly displayName : string ;
500
-
501
- private knownToExist : boolean ;
502
-
503
486
constructor (
504
- pathToExe : string ,
505
- installationName : string ,
506
- { knownToExist = false } : { knownToExist ?: boolean } = { } ) {
507
-
508
- this . exePath = pathToExe ;
509
- this . displayName = installationName ;
510
- this . knownToExist = knownToExist || undefined ;
511
- }
487
+ public readonly exePath : string ,
488
+ public readonly displayName : string ,
489
+ private knownToExist ?: boolean ,
490
+ public readonly supportsProperArguments : boolean = true ) { }
512
491
513
- public exists ( ) : boolean {
492
+ public async exists ( ) : Promise < boolean > {
514
493
if ( this . knownToExist === undefined ) {
515
- this . knownToExist = fileExistsSync ( this . exePath ) ;
494
+ this . knownToExist = await checkIfFileExists ( this . exePath ) ;
516
495
}
517
496
return this . knownToExist ;
518
497
}
0 commit comments