7643: Automatically detect the rustc-src directory (fixes #3517) r=matklad a=bnjbvr

If the configured rustcSource was not set, then try to automatically
detect a source for the sysroot rustc directory.

I wasn't sure how to do it in the case of the project.json file, though.

7663: Tolerate spaces in nix binary patching r=matklad a=CertainLach

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

Co-authored-by: Benjamin Bouvier <public@benj.me>
Co-authored-by: Yaroslav Bolyukin <iam@lach.pw>
This commit is contained in:
bors[bot] 2021-02-14 15:42:07 +00:00 committed by GitHub
commit d50a37d3aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 70 additions and 23 deletions

View file

@ -44,6 +44,15 @@ impl ops::Index<Target> 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)] #[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig { pub struct CargoConfig {
/// Do not activate the `default` feature. /// Do not activate the `default` feature.
@ -64,7 +73,7 @@ pub struct CargoConfig {
pub no_sysroot: bool, pub no_sysroot: bool,
/// rustc private crate source /// rustc private crate source
pub rustc_source: Option<AbsPathBuf>, pub rustc_source: Option<RustcSource>,
} }
pub type Package = Idx<PackageData>; pub type Package = Idx<PackageData>;

View file

@ -21,8 +21,8 @@ use rustc_hash::FxHashSet;
pub use crate::{ pub use crate::{
build_data::{BuildDataCollector, BuildDataResult}, build_data::{BuildDataCollector, BuildDataResult},
cargo_workspace::{ cargo_workspace::{
CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData, CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target,
TargetKind, TargetData, TargetKind,
}, },
project_json::{ProjectJson, ProjectJsonData}, project_json::{ProjectJson, ProjectJsonData},
sysroot::Sysroot, sysroot::Sysroot,

View file

@ -51,11 +51,18 @@ impl Sysroot {
pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
log::debug!("Discovering sysroot for {}", cargo_toml.display()); log::debug!("Discovering sysroot for {}", cargo_toml.display());
let current_dir = cargo_toml.parent().unwrap(); 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)?; let res = Sysroot::load(&sysroot_src_dir)?;
Ok(res) Ok(res)
} }
pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> {
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<Sysroot> { pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> {
let mut sysroot = Sysroot { crates: Arena::default() }; let mut sysroot = Sysroot { crates: Arena::default() };
@ -110,7 +117,18 @@ impl Sysroot {
} }
} }
fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> { fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
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<AbsPathBuf> {
if let Ok(path) = env::var("RUST_SRC_PATH") { if let Ok(path) = env::var("RUST_SRC_PATH") {
let path = AbsPathBuf::try_from(path.as_str()) let path = AbsPathBuf::try_from(path.as_str())
.map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; .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<AbsPathBuf> {
log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); 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) get_rust_src(&sysroot_path)
.or_else(|| { .or_else(|| {
let mut rustup = Command::new(toolchain::rustup()); 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<AbsPathBuf> {
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<AbsPathBuf> { fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
// Try the new path first since the old one still exists. // Try the new path first since the old one still exists.
let rust_src = sysroot_path.join("lib/rustlib/src/rust"); let rust_src = sysroot_path.join("lib/rustlib/src/rust");

View file

@ -114,6 +114,7 @@ impl ProjectWorkspace {
cargo_version cargo_version
) )
})?; })?;
let sysroot = if config.no_sysroot { let sysroot = if config.no_sysroot {
Sysroot::default() Sysroot::default()
} else { } 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( Some(
CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress) CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
.with_context(|| { .with_context(|| {

View file

@ -18,7 +18,7 @@ use ide_db::helpers::{
}; };
use itertools::Itertools; use itertools::Itertools;
use lsp_types::{ClientCapabilities, MarkupKind}; 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 rustc_hash::FxHashSet;
use serde::{de::DeserializeOwned, Deserialize}; use serde::{de::DeserializeOwned, Deserialize};
use vfs::AbsPathBuf; use vfs::AbsPathBuf;
@ -177,8 +177,9 @@ config_data! {
/// tests or binaries.\nFor example, it may be `--release`. /// tests or binaries.\nFor example, it may be `--release`.
runnables_cargoExtraArgs: Vec<String> = "[]", runnables_cargoExtraArgs: Vec<String> = "[]",
/// 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"
rustcSource : Option<PathBuf> = "null", /// to try to automatically find it.
rustcSource : Option<String> = "null",
/// Additional arguments to `rustfmt`. /// Additional arguments to `rustfmt`.
rustfmt_extraArgs: Vec<String> = "[]", rustfmt_extraArgs: Vec<String> = "[]",
@ -473,7 +474,13 @@ impl Config {
self.data.cargo_loadOutDirsFromCheck self.data.cargo_loadOutDirsFromCheck
} }
pub fn cargo(&self) -> CargoConfig { 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 { CargoConfig {
no_default_features: self.data.cargo_noDefaultFeatures, no_default_features: self.data.cargo_noDefaultFeatures,

View file

@ -105,7 +105,7 @@
[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: [[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`. 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`):: [[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: `[]`):: [[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`)::
Additional arguments to `rustfmt`. Additional arguments to `rustfmt`.
[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: [[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`)::

View file

@ -707,7 +707,7 @@
} }
}, },
"rust-analyzer.rustcSource": { "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, "default": null,
"type": [ "type": [
"null", "null",

View file

@ -246,10 +246,10 @@ async function patchelf(dest: PathLike): Promise<void> {
}, },
async (progress, _) => { async (progress, _) => {
const expression = ` const expression = `
{src, pkgs ? import <nixpkgs> {}}: {srcStr, pkgs ? import <nixpkgs> {}}:
pkgs.stdenv.mkDerivation { pkgs.stdenv.mkDerivation {
name = "rust-analyzer"; name = "rust-analyzer";
inherit src; src = /. + srcStr;
phases = [ "installPhase" "fixupPhase" ]; phases = [ "installPhase" "fixupPhase" ];
installPhase = "cp $src $out"; installPhase = "cp $src $out";
fixupPhase = '' fixupPhase = ''
@ -262,7 +262,7 @@ async function patchelf(dest: PathLike): Promise<void> {
await fs.rename(dest, origFile); await fs.rename(dest, origFile);
progress.report({ message: "Patching executable", increment: 20 }); progress.report({ message: "Patching executable", increment: 20 });
await new Promise((resolve, reject) => { 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) => { (err, stdout, stderr) => {
if (err != null) { if (err != null) {
reject(Error(stderr)); reject(Error(stderr));