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 8194d35
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 66 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
8 changes: 5 additions & 3 deletions crates/project-model/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned;

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

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
135 changes: 80 additions & 55 deletions crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
project_json::Crate,
rustc_cfg::{self, RustcCfgConfig},
sysroot::SysrootCrate,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy,
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};

Expand Down Expand Up @@ -53,14 +53,33 @@ 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 loading failed or a the
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 +138,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 +170,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 +257,54 @@ 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}"))),
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.")),
)
}
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}"
)))
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 +591,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 +619,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 +634,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 +661,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 +875,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 +1061,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
31 changes: 27 additions & 4 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,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, WorkspaceBuildScripts, RustLibSource, ProjectManifest, ManifestPath, RustcWorkspace};
use rustc_hash::FxHashSet;
use stdx::{format_to, thread::ThreadIntent};
use triomphe::Arc;
Expand Down Expand Up @@ -166,7 +166,7 @@ impl GlobalState {
}
}
}
if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws {
if let ProjectWorkspace::Cargo { rustc: RustcWorkspace::Loaded(Err(Some(e))), .. } = ws {
status.health = lsp_ext::Health::Warning;
message.push_str(e);
message.push_str("\n\n");
Expand All @@ -188,7 +188,7 @@ impl GlobalState {
tracing::info!(%cause, "will fetch workspaces");

self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
let linked_projects = self.config.linked_projects();
let mut linked_projects = self.config.linked_projects();
let detached_files = self.config.detached_files().to_vec();
let cargo_config = self.config.cargo();

Expand All @@ -204,6 +204,21 @@ impl GlobalState {

sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();

let mut opening_rustc_workspace = false;
if let Some(RustLibSource::Path(rustc_dir)) = &cargo_config.rustc_source {
if let Ok(rustc_manifest) = ManifestPath::try_from(rustc_dir.clone()) {
if let Some(rustc_index) = linked_projects.iter().position(|project| match project {
LinkedProject::ProjectManifest(ProjectManifest::CargoToml(project_manifest)) => *project_manifest == rustc_manifest,
_ => false,
}) {
// move to the front so that the ensuing workspace's build scripts will be built and available
// to other workspaces (in particular, any that have packages using rustc_private crates)
linked_projects.swap(0, rustc_index);
opening_rustc_workspace = true;
}
}
}

let mut workspaces = linked_projects
.iter()
.map(|project| match project {
Expand All @@ -212,6 +227,7 @@ impl GlobalState {
manifest.clone(),
&cargo_config,
&progress,
opening_rustc_workspace,
)
}
LinkedProject::InlineJsonProject(it) => {
Expand Down Expand Up @@ -506,9 +522,16 @@ impl GlobalState {

let mut crate_graph = CrateGraph::default();
let mut proc_macros = Vec::default();

// if the first workspace is `RustcWorkspace::Opening` then it is guaranteed to be the rustc source
// by virtue of the sorting in `fetch_workspaces`.
let opened_rustc_workspace = match self.workspaces.first() {
Some(ProjectWorkspace::Cargo { rustc: RustcWorkspace::Opening, cargo, build_scripts, .. }) => Some((cargo, build_scripts)),
_ => None,
};
for ws in &**self.workspaces {
let (other, mut crate_proc_macros) =
ws.to_crate_graph(&mut load, &self.config.extra_env());
ws.to_crate_graph(&mut load, &self.config.extra_env(), opened_rustc_workspace);
crate_graph.extend(other, &mut crate_proc_macros);
proc_macros.push(crate_proc_macros);
}
Expand Down

0 comments on commit 8194d35

Please sign in to comment.