Skip to content

Commit 1817bb7

Browse files
fix: detect and handle wrong link.exe on Windows
Adds comprehensive detection and user guidance for the common Windows installation failure where GNU coreutils link (from Git for Windows, MSYS2, etc.) is found before MSVC linker in PATH. Changes: - Proactively check PATH for wrong linker before cargo install - Detect "extra operand" error pattern from wrong linker - Add detailed troubleshooting instructions with multiple fix methods - Improve Build Tools installation to explicitly include C++ workload - Provide verification steps using where.exe Fixes the issue where Windows installations have likely never worked for users with Unix tools in PATH before MSVC tools.
1 parent 813368b commit 1817bb7

File tree

1 file changed

+256
-4
lines changed

1 file changed

+256
-4
lines changed

src/just-lsp-installer.ts

Lines changed: 256 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,80 @@ export class JustLspInstaller {
224224
}
225225
}
226226

227+
/**
228+
* Check if the correct linker is first in PATH on Windows
229+
* Returns information about potential PATH issues
230+
*/
231+
private async checkWindowsLinker(): Promise<{isCorrect: boolean, message: string, linkPath?: string}> {
232+
if (process.platform !== 'win32') {
233+
return { isCorrect: true, message: 'Not Windows' };
234+
}
235+
236+
try {
237+
// Use where.exe to find all link.exe in PATH
238+
const { stdout } = await asyncExec('where.exe link.exe');
239+
const linkPaths = stdout.trim().split('\n').map(p => p.trim()).filter(p => p);
240+
241+
if (linkPaths.length === 0) {
242+
return {
243+
isCorrect: false,
244+
message: 'No link.exe found in PATH. Visual Studio Build Tools may not be installed.'
245+
};
246+
}
247+
248+
const firstLink = linkPaths[0].toLowerCase();
249+
logger.info(`First link.exe in PATH: ${firstLink}`, 'JustLspInstaller');
250+
251+
// Check if the first link.exe is from a Unix-like environment
252+
const unixToolPaths = [
253+
'\\git\\',
254+
'\\msys',
255+
'\\cygwin',
256+
'\\mingw',
257+
'\\usr\\bin',
258+
];
259+
260+
const isUnixLink = unixToolPaths.some(unixPath => firstLink.includes(unixPath.toLowerCase()));
261+
262+
if (isUnixLink) {
263+
return {
264+
isCorrect: false,
265+
message: `Found Unix 'link' command before MSVC linker in PATH: ${firstLink}`,
266+
linkPath: firstLink
267+
};
268+
}
269+
270+
// Check if it's the MSVC linker
271+
const isMsvcLink = firstLink.includes('microsoft visual studio') ||
272+
firstLink.includes('\\vc\\tools\\') ||
273+
firstLink.includes('\\msvc\\');
274+
275+
if (isMsvcLink) {
276+
logger.info('Correct MSVC linker found first in PATH', 'JustLspInstaller');
277+
return {
278+
isCorrect: true,
279+
message: 'MSVC linker correctly positioned in PATH',
280+
linkPath: firstLink
281+
};
282+
}
283+
284+
// Unknown link.exe - could be problematic
285+
return {
286+
isCorrect: false,
287+
message: `Unknown link.exe found first in PATH: ${firstLink}. Expected MSVC linker.`,
288+
linkPath: firstLink
289+
};
290+
291+
} catch (error) {
292+
// where.exe failed - likely no link.exe in PATH
293+
logger.warning('Failed to check for link.exe in PATH', 'JustLspInstaller');
294+
return {
295+
isCorrect: false,
296+
message: 'Could not find link.exe in PATH. Visual Studio Build Tools may not be installed or not in PATH.'
297+
};
298+
}
299+
}
300+
227301
private async installFromLocalBuild(progress: vscode.Progress<{message?: string}>): Promise<InstallationResult> {
228302
progress.report({ message: 'Building from local source...' });
229303

@@ -345,6 +419,35 @@ export class JustLspInstaller {
345419
const { stdout: cargoVersion } = await asyncExec(cargoCheckCmd);
346420
logger.info(`Found cargo: ${cargoVersion.trim()}`, 'JustLspInstaller');
347421

422+
// On Windows, proactively check for the wrong linker issue
423+
if (process.platform === 'win32') {
424+
const linkerCheck = await this.checkWindowsLinker();
425+
if (!linkerCheck.isCorrect) {
426+
logger.warning('Wrong linker detected in PATH', 'JustLspInstaller');
427+
428+
const choice = await vscode.window.showWarningMessage(
429+
`Potential PATH issue detected: ${linkerCheck.message}\n\nThis will likely cause cargo install to fail. Would you like to see instructions to fix this first?`,
430+
'Show Fix Instructions',
431+
'Try Anyway',
432+
'Cancel'
433+
);
434+
435+
if (choice === 'Show Fix Instructions') {
436+
this.showWrongLinkerInstructions();
437+
return {
438+
success: false,
439+
error: 'Installation cancelled - user chose to fix PATH issue first'
440+
};
441+
} else if (choice === 'Cancel') {
442+
return {
443+
success: false,
444+
error: 'Installation cancelled by user'
445+
};
446+
}
447+
// If "Try Anyway", continue with installation
448+
}
449+
}
450+
348451
// Install just-lsp (this will install the original just-lsp from crates.io)
349452
progress.report({ message: 'Running cargo install just-lsp (this may take several minutes)...' });
350453
logger.info('Running cargo install just-lsp...', 'JustLspInstaller');
@@ -386,7 +489,71 @@ export class JustLspInstaller {
386489
const errorMsg = error instanceof Error ? error.message : String(error);
387490
logger.errorFromException(error, 'Cargo installation failed', 'JustLspInstaller');
388491

389-
// Check if this is a linker error indicating missing Visual Studio Build Tools
492+
// Check if this is the "wrong link.exe" problem (GNU coreutils link instead of MSVC linker)
493+
if (errorMsg.includes('link.exe') && errorMsg.includes('extra operand')) {
494+
if (process.platform === 'win32') {
495+
const message = `Rust compilation failed: The wrong 'link.exe' is being used.
496+
497+
This happens when Unix tools (Git for Windows, MSYS2, etc.) are in your PATH before the MSVC linker.
498+
499+
To fix this:
500+
1. Install Visual Studio Build Tools with C++ support
501+
2. Ensure the MSVC linker is found before Unix tools in your PATH
502+
503+
Would you like to see detailed instructions?`;
504+
505+
const choice = await vscode.window.showErrorMessage(
506+
message,
507+
'Show Instructions',
508+
'Try Installing Build Tools',
509+
'Cancel'
510+
);
511+
512+
if (choice === 'Show Instructions') {
513+
this.showWrongLinkerInstructions();
514+
} else if (choice === 'Try Installing Build Tools') {
515+
try {
516+
progress.report({ message: 'Installing Visual Studio Build Tools...' });
517+
await asyncExec('winget install --id Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools;includeRecommended" --accept-source-agreements --accept-package-agreements', {
518+
timeout: 900000 // 15 minutes
519+
});
520+
521+
vscode.window.showInformationMessage(
522+
'Visual Studio Build Tools installed. You may need to restart your computer, then reload VS Code.',
523+
'Reload Window',
524+
'Show PATH Fix Instructions'
525+
).then(choice => {
526+
if (choice === 'Reload Window') {
527+
vscode.commands.executeCommand('workbench.action.reloadWindow');
528+
} else if (choice === 'Show PATH Fix Instructions') {
529+
this.showWrongLinkerInstructions();
530+
}
531+
});
532+
533+
return {
534+
success: false,
535+
error: 'Visual Studio Build Tools installed. Please restart your computer and reload VS Code, then check PATH ordering.'
536+
};
537+
} catch (installError) {
538+
vscode.window.showErrorMessage(
539+
'Failed to install Build Tools automatically. Please install manually.',
540+
'Show Instructions'
541+
).then(choice => {
542+
if (choice === 'Show Instructions') {
543+
this.showWrongLinkerInstructions();
544+
}
545+
});
546+
}
547+
}
548+
549+
return {
550+
success: false,
551+
error: 'Wrong link.exe detected (GNU coreutils instead of MSVC linker). Visual Studio Build Tools required and PATH must be configured correctly.'
552+
};
553+
}
554+
}
555+
556+
// Check if this is a generic linker error indicating missing Visual Studio Build Tools
390557
if (errorMsg.includes('link.exe') && errorMsg.includes('Visual Studio build tools')) {
391558
if (process.platform === 'win32') {
392559
const choice = await vscode.window.showErrorMessage(
@@ -398,9 +565,10 @@ export class JustLspInstaller {
398565

399566
if (choice === 'Install Build Tools') {
400567
try {
401-
// Try using winget to install Visual Studio Build Tools
402-
await asyncExec('winget install --id Microsoft.VisualStudio.2022.BuildTools --accept-source-agreements --accept-package-agreements', {
403-
timeout: 600000 // 10 minutes
568+
progress.report({ message: 'Installing Visual Studio Build Tools...' });
569+
// Install with C++ workload explicitly
570+
await asyncExec('winget install --id Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools;includeRecommended" --accept-source-agreements --accept-package-agreements', {
571+
timeout: 900000 // 15 minutes
404572
});
405573

406574
vscode.window.showInformationMessage(
@@ -456,6 +624,90 @@ export class JustLspInstaller {
456624
}
457625

458626

627+
private showWrongLinkerInstructions(): void {
628+
const instructions = `# Fixing the "Wrong link.exe" Error on Windows
629+
630+
## Problem
631+
Rust is trying to use the GNU coreutils \`link\` command (from Git for Windows, MSYS2, etc.) instead of the Microsoft Visual C++ linker (\`link.exe\`) that it needs.
632+
633+
This error looks like:
634+
\`\`\`
635+
link: extra operand 'C:\\Users\\...\\something.o'
636+
Try 'link --help' for more information.
637+
\`\`\`
638+
639+
## Solution
640+
641+
### Step 1: Install Visual Studio Build Tools
642+
1. Open PowerShell as Administrator
643+
2. Run:
644+
\`\`\`powershell
645+
winget install --id Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools;includeRecommended"
646+
\`\`\`
647+
3. **Restart your computer** after installation completes
648+
649+
### Step 2: Fix Your PATH (Choose One Method)
650+
651+
#### Method A: Use the Visual Studio Developer Command Prompt
652+
- Launch "Developer Command Prompt for VS 2022" or "Developer PowerShell for VS 2022"
653+
- This automatically sets up the correct PATH
654+
- Install just-lsp from there:
655+
\`\`\`powershell
656+
cargo install just-lsp
657+
\`\`\`
658+
659+
#### Method B: Reorder Your System PATH
660+
1. Open System Environment Variables:
661+
- Press Win+R, type \`sysdm.cpl\`, press Enter
662+
- Click "Advanced" tab → "Environment Variables"
663+
2. In "System variables", find and edit "Path"
664+
3. Move these entries to the **top** (before Git, MSYS2, etc.):
665+
\`\`\`
666+
C:\\Program Files\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Tools\\MSVC\\<version>\\bin\\Hostx64\\x64
667+
C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\<version>\\bin\\Hostx64\\x64
668+
\`\`\`
669+
(The exact path depends on your Visual Studio version)
670+
4. **Restart your computer** for PATH changes to take effect
671+
672+
#### Method C: Use rustup's MSVC toolchain explicitly
673+
\`\`\`powershell
674+
rustup default stable-x86_64-pc-windows-msvc
675+
cargo install just-lsp
676+
\`\`\`
677+
678+
### Step 3: Verify the Fix
679+
Open a new PowerShell window and run:
680+
\`\`\`powershell
681+
where.exe link
682+
\`\`\`
683+
684+
The **first** result should be the MSVC linker, something like:
685+
\`\`\`
686+
C:\\Program Files\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Tools\\MSVC\\...\\link.exe
687+
\`\`\`
688+
689+
NOT:
690+
\`\`\`
691+
C:\\Program Files\\Git\\usr\\bin\\link.exe
692+
\`\`\`
693+
694+
### Step 4: Retry Installation
695+
Once the PATH is fixed, try installing just-lsp again:
696+
\`\`\`powershell
697+
cargo install just-lsp
698+
\`\`\`
699+
700+
## Additional Resources
701+
- [Visual Studio Build Tools Download](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022)
702+
- [Rust Windows Prerequisites](https://rust-lang.github.io/rustup/installation/windows.html)
703+
`;
704+
705+
vscode.workspace.openTextDocument({
706+
content: instructions,
707+
language: 'markdown'
708+
}).then(doc => vscode.window.showTextDocument(doc));
709+
}
710+
459711
private showManualInstallationInstructions(): void {
460712
const isWindows = process.platform === 'win32';
461713
const isMac = process.platform === 'darwin';

0 commit comments

Comments
 (0)