Skip to content

Commit

Permalink
Merge pull request #18788 from Veykril/push-zxystwnotuvq
Browse files Browse the repository at this point in the history
Remove `rust-analyzer.cargo.sysrootQueryMetadata` config again
  • Loading branch information
Veykril authored Jan 7, 2025
2 parents f9a3c41 + 764ce49 commit c360bf5
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 252 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use span::Edition;
use toolchain::Tool;

use crate::{CfgOverrides, InvocationStrategy};
use crate::{ManifestPath, Sysroot, SysrootQueryMetadata};
use crate::{ManifestPath, Sysroot};

/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
Expand Down Expand Up @@ -89,8 +89,6 @@ pub struct CargoConfig {
pub target: Option<String>,
/// Sysroot loading behavior
pub sysroot: Option<RustLibSource>,
/// How to query metadata for the sysroot crate.
pub sysroot_query_metadata: SysrootQueryMetadata,
pub sysroot_src: Option<AbsPathBuf>,
/// rustc private crate source
pub rustc_source: Option<RustLibSource>,
Expand Down
14 changes: 10 additions & 4 deletions src/tools/rust-analyzer/crates/project-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,19 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> {
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SysrootQueryMetadata {
pub enum SysrootSourceWorkspaceConfig {
CargoMetadata(CargoMetadataConfig),
None,
Stitched,
}

impl Default for SysrootQueryMetadata {
impl Default for SysrootSourceWorkspaceConfig {
fn default() -> Self {
SysrootQueryMetadata::CargoMetadata(Default::default())
SysrootSourceWorkspaceConfig::default_cargo()
}
}

impl SysrootSourceWorkspaceConfig {
pub fn default_cargo() -> Self {
SysrootSourceWorkspaceConfig::CargoMetadata(Default::default())
}
}
209 changes: 86 additions & 123 deletions src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,37 @@
//! but we can't process `.rlib` and need source code instead. The source code
//! is typically installed with `rustup component add rust-src` command.
use std::{env, fs, ops, path::Path, process::Command};
use std::{
env, fs,
ops::{self, Not},
path::Path,
process::Command,
};

use anyhow::{format_err, Result};
use base_db::CrateName;
use itertools::Itertools;
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashMap;
use stdx::format_to;
use toolchain::{probe_for_binary, Tool};

use crate::{
cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath,
SysrootQueryMetadata,
SysrootSourceWorkspaceConfig,
};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sysroot {
root: Option<AbsPathBuf>,
src_root: Option<AbsPathBuf>,
mode: SysrootMode,
workspace: SysrootWorkspace,
error: Option<String>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum SysrootMode {
pub(crate) enum SysrootWorkspace {
Workspace(CargoWorkspace),
Stitched(Stitched),
Empty,
Expand Down Expand Up @@ -82,7 +88,7 @@ pub(crate) struct SysrootCrateData {

impl Sysroot {
pub const fn empty() -> Sysroot {
Sysroot { root: None, src_root: None, mode: SysrootMode::Empty, error: None }
Sysroot { root: None, src_root: None, workspace: SysrootWorkspace::Empty, error: None }
}

/// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
Expand All @@ -99,10 +105,10 @@ impl Sysroot {
}

pub fn is_empty(&self) -> bool {
match &self.mode {
SysrootMode::Workspace(ws) => ws.packages().next().is_none(),
SysrootMode::Stitched(stitched) => stitched.crates.is_empty(),
SysrootMode::Empty => true,
match &self.workspace {
SysrootWorkspace::Workspace(ws) => ws.packages().next().is_none(),
SysrootWorkspace::Stitched(stitched) => stitched.crates.is_empty(),
SysrootWorkspace::Empty => true,
}
}

Expand All @@ -111,64 +117,51 @@ impl Sysroot {
}

pub fn num_packages(&self) -> usize {
match &self.mode {
SysrootMode::Workspace(ws) => ws.packages().count(),
SysrootMode::Stitched(c) => c.crates().count(),
SysrootMode::Empty => 0,
match &self.workspace {
SysrootWorkspace::Workspace(ws) => ws.packages().count(),
SysrootWorkspace::Stitched(c) => c.crates().count(),
SysrootWorkspace::Empty => 0,
}
}

pub(crate) fn mode(&self) -> &SysrootMode {
&self.mode
pub(crate) fn workspace(&self) -> &SysrootWorkspace {
&self.workspace
}
}

// 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>,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
pub fn discover(dir: &AbsPath, extra_env: &FxHashMap<String, String>) -> Sysroot {
let sysroot_dir = discover_sysroot_dir(dir, extra_env);
let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| {
discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env)
});
Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir, sysroot_query_metadata)
Sysroot::assemble(Some(sysroot_dir), sysroot_src_dir)
}

pub fn discover_with_src_override(
current_dir: &AbsPath,
extra_env: &FxHashMap<String, String>,
sysroot_src_dir: AbsPathBuf,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let sysroot_dir = discover_sysroot_dir(current_dir, extra_env);
Sysroot::load_core_check(
Some(sysroot_dir),
Some(Ok(sysroot_src_dir)),
sysroot_query_metadata,
)
Sysroot::assemble(Some(sysroot_dir), Some(Ok(sysroot_src_dir)))
}

pub fn discover_sysroot_src_dir(
sysroot_dir: AbsPathBuf,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir)
.ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}"));
Sysroot::load_core_check(
Some(Ok(sysroot_dir)),
Some(sysroot_src_dir),
sysroot_query_metadata,
)
Sysroot::assemble(Some(Ok(sysroot_dir)), Some(sysroot_src_dir))
}

pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
get_rustc_src(self.root()?)
}

pub fn new(sysroot_dir: Option<AbsPathBuf>, sysroot_src_dir: Option<AbsPathBuf>) -> Sysroot {
Self::assemble(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok))
}

/// Returns a command to run a tool preferring the cargo proxies if the sysroot exists.
pub fn tool(&self, tool: Tool, current_dir: impl AsRef<Path>) -> Command {
match self.root() {
Expand Down Expand Up @@ -205,101 +198,59 @@ impl Sysroot {
})
}

pub fn load(
sysroot_dir: Option<AbsPathBuf>,
sysroot_src_dir: Option<AbsPathBuf>,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), sysroot_query_metadata)
}

fn load_core_check(
sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, sysroot_query_metadata);
if sysroot.error.is_none() {
if let Some(src_root) = &sysroot.src_root {
let has_core = match &sysroot.mode {
SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(),
SysrootMode::Empty => true,
};
if !has_core {
let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
" (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
} else {
", try running `rustup component add rust-src` to possibly fix this"
};
sysroot.error = Some(format!(
"sysroot at `{src_root}` is missing a `core` library{var_note}",
));
}
}
}
sysroot
}

