Skip to content

Commit 8e6a409

Browse files
committed
implement new util ra_project for bootstrap
Contains the implementation for generating rust-project.json data which can be utilized for LSPs (Language Server Protocols). Signed-off-by: onur-ozkan <work@onurozkan.dev>
1 parent 6533515 commit 8e6a409

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

src/bootstrap/src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ pub(crate) mod helpers;
1212
pub(crate) mod job;
1313
#[cfg(feature = "build-metrics")]
1414
pub(crate) mod metrics;
15+
pub(crate) mod ra_project;
1516
pub(crate) mod render_tests;
1617
pub(crate) mod tarball;

src/bootstrap/src/utils/ra_project.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//! This module contains the implementation for generating rust-project.json data which can be
2+
//! utilized for LSPs (Language Server Protocols).
3+
//!
4+
//! The primary reason for relying on rust-analyzer.json instead of the default rust-analyzer
5+
//! is because rust-analyzer is not so capable of handling rust-lang/rust workspaces out of the box.
6+
//! It often encounters new issues while trying to fix current problems with some hacky workarounds.
7+
//!
8+
//! For additional context, see the [zulip thread].
9+
//!
10+
//! [zulip thread]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/r-a.20support.20for.20rust-lang.2Frust.20via.20project-rust.2Ejson/near/412505824
11+
12+
use serde_derive::{Deserialize, Serialize};
13+
use std::collections::{BTreeMap, BTreeSet};
14+
use std::io;
15+
use std::path::Path;
16+
17+
use crate::core::metadata::{project_metadata, workspace_members, Dependency};
18+
use crate::Build;
19+
20+
#[derive(Debug, Deserialize, Serialize)]
21+
/// FIXME(before-merge): doc-comment
22+
struct RustAnalyzerProject {
23+
crates: Vec<Crate>,
24+
sysroot: String,
25+
sysroot_src: String,
26+
}
27+
28+
#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
29+
struct Crate {
30+
cfg: Vec<String>,
31+
deps: BTreeSet<Dep>,
32+
display_name: String,
33+
edition: String,
34+
env: BTreeMap<String, String>,
35+
is_proc_macro: bool,
36+
proc_macro_dylib_path: Option<String>,
37+
is_workspace_member: bool,
38+
root_module: String,
39+
}
40+
41+
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
42+
struct Dep {
43+
#[serde(rename = "crate")]
44+
crate_index: usize,
45+
name: String,
46+
}
47+
48+
impl RustAnalyzerProject {
49+
#[allow(dead_code)] // FIXME(before-merge): remove this
50+
pub(crate) fn collect_ra_project_data(build: &mut Build) -> Self {
51+
let mut ra_project = RustAnalyzerProject {
52+
crates: vec![],
53+
sysroot: format!("{}", build.out.join("host").join("stage0").display()),
54+
sysroot_src: format!("{}", build.src.join("library").display()),
55+
};
56+
57+
let packages: Vec<_> = project_metadata(build).collect();
58+
let workspace_members: Vec<_> = workspace_members(build).collect();
59+
60+
for package in &packages {
61+
let is_not_indirect_dependency = packages
62+
.iter()
63+
.filter(|t| {
64+
let used_from_other_crates = t.dependencies.contains(&Dependency {
65+
name: package.name.clone(),
66+
source: package.source.clone(),
67+
});
68+
69+
let is_local = t.source.is_none();
70+
71+
(used_from_other_crates && is_local) || package.source.is_none()
72+
})
73+
.next()
74+
.is_some();
75+
76+
if !is_not_indirect_dependency {
77+
continue;
78+
}
79+
80+
for target in &package.targets {
81+
let mut krate = Crate::default();
82+
krate.display_name = target.name.clone();
83+
krate.root_module = target.src_path.clone();
84+
krate.edition = target.edition.clone();
85+
krate.is_workspace_member = workspace_members.iter().any(|p| p.name == target.name);
86+
krate.is_proc_macro = target.crate_types.contains(&"proc-macro".to_string());
87+
88+
// FIXME(before-merge): We need to figure out how to find proc-macro dylibs.
89+
// if krate.is_proc_macro {
90+
// krate.proc_macro_dylib_path =
91+
// }
92+
93+
krate.env.insert("RUSTC_BOOTSTRAP".into(), "1".into());
94+
95+
if target
96+
.src_path
97+
.starts_with(&build.src.join("library").to_string_lossy().to_string())
98+
{
99+
krate.cfg.push("bootstrap".into());
100+
}
101+
102+
ra_project.crates.push(krate);
103+
}
104+
}
105+
106+
ra_project.crates.sort_by_key(|c| c.display_name.clone());
107+
ra_project.crates.dedup_by_key(|c| c.display_name.clone());
108+
109+
// Find and fill dependencies of crates.
110+
for package in packages {
111+
if package.dependencies.is_empty() {
112+
continue;
113+
}
114+
115+
for dependency in package.dependencies {
116+
if let Some(index) =
117+
ra_project.crates.iter().position(|c| c.display_name == package.name)
118+
{
119+
if let Some(dependency_index) =
120+
ra_project.crates.iter().position(|c| c.display_name == dependency.name)
121+
{
122+
// no need to find indirect dependencies of direct dependencies, just continue
123+
if ra_project.crates[index].root_module.contains(".cargo/registry") {
124+
continue;
125+
}
126+
127+
let dependency_name = dependency.name.replace('-', "_").to_lowercase();
128+
129+
ra_project.crates[index]
130+
.deps
131+
.insert(Dep { crate_index: dependency_index, name: dependency_name });
132+
}
133+
}
134+
}
135+
}
136+
137+
ra_project
138+
}
139+
140+
#[allow(dead_code)] // FIXME(before-merge): remove this
141+
pub(crate) fn generate_file(&self, path: &Path) -> io::Result<()> {
142+
if path.exists() {
143+
return Err(io::Error::new(
144+
io::ErrorKind::AlreadyExists,
145+
format!("File '{}' already exists.", path.display()),
146+
));
147+
}
148+
149+
let mut file = std::fs::File::create(path)?;
150+
serde_json::to_writer_pretty(&mut file, self)?;
151+
152+
Ok(())
153+
}
154+
}

0 commit comments

Comments
 (0)