Skip to content

Commit

Permalink
Use build scripts from rustc source workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
eggyal committed Nov 29, 2023
1 parent c7c582a commit b5dfdf5
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 68 deletions.
3 changes: 2 additions & 1 deletion crates/load-cargo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub fn load_workspace_at(
) -> anyhow::Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
let root = ProjectManifest::discover_single(&root)?;
let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?;
let mut workspace = ProjectWorkspace::load(root, cargo_config, progress, false)?;

if load_config.load_out_dirs_from_check {
let build_scripts = workspace.run_build_scripts(cargo_config, progress)?;
Expand Down Expand Up @@ -81,6 +81,7 @@ pub fn load_workspace(
vfs.file_id(&path)
},
extra_env,
None,
);
let proc_macros = {
let proc_macro_server = match &proc_macro_server {
Expand Down
2 changes: 1 addition & 1 deletion crates/project-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub use crate::{
manifest_path::ManifestPath,
project_json::{ProjectJson, ProjectJsonData},
sysroot::Sysroot,
workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
workspace::{CfgOverrides, PackageRoot, ProjectWorkspace, RustcWorkspace},
};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
Expand Down
10 changes: 6 additions & 4 deletions crates/project-model/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned;

use crate::{
CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
WorkspaceBuildScripts,
CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, RustcWorkspace,
Sysroot, WorkspaceBuildScripts,
};

fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
Expand All @@ -29,7 +29,7 @@ fn load_cargo_with_overrides(
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
sysroot: Err(None),
rustc: Err(None),
rustc: RustcWorkspace::Loaded(Err(None)),
rustc_cfg: Vec::new(),
cfg_overrides,
toolchain: None,
Expand All @@ -48,7 +48,7 @@ fn load_cargo_with_sysroot(
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
sysroot: Ok(get_fake_sysroot()),
rustc: Err(None),
rustc: RustcWorkspace::Loaded(Err(None)),
rustc_cfg: Vec::new(),
cfg_overrides: Default::default(),
toolchain: None,
Expand All @@ -62,6 +62,7 @@ fn load_cargo_with_sysroot(
}
},
&Default::default(),
None,
)
}

Expand Down Expand Up @@ -146,6 +147,7 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacro
}
},
&Default::default(),
None,
)
}

Expand Down
140 changes: 84 additions & 56 deletions crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use crate::{
project_json::Crate,
rustc_cfg::{self, RustcCfgConfig},
sysroot::SysrootCrate,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, Package,
ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};

/// A set of cfg-overrides per crate.
Expand Down Expand Up @@ -53,14 +53,34 @@ pub struct PackageRoot {
pub exclude: Vec<AbsPathBuf>,
}

#[derive(Clone, PartialEq)]
pub enum RustcWorkspace {
/// A globally-configured rustc source location is being opened as a rust-analyzer workspace
Opening,
/// The rustc source is loaded, e.g. from sysroot, but is not a rust-analyzer workspace
Loaded(Result<(CargoWorkspace, WorkspaceBuildScripts), Option<String>>),
}

impl RustcWorkspace {
/// Returns the loaded `CargoWorkspace` of the rustc source.
/// Will be `None` if either the rustc source is opened as a rust-analyzer workspace
/// or its loading failed.
fn loaded(&self) -> Option<&(CargoWorkspace, WorkspaceBuildScripts)> {
match self {
Self::Opening => None,
Self::Loaded(res) => res.as_ref().ok(),
}
}
}

