Auto merge of #16537 - Veykril:sysroot-tools, r=Veykril

internal: tool discovery prefers sysroot tools

Fixes https://github.com/rust-lang/rust-analyzer/issues/15927, Fixes https://github.com/rust-lang/rust-analyzer/issues/16523

After this PR we will look for `cargo` and `rustc` in the sysroot if it was succesfully loaded instead of using the current lookup scheme. This should be more correct than the current approach as that relies on the working directory of the server binary or loade workspace, meaning it can behave a bit odd wrt overrides.

Additionally, rust-project.json projects now get the target data layout set so there should be better const eval support now.
This commit is contained in:
bors 2024-02-12 11:26:53 +00:00
commit 5e1b09bb76
14 changed files with 406 additions and 250 deletions

View File

@ -89,9 +89,10 @@ impl FlycheckHandle {
id: usize,
sender: Box<dyn Fn(Message) + Send>,
config: FlycheckConfig,
cargo: PathBuf,
workspace_root: AbsPathBuf,
) -> FlycheckHandle {
let actor = FlycheckActor::new(id, sender, config, workspace_root);
let actor = FlycheckActor::new(id, sender, config, cargo, workspace_root);
let (sender, receiver) = unbounded::<StateChange>();
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("Flycheck".to_owned())
@ -171,6 +172,7 @@ struct FlycheckActor {
/// Either the workspace root of the workspace we are flychecking,
/// or the project root of the project.
root: AbsPathBuf,
cargo: PathBuf,
/// CargoHandle exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
@ -189,10 +191,11 @@ impl FlycheckActor {
id: usize,
sender: Box<dyn Fn(Message) + Send>,
config: FlycheckConfig,
cargo: PathBuf,
workspace_root: AbsPathBuf,
) -> FlycheckActor {
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
FlycheckActor { id, sender, config, root: workspace_root, command_handle: None }
FlycheckActor { id, sender, config, cargo, root: workspace_root, command_handle: None }
}
fn report_progress(&self, progress: Progress) {
@ -316,7 +319,7 @@ impl FlycheckActor {
ansi_color_output,
target_dir,
} => {
let mut cmd = Command::new(toolchain::cargo());
let mut cmd = Command::new(&self.cargo);
cmd.arg(command);
cmd.current_dir(&self.root);

View File

@ -1,6 +1,7 @@
use chalk_ir::{AdtId, TyKind};
use either::Either;
use hir_def::db::DefDatabase;
use project_model::target_data_layout::RustcDataLayoutConfig;
use rustc_hash::FxHashMap;
use test_fixture::WithFixture;
use triomphe::Arc;
@ -15,7 +16,12 @@ use crate::{
mod closure;
fn current_machine_data_layout() -> String {
project_model::target_data_layout::get(None, None, &FxHashMap::default()).unwrap()
project_model::target_data_layout::get(
RustcDataLayoutConfig::Rustc(None),
None,
&FxHashMap::default(),
)
.unwrap()
}
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {

View File

@ -20,10 +20,11 @@ use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use serde::Deserialize;
use toolchain::Tool;
use crate::{
cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
InvocationStrategy, Package,
InvocationStrategy, Package, Sysroot,
};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
@ -61,6 +62,7 @@ impl WorkspaceBuildScripts {
config: &CargoConfig,
allowed_features: &FxHashSet<String>,
workspace_root: &AbsPathBuf,
sysroot: Option<&Sysroot>,
) -> io::Result<Command> {
let mut cmd = match config.run_build_script_command.as_deref() {
Some([program, args @ ..]) => {
@ -69,7 +71,10 @@ impl WorkspaceBuildScripts {
cmd
}
_ => {
let mut cmd = Command::new(toolchain::cargo());
let mut cmd = Command::new(
Sysroot::discover_tool(sysroot, Tool::Cargo)
.map_err(|e| io::Error::new(io::ErrorKind::NotFound, e))?,
);
cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]);
cmd.args(&config.extra_args);
@ -133,6 +138,7 @@ impl WorkspaceBuildScripts {
workspace: &CargoWorkspace,
progress: &dyn Fn(String),
toolchain: &Option<Version>,
sysroot: Option<&Sysroot>,
) -> io::Result<WorkspaceBuildScripts> {
const RUST_1_62: Version = Version::new(1, 62, 0);
@ -151,6 +157,7 @@ impl WorkspaceBuildScripts {
config,
&allowed_features,
&workspace.workspace_root().to_path_buf(),
sysroot,
)?,
workspace,
current_dir,
@ -165,6 +172,7 @@ impl WorkspaceBuildScripts {
config,
&allowed_features,
&workspace.workspace_root().to_path_buf(),
sysroot,
)?;
cmd.args(["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
@ -194,7 +202,7 @@ impl WorkspaceBuildScripts {
))
}
};
let cmd = Self::build_command(config, &Default::default(), workspace_root)?;
let cmd = Self::build_command(config, &Default::default(), workspace_root, None)?;
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.
@ -415,6 +423,7 @@ impl WorkspaceBuildScripts {
rustc: &CargoWorkspace,
current_dir: &AbsPath,
extra_env: &FxHashMap<String, String>,
sysroot: Option<&Sysroot>,
) -> Self {
let mut bs = WorkspaceBuildScripts::default();
for p in rustc.packages() {
@ -422,7 +431,7 @@ impl WorkspaceBuildScripts {
}
let res = (|| {
let target_libdir = (|| {
let mut cargo_config = Command::new(toolchain::cargo());
let mut cargo_config = Command::new(Sysroot::discover_tool(sysroot, Tool::Cargo)?);
cargo_config.envs(extra_env);
cargo_config
.current_dir(current_dir)
@ -431,7 +440,7 @@ impl WorkspaceBuildScripts {
if let Ok(it) = utf8_stdout(cargo_config) {
return Ok(it);
}
let mut cmd = Command::new(toolchain::rustc());
let mut cmd = Command::new(Sysroot::discover_tool(sysroot, Tool::Rustc)?);
cmd.envs(extra_env);
cmd.args(["--print", "target-libdir"]);
utf8_stdout(cmd)

View File

@ -12,8 +12,9 @@ use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize;
use serde_json::from_value;
use toolchain::Tool;
use crate::{utf8_stdout, InvocationLocation, ManifestPath};
use crate::{utf8_stdout, InvocationLocation, ManifestPath, Sysroot};
use crate::{CfgOverrides, InvocationStrategy};
/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
@ -236,12 +237,13 @@ impl CargoWorkspace {
cargo_toml: &ManifestPath,
current_dir: &AbsPath,
config: &CargoConfig,
sysroot: Option<&Sysroot>,
progress: &dyn Fn(String),
) -> anyhow::Result<cargo_metadata::Metadata> {
let targets = find_list_of_build_targets(config, cargo_toml);
let targets = find_list_of_build_targets(config, cargo_toml, sysroot);
let mut meta = MetadataCommand::new();
meta.cargo_path(toolchain::cargo());
meta.cargo_path(Sysroot::discover_tool(sysroot, Tool::Cargo)?);
meta.manifest_path(cargo_toml.to_path_buf());
match &config.features {
CargoFeatures::All => {
@ -476,24 +478,29 @@ impl CargoWorkspace {
}
}
fn find_list_of_build_targets(config: &CargoConfig, cargo_toml: &ManifestPath) -> Vec<String> {
fn find_list_of_build_targets(
config: &CargoConfig,
cargo_toml: &ManifestPath,
sysroot: Option<&Sysroot>,
) -> Vec<String> {
if let Some(target) = &config.target {
return [target.into()].to_vec();
}
let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env);
let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env, sysroot);
if !build_targets.is_empty() {
return build_targets;
}
rustc_discover_host_triple(cargo_toml, &config.extra_env).into_iter().collect()
rustc_discover_host_triple(cargo_toml, &config.extra_env, sysroot).into_iter().collect()
}
fn rustc_discover_host_triple(
cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>,
sysroot: Option<&Sysroot>,
) -> Option<String> {
let mut rustc = Command::new(toolchain::rustc());
let mut rustc = Command::new(Sysroot::discover_tool(sysroot, Tool::Rustc).ok()?);
rustc.envs(extra_env);
rustc.current_dir(cargo_toml.parent()).arg("-vV");
tracing::debug!("Discovering host platform by {:?}", rustc);
@ -519,8 +526,10 @@ fn rustc_discover_host_triple(
fn cargo_config_build_target(
cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>,
sysroot: Option<&Sysroot>,
) -> Vec<String> {
let mut cargo_config = Command::new(toolchain::cargo());
let Ok(program) = Sysroot::discover_tool(sysroot, Tool::Cargo) else { return vec![] };
let mut cargo_config = Command::new(program);
cargo_config.envs(extra_env);
cargo_config
.current_dir(cargo_toml.parent())

View File

@ -8,17 +8,13 @@ use rustc_hash::FxHashMap;
use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot};
/// Determines how `rustc --print cfg` is discovered and invoked.
///
/// There options are supported:
/// - [`RustcCfgConfig::Cargo`], which relies on `cargo rustc --print cfg`
/// and `RUSTC_BOOTSTRAP`.
/// - [`RustcCfgConfig::Explicit`], which uses an explicit path to the `rustc`
/// binary in the sysroot.
/// - [`RustcCfgConfig::Discover`], which uses [`toolchain::rustc`].
pub(crate) enum RustcCfgConfig<'a> {
Cargo(&'a ManifestPath),
Explicit(&'a Sysroot),
Discover,
/// Use `rustc --print cfg`, either from with the binary from the sysroot or by discovering via
/// [`toolchain::rustc`].
Rustc(Option<&'a Sysroot>),
/// Use `cargo --print cfg`, either from with the binary from the sysroot or by discovering via
/// [`toolchain::cargo`].
Cargo(Option<&'a Sysroot>, &'a ManifestPath),
}
pub(crate) fn get(
@ -71,36 +67,31 @@ fn get_rust_cfgs(
extra_env: &FxHashMap<String, String>,
config: RustcCfgConfig<'_>,
) -> anyhow::Result<String> {
let mut cmd = match config {
RustcCfgConfig::Cargo(cargo_toml) => {
let mut cmd = Command::new(toolchain::cargo());
match config {
RustcCfgConfig::Cargo(sysroot, cargo_oml) => {
let cargo = Sysroot::discover_tool(sysroot, toolchain::Tool::Cargo)?;
let mut cmd = Command::new(cargo);
cmd.envs(extra_env);
cmd.current_dir(cargo_toml.parent())
cmd.current_dir(cargo_oml.parent())
.args(["rustc", "-Z", "unstable-options", "--print", "cfg"])
.env("RUSTC_BOOTSTRAP", "1");
if let Some(target) = target {
cmd.args(["--target", target]);
}
return utf8_stdout(cmd).context("Unable to run `cargo rustc`");
utf8_stdout(cmd).context("Unable to run `cargo rustc`")
}
RustcCfgConfig::Explicit(sysroot) => {
let rustc: std::path::PathBuf = sysroot.discover_rustc()?.into();
RustcCfgConfig::Rustc(sysroot) => {
let rustc = Sysroot::discover_tool(sysroot, toolchain::Tool::Rustc)?;
tracing::debug!(?rustc, "using explicit rustc from sysroot");
Command::new(rustc)
}
RustcCfgConfig::Discover => {
let rustc = toolchain::rustc();
tracing::debug!(?rustc, "using rustc from env");
Command::new(rustc)
}
};
let mut cmd = Command::new(rustc);
cmd.envs(extra_env);
cmd.args(["--print", "cfg", "-O"]);
if let Some(target) = target {
cmd.args(["--target", target]);
}
cmd.envs(extra_env);
cmd.args(["--print", "cfg", "-O"]);
if let Some(target) = target {
cmd.args(["--target", target]);
utf8_stdout(cmd).context("Unable to run `rustc`")
}
}
utf8_stdout(cmd).context("Unable to run `rustc`")
}

View File

@ -4,7 +4,7 @@
//! 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, iter, ops, path::PathBuf, process::Command};
use std::{env, fs, iter, ops, path::PathBuf, process::Command, sync::Arc};
use anyhow::{format_err, Context, Result};
use base_db::CrateName;
@ -12,16 +12,30 @@ use itertools::Itertools;
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use toolchain::{probe_for_binary, Tool};
use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath};
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone)]
pub struct Sysroot {
root: AbsPathBuf,
src_root: AbsPathBuf,
src_root: Option<Result<AbsPathBuf, Arc<anyhow::Error>>>,
mode: SysrootMode,
}
impl Eq for Sysroot {}
impl PartialEq for Sysroot {
fn eq(&self, other: &Self) -> bool {
self.root == other.root
&& self.mode == other.mode
&& match (&self.src_root, &other.src_root) {
(Some(Ok(this)), Some(Ok(other))) => this == other,
(None, None) | (Some(Err(_)), Some(Err(_))) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum SysrootMode {
Workspace(CargoWorkspace),
@ -86,8 +100,8 @@ impl Sysroot {
/// Returns the sysroot "source" directory, where stdlib sources are located, like:
/// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library`
pub fn src_root(&self) -> &AbsPath {
&self.src_root
pub fn src_root(&self) -> Option<&AbsPath> {
self.src_root.as_ref()?.as_deref().ok()
}
pub fn is_empty(&self) -> bool {
@ -98,6 +112,11 @@ impl Sysroot {
}
pub fn loading_warning(&self) -> Option<String> {
let src_root = match &self.src_root {
None => return Some(format!("sysroot at `{}` has no library sources", self.root)),
Some(Ok(src_root)) => src_root,
Some(Err(e)) => return Some(e.to_string()),
};
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(),
@ -108,10 +127,7 @@ impl Sysroot {
} else {
" try running `rustup component add rust-src` to possible fix this"
};
Some(format!(
"could not find libcore in loaded sysroot at `{}`{var_note}",
self.src_root.as_path(),
))
Some(format!("could not find libcore in loaded sysroot at `{}`{var_note}", src_root,))
} else {
None
}
@ -140,8 +156,19 @@ impl 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, metadata))
discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env);
Ok(Sysroot::load(sysroot_dir, Some(sysroot_src_dir), metadata))
}
pub fn discover_no_source(
dir: &AbsPath,
extra_env: &FxHashMap<String, String>,
) -> 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, Some(sysroot_src_dir), false))
}
pub fn discover_with_src_override(
@ -152,33 +179,73 @@ impl Sysroot {
) -> 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, metadata))
Ok(Sysroot::load(sysroot_dir, Some(Ok(src)), metadata))
}
pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
get_rustc_src(&self.root)
}
pub fn discover_rustc(&self) -> anyhow::Result<AbsPathBuf> {
let rustc = self.root.join("bin/rustc");
tracing::debug!(?rustc, "checking for rustc binary at location");
match fs::metadata(&rustc) {
Ok(_) => Ok(rustc),
Err(e) => Err(e).context(format!(
"failed to discover rustc in sysroot: {:?}",
AsRef::<std::path::Path>::as_ref(&self.root)
)),
}
}
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, metadata))
});
Ok(Sysroot::load(sysroot_dir, Some(sysroot_src_dir), metadata))
}
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf, metadata: bool) -> Sysroot {
pub fn discover_binary(&self, binary: &str) -> anyhow::Result<AbsPathBuf> {
toolchain::probe_for_binary(self.root.join("bin").join(binary).into())
.ok_or_else(|| anyhow::anyhow!("no rustc binary found in {}", self.root.join("bin")))
.and_then(|rustc| {
fs::metadata(&rustc).map(|_| AbsPathBuf::assert(rustc)).with_context(|| {
format!(
"failed to discover rustc in sysroot: {:?}",
AsRef::<std::path::Path>::as_ref(&self.root)
)
})
})
}
pub fn discover_tool(sysroot: Option<&Self>, tool: Tool) -> anyhow::Result<PathBuf> {
match sysroot {
Some(sysroot) => sysroot.discover_binary(tool.name()).map(Into::into),
None => Ok(tool.path()),
}
}
pub fn discover_proc_macro_srv(&self) -> anyhow::Result<AbsPathBuf> {
["libexec", "lib"]
.into_iter()
.map(|segment| self.root().join(segment).join("rust-analyzer-proc-macro-srv"))
.find_map(|server_path| probe_for_binary(server_path.into()))
.map(AbsPathBuf::assert)
.ok_or_else(|| {
anyhow::format_err!("cannot find proc-macro server in sysroot `{}`", self.root())
})
}
pub fn load(
sysroot_dir: AbsPathBuf,
sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
metadata: bool,
) -> Sysroot {
let sysroot_src_dir = match sysroot_src_dir {
Some(Ok(sysroot_src_dir)) => sysroot_src_dir,
Some(Err(e)) => {
return Sysroot {
root: sysroot_dir,
src_root: Some(Err(Arc::new(e))),
mode: SysrootMode::Stitched(Stitched { crates: Arena::default() }),
}
}
None => {
return Sysroot {
root: sysroot_dir,
src_root: None,
mode: SysrootMode::Stitched(Stitched { crates: Arena::default() }),
}
}
};
if metadata {
let sysroot: Option<_> = (|| {
let sysroot_cargo_toml = ManifestPath::try_from(
@ -191,6 +258,7 @@ impl Sysroot {
&sysroot_cargo_toml,
&current_dir,
&CargoConfig::default(),
None,
&|_| (),
)
.map_err(|e| {
@ -274,7 +342,7 @@ impl Sysroot {
let cargo_workspace = CargoWorkspace::new(res);
Some(Sysroot {
root: sysroot_dir.clone(),
src_root: sysroot_src_dir.clone(),
src_root: Some(Ok(sysroot_src_dir.clone())),
mode: SysrootMode::Workspace(cargo_workspace),
})
})();
@ -326,7 +394,7 @@ impl Sysroot {
}
Sysroot {
root: sysroot_dir,
src_root: sysroot_src_dir,
src_root: Some(Ok(sysroot_src_dir)),
mode: SysrootMode::Stitched(stitched),
}
}

View File

@ -3,16 +3,27 @@ use std::process::Command;
use rustc_hash::FxHashMap;
use crate::{utf8_stdout, ManifestPath};
use crate::{utf8_stdout, ManifestPath, Sysroot};
/// Determines how `rustc --print target-spec-json` is discovered and invoked.
pub enum RustcDataLayoutConfig<'a> {
/// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via
/// [`toolchain::rustc`].
Rustc(Option<&'a Sysroot>),
/// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via
/// [`toolchain::cargo`].
Cargo(Option<&'a Sysroot>, &'a ManifestPath),
}
pub fn get(
cargo_toml: Option<&ManifestPath>,
config: RustcDataLayoutConfig<'_>,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
) -> anyhow::Result<String> {
let output = (|| {
if let Some(cargo_toml) = cargo_toml {
let mut cmd = Command::new(toolchain::rustc());
let output = match config {
RustcDataLayoutConfig::Cargo(sysroot, cargo_toml) => {
let cargo = Sysroot::discover_tool(sysroot, toolchain::Tool::Cargo)?;
let mut cmd = Command::new(cargo);
cmd.envs(extra_env);
cmd.current_dir(cargo_toml.parent())
.args(["-Z", "unstable-options", "--print", "target-spec-json"])
@ -20,21 +31,20 @@ pub fn get(
if let Some(target) = target {
cmd.args(["--target", target]);
}
match utf8_stdout(cmd) {
Ok(it) => return Ok(it),
Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"),
utf8_stdout(cmd)
}
RustcDataLayoutConfig::Rustc(sysroot) => {
let rustc = Sysroot::discover_tool(sysroot, toolchain::Tool::Rustc)?;
let mut cmd = Command::new(rustc);
cmd.envs(extra_env)
.args(["-Z", "unstable-options", "--print", "target-spec-json"])
.env("RUSTC_BOOTSTRAP", "1");
if let Some(target) = target {
cmd.args(["--target", target]);
}
utf8_stdout(cmd)
}
// using unstable cargo features failed, fall back to using plain rustc
let mut cmd = Command::new(toolchain::rustc());
cmd.envs(extra_env)
.args(["-Z", "unstable-options", "--print", "target-spec-json"])
.env("RUSTC_BOOTSTRAP", "1");
if let Some(target) = target {
cmd.args(["--target", target]);
}
utf8_stdout(cmd)
})()?;
}?;
(|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))()
.ok_or_else(|| anyhow::format_err!("could not fetch target-spec-json from command output"))
}

View File

@ -69,8 +69,13 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
let data = get_test_json_file(file);
let project = rooted_project_json(data);
let sysroot = Ok(get_fake_sysroot());
let project_workspace =
ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new(), toolchain: None };
let project_workspace = ProjectWorkspace::Json {
project,
sysroot,
rustc_cfg: Vec::new(),
toolchain: None,
target_layout: Err("test has no data layout".to_owned()),
};
to_crate_graph(project_workspace)
}
@ -125,7 +130,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, false)
Sysroot::load(sysroot_dir, Some(Ok(sysroot_src_dir)), false)
}
fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {

View File

@ -2,7 +2,9 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, sync};
use std::{
collections::VecDeque, fmt, fs, iter, path::PathBuf, process::Command, str::FromStr, sync,
};
use anyhow::{format_err, Context};
use base_db::{
@ -23,8 +25,9 @@ use crate::{
project_json::Crate,
rustc_cfg::{self, RustcCfgConfig},
sysroot::{SysrootCrate, SysrootMode},
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
target_data_layout::{self, RustcDataLayoutConfig},
utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package,
ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};
/// A set of cfg-overrides per crate.
@ -79,6 +82,7 @@ pub enum ProjectWorkspace {
/// `rustc --print cfg`.
rustc_cfg: Vec<CfgFlag>,
toolchain: Option<Version>,
target_layout: Result<String, String>,
},
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for.
@ -126,14 +130,22 @@ impl fmt::Debug for ProjectWorkspace {
.field("toolchain", &toolchain)
.field("data_layout", &data_layout)
.finish(),
ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
ProjectWorkspace::Json {
project,
sysroot,
rustc_cfg,
toolchain,
target_layout: data_layout,
} => {
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.num_packages());
}
debug_struct.field("toolchain", &toolchain);
debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
debug_struct
.field("toolchain", &toolchain)
.field("n_rustc_cfg", &rustc_cfg.len())
.field("data_layout", &data_layout);
debug_struct.finish()
}
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
@ -146,6 +158,26 @@ impl fmt::Debug for ProjectWorkspace {
}
}
fn get_toolchain_version(
current_dir: &AbsPath,
cmd_path: Result<PathBuf, anyhow::Error>,
extra_env: &FxHashMap<String, String>,
prefix: &str,
) -> Result<Option<Version>, anyhow::Error> {
let cargo_version = utf8_stdout({
let mut cmd = Command::new(cmd_path?);
cmd.envs(extra_env);
cmd.arg("--version").current_dir(current_dir);
cmd
})
.with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?;
anyhow::Ok(
cargo_version
.get(prefix.len()..)
.and_then(|it| Version::parse(it.split_whitespace().next()?).ok()),
)
}
impl ProjectWorkspace {
pub fn load(
manifest: ProjectManifest,
@ -161,20 +193,6 @@ impl ProjectWorkspace {
config: &CargoConfig,
progress: &dyn Fn(String),
) -> anyhow::Result<ProjectWorkspace> {
let version = |current_dir, cmd_path, prefix: &str| {
let cargo_version = utf8_stdout({
let mut cmd = Command::new(cmd_path);
cmd.envs(&config.extra_env);
cmd.arg("--version").current_dir(current_dir);
cmd
})
.with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?;
anyhow::Ok(
cargo_version
.get(prefix.len()..)
.and_then(|it| Version::parse(it.split_whitespace().next()?).ok()),
)
};
let res = match manifest {
ProjectManifest::ProjectJson(project_json) => {
let file = fs::read_to_string(project_json)
@ -182,30 +200,14 @@ impl ProjectWorkspace {
let data = serde_json::from_str(&file)
.with_context(|| format!("Failed to deserialize json file {project_json}"))?;
let project_location = project_json.parent().to_path_buf();
let toolchain = version(&*project_location, toolchain::rustc(), "rustc ")?;
let project_json = ProjectJson::new(&project_location, data);
let project_json: ProjectJson = ProjectJson::new(&project_location, data);
ProjectWorkspace::load_inline(
project_json,
config.target.as_deref(),
&config.extra_env,
toolchain,
)
}
ProjectManifest::CargoToml(cargo_toml) => {
let toolchain = version(cargo_toml.parent(), toolchain::cargo(), "cargo ")?;
let meta = CargoWorkspace::fetch_metadata(
cargo_toml,
cargo_toml.parent(),
config,
progress,
)
.with_context(|| {
format!(
"Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
)
})?;
let cargo = CargoWorkspace::new(meta);
let sysroot = match (&config.sysroot, &config.sysroot_src) {
(Some(RustLibSource::Path(path)), None) => {
Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata).map_err(|e| {
@ -218,7 +220,7 @@ impl ProjectWorkspace {
})
}
(Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone(), config.sysroot_query_metadata))
Ok(Sysroot::load(sysroot.clone(), Some(Ok(sysroot_src.clone())), config.sysroot_query_metadata))
}
(Some(RustLibSource::Discover), Some(sysroot_src)) => {
Sysroot::discover_with_src_override(
@ -231,18 +233,19 @@ impl ProjectWorkspace {
}
(None, _) => Err(None),
};
let sysroot_ref = sysroot.as_ref().ok();
if let Ok(sysroot) = &sysroot {
tracing::info!(workspace = %cargo_toml, src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
}
let rustc_dir = match &config.rustc_source {
Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
.map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
Some(RustLibSource::Discover) => {
sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else(
|| Some("Failed to discover rustc source for sysroot.".to_owned()),
)
sysroot_ref.and_then(Sysroot::discover_rustc_src).ok_or_else(|| {
Some("Failed to discover rustc source for sysroot.".to_owned())
})
}
None => Err(None),
};
@ -256,6 +259,7 @@ impl ProjectWorkspace {
features: crate::CargoFeatures::default(),
..config.clone()
},
sysroot_ref,
progress,
) {
Ok(meta) => {
@ -264,6 +268,7 @@ impl ProjectWorkspace {
&workspace,
cargo_toml.parent(),
&config.extra_env,
sysroot_ref
);
Ok(Box::new((workspace, buildscripts)))
}
@ -279,21 +284,42 @@ impl ProjectWorkspace {
}
});
let toolchain = get_toolchain_version(
cargo_toml.parent(),
Sysroot::discover_tool(sysroot_ref, toolchain::Tool::Cargo),
&config.extra_env,
"cargo ",
)?;
let rustc_cfg = rustc_cfg::get(
config.target.as_deref(),
&config.extra_env,
RustcCfgConfig::Cargo(cargo_toml),
RustcCfgConfig::Cargo(sysroot_ref, cargo_toml),
);
let cfg_overrides = config.cfg_overrides.clone();
let data_layout = target_data_layout::get(
Some(cargo_toml),
RustcDataLayoutConfig::Cargo(sysroot_ref, cargo_toml),
config.target.as_deref(),
&config.extra_env,
);
if let Err(e) = &data_layout {
tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace");
}
let meta = CargoWorkspace::fetch_metadata(
cargo_toml,
cargo_toml.parent(),
config,
sysroot_ref,
progress,
)
.with_context(|| {
format!(
"Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
)
})?;
let cargo = CargoWorkspace::new(meta);
ProjectWorkspace::Cargo {
cargo,
build_scripts: WorkspaceBuildScripts::default(),
@ -314,15 +340,16 @@ impl ProjectWorkspace {
project_json: ProjectJson,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
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, false)),
(Some(sysroot), Some(sysroot_src)) => {
Ok(Sysroot::load(sysroot, Some(Ok(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, false))
Ok(Sysroot::load(sysroot, Some(Ok(sysroot_src)), false))
}
(None, Some(sysroot_src)) => {
// assume sysroot is structured like rustup's and guess `sysroot`
@ -330,23 +357,32 @@ impl ProjectWorkspace {
for _ in 0..5 {
sysroot.pop();
}
Ok(Sysroot::load(sysroot, sysroot_src, false))
Ok(Sysroot::load(sysroot, Some(Ok(sysroot_src)), false))
}
(None, None) => Err(None),
};
let config = match &sysroot {
Ok(sysroot) => {
tracing::debug!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
RustcCfgConfig::Explicit(sysroot)
}
Err(_) => {
tracing::debug!("discovering sysroot");
RustcCfgConfig::Discover
let sysroot_ref = sysroot.as_ref().ok();
let cfg_config = RustcCfgConfig::Rustc(sysroot_ref);
let data_layout_config = RustcDataLayoutConfig::Rustc(sysroot_ref);
let rustc = Sysroot::discover_tool(sysroot_ref, toolchain::Tool::Rustc).map(Into::into);
let toolchain = match get_toolchain_version(project_json.path(), rustc, extra_env, "rustc ")
{
Ok(it) => it,
Err(e) => {
tracing::error!("{e}");
None
}
};
let rustc_cfg = rustc_cfg::get(target, extra_env, config);
ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain }
let rustc_cfg = rustc_cfg::get(target, extra_env, cfg_config);
let data_layout = target_data_layout::get(data_layout_config, target, extra_env);
ProjectWorkspace::Json {
project: project_json,
sysroot,
rustc_cfg,
toolchain,
target_layout: data_layout.map_err(|it| it.to_string()),
}
}
pub fn load_detached_files(
@ -373,18 +409,11 @@ impl ProjectWorkspace {
}
None => Err(None),
};
let rustc_config = match &sysroot {
Ok(sysroot) => {
tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
RustcCfgConfig::Explicit(sysroot)
}
Err(_) => {
tracing::info!("discovering sysroot");
RustcCfgConfig::Discover
}
};
let rustc_cfg = rustc_cfg::get(None, &FxHashMap::default(), rustc_config);
let rustc_cfg = rustc_cfg::get(
None,
&FxHashMap::default(),
RustcCfgConfig::Rustc(sysroot.as_ref().ok()),
);
Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
}
@ -395,11 +424,17 @@ impl ProjectWorkspace {
progress: &dyn Fn(String),
) -> anyhow::Result<WorkspaceBuildScripts> {
match self {
ProjectWorkspace::Cargo { cargo, toolchain, .. } => {
WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, toolchain)
.with_context(|| {
format!("Failed to run build scripts for {}", cargo.workspace_root())
})
ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => {
WorkspaceBuildScripts::run_for_workspace(
config,
cargo,
progress,
toolchain,
sysroot.as_ref().ok(),
)
.with_context(|| {
format!("Failed to run build scripts for {}", cargo.workspace_root())
})
}
ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
Ok(WorkspaceBuildScripts::default())
@ -472,18 +507,7 @@ impl ProjectWorkspace {
ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
| ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
| ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
let standalone_server_name =
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
["libexec", "lib"]
.into_iter()
.map(|segment| sysroot.root().join(segment).join(&standalone_server_name))
.find(|server_path| std::fs::metadata(server_path).is_ok())
.ok_or_else(|| {
anyhow::format_err!(
"cannot find proc-macro server in sysroot `{}`",
sysroot.root()
)
})
sysroot.discover_proc_macro_srv()
}
ProjectWorkspace::DetachedFiles { .. } => {
Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found"))
@ -503,8 +527,7 @@ 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<_, _>, project_root: Option<&AbsPath>| {
let project_root = project_root.map(ToOwned::to_owned);
let mk_sysroot = |sysroot: Result<_, _>| {
sysroot.into_iter().flat_map(move |sysroot: &Sysroot| {
let mut r = match sysroot.mode() {
SysrootMode::Workspace(ws) => ws
@ -532,18 +555,21 @@ impl ProjectWorkspace {
};
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()],
is_local: false,
include: sysroot.src_root().map(|it| it.to_path_buf()).into_iter().collect(),
exclude: Vec::new(),
});
r
})
};
match self {
ProjectWorkspace::Json { project, sysroot, rustc_cfg: _, toolchain: _ } => project
ProjectWorkspace::Json {
project,
sysroot,
rustc_cfg: _,
toolchain: _,
target_layout: _,
} => project
.crates()
.map(|(_, krate)| PackageRoot {
is_local: krate.is_workspace_member,
@ -552,7 +578,7 @@ impl ProjectWorkspace {
})
.collect::<FxHashSet<_>>()
.into_iter()
.chain(mk_sysroot(sysroot.as_ref(), Some(project.path())))
.chain(mk_sysroot(sysroot.as_ref()))
.collect::<Vec<_>>(),
ProjectWorkspace::Cargo {
cargo,
@ -602,7 +628,7 @@ impl ProjectWorkspace {
}
PackageRoot { is_local, include, exclude }
})
.chain(mk_sysroot(sysroot.as_ref(), Some(cargo.workspace_root())))
.chain(mk_sysroot(sysroot.as_ref()))
.chain(rustc.iter().map(|a| a.as_ref()).flat_map(|(rustc, _)| {
rustc.packages().map(move |krate| PackageRoot {
is_local: false,
@ -619,7 +645,7 @@ impl ProjectWorkspace {
include: vec![detached_file.clone()],
exclude: Vec::new(),
})
.chain(mk_sysroot(sysroot.as_ref(), None))
.chain(mk_sysroot(sysroot.as_ref()))
.collect(),
}
}
@ -651,14 +677,17 @@ impl ProjectWorkspace {
let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered();
let (mut crate_graph, proc_macros) = match self {
ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain, target_layout } => {
project_json_to_crate_graph(
rustc_cfg.clone(),
load,
project,
sysroot.as_ref().ok(),
extra_env,
Err("rust-project.json projects have no target layout set".into()),
match target_layout.as_ref() {
Ok(it) => Ok(Arc::from(it.as_str())),
Err(it) => Err(Arc::from(it.as_str())),
},
toolchain.clone(),
)
}
@ -735,12 +764,13 @@ impl ProjectWorkspace {
&& sysroot == o_sysroot
}
(
Self::Json { project, sysroot, rustc_cfg, toolchain },
Self::Json { project, sysroot, rustc_cfg, toolchain, target_layout: _ },
Self::Json {
project: o_project,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
toolchain: o_toolchain,
target_layout: _,
},
) => {
project == o_project
@ -813,12 +843,7 @@ fn project_json_to_crate_graph(
let target_cfgs = match target.as_deref() {
Some(target) => cfg_cache.entry(target).or_insert_with(|| {
let rustc_cfg = match sysroot {
Some(sysroot) => RustcCfgConfig::Explicit(sysroot),
None => RustcCfgConfig::Discover,
};
rustc_cfg::get(Some(target), extra_env, rustc_cfg)
rustc_cfg::get(Some(target), extra_env, RustcCfgConfig::Rustc(sysroot))
}),
None => &rustc_cfg,
};
@ -959,21 +984,6 @@ fn cargo_to_crate_graph(
}
let &TargetData { ref name, kind, is_proc_macro, ref root, .. } = &cargo[tgt];
if kind == TargetKind::Lib
&& sysroot.map_or(false, |sysroot| root.starts_with(sysroot.src_root()))
{
if let Some(&(_, crate_id, _)) =
public_deps.deps.iter().find(|(dep_name, ..)| dep_name.as_smol_str() == name)
{
pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
lib_tgt = Some((crate_id, name.clone()));
pkg_to_lib_crate.insert(pkg, crate_id);
// sysroot is inside the workspace, prevent the sysroot crates from being duplicated here
continue;
}
}
let Some(file_id) = load(root) else { continue };
let crate_id = add_target_crate_root(

View File

@ -37,7 +37,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -70,7 +70,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -103,7 +103,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -136,7 +136,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -186,7 +186,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -219,7 +219,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -317,7 +317,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -350,7 +350,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -383,7 +383,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -416,7 +416,7 @@
),
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},
@ -493,7 +493,7 @@
},
is_proc_macro: false,
target_layout: Err(
"rust-project.json projects have no target layout set",
"test has no data layout",
),
toolchain: None,
},

View File

@ -1937,6 +1937,7 @@ fn run_rustfmt(
let mut command = match snap.config.rustfmt() {
RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
// FIXME: This should use the sysroot's rustfmt if its loaded
let mut cmd = process::Command::new(toolchain::rustfmt());
cmd.envs(snap.config.extra_env());
cmd.args(extra_args);

View File

@ -24,7 +24,7 @@ use ide_db::{
use itertools::Itertools;
use load_cargo::{load_proc_macro, ProjectFolders};
use proc_macro_api::ProcMacroServer;
use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
use project_model::{ProjectWorkspace, Sysroot, WorkspaceBuildScripts};
use rustc_hash::FxHashSet;
use stdx::{format_to, thread::ThreadIntent};
use triomphe::Arc;
@ -234,7 +234,6 @@ impl GlobalState {
it.clone(),
cargo_config.target.as_deref(),
&cargo_config.extra_env,
None,
))
}
})
@ -605,6 +604,7 @@ impl GlobalState {
0,
Box::new(move |msg| sender.send(msg).unwrap()),
config,
toolchain::cargo(),
self.config.root_path().clone(),
)],
flycheck::InvocationStrategy::PerWorkspace => {
@ -612,23 +612,35 @@ impl GlobalState {
.iter()
.enumerate()
.filter_map(|(id, w)| match w {
ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
ProjectWorkspace::Json { project, .. } => {
ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((
id,
cargo.workspace_root(),
Sysroot::discover_tool(sysroot.as_ref().ok(), toolchain::Tool::Cargo),
)),
ProjectWorkspace::Json { project, sysroot, .. } => {
// Enable flychecks for json projects if a custom flycheck command was supplied
// in the workspace configuration.
match config {
FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
FlycheckConfig::CustomCommand { .. } => Some((
id,
project.path(),
Sysroot::discover_tool(
sysroot.as_ref().ok(),
toolchain::Tool::Cargo,
),
)),
_ => None,
}
}
ProjectWorkspace::DetachedFiles { .. } => None,
})
.map(|(id, root)| {
.map(|(id, root, cargo)| {
let sender = sender.clone();
FlycheckHandle::spawn(
id,
Box::new(move |msg| sender.send(msg).unwrap()),
config.clone(),
cargo.unwrap_or_else(|_| toolchain::cargo()),
root.to_path_buf(),
)
})

View File

@ -911,20 +911,18 @@ fn root_contains_symlink_out_dirs_check() {
#[cfg(any(feature = "sysroot-abi", rust_analyzer))]
fn resolve_proc_macro() {
use expect_test::expect;
use vfs::AbsPathBuf;
if skip_slow_tests() {
return;
}
// skip using the sysroot config as to prevent us from loading the sysroot sources
let mut rustc = std::process::Command::new(toolchain::rustc());
rustc.args(["--print", "sysroot"]);
let output = rustc.output().unwrap();
let sysroot =
vfs::AbsPathBuf::try_from(std::str::from_utf8(&output.stdout).unwrap().trim()).unwrap();
let sysroot = project_model::Sysroot::discover_no_source(
&AbsPathBuf::assert(std::env::current_dir().unwrap()),
&Default::default(),
)
.unwrap();
let standalone_server_name =
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
let proc_macro_server_path = sysroot.join("libexec").join(&standalone_server_name);
let proc_macro_server_path = sysroot.discover_proc_macro_srv().unwrap();
let server = Project::with_fixture(
r###"

View File

@ -2,7 +2,41 @@
#![warn(rust_2018_idioms, unused_lifetimes)]
use std::{env, iter, path::PathBuf};
use std::{
env, iter,
path::{Path, PathBuf},
};
#[derive(Copy, Clone)]
pub enum Tool {
Cargo,
Rustc,
Rustup,
Rustfmt,
}
impl Tool {
pub fn path(self) -> PathBuf {
get_path_for_executable(self.name())
}
pub fn path_in(self, path: &Path) -> Option<PathBuf> {
probe_for_binary(path.join(self.name()))
}
pub fn path_in_or_discover(self, path: &Path) -> PathBuf {
probe_for_binary(path.join(self.name())).unwrap_or_else(|| self.path())
}
pub fn name(self) -> &'static str {
match self {
Tool::Cargo => "cargo",
Tool::Rustc => "rustc",
Tool::Rustup => "rustup",
Tool::Rustfmt => "rustfmt",
}
}
}
pub fn cargo() -> PathBuf {
get_path_for_executable("cargo")
@ -47,7 +81,7 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
if let Some(mut path) = get_cargo_home() {
path.push("bin");
path.push(executable_name);
if let Some(path) = probe(path) {
if let Some(path) = probe_for_binary(path) {
return path;
}
}
@ -57,7 +91,7 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
fn lookup_in_path(exec: &str) -> bool {
let paths = env::var_os("PATH").unwrap_or_default();
env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe).is_some()
env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe_for_binary).is_some()
}
fn get_cargo_home() -> Option<PathBuf> {
@ -73,7 +107,7 @@ fn get_cargo_home() -> Option<PathBuf> {
None
}
fn probe(path: PathBuf) -> Option<PathBuf> {
pub fn probe_for_binary(path: PathBuf) -> Option<PathBuf> {
let with_extension = match env::consts::EXE_EXTENSION {
"" => None,
it => Some(path.with_extension(it)),