Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'editors/code/src/toolchain.ts')
-rw-r--r--editors/code/src/toolchain.ts74
1 files changed, 65 insertions, 9 deletions
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index 76946d1510..6ae365c71c 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -3,7 +3,7 @@ import * as os from "os";
import * as path from "path";
import * as readline from "readline";
import * as vscode from "vscode";
-import { Env, log, memoizeAsync, unwrapUndefinable } from "./util";
+import { Env, isWindows, log, memoizeAsync, unwrapUndefinable } from "./util";
import type { CargoRunnableArgs } from "./lsp_ext";
interface CompilationArtifact {
@@ -160,9 +160,46 @@ export function cargoPath(env?: Env): Promise<string> {
if (env?.["RUSTC_TOOLCHAIN"]) {
return Promise.resolve("cargo");
}
+ if (env) {
+ return getPathForExecutableWithEnv("cargo", env);
+ }
return getPathForExecutable("cargo");
}
+/**
+ * Resolves an executable using an explicitly supplied environment instead of the VS Code host
+ * process environment.
+ *
+ * Some extension call sites already construct the exact env they will use for spawning, so path
+ * resolution needs to honor that same `PATH`/`CARGO_HOME` view to avoid launching a different
+ * toolchain than the one that was resolved.
+ */
+async function getPathForExecutableWithEnv(
+ executableName: "cargo" | "rustc" | "rustup",
+ env: Env,
+): Promise<string> {
+ const envVar = getEnvVar(env, executableName.toUpperCase());
+ if (envVar) {
+ return envVar;
+ }
+
+ if (await lookupInPath(executableName, getEnvVar(env, "PATH") ?? "")) {
+ return executableName;
+ }
+
+ const cargoHome = getCargoHomeFromPath(getEnvVar(env, "CARGO_HOME"));
+ if (cargoHome) {
+ for (const candidate of executableCandidates(executableName)) {
+ const standardPath = vscode.Uri.joinPath(cargoHome, "bin", candidate);
+ if (await isFileAtUri(standardPath)) {
+ return standardPath.fsPath;
+ }
+ }
+ }
+
+ return executableName;
+}
+
/** Mirrors `toolchain::get_path_for_executable()` implementation */
const getPathForExecutable = memoizeAsync(
// We apply caching to decrease file-system interactions
@@ -172,23 +209,22 @@ const getPathForExecutable = memoizeAsync(
if (envVar) return envVar;
}
- if (await lookupInPath(executableName)) return executableName;
+ if (await lookupInPath(executableName, process.env["PATH"] ?? "")) return executableName;
const cargoHome = getCargoHome();
if (cargoHome) {
- const standardPath = vscode.Uri.joinPath(cargoHome, "bin", executableName);
- if (await isFileAtUri(standardPath)) return standardPath.fsPath;
+ for (const candidate of executableCandidates(executableName)) {
+ const standardPath = vscode.Uri.joinPath(cargoHome, "bin", candidate);
+ if (await isFileAtUri(standardPath)) return standardPath.fsPath;
+ }
}
return executableName;
},
);
-async function lookupInPath(exec: string): Promise<boolean> {
- const paths = process.env["PATH"] ?? "";
-
+async function lookupInPath(exec: string, paths: string): Promise<boolean> {
const candidates = paths.split(path.delimiter).flatMap((dirInPath) => {
- const candidate = path.join(dirInPath, exec);
- return os.type() === "Windows_NT" ? [candidate, `${candidate}.exe`] : [candidate];
+ return executableCandidates(exec).map((candidate) => path.join(dirInPath, candidate));
});
for await (const isFile of candidates.map(isFileAtPath)) {
@@ -199,8 +235,28 @@ async function lookupInPath(exec: string): Promise<boolean> {
return false;
}
+function executableCandidates(executableName: string): string[] {
+ // Keep the extension-side probe aligned with `crates/toolchain::probe_for_binary()`, which
+ // checks both the bare executable name and the platform suffix such as `.exe` on Windows.
+ // That matters for both PATH lookups and `$CARGO_HOME/bin/<tool>` fallbacks.
+ return isWindows ? [executableName, `${executableName}.exe`] : [executableName];
+}
+
+function getEnvVar(env: Env, name: string): string | undefined {
+ if (!isWindows) {
+ return env[name];
+ }
+
+ const foldedName = name.toLowerCase();
+ return Object.entries(env).find(([key]) => key.toLowerCase() === foldedName)?.[1];
+}
+
function getCargoHome(): vscode.Uri | null {
const envVar = process.env["CARGO_HOME"];
+ return getCargoHomeFromPath(envVar);
+}
+
+function getCargoHomeFromPath(envVar: string | undefined): vscode.Uri | null {
if (envVar) return vscode.Uri.file(envVar);
try {