#[derive(Clone)]
pub enum ProjectWorkspace {
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
Cargo {
cargo: CargoWorkspace,
build_scripts: WorkspaceBuildScripts,
sysroot: Result<Sysroot, Option<String>>,
rustc: Result<(CargoWorkspace, WorkspaceBuildScripts), Option<String>>,
rustc: RustcWorkspace,
/// Holds cfg flags for the current target. We get those by running
/// `rustc --print cfg`.
///
Expand Down Expand Up @@ -119,7 +139,7 @@ impl fmt::Debug for ProjectWorkspace {
.field("sysroot", &sysroot.is_ok())
.field(
"n_rustc_compiler_crates",
&rustc.as_ref().map_or(0, |(rc, _)| rc.packages().len()),
&rustc.loaded().map_or(0, |(rc, _)| rc.packages().len()),
)
.field("n_rustc_cfg", &rustc_cfg.len())
.field("n_cfg_overrides", &cfg_overrides.len())
Expand Down Expand Up @@ -151,15 +171,17 @@ impl ProjectWorkspace {
manifest: ProjectManifest,
config: &CargoConfig,
progress: &dyn Fn(String),
opening_rustc_workspace: bool,
) -> anyhow::Result<ProjectWorkspace> {
ProjectWorkspace::load_inner(&manifest, config, progress)
ProjectWorkspace::load_inner(&manifest, config, progress, opening_rustc_workspace)
.with_context(|| format!("Failed to load the project at {manifest}"))
}

fn load_inner(
manifest: &ProjectManifest,
config: &CargoConfig,
progress: &dyn Fn(String),
opening_rustc_workspace: bool,
) -> anyhow::Result<ProjectWorkspace> {
let version = |current_dir, cmd_path, prefix: &str| {
let cargo_version = utf8_stdout({
Expand Down Expand Up @@ -236,48 +258,56 @@ impl ProjectWorkspace {
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(format!("Failed to discover rustc source for sysroot.")),
)
}
None => Err(None),
};

let rustc = rustc_dir.and_then(|rustc_dir| {
tracing::info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
match CargoWorkspace::fetch_metadata(
&rustc_dir,
cargo_toml.parent(),
&CargoConfig {
features: crate::CargoFeatures::default(),
..config.clone()
},
progress,
) {
Ok(meta) => {
let workspace = CargoWorkspace::new(meta);
let buildscripts = WorkspaceBuildScripts::rustc_crates(
&workspace,
cargo_toml.parent(),
&config.extra_env,
);
Ok((workspace, buildscripts))
let rustc = if opening_rustc_workspace {
RustcWorkspace::Opening
} else {
let rustc_dir = match &config.rustc_source {
// `config.rustc_source == Some(Path(...))` while `!opening_rustc_workspace` should only occur if
// `ManifestPath::try_from(rustc_dir)` failed in `fetch_workspaces`, so no need to attempt it here
// again.
Some(RustLibSource::Path(path)) => {
Err(Some(format!("rustc source path is not absolute: {path}")))
}
Err(e) => {
tracing::error!(
%e,
"Failed to read Cargo metadata from rustc source at {rustc_dir}",
);
Err(Some(format!(
"Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
)))
Some(RustLibSource::Discover) => {
sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else(
|| Some(format!("Failed to discover rustc source for sysroot.")),
)
}
}
});
None => Err(None),
};

RustcWorkspace::Loaded(rustc_dir.and_then(|rustc_dir| {
tracing::info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
match CargoWorkspace::fetch_metadata(
&rustc_dir,
cargo_toml.parent(),
&CargoConfig {
features: crate::CargoFeatures::default(),
..config.clone()
},
progress,
) {
Ok(meta) => {
let workspace = CargoWorkspace::new(meta);
let buildscripts = WorkspaceBuildScripts::rustc_crates(
&workspace,
cargo_toml.parent(),
&config.extra_env,
);
Ok((workspace, buildscripts))
}
Err(e) => {
tracing::error!(
%e,
"Failed to read Cargo metadata from rustc source at {rustc_dir}",
);
Err(Some(format!(
"Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
)))
}
}
}))
};

let rustc_cfg = rustc_cfg::get(
config.target.as_deref(),
Expand Down Expand Up @@ -564,7 +594,7 @@ impl ProjectWorkspace {
PackageRoot { is_local, include, exclude }
})
.chain(mk_sysroot(sysroot.as_ref(), Some(cargo.workspace_root())))
.chain(rustc.iter().flat_map(|(rustc, _)| {
.chain(rustc.loaded().iter().flat_map(|(rustc, _)| {
rustc.packages().map(move |krate| PackageRoot {
is_local: false,
include: vec![rustc[krate].manifest.parent().to_path_buf()],
Expand Down Expand Up @@ -592,7 +622,7 @@ impl ProjectWorkspace {
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 rustc_package_len = rustc.loaded().map_or(0, |(it, _)| it.packages().len());
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
cargo.packages().len() + sysroot_package_len + rustc_package_len
}
Expand All @@ -607,6 +637,7 @@ impl ProjectWorkspace {
&self,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
extra_env: &FxHashMap<String, String>,
opened_rustc_workspace: Option<(&CargoWorkspace, &WorkspaceBuildScripts)>,
) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("ProjectWorkspace::to_crate_graph");

Expand All @@ -633,7 +664,10 @@ impl ProjectWorkspace {
target_layout,
} => cargo_to_crate_graph(
load,
rustc.as_ref().ok(),
match rustc {
RustcWorkspace::Opening => opened_rustc_workspace,
RustcWorkspace::Loaded(res) => res.as_ref().ok().map(|(a, b)| (a, b)),
},
cargo,
sysroot.as_ref().ok(),
rustc_cfg.clone(),
Expand Down Expand Up @@ -844,7 +878,7 @@ fn project_json_to_crate_graph(

fn cargo_to_crate_graph(
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
rustc: Option<(&CargoWorkspace, &WorkspaceBuildScripts)>,
cargo: &CargoWorkspace,
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
Expand Down Expand Up @@ -1030,13 +1064,7 @@ fn cargo_to_crate_graph(
&pkg_crates,
&cfg_options,
override_cfg,
if rustc_workspace.workspace_root() == cargo.workspace_root() {
// the rustc workspace does not use the installed toolchain's proc-macro server
// so we need to make sure we don't use the pre compiled proc-macros there either
build_scripts
} else {
rustc_build_scripts
},
rustc_build_scripts,
target_layout,
channel,
);
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-analyzer/src/cli/analysis_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl flags::AnalysisStats {
let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path));
let manifest = ProjectManifest::discover_single(&path)?;

let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress, false)?;
let metadata_time = db_load_sw.elapsed();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: !self.disable_build_scripts,
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-analyzer/src/cli/lsif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ impl flags::Lsif {
let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path));
let manifest = ProjectManifest::discover_single(&path)?;

let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress, false)?;

let (host, vfs, _proc_macro) =
load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
Expand Down
Loading

0 comments on commit b5dfdf5

Please sign in to comment.