Skip to content
This repository was archived by the owner on Nov 18, 2022. It is now read-only.

Commit 4b7a7d5

Browse files
author
=
committed
switch exec -> execFile; ShellExecution -> ProcessExecution to easily handle whitespace in paths
1 parent fcb2d73 commit 4b7a7d5

File tree

6 files changed

+84
-20
lines changed

6 files changed

+84
-20
lines changed

src/configuration.ts

+9
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export class RLSConfiguration {
7979
return this.configuration.get('rust-client.rustupPath', 'rustup');
8080
}
8181

82+
public get rustupPathQuotes(): string {
83+
return '"' + this.rustupPath + '"';
84+
}
85+
8286
public get useWSL(): boolean {
8387
return this.configuration.get<boolean>('rust-client.useWSL', false);
8488
}
@@ -118,6 +122,10 @@ export class RLSConfiguration {
118122
return this.configuration.get<string>('rust-client.rlsPath');
119123
}
120124

125+
public get rlsPathQuotes(): string | undefined {
126+
return this.rlsPath ? '"' + this.rlsPath + '"' : undefined;
127+
}
128+
121129
public get multiProjectEnabled(): boolean {
122130
return this.configuration.get<boolean>(
123131
'rust-client.enableMultiProjectSetup',
@@ -130,6 +138,7 @@ export class RLSConfiguration {
130138
return {
131139
channel: ignoreChannel ? '' : this.channel,
132140
path: this.rustupPath,
141+
pathQuotes: this.rustupPathQuotes,
133142
useWSL: this.useWSL,
134143
};
135144
}

src/extension.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,9 @@ class ClientWorkspace {
362362
const rustcPrintSysroot = () =>
363363
this.config.rustupDisabled
364364
? wslWrapper.exec('rustc --print sysroot', { env })
365-
: wslWrapper.exec(
366-
`${this.config.rustupPath} run ${this.config.channel} rustc --print sysroot`,
365+
: wslWrapper.execFile(
366+
this.config.rustupPath,
367+
['run', this.config.channel, 'rustc', '--print', 'sysroot'],
367368
{ env },
368369
);
369370

@@ -416,7 +417,7 @@ class ClientWorkspace {
416417

417418
private async makeRlsProcess(): Promise<child_process.ChildProcess> {
418419
// Run "rls" from the PATH unless there's an override.
419-
const rlsPath = this.config.rlsPath || 'rls';
420+
const rlsPath = this.config.rlsPathQuotes || 'rls';
420421

421422
// We don't need to set [DY]LD_LIBRARY_PATH if we're using rustup,
422423
// as rustup will set it for us when it chooses a toolchain.
@@ -451,8 +452,9 @@ class ClientWorkspace {
451452
}
452453

453454
const env = await makeRlsEnv();
455+
454456
childProcess = withWsl(config.useWSL).spawn(
455-
config.path,
457+
config.pathQuotes,
456458
['run', config.channel, rlsPath],
457459
{ env, cwd, shell: true },
458460
);

src/rustup.ts

+55-15
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ function isInstalledRegex(componentName: string): RegExp {
1717
export interface RustupConfig {
1818
channel: string;
1919
path: string;
20+
pathQuotes: string;
2021
useWSL: boolean;
2122
}
2223

2324
export async function rustupUpdate(config: RustupConfig) {
2425
startSpinner('RLS', 'Updating…');
2526

2627
try {
27-
const { stdout } = await withWsl(config.useWSL).exec(
28-
`${config.path} update`,
29-
);
28+
const { stdout } = await withWsl(config.useWSL).execFile(config.path, [
29+
'update',
30+
]);
3031

3132
// This test is imperfect because if the user has multiple toolchains installed, they
3233
// might have one updated and one unchanged. But I don't want to go too far down the
@@ -65,13 +66,38 @@ export async function ensureToolchain(config: RustupConfig) {
6566
* Checks for required RLS components and prompts the user to install if it's
6667
* not already.
6768
*/
69+
6870
export async function checkForRls(config: RustupConfig) {
6971
if (await hasRlsComponents(config)) {
7072
return;
7173
}
7274

75+
// Format an easier to understand component install prompt
76+
const componentsNeededUserOutput = REQUIRED_COMPONENTS.map((component, i) => {
77+
if (
78+
REQUIRED_COMPONENTS.length > 1 &&
79+
i === REQUIRED_COMPONENTS.length - 1
80+
) {
81+
return ' and `' + component + '`';
82+
} else if (
83+
i !== 0 &&
84+
i !== REQUIRED_COMPONENTS.length - 1 &&
85+
REQUIRED_COMPONENTS.length > 2
86+
) {
87+
return ', `' + component + '`';
88+
} else {
89+
return '`' + component + '`';
90+
}
91+
});
7392
const clicked = await Promise.resolve(
74-
window.showInformationMessage('RLS not installed. Install?', 'Yes'),
93+
window.showInformationMessage(
94+
`
95+
Rustup
96+
${
97+
componentsNeededUserOutput.length > 1 ? 'components' : 'component'
98+
} ${componentsNeededUserOutput.join('')} not installed. Install?`,
99+
'Yes',
100+
),
75101
);
76102
if (clicked) {
77103
await installRlsComponents(config);
@@ -83,9 +109,10 @@ export async function checkForRls(config: RustupConfig) {
83109

84110
async function hasToolchain(config: RustupConfig): Promise<boolean> {
85111
try {
86-
const { stdout } = await withWsl(config.useWSL).exec(
87-
`${config.path} toolchain list`,
88-
);
112+
const { stdout } = await withWsl(config.useWSL).execFile(config.path, [
113+
'toolchain',
114+
'list',
115+
]);
89116
return stdout.includes(config.channel);
90117
} catch (e) {
91118
console.log(e);
@@ -129,7 +156,7 @@ async function tryToInstallToolchain(config: RustupConfig) {
129156
*/
130157
async function listComponents(config: RustupConfig): Promise<string[]> {
131158
return withWsl(config.useWSL)
132-
.exec(`${config.path} component list --toolchain ${config.channel}`)
159+
.execFile(config.path, ['component', 'list', '--toolchain', config.channel])
133160
.then(({ stdout }) =>
134161
stdout
135162
.toString()
@@ -142,9 +169,20 @@ async function hasRlsComponents(config: RustupConfig): Promise<boolean> {
142169
try {
143170
const components = await listComponents(config);
144171

145-
return REQUIRED_COMPONENTS.map(isInstalledRegex).every(isInstalledRegex =>
146-
components.some(c => isInstalledRegex.test(c)),
147-
);
172+
// Splice the components that are installed from the REQUIRED_COMPONENTS array
173+
for (let i = REQUIRED_COMPONENTS.length; i >= 0; --i) {
174+
const installedRegex = isInstalledRegex(REQUIRED_COMPONENTS[i]);
175+
components.forEach(component => {
176+
if (installedRegex.test(component)) {
177+
REQUIRED_COMPONENTS.splice(i, 1);
178+
}
179+
});
180+
}
181+
if (REQUIRED_COMPONENTS.length === 0) {
182+
return true;
183+
} else {
184+
return false;
185+
}
148186
} catch (e) {
149187
console.log(e);
150188
window.showErrorMessage(`Can't detect RLS components: ${e.message}`);
@@ -153,7 +191,7 @@ async function hasRlsComponents(config: RustupConfig): Promise<boolean> {
153191
}
154192
}
155193

156-
async function installRlsComponents(config: RustupConfig) {
194+
export async function installRlsComponents(config: RustupConfig) {
157195
startSpinner('RLS', 'Installing components…');
158196

159197
for (const component of REQUIRED_COMPONENTS) {
@@ -226,7 +264,9 @@ export function parseActiveToolchain(rustupOutput: string): string {
226264
export async function getVersion(config: RustupConfig): Promise<string> {
227265
const versionRegex = /rustup ([0-9]+\.[0-9]+\.[0-9]+)/;
228266

229-
const output = await withWsl(config.useWSL).exec(`${config.path} --version`);
267+
const output = await withWsl(config.useWSL).execFile(config.path, [
268+
'--version',
269+
]);
230270
const versionMatch = output.stdout.toString().match(versionRegex);
231271
if (versionMatch && versionMatch.length >= 2) {
232272
return versionMatch[1];
@@ -257,7 +297,7 @@ export function getActiveChannel(wsPath: string, config: RustupConfig): string {
257297
try {
258298
// `rustup show active-toolchain` is available since rustup 1.12.0
259299
activeChannel = withWsl(config.useWSL)
260-
.execSync(`${config.path} show active-toolchain`, { cwd: wsPath })
300+
.execSync(`${config.pathQuotes} show active-toolchain`, { cwd: wsPath })
261301
.toString()
262302
.trim();
263303
// Since rustup 1.17.0 if the active toolchain is the default, we're told
@@ -268,7 +308,7 @@ export function getActiveChannel(wsPath: string, config: RustupConfig): string {
268308
} catch (e) {
269309
// Possibly an old rustup version, so try rustup show
270310
const showOutput = withWsl(config.useWSL)
271-
.execSync(`${config.path} show`, { cwd: wsPath })
311+
.execSync(`${config.pathQuotes} show`, { cwd: wsPath })
272312
.toString();
273313
activeChannel = parseActiveToolchain(showOutput);
274314
}

src/tasks.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as crypto from 'crypto';
22
import {
33
Disposable,
4+
ProcessExecution,
45
ShellExecution,
56
Task,
67
TaskGroup,
@@ -127,14 +128,17 @@ export async function runTaskCommand(
127128
displayName: string,
128129
folder?: WorkspaceFolder,
129130
) {
131+
if (!command) {
132+
return;
133+
}
130134
const uniqueId = crypto.randomBytes(20).toString();
131135

132136
const task = new Task(
133137
{ label: uniqueId, type: 'shell' },
134138
folder ? folder : workspace.workspaceFolders![0],
135139
displayName,
136140
TASK_SOURCE,
137-
new ShellExecution(`${command} ${args.join(' ')}`, {
141+
new ProcessExecution(command, args, {
138142
cwd: cwd || (folder && folder.uri.fsPath),
139143
env,
140144
}),

src/utils/child_process.ts

+4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import * as util from 'util';
44
import { modifyParametersForWSL } from './wslpath';
55

66
const execAsync = util.promisify(child_process.exec);
7+
const execFile = util.promisify(child_process.execFile);
78

89
export interface SpawnFunctions {
910
exec: typeof execAsync;
11+
execFile: typeof execFile;
1012
execSync: typeof child_process.execSync;
1113
spawn: typeof child_process.spawn;
1214
modifyArgs: (
@@ -19,12 +21,14 @@ export function withWsl(withWsl: boolean): SpawnFunctions {
1921
return withWsl
2022
? {
2123
exec: withWslModifiedParameters(execAsync),
24+
execFile: withWslModifiedParameters(execFile),
2225
execSync: withWslModifiedParameters(child_process.execSync),
2326
spawn: withWslModifiedParameters(child_process.spawn),
2427
modifyArgs: modifyParametersForWSL,
2528
}
2629
: {
2730
exec: execAsync,
31+
execFile,
2832
execSync: child_process.execSync,
2933
spawn: child_process.spawn,
3034
modifyArgs: (command: string, args: string[]) => ({ command, args }),

test/suite/rustup.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ assert(rustupVersion);
99

1010
const config: rustup.RustupConfig = {
1111
path: 'rustup',
12+
pathQuotes: 'rustup',
1213
channel: 'stable',
1314
// TODO: Detect if we're running in Windows and if wsl works?
1415
useWSL: false,
@@ -22,4 +23,8 @@ suite('Rustup Tests', () => {
2223
test('getActiveChannel', async () => {
2324
rustup.getActiveChannel('.', config);
2425
});
26+
test('installRlsComponents', async function() {
27+
this.timeout(30000);
28+
await rustup.installRlsComponents(config);
29+
});
2530
});

0 commit comments

Comments
 (0)