mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Auto merge of #16356 - Veykril:sysroot-metadata, r=Veykril
internal: Add unstable config for loading the sysroot sources via `cargo metadata` cc https://github.com/rust-lang/rust-analyzer/issues/7637 This takes the approach of having `cargo metadata` generate a lock file that we then delete again, hence why this is behind a flag. If people need this for their workflow they can just enable it, if not, they are probably better off keeping it disabled. [example](https://dreampuf.github.io/GraphvizOnline/#digraph%20rust_analyzer_crate_graph%20%7B%0A%20%20%20%20_0%5Blabel%3D%22core%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_17%5Blabel%3D%22ra_playground%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_14%5Blabel%3D%22getopts%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_11%5Blabel%3D%22std_detect%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_8%5Blabel%3D%22unwind%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_5%5Blabel%3D%22hashbrown%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_2%5Blabel%3D%22alloc%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_16%5Blabel%3D%22test%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_13%5Blabel%3D%22unicode_width%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_10%5Blabel%3D%22rustc_demangle%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_7%5Blabel%3D%22panic_abort%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_4%5Blabel%3D%22cfg_if%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_1%5Blabel%3D%22compiler_builtins%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_18%5Blabel%3D%22build_script_build%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_15%5Blabel%3D%22proc_macro%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_12%5Blabel%3D%22std%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_9%5Blabel%3D%22panic_unwind%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_6%5Blabel%3D%22libc%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_3%5Blabel%3D%22allocator_api2%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_15%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_16%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_14%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_14%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_14%20-%3E%20_13%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_3%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_2%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_2%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_7%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_9%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_14%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_13%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_13%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_13%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_10%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_10%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_1%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_15%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_16%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_15%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_15%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_5%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_7%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_8%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_9%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_10%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_11%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_8%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_6%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%7D%0A) ![image](https://github.com/rust-lang/rust-analyzer/assets/3757771/7709bb38-d948-4106-82c2-9b76677620bd) hashbrown resolves as a dependency now
This commit is contained in:
commit
a616c4d117
11 changed files with 507 additions and 249 deletions
|
@ -9,7 +9,7 @@
|
|||
use std::{fmt, mem, ops, str::FromStr};
|
||||
|
||||
use cfg::CfgOptions;
|
||||
use la_arena::{Arena, Idx};
|
||||
use la_arena::{Arena, Idx, RawIdx};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use semver::Version;
|
||||
use syntax::SmolStr;
|
||||
|
@ -157,6 +157,10 @@ impl CrateOrigin {
|
|||
pub fn is_lib(&self) -> bool {
|
||||
matches!(self, CrateOrigin::Library { .. })
|
||||
}
|
||||
|
||||
pub fn is_lang(&self) -> bool {
|
||||
matches!(self, CrateOrigin::Lang { .. })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -174,7 +178,7 @@ impl From<&str> for LangCrateOrigin {
|
|||
match s {
|
||||
"alloc" => LangCrateOrigin::Alloc,
|
||||
"core" => LangCrateOrigin::Core,
|
||||
"proc-macro" => LangCrateOrigin::ProcMacro,
|
||||
"proc-macro" | "proc_macro" => LangCrateOrigin::ProcMacro,
|
||||
"std" => LangCrateOrigin::Std,
|
||||
"test" => LangCrateOrigin::Test,
|
||||
_ => LangCrateOrigin::Other,
|
||||
|
@ -522,7 +526,7 @@ impl CrateGraph {
|
|||
self.arena.iter().map(|(idx, _)| idx)
|
||||
}
|
||||
|
||||
// FIXME: used for `handle_hack_cargo_workspace`, should be removed later
|
||||
// FIXME: used for fixing up the toolchain sysroot, should be removed and done differently
|
||||
#[doc(hidden)]
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_ {
|
||||
self.arena.iter_mut()
|
||||
|
@ -619,7 +623,12 @@ impl CrateGraph {
|
|||
/// This will deduplicate the crates of the graph where possible.
|
||||
/// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
|
||||
/// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
|
||||
pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
|
||||
pub fn extend(
|
||||
&mut self,
|
||||
mut other: CrateGraph,
|
||||
proc_macros: &mut ProcMacroPaths,
|
||||
on_finished: impl FnOnce(&FxHashMap<CrateId, CrateId>),
|
||||
) {
|
||||
let topo = other.crates_in_topological_order();
|
||||
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
|
||||
for topo in topo {
|
||||
|
@ -670,6 +679,8 @@ impl CrateGraph {
|
|||
|
||||
*proc_macros =
|
||||
mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect();
|
||||
|
||||
on_finished(&id_map);
|
||||
}
|
||||
|
||||
fn find_path(
|
||||
|
@ -721,6 +732,29 @@ impl CrateGraph {
|
|||
fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
|
||||
self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
|
||||
}
|
||||
|
||||
/// Removes all crates from this crate graph except for the ones in `to_keep` and fixes up the dependencies.
|
||||
/// Returns a mapping from old crate ids to new crate ids.
|
||||
pub fn remove_crates_except(&mut self, to_keep: &[CrateId]) -> Vec<Option<CrateId>> {
|
||||
let mut id_map = vec![None; self.arena.len()];
|
||||
self.arena = std::mem::take(&mut self.arena)
|
||||
.into_iter()
|
||||
.filter_map(|(id, data)| if to_keep.contains(&id) { Some((id, data)) } else { None })
|
||||
.enumerate()
|
||||
.map(|(new_id, (id, data))| {
|
||||
id_map[id.into_raw().into_u32() as usize] =
|
||||
Some(CrateId::from_raw(RawIdx::from_u32(new_id as u32)));
|
||||
data
|
||||
})
|
||||
.collect();
|
||||
for (_, data) in self.arena.iter_mut() {
|
||||
data.dependencies.iter_mut().for_each(|dep| {
|
||||
dep.crate_id =
|
||||
id_map[dep.crate_id.into_raw().into_u32() as usize].expect("crate was filtered")
|
||||
});
|
||||
}
|
||||
id_map
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<CrateId> for CrateGraph {
|
||||
|
|
|
@ -82,6 +82,8 @@ pub struct CargoConfig {
|
|||
pub target: Option<String>,
|
||||
/// Sysroot loading behavior
|
||||
pub sysroot: Option<RustLibSource>,
|
||||
/// Whether to invoke `cargo metadata` on the sysroot crate.
|
||||
pub sysroot_query_metadata: bool,
|
||||
pub sysroot_src: Option<AbsPathBuf>,
|
||||
/// rustc private crate source
|
||||
pub rustc_source: Option<RustLibSource>,
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::{env, fs, iter, ops, path::PathBuf, process::Command};
|
|||
|
||||
use anyhow::{format_err, Context, Result};
|
||||
use base_db::CrateName;
|
||||
use itertools::Itertools;
|
||||
use la_arena::{Arena, Idx};
|
||||
use paths::{AbsPath, AbsPathBuf};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -18,25 +19,61 @@ use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath};
|
|||
pub struct Sysroot {
|
||||
root: AbsPathBuf,
|
||||
src_root: AbsPathBuf,
|
||||
mode: SysrootMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum SysrootMode {
|
||||
Workspace(CargoWorkspace),
|
||||
Stitched(Stitched),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) struct Stitched {
|
||||
crates: Arena<SysrootCrateData>,
|
||||
/// Stores the result of `cargo metadata` of the `RA_UNSTABLE_SYSROOT_HACK` workspace.
|
||||
pub hack_cargo_workspace: Option<CargoWorkspace>,
|
||||
}
|
||||
|
||||
impl ops::Index<SysrootCrate> for Stitched {
|
||||
type Output = SysrootCrateData;
|
||||
fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
|
||||
&self.crates[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Stitched {
|
||||
pub(crate) fn public_deps(&self) -> impl Iterator<Item = (CrateName, SysrootCrate, bool)> + '_ {
|
||||
// core is added as a dependency before std in order to
|
||||
// mimic rustcs dependency order
|
||||
["core", "alloc", "std"]
|
||||
.into_iter()
|
||||
.zip(iter::repeat(true))
|
||||
.chain(iter::once(("test", false)))
|
||||
.filter_map(move |(name, prelude)| {
|
||||
Some((CrateName::new(name).unwrap(), self.by_name(name)?, prelude))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn proc_macro(&self) -> Option<SysrootCrate> {
|
||||
self.by_name("proc_macro")
|
||||
}
|
||||
|
||||
pub(crate) fn crates(&self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + '_ {
|
||||
self.crates.iter().map(|(id, _data)| id)
|
||||
}
|
||||
|
||||
fn by_name(&self, name: &str) -> Option<SysrootCrate> {
|
||||
let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
|
||||
Some(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct SysrootCrateData {
|
||||
pub name: String,
|
||||
pub root: ManifestPath,
|
||||
pub deps: Vec<SysrootCrate>,
|
||||
}
|
||||
|
||||
impl ops::Index<SysrootCrate> for Sysroot {
|
||||
type Output = SysrootCrateData;
|
||||
fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
|
||||
&self.crates[index]
|
||||
}
|
||||
pub(crate) struct SysrootCrateData {
|
||||
pub(crate) name: String,
|
||||
pub(crate) root: ManifestPath,
|
||||
pub(crate) deps: Vec<SysrootCrate>,
|
||||
}
|
||||
|
||||
impl Sysroot {
|
||||
|
@ -53,32 +90,19 @@ impl Sysroot {
|
|||
&self.src_root
|
||||
}
|
||||
|
||||
pub fn public_deps(&self) -> impl Iterator<Item = (CrateName, SysrootCrate, bool)> + '_ {
|
||||
// core is added as a dependency before std in order to
|
||||
// mimic rustcs dependency order
|
||||
["core", "alloc", "std"]
|
||||
.into_iter()
|
||||
.zip(iter::repeat(true))
|
||||
.chain(iter::once(("test", false)))
|
||||
.filter_map(move |(name, prelude)| {
|
||||
Some((CrateName::new(name).unwrap(), self.by_name(name)?, prelude))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn proc_macro(&self) -> Option<SysrootCrate> {
|
||||
self.by_name("proc_macro")
|
||||
}
|
||||
|
||||
pub fn crates(&self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + '_ {
|
||||
self.crates.iter().map(|(id, _data)| id)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.crates.is_empty()
|
||||
match &self.mode {
|
||||
SysrootMode::Workspace(ws) => ws.packages().next().is_none(),
|
||||
SysrootMode::Stitched(stitched) => stitched.crates.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loading_warning(&self) -> Option<String> {
|
||||
if self.by_name("core").is_none() {
|
||||
let has_core = match &self.mode {
|
||||
SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
|
||||
SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(),
|
||||
};
|
||||
if !has_core {
|
||||
let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
|
||||
" (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
|
||||
} else {
|
||||
|
@ -92,27 +116,43 @@ impl Sysroot {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_packages(&self) -> usize {
|
||||
match &self.mode {
|
||||
SysrootMode::Workspace(ws) => ws.packages().count(),
|
||||
SysrootMode::Stitched(c) => c.crates().count(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mode(&self) -> &SysrootMode {
|
||||
&self.mode
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
|
||||
impl Sysroot {
|
||||
/// Attempts to discover the toolchain's sysroot from the given `dir`.
|
||||
pub fn discover(dir: &AbsPath, extra_env: &FxHashMap<String, String>) -> Result<Sysroot> {
|
||||
pub fn discover(
|
||||
dir: &AbsPath,
|
||||
extra_env: &FxHashMap<String, String>,
|
||||
metadata: bool,
|
||||
) -> Result<Sysroot> {
|
||||
tracing::debug!("discovering sysroot for {dir}");
|
||||
let sysroot_dir = discover_sysroot_dir(dir, extra_env)?;
|
||||
let sysroot_src_dir =
|
||||
discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?;
|
||||
Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
|
||||
Ok(Sysroot::load(sysroot_dir, sysroot_src_dir, metadata))
|
||||
}
|
||||
|
||||
pub fn discover_with_src_override(
|
||||
current_dir: &AbsPath,
|
||||
extra_env: &FxHashMap<String, String>,
|
||||
src: AbsPathBuf,
|
||||
metadata: bool,
|
||||
) -> Result<Sysroot> {
|
||||
tracing::debug!("discovering sysroot for {current_dir}");
|
||||
let sysroot_dir = discover_sysroot_dir(current_dir, extra_env)?;
|
||||
Ok(Sysroot::load(sysroot_dir, src))
|
||||
Ok(Sysroot::load(sysroot_dir, src, metadata))
|
||||
}
|
||||
|
||||
pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
|
||||
|
@ -131,49 +171,132 @@ impl Sysroot {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
|
||||
pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf, metadata: bool) -> Result<Sysroot> {
|
||||
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
|
||||
format_err!("can't load standard library from sysroot path {sysroot_dir}")
|
||||
})?;
|
||||
Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
|
||||
Ok(Sysroot::load(sysroot_dir, sysroot_src_dir, metadata))
|
||||
}
|
||||
|
||||
pub fn load(sysroot_dir: AbsPathBuf, mut sysroot_src_dir: AbsPathBuf) -> Sysroot {
|
||||
// FIXME: Remove this `hack_cargo_workspace` field completely once we support sysroot dependencies
|
||||
let hack_cargo_workspace = if let Ok(path) = std::env::var("RA_UNSTABLE_SYSROOT_HACK") {
|
||||
let cargo_toml = ManifestPath::try_from(
|
||||
AbsPathBuf::try_from(&*format!("{path}/Cargo.toml")).unwrap(),
|
||||
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf, metadata: bool) -> Sysroot {
|
||||
if metadata {
|
||||
let sysroot: Option<_> = (|| {
|
||||
let sysroot_cargo_toml = ManifestPath::try_from(
|
||||
AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot/Cargo.toml")).ok()?,
|
||||
)
|
||||
.unwrap();
|
||||
sysroot_src_dir = AbsPathBuf::try_from(&*path).unwrap().join("library");
|
||||
CargoWorkspace::fetch_metadata(
|
||||
&cargo_toml,
|
||||
&AbsPathBuf::try_from("/").unwrap(),
|
||||
.ok()?;
|
||||
let current_dir =
|
||||
AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot")).ok()?;
|
||||
let res = CargoWorkspace::fetch_metadata(
|
||||
&sysroot_cargo_toml,
|
||||
¤t_dir,
|
||||
&CargoConfig::default(),
|
||||
&|_| (),
|
||||
)
|
||||
.map(CargoWorkspace::new)
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut sysroot = Sysroot {
|
||||
root: sysroot_dir,
|
||||
src_root: sysroot_src_dir,
|
||||
crates: Arena::default(),
|
||||
hack_cargo_workspace,
|
||||
};
|
||||
.map_err(|e| {
|
||||
tracing::error!(
|
||||
"failed to load sysroot `{sysroot_src_dir}/sysroot/Cargo.toml`: {}",
|
||||
e
|
||||
);
|
||||
e
|
||||
});
|
||||
if let Err(e) =
|
||||
std::fs::remove_file(&format!("{sysroot_src_dir}/sysroot/Cargo.lock"))
|
||||
{
|
||||
tracing::error!(
|
||||
"failed to remove sysroot `{sysroot_src_dir}/sysroot/Cargo.lock`: {}",
|
||||
e
|
||||
)
|
||||
}
|
||||
let mut res = res.ok()?;
|
||||
|
||||
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
|
||||
// This is done prior to `CrateGraph` construction to avoid having duplicate `std` targets.
|
||||
|
||||
let mut fake_core = None;
|
||||
let mut fake_alloc = None;
|
||||
let mut fake_std = None;
|
||||
let mut real_core = None;
|
||||
let mut real_alloc = None;
|
||||
let mut real_std = None;
|
||||
res.packages.iter().enumerate().for_each(|(idx, package)| {
|
||||
match package.name.strip_prefix("rustc-std-workspace-") {
|
||||
Some("core") => fake_core = Some((idx, package.id.clone())),
|
||||
Some("alloc") => fake_alloc = Some((idx, package.id.clone())),
|
||||
Some("std") => fake_std = Some((idx, package.id.clone())),
|
||||
Some(_) => {
|
||||
tracing::warn!("unknown rustc-std-workspace-* crate: {}", package.name)
|
||||
}
|
||||
None => match &*package.name {
|
||||
"core" => real_core = Some(package.id.clone()),
|
||||
"alloc" => real_alloc = Some(package.id.clone()),
|
||||
"std" => real_std = Some(package.id.clone()),
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let patches =
|
||||
[fake_core.zip(real_core), fake_alloc.zip(real_alloc), fake_std.zip(real_std)]
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let resolve = res.resolve.as_mut().expect("metadata executed with deps");
|
||||
let mut remove_nodes = vec![];
|
||||
for (idx, node) in resolve.nodes.iter_mut().enumerate() {
|
||||
// Replace them in the dependency list
|
||||
node.deps.iter_mut().for_each(|dep| {
|
||||
if let Some((_, real)) =
|
||||
patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg)
|
||||
{
|
||||
dep.pkg = real;
|
||||
}
|
||||
});
|
||||
if patches.clone().any(|((_, fake), _)| fake == node.id) {
|
||||
remove_nodes.push(idx);
|
||||
}
|
||||
}
|
||||
// Remove the fake ones from the resolve data
|
||||
remove_nodes.into_iter().rev().for_each(|r| {
|
||||
resolve.nodes.remove(r);
|
||||
});
|
||||
// Remove the fake ones from the packages
|
||||
patches.map(|((r, _), _)| r).sorted().rev().for_each(|r| {
|
||||
res.packages.remove(r);
|
||||
});
|
||||
|
||||
res.workspace_members = res
|
||||
.packages
|
||||
.iter()
|
||||
.filter_map(|package| {
|
||||
RELEVANT_SYSROOT_CRATES
|
||||
.contains(&&*package.name)
|
||||
.then(|| package.id.clone())
|
||||
})
|
||||
.collect();
|
||||
let cargo_workspace = CargoWorkspace::new(res);
|
||||
Some(Sysroot {
|
||||
root: sysroot_dir.clone(),
|
||||
src_root: sysroot_src_dir.clone(),
|
||||
mode: SysrootMode::Workspace(cargo_workspace),
|
||||
})
|
||||
})();
|
||||
if let Some(sysroot) = sysroot {
|
||||
return sysroot;
|
||||
}
|
||||
}
|
||||
let mut stitched = Stitched { crates: Arena::default() };
|
||||
|
||||
for path in SYSROOT_CRATES.trim().lines() {
|
||||
let name = path.split('/').last().unwrap();
|
||||
let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")]
|
||||
.into_iter()
|
||||
.map(|it| sysroot.src_root.join(it))
|
||||
.map(|it| sysroot_src_dir.join(it))
|
||||
.filter_map(|it| ManifestPath::try_from(it).ok())
|
||||
.find(|it| fs::metadata(it).is_ok());
|
||||
|
||||
if let Some(root) = root {
|
||||
sysroot.crates.alloc(SysrootCrateData {
|
||||
stitched.crates.alloc(SysrootCrateData {
|
||||
name: name.into(),
|
||||
root,
|
||||
deps: Vec::new(),
|
||||
|
@ -181,36 +304,34 @@ impl Sysroot {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(std) = sysroot.by_name("std") {
|
||||
if let Some(std) = stitched.by_name("std") {
|
||||
for dep in STD_DEPS.trim().lines() {
|
||||
if let Some(dep) = sysroot.by_name(dep) {
|
||||
sysroot.crates[std].deps.push(dep)
|
||||
if let Some(dep) = stitched.by_name(dep) {
|
||||
stitched.crates[std].deps.push(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(alloc) = sysroot.by_name("alloc") {
|
||||
if let Some(alloc) = stitched.by_name("alloc") {
|
||||
for dep in ALLOC_DEPS.trim().lines() {
|
||||
if let Some(dep) = sysroot.by_name(dep) {
|
||||
sysroot.crates[alloc].deps.push(dep)
|
||||
if let Some(dep) = stitched.by_name(dep) {
|
||||
stitched.crates[alloc].deps.push(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(proc_macro) = sysroot.by_name("proc_macro") {
|
||||
if let Some(proc_macro) = stitched.by_name("proc_macro") {
|
||||
for dep in PROC_MACRO_DEPS.trim().lines() {
|
||||
if let Some(dep) = sysroot.by_name(dep) {
|
||||
sysroot.crates[proc_macro].deps.push(dep)
|
||||
if let Some(dep) = stitched.by_name(dep) {
|
||||
stitched.crates[proc_macro].deps.push(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sysroot
|
||||
Sysroot {
|
||||
root: sysroot_dir,
|
||||
src_root: sysroot_src_dir,
|
||||
mode: SysrootMode::Stitched(stitched),
|
||||
}
|
||||
|
||||
fn by_name(&self, name: &str) -> Option<SysrootCrate> {
|
||||
let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
|
||||
Some(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,3 +439,5 @@ test";
|
|||
const PROC_MACRO_DEPS: &str = "
|
||||
std
|
||||
core";
|
||||
|
||||
const RELEVANT_SYSROOT_CRATES: &[&str] = &["core", "alloc", "std", "test", "proc_macro"];
|
||||
|
|
|
@ -38,7 +38,7 @@ fn load_cargo_with_overrides(
|
|||
to_crate_graph(project_workspace)
|
||||
}
|
||||
|
||||
fn load_cargo_with_sysroot(
|
||||
fn load_cargo_with_fake_sysroot(
|
||||
file_map: &mut FxHashMap<AbsPathBuf, FileId>,
|
||||
file: &str,
|
||||
) -> (CrateGraph, ProcMacroPaths) {
|
||||
|
@ -125,7 +125,7 @@ fn get_fake_sysroot() -> Sysroot {
|
|||
// fake sysroot, so we give them both the same path:
|
||||
let sysroot_dir = AbsPathBuf::assert(sysroot_path);
|
||||
let sysroot_src_dir = sysroot_dir.clone();
|
||||
Sysroot::load(sysroot_dir, sysroot_src_dir)
|
||||
Sysroot::load(sysroot_dir, sysroot_src_dir, false)
|
||||
}
|
||||
|
||||
fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
|
||||
|
@ -225,12 +225,12 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
|
|||
#[test]
|
||||
fn crate_graph_dedup_identical() {
|
||||
let (mut crate_graph, proc_macros) =
|
||||
load_cargo_with_sysroot(&mut Default::default(), "regex-metadata.json");
|
||||
load_cargo_with_fake_sysroot(&mut Default::default(), "regex-metadata.json");
|
||||
crate_graph.sort_deps();
|
||||
|
||||
let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone());
|
||||
|
||||
crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros);
|
||||
crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros, |_| ());
|
||||
assert!(crate_graph.iter().eq(d_crate_graph.iter()));
|
||||
assert_eq!(proc_macros, d_proc_macros);
|
||||
}
|
||||
|
@ -239,14 +239,14 @@ fn crate_graph_dedup_identical() {
|
|||
fn crate_graph_dedup() {
|
||||
let path_map = &mut Default::default();
|
||||
let (mut crate_graph, _proc_macros) =
|
||||
load_cargo_with_sysroot(path_map, "ripgrep-metadata.json");
|
||||
load_cargo_with_fake_sysroot(path_map, "ripgrep-metadata.json");
|
||||
assert_eq!(crate_graph.iter().count(), 81);
|
||||
crate_graph.sort_deps();
|
||||
let (regex_crate_graph, mut regex_proc_macros) =
|
||||
load_cargo_with_sysroot(path_map, "regex-metadata.json");
|
||||
load_cargo_with_fake_sysroot(path_map, "regex-metadata.json");
|
||||
assert_eq!(regex_crate_graph.iter().count(), 60);
|
||||
|
||||
crate_graph.extend(regex_crate_graph, &mut regex_proc_macros);
|
||||
crate_graph.extend(regex_crate_graph, &mut regex_proc_macros, |_| ());
|
||||
assert_eq!(crate_graph.iter().count(), 118);
|
||||
}
|
||||
|
||||
|
@ -254,12 +254,12 @@ fn crate_graph_dedup() {
|
|||
fn test_deduplicate_origin_dev() {
|
||||
let path_map = &mut Default::default();
|
||||
let (mut crate_graph, _proc_macros) =
|
||||
load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json");
|
||||
load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_A.json");
|
||||
crate_graph.sort_deps();
|
||||
let (crate_graph_1, mut _proc_macros_2) =
|
||||
load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json");
|
||||
load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_B.json");
|
||||
|
||||
crate_graph.extend(crate_graph_1, &mut _proc_macros_2);
|
||||
crate_graph.extend(crate_graph_1, &mut _proc_macros_2, |_| ());
|
||||
|
||||
let mut crates_named_p2 = vec![];
|
||||
for id in crate_graph.iter() {
|
||||
|
@ -280,12 +280,12 @@ fn test_deduplicate_origin_dev() {
|
|||
fn test_deduplicate_origin_dev_rev() {
|
||||
let path_map = &mut Default::default();
|
||||
let (mut crate_graph, _proc_macros) =
|
||||
load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json");
|
||||
load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_B.json");
|
||||
crate_graph.sort_deps();
|
||||
let (crate_graph_1, mut _proc_macros_2) =
|
||||
load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json");
|
||||
load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_A.json");
|
||||
|
||||
crate_graph.extend(crate_graph_1, &mut _proc_macros_2);
|
||||
crate_graph.extend(crate_graph_1, &mut _proc_macros_2, |_| ());
|
||||
|
||||
let mut crates_named_p2 = vec![];
|
||||
for id in crate_graph.iter() {
|
||||
|
@ -301,3 +301,40 @@ fn test_deduplicate_origin_dev_rev() {
|
|||
let p2 = crates_named_p2[0];
|
||||
assert!(p2.origin.is_local());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_test_real_sysroot_cargo() {
|
||||
if std::env::var("SYSROOT_CARGO_METADATA").is_err() {
|
||||
return;
|
||||
}
|
||||
let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default();
|
||||
let meta = get_test_json_file("hello-world-metadata.json");
|
||||
|
||||
let cargo_workspace = CargoWorkspace::new(meta);
|
||||
let sysroot = Ok(Sysroot::discover(
|
||||
AbsPath::assert(Path::new(env!("CARGO_MANIFEST_DIR"))),
|
||||
&Default::default(),
|
||||
true,
|
||||
)
|
||||
.unwrap());
|
||||
|
||||
let project_workspace = ProjectWorkspace::Cargo {
|
||||
cargo: cargo_workspace,
|
||||
build_scripts: WorkspaceBuildScripts::default(),
|
||||
sysroot,
|
||||
rustc: Err(None),
|
||||
rustc_cfg: Vec::new(),
|
||||
cfg_overrides: Default::default(),
|
||||
toolchain: None,
|
||||
target_layout: Err("target_data_layout not loaded".into()),
|
||||
};
|
||||
project_workspace.to_crate_graph(
|
||||
&mut {
|
||||
|path| {
|
||||
let len = file_map.len();
|
||||
Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32)))
|
||||
}
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use base_db::{
|
|||
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, DependencyKind,
|
||||
Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
|
||||
};
|
||||
use cfg::{CfgDiff, CfgOptions};
|
||||
use cfg::{CfgAtom, CfgDiff, CfgOptions};
|
||||
use paths::{AbsPath, AbsPathBuf};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use semver::Version;
|
||||
|
@ -22,7 +22,7 @@ use crate::{
|
|||
cfg_flag::CfgFlag,
|
||||
project_json::Crate,
|
||||
rustc_cfg::{self, RustcCfgConfig},
|
||||
sysroot::SysrootCrate,
|
||||
sysroot::{SysrootCrate, SysrootMode},
|
||||
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
|
||||
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
|
||||
};
|
||||
|
@ -130,7 +130,7 @@ impl fmt::Debug for ProjectWorkspace {
|
|||
let mut debug_struct = f.debug_struct("Json");
|
||||
debug_struct.field("n_crates", &project.n_crates());
|
||||
if let Ok(sysroot) = sysroot {
|
||||
debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
|
||||
debug_struct.field("n_sysroot_crates", &sysroot.num_packages());
|
||||
}
|
||||
debug_struct.field("toolchain", &toolchain);
|
||||
debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
|
||||
|
@ -208,23 +208,23 @@ impl ProjectWorkspace {
|
|||
|
||||
let sysroot = match (&config.sysroot, &config.sysroot_src) {
|
||||
(Some(RustLibSource::Path(path)), None) => {
|
||||
Sysroot::with_sysroot_dir(path.clone()).map_err(|e| {
|
||||
Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata).map_err(|e| {
|
||||
Some(format!("Failed to find sysroot at {path}:{e}"))
|
||||
})
|
||||
}
|
||||
(Some(RustLibSource::Discover), None) => {
|
||||
Sysroot::discover(cargo_toml.parent(), &config.extra_env).map_err(|e| {
|
||||
Sysroot::discover(cargo_toml.parent(), &config.extra_env, config.sysroot_query_metadata).map_err(|e| {
|
||||
Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}"))
|
||||
})
|
||||
}
|
||||
(Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
|
||||
Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone()))
|
||||
Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone(), config.sysroot_query_metadata))
|
||||
}
|
||||
(Some(RustLibSource::Discover), Some(sysroot_src)) => {
|
||||
Sysroot::discover_with_src_override(
|
||||
cargo_toml.parent(),
|
||||
&config.extra_env,
|
||||
sysroot_src.clone(),
|
||||
sysroot_src.clone(), config.sysroot_query_metadata,
|
||||
).map_err(|e| {
|
||||
Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}"))
|
||||
})
|
||||
|
@ -317,12 +317,12 @@ impl ProjectWorkspace {
|
|||
toolchain: Option<Version>,
|
||||
) -> ProjectWorkspace {
|
||||
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
|
||||
(Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)),
|
||||
(Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src, false)),
|
||||
(Some(sysroot), None) => {
|
||||
// assume sysroot is structured like rustup's and guess `sysroot_src`
|
||||
let sysroot_src =
|
||||
sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
|
||||
Ok(Sysroot::load(sysroot, sysroot_src))
|
||||
Ok(Sysroot::load(sysroot, sysroot_src, false))
|
||||
}
|
||||
(None, Some(sysroot_src)) => {
|
||||
// assume sysroot is structured like rustup's and guess `sysroot`
|
||||
|
@ -330,7 +330,7 @@ impl ProjectWorkspace {
|
|||
for _ in 0..5 {
|
||||
sysroot.pop();
|
||||
}
|
||||
Ok(Sysroot::load(sysroot, sysroot_src))
|
||||
Ok(Sysroot::load(sysroot, sysroot_src, false))
|
||||
}
|
||||
(None, None) => Err(None),
|
||||
};
|
||||
|
@ -354,16 +354,22 @@ impl ProjectWorkspace {
|
|||
config: &CargoConfig,
|
||||
) -> anyhow::Result<ProjectWorkspace> {
|
||||
let sysroot = match &config.sysroot {
|
||||
Some(RustLibSource::Path(path)) => Sysroot::with_sysroot_dir(path.clone())
|
||||
.map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))),
|
||||
Some(RustLibSource::Path(path)) => {
|
||||
Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
|
||||
.map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
|
||||
}
|
||||
Some(RustLibSource::Discover) => {
|
||||
let dir = &detached_files
|
||||
.first()
|
||||
.and_then(|it| it.parent())
|
||||
.ok_or_else(|| format_err!("No detached files to load"))?;
|
||||
Sysroot::discover(dir, &config.extra_env).map_err(|e| {
|
||||
Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}"))
|
||||
})
|
||||
Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata).map_err(
|
||||
|e| {
|
||||
Some(format!(
|
||||
"Failed to find sysroot for {dir}. Is rust-src installed? {e}"
|
||||
))
|
||||
},
|
||||
)
|
||||
}
|
||||
None => Err(None),
|
||||
};
|
||||
|
@ -494,13 +500,43 @@ impl ProjectWorkspace {
|
|||
/// The return type contains the path and whether or not
|
||||
/// the root is a member of the current workspace
|
||||
pub fn to_roots(&self) -> Vec<PackageRoot> {
|
||||
let mk_sysroot = |sysroot: Result<&Sysroot, _>, project_root: Option<&AbsPath>| {
|
||||
sysroot.map(|sysroot| PackageRoot {
|
||||
let mk_sysroot = |sysroot: Result<_, _>, project_root: Option<&AbsPath>| {
|
||||
let project_root = project_root.map(ToOwned::to_owned);
|
||||
sysroot.into_iter().flat_map(move |sysroot: &Sysroot| {
|
||||
let mut r = match sysroot.mode() {
|
||||
SysrootMode::Workspace(ws) => ws
|
||||
.packages()
|
||||
.filter_map(|pkg| {
|
||||
if ws[pkg].is_local {
|
||||
// the local ones are included in the main `PackageRoot`` below
|
||||
return None;
|
||||
}
|
||||
let pkg_root = ws[pkg].manifest.parent().to_path_buf();
|
||||
|
||||
let include = vec![pkg_root.clone()];
|
||||
|
||||
let exclude = vec![
|
||||
pkg_root.join(".git"),
|
||||
pkg_root.join("target"),
|
||||
pkg_root.join("tests"),
|
||||
pkg_root.join("examples"),
|
||||
pkg_root.join("benches"),
|
||||
];
|
||||
Some(PackageRoot { is_local: false, include, exclude })
|
||||
})
|
||||
.collect(),
|
||||
SysrootMode::Stitched(_) => vec![],
|
||||
};
|
||||
|
||||
r.push(PackageRoot {
|
||||
// mark the sysroot as mutable if it is located inside of the project
|
||||
is_local: project_root
|
||||
.as_ref()
|
||||
.map_or(false, |project_root| sysroot.src_root().starts_with(project_root)),
|
||||
include: vec![sysroot.src_root().to_path_buf()],
|
||||
exclude: Vec::new(),
|
||||
});
|
||||
r
|
||||
})
|
||||
};
|
||||
match self {
|
||||
|
@ -588,16 +624,16 @@ impl ProjectWorkspace {
|
|||
pub fn n_packages(&self) -> usize {
|
||||
match self {
|
||||
ProjectWorkspace::Json { project, sysroot, .. } => {
|
||||
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
|
||||
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
|
||||
sysroot_package_len + project.n_crates()
|
||||
}
|
||||
ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
|
||||
let rustc_package_len = rustc.as_ref().map_or(0, |(it, _)| it.packages().len());
|
||||
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
|
||||
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
|
||||
cargo.packages().len() + sysroot_package_len + rustc_package_len
|
||||
}
|
||||
ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
|
||||
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
|
||||
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
|
||||
sysroot_package_len + files.len()
|
||||
}
|
||||
}
|
||||
|
@ -638,7 +674,6 @@ impl ProjectWorkspace {
|
|||
sysroot.as_ref().ok(),
|
||||
rustc_cfg.clone(),
|
||||
cfg_overrides,
|
||||
None,
|
||||
build_scripts,
|
||||
match target_layout.as_ref() {
|
||||
Ok(it) => Ok(Arc::from(it.as_str())),
|
||||
|
@ -849,8 +884,6 @@ fn cargo_to_crate_graph(
|
|||
sysroot: Option<&Sysroot>,
|
||||
rustc_cfg: Vec<CfgFlag>,
|
||||
override_cfg: &CfgOverrides,
|
||||
// Don't compute cfg and use this if present, only used for the sysroot experiment hack
|
||||
forced_cfg: Option<CfgOptions>,
|
||||
build_scripts: &WorkspaceBuildScripts,
|
||||
target_layout: TargetLayoutLoadResult,
|
||||
toolchain: Option<&Version>,
|
||||
|
@ -883,7 +916,7 @@ fn cargo_to_crate_graph(
|
|||
for pkg in cargo.packages() {
|
||||
has_private |= cargo[pkg].metadata.rustc_private;
|
||||
|
||||
let cfg_options = forced_cfg.clone().unwrap_or_else(|| {
|
||||
let cfg_options = {
|
||||
let mut cfg_options = cfg_options.clone();
|
||||
|
||||
// Add test cfg for local crates
|
||||
|
@ -908,7 +941,7 @@ fn cargo_to_crate_graph(
|
|||
cfg_options.apply_diff(diff.clone());
|
||||
};
|
||||
cfg_options
|
||||
});
|
||||
};
|
||||
|
||||
let mut lib_tgt = None;
|
||||
for &tgt in cargo[pkg].targets.iter() {
|
||||
|
@ -1349,26 +1382,84 @@ fn sysroot_to_crate_graph(
|
|||
toolchain: Option<&Version>,
|
||||
) -> (SysrootPublicDeps, Option<CrateId>) {
|
||||
let _p = profile::span("sysroot_to_crate_graph");
|
||||
let cfg_options = create_cfg_options(rustc_cfg.clone());
|
||||
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace {
|
||||
Some(cargo) => handle_hack_cargo_workspace(
|
||||
match sysroot.mode() {
|
||||
SysrootMode::Workspace(cargo) => {
|
||||
let (mut cg, mut pm) = cargo_to_crate_graph(
|
||||
load,
|
||||
None,
|
||||
cargo,
|
||||
None,
|
||||
rustc_cfg,
|
||||
cfg_options,
|
||||
&CfgOverrides::default(),
|
||||
&WorkspaceBuildScripts::default(),
|
||||
target_layout,
|
||||
toolchain,
|
||||
crate_graph,
|
||||
sysroot,
|
||||
),
|
||||
None => sysroot
|
||||
);
|
||||
|
||||
let mut pub_deps = vec![];
|
||||
let mut libproc_macro = None;
|
||||
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap();
|
||||
for (cid, c) in cg.iter_mut() {
|
||||
// uninject `test` flag so `core` keeps working.
|
||||
c.cfg_options.apply_diff(diff.clone());
|
||||
// patch the origin
|
||||
if c.origin.is_local() {
|
||||
let lang_crate = LangCrateOrigin::from(
|
||||
c.display_name.as_ref().map_or("", |it| it.canonical_name()),
|
||||
);
|
||||
c.origin = CrateOrigin::Lang(lang_crate);
|
||||
match lang_crate {
|
||||
LangCrateOrigin::Test
|
||||
| LangCrateOrigin::Alloc
|
||||
| LangCrateOrigin::Core
|
||||
| LangCrateOrigin::Std => pub_deps.push((
|
||||
CrateName::normalize_dashes(&lang_crate.to_string()),
|
||||
cid,
|
||||
!matches!(lang_crate, LangCrateOrigin::Test),
|
||||
)),
|
||||
LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
|
||||
LangCrateOrigin::Other => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut marker_set = vec![];
|
||||
for &(_, cid, _) in pub_deps.iter() {
|
||||
marker_set.extend(cg.transitive_deps(cid));
|
||||
}
|
||||
if let Some(cid) = libproc_macro {
|
||||
marker_set.extend(cg.transitive_deps(cid));
|
||||
}
|
||||
|
||||
marker_set.sort();
|
||||
marker_set.dedup();
|
||||
|
||||
// Remove all crates except the ones we are interested in to keep the sysroot graph small.
|
||||
let removed_mapping = cg.remove_crates_except(&marker_set);
|
||||
|
||||
crate_graph.extend(cg, &mut pm, |mapping| {
|
||||
// Map the id through the removal mapping first, then through the crate graph extension mapping.
|
||||
pub_deps.iter_mut().for_each(|(_, cid, _)| {
|
||||
*cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
|
||||
});
|
||||
if let Some(libproc_macro) = &mut libproc_macro {
|
||||
*libproc_macro = mapping
|
||||
[&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
|
||||
}
|
||||
});
|
||||
|
||||
(SysrootPublicDeps { deps: pub_deps }, libproc_macro)
|
||||
}
|
||||
SysrootMode::Stitched(stitched) => {
|
||||
let cfg_options = create_cfg_options(rustc_cfg.clone());
|
||||
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched
|
||||
.crates()
|
||||
.filter_map(|krate| {
|
||||
let file_id = load(&sysroot[krate].root)?;
|
||||
let file_id = load(&stitched[krate].root)?;
|
||||
|
||||
let env = Env::default();
|
||||
let display_name =
|
||||
CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
|
||||
CrateDisplayName::from_canonical_name(stitched[krate].name.clone());
|
||||
let crate_id = crate_graph.add_crate_root(
|
||||
file_id,
|
||||
Edition::CURRENT,
|
||||
|
@ -1378,95 +1469,39 @@ fn sysroot_to_crate_graph(
|
|||
None,
|
||||
env,
|
||||
false,
|
||||
CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
|
||||
CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
|
||||
target_layout.clone(),
|
||||
toolchain.cloned(),
|
||||
);
|
||||
Some((krate, crate_id))
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
for from in sysroot.crates() {
|
||||
for &to in sysroot[from].deps.iter() {
|
||||
let name = CrateName::new(&sysroot[to].name).unwrap();
|
||||
if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
|
||||
.collect();
|
||||
|
||||
for from in stitched.crates() {
|
||||
for &to in stitched[from].deps.iter() {
|
||||
let name = CrateName::new(&stitched[to].name).unwrap();
|
||||
if let (Some(&from), Some(&to)) =
|
||||
(sysroot_crates.get(&from), sysroot_crates.get(&to))
|
||||
{
|
||||
add_dep(crate_graph, from, name, to, DependencyKind::Normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let public_deps = SysrootPublicDeps {
|
||||
deps: sysroot
|
||||
deps: stitched
|
||||
.public_deps()
|
||||
.filter_map(|(name, idx, prelude)| Some((name, *sysroot_crates.get(&idx)?, prelude)))
|
||||
.filter_map(|(name, idx, prelude)| {
|
||||
Some((name, *sysroot_crates.get(&idx)?, prelude))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
|
||||
let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
|
||||
let libproc_macro =
|
||||
stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
|
||||
(public_deps, libproc_macro)
|
||||
}
|
||||
|
||||
fn handle_hack_cargo_workspace(
|
||||
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
|
||||
cargo: &CargoWorkspace,
|
||||
rustc_cfg: Vec<CfgFlag>,
|
||||
cfg_options: CfgOptions,
|
||||
target_layout: Result<Arc<str>, Arc<str>>,
|
||||
toolchain: Option<&Version>,
|
||||
crate_graph: &mut CrateGraph,
|
||||
sysroot: &Sysroot,
|
||||
) -> FxHashMap<SysrootCrate, CrateId> {
|
||||
let (cg, mut pm) = cargo_to_crate_graph(
|
||||
load,
|
||||
None,
|
||||
cargo,
|
||||
None,
|
||||
rustc_cfg,
|
||||
&CfgOverrides::default(),
|
||||
Some(cfg_options),
|
||||
&WorkspaceBuildScripts::default(),
|
||||
target_layout,
|
||||
toolchain,
|
||||
);
|
||||
crate_graph.extend(cg, &mut pm);
|
||||
for crate_name in ["std", "alloc", "core"] {
|
||||
let original = crate_graph
|
||||
.iter()
|
||||
.find(|x| {
|
||||
crate_graph[*x]
|
||||
.display_name
|
||||
.as_ref()
|
||||
.map(|x| x.canonical_name() == crate_name)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.unwrap();
|
||||
let fake_crate_name = format!("rustc-std-workspace-{}", crate_name);
|
||||
let fake = crate_graph
|
||||
.iter()
|
||||
.find(|x| {
|
||||
crate_graph[*x]
|
||||
.display_name
|
||||
.as_ref()
|
||||
.map(|x| x.canonical_name() == fake_crate_name)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.unwrap();
|
||||
crate_graph.remove_and_replace(fake, original).unwrap();
|
||||
}
|
||||
for (_, c) in crate_graph.iter_mut() {
|
||||
if c.origin.is_local() {
|
||||
// LangCrateOrigin::Other is good enough for a hack.
|
||||
c.origin = CrateOrigin::Lang(LangCrateOrigin::Other);
|
||||
}
|
||||
}
|
||||
sysroot
|
||||
.crates()
|
||||
.filter_map(|krate| {
|
||||
let file_id = load(&sysroot[krate].root)?;
|
||||
let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
|
||||
Some((krate, crate_id))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn add_dep(
|
||||
|
|
|
@ -182,7 +182,7 @@
|
|||
},
|
||||
],
|
||||
origin: Lang(
|
||||
Other,
|
||||
ProcMacro,
|
||||
),
|
||||
is_proc_macro: false,
|
||||
target_layout: Err(
|
||||
|
|
|
@ -61,9 +61,12 @@ impl Tester {
|
|||
cargo_config.sysroot = Some(RustLibSource::Discover);
|
||||
let workspace = ProjectWorkspace::DetachedFiles {
|
||||
files: vec![tmp_file.clone()],
|
||||
sysroot: Ok(
|
||||
Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env).unwrap()
|
||||
),
|
||||
sysroot: Ok(Sysroot::discover(
|
||||
tmp_file.parent().unwrap(),
|
||||
&cargo_config.extra_env,
|
||||
false,
|
||||
)
|
||||
.unwrap()),
|
||||
rustc_cfg: vec![],
|
||||
};
|
||||
let load_cargo_config = LoadCargoConfig {
|
||||
|
|
|
@ -135,6 +135,13 @@ config_data! {
|
|||
///
|
||||
/// This option does not take effect until rust-analyzer is restarted.
|
||||
cargo_sysroot: Option<String> = "\"discover\"",
|
||||
/// Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze
|
||||
/// third-party dependencies of the standard libraries.
|
||||
///
|
||||
/// This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer
|
||||
/// will attempt to clean up afterwards, but nevertheless requires the location to be
|
||||
/// writable to.
|
||||
cargo_sysrootQueryMetadata: bool = "false",
|
||||
/// Relative path to the sysroot library sources. If left unset, this will default to
|
||||
/// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
|
||||
///
|
||||
|
@ -1233,6 +1240,7 @@ impl Config {
|
|||
});
|
||||
let sysroot_src =
|
||||
self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot));
|
||||
let sysroot_query_metadata = self.data.cargo_sysrootQueryMetadata;
|
||||
|
||||
CargoConfig {
|
||||
features: match &self.data.cargo_features {
|
||||
|
@ -1244,6 +1252,7 @@ impl Config {
|
|||
},
|
||||
target: self.data.cargo_target.clone(),
|
||||
sysroot,
|
||||
sysroot_query_metadata,
|
||||
sysroot_src,
|
||||
rustc_source,
|
||||
cfg_overrides: project_model::CfgOverrides {
|
||||
|
|
|
@ -516,7 +516,7 @@ impl GlobalState {
|
|||
for ws in &**self.workspaces {
|
||||
let (other, mut crate_proc_macros) =
|
||||
ws.to_crate_graph(&mut load, &self.config.extra_env());
|
||||
crate_graph.extend(other, &mut crate_proc_macros);
|
||||
crate_graph.extend(other, &mut crate_proc_macros, |_| {});
|
||||
proc_macros.push(crate_proc_macros);
|
||||
}
|
||||
(crate_graph, proc_macros, crate_graph_file_dependencies)
|
||||
|
|
|
@ -121,6 +121,16 @@ Unsetting this disables sysroot loading.
|
|||
|
||||
This option does not take effect until rust-analyzer is restarted.
|
||||
--
|
||||
[[rust-analyzer.cargo.sysrootQueryMetadata]]rust-analyzer.cargo.sysrootQueryMetadata (default: `false`)::
|
||||
+
|
||||
--
|
||||
Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze
|
||||
third-party dependencies of the standard libraries.
|
||||
|
||||
This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer
|
||||
will attempt to clean up afterwards, but nevertheless requires the location to be
|
||||
writable to.
|
||||
--
|
||||
[[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`)::
|
||||
+
|
||||
--
|
||||
|
|
|
@ -648,6 +648,11 @@
|
|||
"string"
|
||||
]
|
||||
},
|
||||
"rust-analyzer.cargo.sysrootQueryMetadata": {
|
||||
"markdownDescription": "Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze\nthird-party dependencies of the standard libraries.\n\nThis will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer\nwill attempt to clean up afterwards, but nevertheless requires the location to be\nwritable to.",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"rust-analyzer.cargo.sysrootSrc": {
|
||||
"markdownDescription": "Relative path to the sysroot library sources. If left unset, this will default to\n`{cargo.sysroot}/lib/rustlib/src/rust/library`.\n\nThis option does not take effect until rust-analyzer is restarted.",
|
||||
"default": null,
|
||||
|
|
Loading…
Reference in a new issue