Skip to content

Commit 8194d35

Browse files
committed
Use build scripts from rustc source workspace
1 parent c7c582a commit 8194d35

File tree

7 files changed

+117
-66
lines changed

7 files changed

+117
-66
lines changed

crates/load-cargo/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub fn load_workspace_at(
4040
) -> anyhow::Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
4141
let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
4242
let root = ProjectManifest::discover_single(&root)?;
43-
let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?;
43+
let mut workspace = ProjectWorkspace::load(root, cargo_config, progress, false)?;
4444

4545
if load_config.load_out_dirs_from_check {
4646
let build_scripts = workspace.run_build_scripts(cargo_config, progress)?;
@@ -81,6 +81,7 @@ pub fn load_workspace(
8181
vfs.file_id(&path)
8282
},
8383
extra_env,
84+
None,
8485
);
8586
let proc_macros = {
8687
let proc_macro_server = match &proc_macro_server {

crates/project-model/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub use crate::{
5050
manifest_path::ManifestPath,
5151
project_json::{ProjectJson, ProjectJsonData},
5252
sysroot::Sysroot,
53-
workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
53+
workspace::{CfgOverrides, PackageRoot, ProjectWorkspace, RustcWorkspace},
5454
};
5555

5656
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]

crates/project-model/src/tests.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_hash::FxHashMap;
1111
use serde::de::DeserializeOwned;
1212

1313
use crate::{
14-
CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
14+
CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, RustcWorkspace, Sysroot,
1515
WorkspaceBuildScripts,
1616
};
1717

@@ -29,7 +29,7 @@ fn load_cargo_with_overrides(
2929
cargo: cargo_workspace,
3030
build_scripts: WorkspaceBuildScripts::default(),
3131
sysroot: Err(None),
32-
rustc: Err(None),
32+
rustc: RustcWorkspace::Loaded(Err(None)),
3333
rustc_cfg: Vec::new(),
3434
cfg_overrides,
3535
toolchain: None,
@@ -48,7 +48,7 @@ fn load_cargo_with_sysroot(
4848
cargo: cargo_workspace,
4949
build_scripts: WorkspaceBuildScripts::default(),
5050
sysroot: Ok(get_fake_sysroot()),
51-
rustc: Err(None),
51+
rustc: RustcWorkspace::Loaded(Err(None)),
5252
rustc_cfg: Vec::new(),
5353
cfg_overrides: Default::default(),
5454
toolchain: None,
@@ -62,6 +62,7 @@ fn load_cargo_with_sysroot(
6262
}
6363
},
6464
&Default::default(),
65+
None,
6566
)
6667
}
6768

@@ -146,6 +147,7 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacro
146147
}
147148
},
148149
&Default::default(),
150+
None,
149151
)
150152
}
151153

crates/project-model/src/workspace.rs

Lines changed: 80 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::{
2323
project_json::Crate,
2424
rustc_cfg::{self, RustcCfgConfig},
2525
sysroot::SysrootCrate,
26-
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
26+
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy,
2727
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
2828
};
2929

@@ -53,14 +53,33 @@ pub struct PackageRoot {
5353
pub exclude: Vec<AbsPathBuf>,
5454
}
5555

