From 4a6e602c9419cc5ed464343b2373c7fe86fb89d6 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 11 Feb 2021 17:34:56 +0100 Subject: Allow automatically detect the rustc-src directory (fixes #3517). If the configured rustcSource is set to "discover", try to automatically detect a source from the sysroot rustc directory. --- crates/project_model/src/cargo_workspace.rs | 11 +++++++- crates/project_model/src/lib.rs | 4 +-- crates/project_model/src/sysroot.rs | 40 +++++++++++++++++++++-------- crates/project_model/src/workspace.rs | 13 +++++++++- crates/rust-analyzer/src/config.rs | 15 ++++++++--- docs/user/generated_config.adoc | 2 +- editors/code/package.json | 2 +- 7 files changed, 67 insertions(+), 20 deletions(-) diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index f47898b9b..1d8d34a0b 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs @@ -44,6 +44,15 @@ impl ops::Index for CargoWorkspace { } } +/// Describes how to set the rustc source directory. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RustcSource { + /// Explicit path for the rustc source directory. + Path(AbsPathBuf), + /// Try to automatically detect where the rustc source directory is. + Discover, +} + #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CargoConfig { /// Do not activate the `default` feature. @@ -64,7 +73,7 @@ pub struct CargoConfig { pub no_sysroot: bool, /// rustc private crate source - pub rustc_source: Option, + pub rustc_source: Option, } pub type Package = Idx; diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index d712095a6..a5b35ed95 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs @@ -21,8 +21,8 @@ use rustc_hash::FxHashSet; pub use crate::{ build_data::{BuildDataCollector, BuildDataResult}, cargo_workspace::{ - CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData, - TargetKind, + CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target, + TargetData, TargetKind, }, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index ff44dae4a..3b0ff506d 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs @@ -51,11 +51,18 @@ impl Sysroot { pub fn discover(cargo_toml: &AbsPath) -> Result { log::debug!("Discovering sysroot for {}", cargo_toml.display()); let current_dir = cargo_toml.parent().unwrap(); - let sysroot_src_dir = discover_sysroot_src_dir(current_dir)?; + let sysroot_dir = discover_sysroot_dir(current_dir)?; + let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?; let res = Sysroot::load(&sysroot_src_dir)?; Ok(res) } + pub fn discover_rustc(cargo_toml: &AbsPath) -> Option { + log::debug!("Discovering rustc source for {}", cargo_toml.display()); + let current_dir = cargo_toml.parent().unwrap(); + discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) + } + pub fn load(sysroot_src_dir: &AbsPath) -> Result { let mut sysroot = Sysroot { crates: Arena::default() }; @@ -110,7 +117,18 @@ impl Sysroot { } } -fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result { +fn discover_sysroot_dir(current_dir: &AbsPath) -> Result { + let mut rustc = Command::new(toolchain::rustc()); + rustc.current_dir(current_dir).args(&["--print", "sysroot"]); + log::debug!("Discovering sysroot by {:?}", rustc); + let stdout = utf8_stdout(rustc)?; + Ok(AbsPathBuf::assert(PathBuf::from(stdout))) +} + +fn discover_sysroot_src_dir( + sysroot_path: &AbsPathBuf, + current_dir: &AbsPath, +) -> Result { if let Ok(path) = env::var("RUST_SRC_PATH") { let path = AbsPathBuf::try_from(path.as_str()) .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; @@ -122,14 +140,6 @@ fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result { log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); } - let sysroot_path = { - let mut rustc = Command::new(toolchain::rustc()); - rustc.current_dir(current_dir).args(&["--print", "sysroot"]); - log::debug!("Discovering sysroot by {:?}", rustc); - let stdout = utf8_stdout(rustc)?; - AbsPathBuf::assert(PathBuf::from(stdout)) - }; - get_rust_src(&sysroot_path) .or_else(|| { let mut rustup = Command::new(toolchain::rustup()); @@ -149,6 +159,16 @@ try installing the Rust source the same way you installed rustc", }) } +fn get_rustc_src(sysroot_path: &AbsPath) -> Option { + let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml"); + log::debug!("Checking for rustc source code: {}", rustc_src.display()); + if rustc_src.exists() { + Some(rustc_src) + } else { + None + } +} + fn get_rust_src(sysroot_path: &AbsPath) -> Option { // Try the new path first since the old one still exists. let rust_src = sysroot_path.join("lib/rustlib/src/rust"); diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index c30861976..0220efdb4 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs @@ -114,6 +114,7 @@ impl ProjectWorkspace { cargo_version ) })?; + let sysroot = if config.no_sysroot { Sysroot::default() } else { @@ -125,7 +126,17 @@ impl ProjectWorkspace { })? }; - let rustc = if let Some(rustc_dir) = &config.rustc_source { + let rustc_dir = if let Some(rustc_source) = &config.rustc_source { + use cargo_workspace::RustcSource; + match rustc_source { + RustcSource::Path(path) => Some(path.clone()), + RustcSource::Discover => Sysroot::discover_rustc(&cargo_toml), + } + } else { + None + }; + + let rustc = if let Some(rustc_dir) = rustc_dir { Some( CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress) .with_context(|| { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index cc0b22bff..f9098968a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -18,7 +18,7 @@ use ide_db::helpers::{ }; use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; -use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; +use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; use rustc_hash::FxHashSet; use serde::{de::DeserializeOwned, Deserialize}; use vfs::AbsPathBuf; @@ -177,8 +177,9 @@ config_data! { /// tests or binaries.\nFor example, it may be `--release`. runnables_cargoExtraArgs: Vec = "[]", - /// Path to the rust compiler sources, for usage in rustc_private projects. - rustcSource : Option = "null", + /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" + /// to try to automatically find it. + rustcSource : Option = "null", /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = "[]", @@ -473,7 +474,13 @@ impl Config { self.data.cargo_loadOutDirsFromCheck } pub fn cargo(&self) -> CargoConfig { - let rustc_source = self.data.rustcSource.as_ref().map(|it| self.root_path.join(&it)); + let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { + if rustc_src == "discover" { + RustcSource::Discover + } else { + RustcSource::Path(self.root_path.join(rustc_src)) + } + }); CargoConfig { no_default_features: self.data.cargo_noDefaultFeatures, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 55178c84c..f91e04c31 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -105,7 +105,7 @@ [[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`. [[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: - Path to the rust compiler sources, for usage in rustc_private projects. + Path to the rust compiler sources, for usage in rustc_private projects, or "discover" to try to automatically find it. [[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: Additional arguments to `rustfmt`. [[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: diff --git a/editors/code/package.json b/editors/code/package.json index 55825456e..defa108cb 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -707,7 +707,7 @@ } }, "rust-analyzer.rustcSource": { - "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects.", + "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects, or \"discover\" to try to automatically find it.", "default": null, "type": [ "null", -- cgit v1.2.3 From 471795b019d800dca65e2a400fbdb166e6f821cd Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 13 Feb 2021 23:11:00 +0300 Subject: fix: tolerate spaces in nix binary patching If path to original file contains space (I.e on code insiders, where default data directory is ~/Code - Insiders/), then there is syntax error evaluating src arg. Instead pass path as str, and coerce to path back in nix expression Signed-off-by: Yaroslav Bolyukin --- editors/code/src/main.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index d18d6c8a9..620810d72 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -246,10 +246,10 @@ async function patchelf(dest: PathLike): Promise { }, async (progress, _) => { const expression = ` - {src, pkgs ? import {}}: + {srcStr, pkgs ? import {}}: pkgs.stdenv.mkDerivation { name = "rust-analyzer"; - inherit src; + src = /. + srcStr; phases = [ "installPhase" "fixupPhase" ]; installPhase = "cp $src $out"; fixupPhase = '' @@ -262,7 +262,7 @@ async function patchelf(dest: PathLike): Promise { await fs.rename(dest, origFile); progress.report({ message: "Patching executable", increment: 20 }); await new Promise((resolve, reject) => { - const handle = exec(`nix-build -E - --arg src '${origFile}' -o ${dest}`, + const handle = exec(`nix-build -E - --argstr srcStr '${origFile}' -o '${dest}'`, (err, stdout, stderr) => { if (err != null) { reject(Error(stderr)); -- cgit v1.2.3