fn load_(
fn assemble(
sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let sysroot_dir = match sysroot_dir {
let mut errors = String::new();
let root = match sysroot_dir {
Some(Ok(sysroot_dir)) => Some(sysroot_dir),
Some(Err(e)) => {
return Sysroot {
root: None,
src_root: None,
mode: SysrootMode::Empty,
error: Some(e.to_string()),
}
format_to!(errors, "{e}\n");
None
}
None => None,
};
let sysroot_src_dir = match sysroot_src_dir {
Some(Ok(sysroot_src_dir)) => sysroot_src_dir,
let src_root = match sysroot_src_dir {
Some(Ok(sysroot_src_dir)) => Some(sysroot_src_dir),
Some(Err(e)) => {
return Sysroot {
root: sysroot_dir,
src_root: None,
mode: SysrootMode::Empty,
error: Some(e.to_string()),
}
}
None => {
return Sysroot {
root: sysroot_dir,
src_root: None,
mode: SysrootMode::Empty,
error: None,
}
format_to!(errors, "{e}\n");
None
}
None => None,
};
if let SysrootQueryMetadata::CargoMetadata(cargo_config) = sysroot_query_metadata {
let library_manifest =
ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap();
Sysroot {
root,
src_root,
workspace: SysrootWorkspace::Empty,
error: errors.is_empty().not().then_some(errors),
}
}

pub fn load_workspace(&mut self, sysroot_source_config: &SysrootSourceWorkspaceConfig) {
assert!(matches!(self.workspace, SysrootWorkspace::Empty), "workspace already loaded");
let Self { root: _, src_root: Some(src_root), workspace, error: _ } = self else { return };
if let SysrootSourceWorkspaceConfig::CargoMetadata(cargo_config) = sysroot_source_config {
let library_manifest = ManifestPath::try_from(src_root.join("Cargo.toml")).unwrap();
if fs::metadata(&library_manifest).is_ok() {
if let Some(sysroot) = Self::load_library_via_cargo(
library_manifest,
&sysroot_dir,
&sysroot_src_dir,
cargo_config,
) {
return sysroot;
if let Some(loaded) =
Self::load_library_via_cargo(library_manifest, src_root, cargo_config)
{
*workspace = loaded;
self.load_core_check();
return;
}
}
}
tracing::debug!("Stitching sysroot library: {sysroot_src_dir}");
tracing::debug!("Stitching sysroot library: {src_root}");

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_dir.join(it))
.map(|it| src_root.join(it))
.filter_map(|it| ManifestPath::try_from(it).ok())
.find(|it| fs::metadata(it).is_ok());

Expand Down Expand Up @@ -335,20 +286,37 @@ impl Sysroot {
}
}
}
Sysroot {
root: sysroot_dir,
src_root: Some(sysroot_src_dir),
mode: SysrootMode::Stitched(stitched),
error: None,
*workspace = SysrootWorkspace::Stitched(stitched);
self.load_core_check();
}

fn load_core_check(&mut self) {
if self.error.is_none() {
if let Some(src_root) = &self.src_root {
let has_core = match &self.workspace {
SysrootWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
SysrootWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
SysrootWorkspace::Empty => true,
};
if !has_core {
let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
" (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
} else {
", try running `rustup component add rust-src` to possibly fix this"
};
self.error = Some(format!(
"sysroot at `{src_root}` is missing a `core` library{var_note}",
));
}
}
}
}

fn load_library_via_cargo(
library_manifest: ManifestPath,
sysroot_dir: &Option<AbsPathBuf>,
sysroot_src_dir: &AbsPathBuf,
cargo_config: &CargoMetadataConfig,
) -> Option<Sysroot> {
) -> Option<SysrootWorkspace> {
tracing::debug!("Loading library metadata: {library_manifest}");
let mut cargo_config = cargo_config.clone();
// the sysroot uses `public-dependency`, so we make cargo think it's a nightly
Expand Down Expand Up @@ -423,12 +391,7 @@ impl Sysroot {
});

let cargo_workspace = CargoWorkspace::new(res, library_manifest, Default::default());
Some(Sysroot {
root: sysroot_dir.clone(),
src_root: Some(sysroot_src_dir.clone()),
mode: SysrootMode::Workspace(cargo_workspace),
error: None,
})
Some(SysrootWorkspace::Workspace(cargo_workspace))
}
}

Expand Down
Loading

0 comments on commit c360bf5

Please sign in to comment.