56+
#[derive(Clone, PartialEq)]
57+
pub enum RustcWorkspace {
58+
/// A globally-configured rustc source location is being opened as a rust-analyzer workspace
59+
Opening,
60+
/// The rustc source is loaded, e.g. from sysroot, but is not a rust-analyzer workspace
61+
Loaded(Result<(CargoWorkspace, WorkspaceBuildScripts), Option<String>>),
62+
}
63+
64+
impl RustcWorkspace {
65+
/// Returns the loaded `CargoWorkspace` of the rustc source.
66+
/// Will be `None` if either the rustc_source loading failed or a the
67+
fn loaded(&self) -> Option<&(CargoWorkspace, WorkspaceBuildScripts)> {
68+
match self {
69+
Self::Opening => None,
70+
Self::Loaded(res) => res.as_ref().ok(),
71+
}
72+
}
73+
}
74+
5675
#[derive(Clone)]
5776
pub enum ProjectWorkspace {
5877
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
5978
Cargo {
6079
cargo: CargoWorkspace,
6180
build_scripts: WorkspaceBuildScripts,
6281
sysroot: Result<Sysroot, Option<String>>,
63-
rustc: Result<(CargoWorkspace, WorkspaceBuildScripts), Option<String>>,
82+
rustc: RustcWorkspace,
6483
/// Holds cfg flags for the current target. We get those by running
6584
/// `rustc --print cfg`.
6685
///
@@ -119,7 +138,7 @@ impl fmt::Debug for ProjectWorkspace {
119138
.field("sysroot", &sysroot.is_ok())
120139
.field(
121140
"n_rustc_compiler_crates",
122-
&rustc.as_ref().map_or(0, |(rc, _)| rc.packages().len()),
141+
&rustc.loaded().map_or(0, |(rc, _)| rc.packages().len()),
123142
)
124143
.field("n_rustc_cfg", &rustc_cfg.len())
125144
.field("n_cfg_overrides", &cfg_overrides.len())
@@ -151,15 +170,17 @@ impl ProjectWorkspace {
151170
manifest: ProjectManifest,
152171
config: &CargoConfig,
153172
progress: &dyn Fn(String),
173+
opening_rustc_workspace: bool,
154174
) -> anyhow::Result<ProjectWorkspace> {
155-
ProjectWorkspace::load_inner(&manifest, config, progress)
175+
ProjectWorkspace::load_inner(&manifest, config, progress, opening_rustc_workspace)
156176
.with_context(|| format!("Failed to load the project at {manifest}"))
157177
}
158178

159179
fn load_inner(
160180
manifest: &ProjectManifest,
161181
config: &CargoConfig,
162182
progress: &dyn Fn(String),
183+
opening_rustc_workspace: bool,
163184
) -> anyhow::Result<ProjectWorkspace> {
164185
let version = |current_dir, cmd_path, prefix: &str| {
165186
let cargo_version = utf8_stdout({
@@ -236,48 +257,54 @@ impl ProjectWorkspace {
236257
tracing::info!(workspace = %cargo_toml, src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
237258
}
238259

239-
let rustc_dir = match &config.rustc_source {
240-
Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
241-
.map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
242-
Some(RustLibSource::Discover) => {
243-
sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else(
244-
|| Some(format!("Failed to discover rustc source for sysroot.")),
245-
)
246-
}
247-
None => Err(None),
248-
};
249-
250-
let rustc = rustc_dir.and_then(|rustc_dir| {
251-
tracing::info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
252-
match CargoWorkspace::fetch_metadata(
253-
&rustc_dir,
254-
cargo_toml.parent(),
255-
&CargoConfig {
256-
features: crate::CargoFeatures::default(),
257-
..config.clone()
258-
},
259-
progress,
260-
) {
261-
Ok(meta) => {
262-
let workspace = CargoWorkspace::new(meta);
263-
let buildscripts = WorkspaceBuildScripts::rustc_crates(
264-
&workspace,
265-
cargo_toml.parent(),
266-
&config.extra_env,
267-
);
268-
Ok((workspace, buildscripts))
260+
let rustc = if opening_rustc_workspace {
261+
RustcWorkspace::Opening
262+
} else {
263+
let rustc_dir = match &config.rustc_source {
264+
// `config.rustc_source == Some(Path(...))` while `!opening_rustc_workspace` should only occur if
265+
// `ManifestPath::try_from(rustc_dir)` failed in `fetch_workspaces`, so no need to attempt it here
266+
// again.
267+
Some(RustLibSource::Path(path)) => Err(Some(format!("rustc source path is not absolute: {path}"))),
268+
Some(RustLibSource::Discover) => {
269+
sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else(
270+
|| Some(format!("Failed to discover rustc source for sysroot.")),
271+
)
269272
}
270-
Err(e) => {
271-
tracing::error!(
272-
%e,
273-
"Failed to read Cargo metadata from rustc source at {rustc_dir}",
274-
);
275-
Err(Some(format!(
276-
"Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
277-
)))
273+
None => Err(None),
274+
};
275+
276+
RustcWorkspace::Loaded(rustc_dir.and_then(|rustc_dir| {
277+
tracing::info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
278+
match CargoWorkspace::fetch_metadata(
279+
&rustc_dir,
280+
cargo_toml.parent(),
281+
&CargoConfig {
282+
features: crate::CargoFeatures::default(),
283+
..config.clone()
284+
},
285+
progress,
286+
) {
287+
Ok(meta) => {
288+
let workspace = CargoWorkspace::new(meta);
289+
let buildscripts = WorkspaceBuildScripts::rustc_crates(
290+
&workspace,
291+
cargo_toml.parent(),
292+
&config.extra_env,
293+
);
294+
Ok((workspace, buildscripts))
295+
}
296+
Err(e) => {
297+
tracing::error!(
298+
%e,
299+
"Failed to read Cargo metadata from rustc source at {rustc_dir}",
300+
);
301+
Err(Some(format!(
302+
"Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
303+
)))
304+
}
278305
}
279-
}
280-
});
306+
}))
307+
};
281308

282309
let rustc_cfg = rustc_cfg::get(
283310
config.target.as_deref(),
@@ -564,7 +591,7 @@ impl ProjectWorkspace {
564591
PackageRoot { is_local, include, exclude }
565592
})
566593
.chain(mk_sysroot(sysroot.as_ref(), Some(cargo.workspace_root())))
567-
.chain(rustc.iter().flat_map(|(rustc, _)| {
594+
.chain(rustc.loaded().iter().flat_map(|(rustc, _)| {
568595
rustc.packages().map(move |krate| PackageRoot {
569596
is_local: false,
570597
include: vec![rustc[krate].manifest.parent().to_path_buf()],
@@ -592,7 +619,7 @@ impl ProjectWorkspace {
592619
sysroot_package_len + project.n_crates()
593620
}
594621
ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
595-
let rustc_package_len = rustc.as_ref().map_or(0, |(it, _)| it.packages().len());
622+
let rustc_package_len = rustc.loaded().map_or(0, |(it, _)| it.packages().len());
596623
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
597624
cargo.packages().len() + sysroot_package_len + rustc_package_len
598625
}
@@ -607,6 +634,7 @@ impl ProjectWorkspace {
607634
&self,
608635
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
609636
extra_env: &FxHashMap<String, String>,
637+
opened_rustc_workspace: Option<(&CargoWorkspace, &WorkspaceBuildScripts)>,
610638
) -> (CrateGraph, ProcMacroPaths) {
611639
let _p = profile::span("ProjectWorkspace::to_crate_graph");
612640

@@ -633,7 +661,10 @@ impl ProjectWorkspace {
633661
target_layout,
634662
} => cargo_to_crate_graph(
635663
load,
636-
rustc.as_ref().ok(),
664+
match rustc {
665+
RustcWorkspace::Opening => opened_rustc_workspace,
666+
RustcWorkspace::Loaded(res) => res.as_ref().ok().map(|(a, b)| (a, b)),
667+
},
637668
cargo,
638669
sysroot.as_ref().ok(),
639670
rustc_cfg.clone(),
@@ -844,7 +875,7 @@ fn project_json_to_crate_graph(
844875

845876
fn cargo_to_crate_graph(
846877
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
847-
rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
878+
rustc: Option<(&CargoWorkspace, &WorkspaceBuildScripts)>,
848879
cargo: &CargoWorkspace,
849880
sysroot: Option<&Sysroot>,
850881
rustc_cfg: Vec<CfgFlag>,
@@ -1030,13 +1061,7 @@ fn cargo_to_crate_graph(
10301061
&pkg_crates,
10311062
&cfg_options,
10321063
override_cfg,
1033-
if rustc_workspace.workspace_root() == cargo.workspace_root() {
1034-
// the rustc workspace does not use the installed toolchain's proc-macro server
1035-
// so we need to make sure we don't use the pre compiled proc-macros there either
1036-
build_scripts
1037-
} else {
1038-
rustc_build_scripts
1039-
},
1064+
rustc_build_scripts,
10401065
target_layout,
10411066
channel,
10421067
);

crates/rust-analyzer/src/cli/analysis_stats.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl flags::AnalysisStats {
7070
let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path));
7171
let manifest = ProjectManifest::discover_single(&path)?;
7272

73-
let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
73+
let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress, false)?;
7474
let metadata_time = db_load_sw.elapsed();
7575
let load_cargo_config = LoadCargoConfig {
7676
load_out_dirs_from_check: !self.disable_build_scripts,

crates/rust-analyzer/src/cli/lsif.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ impl flags::Lsif {
299299
let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path));
300300
let manifest = ProjectManifest::discover_single(&path)?;
301301

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

304304
let (host, vfs, _proc_macro) =
305305
load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;

crates/rust-analyzer/src/reload.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use ide_db::{
2525
use itertools::Itertools;
2626
use load_cargo::{load_proc_macro, ProjectFolders};
2727
use proc_macro_api::ProcMacroServer;
28-
use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
28+
use project_model::{ProjectWorkspace, WorkspaceBuildScripts, RustLibSource, ProjectManifest, ManifestPath, RustcWorkspace};
2929
use rustc_hash::FxHashSet;
3030
use stdx::{format_to, thread::ThreadIntent};
3131
use triomphe::Arc;
@@ -166,7 +166,7 @@ impl GlobalState {
166166
}
167167
}
168168
}
169-
if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws {
169+
if let ProjectWorkspace::Cargo { rustc: RustcWorkspace::Loaded(Err(Some(e))), .. } = ws {
170170
status.health = lsp_ext::Health::Warning;
171171
message.push_str(e);
172172
message.push_str("\n\n");
@@ -188,7 +188,7 @@ impl GlobalState {
188188
tracing::info!(%cause, "will fetch workspaces");
189189

190190
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
191-
let linked_projects = self.config.linked_projects();
191+
let mut linked_projects = self.config.linked_projects();
192192
let detached_files = self.config.detached_files().to_vec();
193193
let cargo_config = self.config.cargo();
194194

@@ -204,6 +204,21 @@ impl GlobalState {
204204

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

207+
let mut opening_rustc_workspace = false;
208+
if let Some(RustLibSource::Path(rustc_dir)) = &cargo_config.rustc_source {
209+
if let Ok(rustc_manifest) = ManifestPath::try_from(rustc_dir.clone()) {
210+
if let Some(rustc_index) = linked_projects.iter().position(|project| match project {
211+
LinkedProject::ProjectManifest(ProjectManifest::CargoToml(project_manifest)) => *project_manifest == rustc_manifest,
212+
_ => false,
213+
}) {
214+
// move to the front so that the ensuing workspace's build scripts will be built and available
215+
// to other workspaces (in particular, any that have packages using rustc_private crates)
216+
linked_projects.swap(0, rustc_index);
217+
opening_rustc_workspace = true;
218+
}
219+
}
220+
}
221+
207222
let mut workspaces = linked_projects
208223
.iter()
209224
.map(|project| match project {
@@ -212,6 +227,7 @@ impl GlobalState {
212227
manifest.clone(),
213228
&cargo_config,
214229
&progress,
230+
opening_rustc_workspace,
215231
)
216232
}
217233
LinkedProject::InlineJsonProject(it) => {
@@ -506,9 +522,16 @@ impl GlobalState {
506522

507523
let mut crate_graph = CrateGraph::default();
508524
let mut proc_macros = Vec::default();
525+
526+
// if the first workspace is `RustcWorkspace::Opening` then it is guaranteed to be the rustc source
527+
// by virtue of the sorting in `fetch_workspaces`.
528+
let opened_rustc_workspace = match self.workspaces.first() {
529+
Some(ProjectWorkspace::Cargo { rustc: RustcWorkspace::Opening, cargo, build_scripts, .. }) => Some((cargo, build_scripts)),
530+
_ => None,
531+
};
509532
for ws in &**self.workspaces {
510533
let (other, mut crate_proc_macros) =
511-
ws.to_crate_graph(&mut load, &self.config.extra_env());
534+
ws.to_crate_graph(&mut load, &self.config.extra_env(), opened_rustc_workspace);
512535
crate_graph.extend(other, &mut crate_proc_macros);
513536
proc_macros.push(crate_proc_macros);
514537
}

0 commit comments

Comments
 (0)