diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index de4c9586df..1976e4de30 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -579,6 +579,7 @@ impl GlobalStateSnapshot { target_kind: target_data.kind, required_features: target_data.required_features.clone(), features: package_data.features.keys().cloned().collect(), + sysroot_root: workspace.sysroot.root().map(ToOwned::to_owned), })); } ProjectWorkspaceKind::Json(project) => { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index e19f7a4898..8996143114 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -50,7 +50,7 @@ use crate::{ self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams, FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, }, - target_spec::TargetSpec, + target_spec::{CargoTargetSpec, TargetSpec}, }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { @@ -848,6 +848,14 @@ pub(crate) fn handle_runnables( if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args { runnable.label = format!("{} + expect", runnable.label); r.environment.insert("UPDATE_EXPECT".to_owned(), "1".to_owned()); + if let Some(TargetSpec::Cargo(CargoTargetSpec { + sysroot_root: Some(sysroot_root), + .. + })) = &target_spec + { + r.environment + .insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string()); + } } } res.push(runnable); @@ -889,7 +897,12 @@ pub(crate) fn handle_runnables( override_cargo: config.override_cargo.clone(), cargo_args, executable_args: Vec::new(), - environment: Default::default(), + environment: spec + .sysroot_root + .as_ref() + .map(|root| ("RUSTC_TOOLCHAIN".to_owned(), root.to_string())) + .into_iter() + .collect(), }), }) } diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 323926e435..cb9b141002 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1399,7 +1399,11 @@ pub(crate) fn runnable( cargo_args, cwd: cwd.into(), executable_args, - environment: Default::default(), + environment: spec + .sysroot_root + .map(|root| ("RUSTC_TOOLCHAIN".to_owned(), root.to_string())) + .into_iter() + .collect(), }), })) } diff --git a/crates/rust-analyzer/src/target_spec.rs b/crates/rust-analyzer/src/target_spec.rs index 045b9e4198..162faa5619 100644 --- a/crates/rust-analyzer/src/target_spec.rs +++ b/crates/rust-analyzer/src/target_spec.rs @@ -57,6 +57,7 @@ pub(crate) struct CargoTargetSpec { pub(crate) crate_id: CrateId, pub(crate) required_features: Vec, pub(crate) features: FxHashSet, + pub(crate) sysroot_root: Option, } #[derive(Clone, Debug)] diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index f23e368093..d9622b4a0d 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -3,10 +3,10 @@ import * as vscode from "vscode"; import * as path from "path"; import type * as ra from "./lsp_ext"; -import { Cargo, getRustcId, getSysroot } from "./toolchain"; +import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { prepareEnv } from "./run"; -import { isCargoRunnableArgs, unwrapUndefinable } from "./util"; +import { execute, isCargoRunnableArgs, unwrapUndefinable } from "./util"; const debugOutput = vscode.window.createOutputChannel("Debug"); type DebugConfigProvider = ( @@ -142,18 +142,29 @@ async function getDebugConfiguration( const executable = await getDebugExecutable(runnableArgs, env); let sourceFileMap = debugOptions.sourceFileMap; if (sourceFileMap === "auto") { - // let's try to use the default toolchain - const [commitHash, sysroot] = await Promise.all([ - getRustcId(wsFolder), - getSysroot(wsFolder), - ]); - const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust"); sourceFileMap = {}; - sourceFileMap[`/rustc/${commitHash}/`] = rustlib; + const sysroot = env["RUSTC_TOOLCHAIN"]; + if (sysroot) { + // let's try to use the default toolchain + const data = await execute(`rustc -V -v`, { cwd: wsFolder, env }); + const rx = /commit-hash:\s(.*)$/m; + + const commitHash = rx.exec(data)?.[1]; + if (commitHash) { + const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust"); + sourceFileMap[`/rustc/${commitHash}/`] = rustlib; + } + } } const provider = unwrapUndefinable(knownEngines[debugEngine.id]); - const debugConfig = provider(runnable, runnableArgs, simplifyPath(executable), env); + const debugConfig = provider( + runnable, + runnableArgs, + simplifyPath(executable), + env, + sourceFileMap, + ); if (debugConfig.type in debugOptions.engineSettings) { const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; for (var key in settingsMap) { diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 783bbc1607..7179eb3744 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -8,7 +8,6 @@ import { makeDebugConfig } from "./debug"; import type { Config, RunnableEnvCfg, RunnableEnvCfgItem } from "./config"; import type { LanguageClient } from "vscode-languageclient/node"; import { unwrapUndefinable, type RustEditor } from "./util"; -import * as toolchain from "./toolchain"; const quickPickButtons = [ { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." }, @@ -115,7 +114,7 @@ export async function createTaskFromRunnable( let definition: tasks.TaskDefinition; let options; - let cargo; + let cargo = "cargo"; if (runnable.kind === "cargo") { const runnableArgs = runnable.args; let args = createCargoArgs(runnableArgs); @@ -126,8 +125,6 @@ export async function createTaskFromRunnable( cargo = unwrapUndefinable(cargoParts[0]); args = [...cargoParts.slice(1), ...args]; - } else { - cargo = await toolchain.cargoPath(); } definition = { @@ -200,7 +197,7 @@ async function getRunnables( continue; } - if (debuggeeOnly && (r.label.startsWith("doctest") || r.label.startsWith("cargo"))) { + if (debuggeeOnly && r.label.startsWith("doctest")) { continue; } items.push(new RunnableQuickPick(r)); diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index fac1cc6394..730ec6d1e9 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -125,7 +125,7 @@ export async function targetToExecution( let command, args; if (isCargoTask(definition)) { // FIXME: The server should provide cargo - command = cargo || (await toolchain.cargoPath()); + command = cargo || (await toolchain.cargoPath(options?.env)); args = [definition.command].concat(definition.args || []); } else { command = definition.command; diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 6a0b5c26d8..850a6a5561 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 { execute, log, memoizeAsync, unwrapNullable, unwrapUndefinable } from "./util"; +import { log, memoizeAsync, unwrapUndefinable } from "./util"; import type { CargoRunnableArgs } from "./lsp_ext"; interface CompilationArtifact { @@ -55,7 +55,10 @@ export class Cargo { return result; } - private async getArtifacts(spec: ArtifactSpec): Promise { + private async getArtifacts( + spec: ArtifactSpec, + env?: Record, + ): Promise { const artifacts: CompilationArtifact[] = []; try { @@ -78,6 +81,7 @@ export class Cargo { } }, (stderr) => this.output.append(stderr), + env, ); } catch (err) { this.output.show(true); @@ -90,6 +94,7 @@ export class Cargo { async executableFromArgs(runnableArgs: CargoRunnableArgs): Promise { const artifacts = await this.getArtifacts( Cargo.artifactSpec(runnableArgs.cargoArgs, runnableArgs.executableArgs), + runnableArgs.environment, ); if (artifacts.length === 0) { @@ -106,8 +111,9 @@ export class Cargo { cargoArgs: string[], onStdoutJson: (obj: any) => void, onStderrString: (data: string) => void, + env?: Record, ): Promise { - const path = await cargoPath(); + const path = await cargoPath(env); return await new Promise((resolve, reject) => { const cargo = cp.spawn(path, cargoArgs, { stdio: ["ignore", "pipe", "pipe"], @@ -133,29 +139,12 @@ export class Cargo { } } -/** Mirrors `project_model::sysroot::discover_sysroot_dir()` implementation*/ -export async function getSysroot(dir: string): Promise { - const rustcPath = await getPathForExecutable("rustc"); - - // do not memoize the result because the toolchain may change between runs - return await execute(`${rustcPath} --print sysroot`, { cwd: dir }); -} - -export async function getRustcId(dir: string): Promise { - const rustcPath = await getPathForExecutable("rustc"); - - // do not memoize the result because the toolchain may change between runs - const data = await execute(`${rustcPath} -V -v`, { cwd: dir }); - const rx = /commit-hash:\s(.*)$/m; - - const result = unwrapNullable(rx.exec(data)); - const first = unwrapUndefinable(result[1]); - return first; -} - /** Mirrors `toolchain::cargo()` implementation */ // FIXME: The server should provide this -export function cargoPath(): Promise { +export function cargoPath(env?: Record): Promise { + if (env?.["RUSTC_TOOLCHAIN"]) { + return Promise.resolve("cargo"); + } return getPathForExecutable("cargo"); }