Skip to content

Commit faaba55

Browse files
committed
Allow rust-project.json to specify sysroot workspace
1 parent 444ce09 commit faaba55

File tree

4 files changed

+135
-61
lines changed

4 files changed

+135
-61
lines changed

src/tools/rust-analyzer/crates/project-model/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> {
262262
#[derive(Clone, Debug, PartialEq, Eq)]
263263
pub enum RustSourceWorkspaceConfig {
264264
CargoMetadata(CargoMetadataConfig),
265+
Json(ProjectJson),
265266
Stitched,
266267
}
267268

src/tools/rust-analyzer/crates/project-model/src/project_json.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub struct ProjectJson {
6565
pub(crate) sysroot: Option<AbsPathBuf>,
6666
/// e.g. `path/to/sysroot/lib/rustlib/src/rust/library`
6767
pub(crate) sysroot_src: Option<AbsPathBuf>,
68+
/// A nested project describing the layout of the sysroot
69+
pub(crate) sysroot_project: Option<Box<ProjectJson>>,
6870
project_root: AbsPathBuf,
6971
/// The path to the rust-project.json file. May be None if this
7072
/// data was generated by the discoverConfig command.
@@ -91,9 +93,16 @@ impl ProjectJson {
9193
data: ProjectJsonData,
9294
) -> ProjectJson {
9395
let absolutize_on_base = |p| base.absolutize(p);
96+
let sysroot_src = data.sysroot_src.map(absolutize_on_base);
97+
let sysroot_project =
98+
data.sysroot_project.zip(sysroot_src.clone()).map(|(sysroot_data, sysroot_src)| {
99+
Box::new(ProjectJson::new(None, &sysroot_src, *sysroot_data))
100+
});
101+
94102
ProjectJson {
95103
sysroot: data.sysroot.map(absolutize_on_base),
96-
sysroot_src: data.sysroot_src.map(absolutize_on_base),
104+
sysroot_src,
105+
sysroot_project,
97106
project_root: base.to_path_buf(),
98107
manifest,
99108
runnables: data.runnables.into_iter().map(Runnable::from).collect(),
@@ -330,6 +339,7 @@ pub enum RunnableKind {
330339
pub struct ProjectJsonData {
331340
sysroot: Option<Utf8PathBuf>,
332341
sysroot_src: Option<Utf8PathBuf>,
342+
sysroot_project: Option<Box<ProjectJsonData>>,
333343
#[serde(default)]
334344
cfg_groups: FxHashMap<String, CfgList>,
335345
crates: Vec<CrateData>,

src/tools/rust-analyzer/crates/project-model/src/sysroot.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use stdx::format_to;
2121
use toolchain::{probe_for_binary, Tool};
2222

2323
use crate::{
24-
cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath,
24+
cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath, ProjectJson,
2525
RustSourceWorkspaceConfig,
2626
};
2727

@@ -36,6 +36,7 @@ pub struct Sysroot {
3636
#[derive(Debug, Clone, Eq, PartialEq)]
3737
pub enum RustLibSrcWorkspace {
3838
Workspace(CargoWorkspace),
39+
Json(ProjectJson),
3940
Stitched(Stitched),
4041
Empty,
4142
}
@@ -114,6 +115,7 @@ impl Sysroot {
114115
pub fn is_rust_lib_src_empty(&self) -> bool {
115116
match &self.workspace {
116117
RustLibSrcWorkspace::Workspace(ws) => ws.packages().next().is_none(),
118+
RustLibSrcWorkspace::Json(project_json) => project_json.n_crates() == 0,
117119
RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.is_empty(),
118120
RustLibSrcWorkspace::Empty => true,
119121
}
@@ -126,6 +128,7 @@ impl Sysroot {
126128
pub fn num_packages(&self) -> usize {
127129
match &self.workspace {
128130
RustLibSrcWorkspace::Workspace(ws) => ws.packages().count(),
131+
RustLibSrcWorkspace::Json(project_json) => project_json.n_crates(),
129132
RustLibSrcWorkspace::Stitched(c) => c.crates().count(),
130133
RustLibSrcWorkspace::Empty => 0,
131134
}
@@ -252,6 +255,8 @@ impl Sysroot {
252255
return Some(loaded);
253256
}
254257
}
258+
} else if let RustSourceWorkspaceConfig::Json(project_json) = sysroot_source_config {
259+
return Some(RustLibSrcWorkspace::Json(project_json.clone()));
255260
}
256261
tracing::debug!("Stitching sysroot library: {src_root}");
257262

@@ -308,6 +313,10 @@ impl Sysroot {
308313
RustLibSrcWorkspace::Workspace(ws) => {
309314
ws.packages().any(|p| ws[p].name == "core")
310315
}
316+
RustLibSrcWorkspace::Json(project_json) => project_json
317+
.crates()
318+
.filter_map(|(_, krate)| krate.display_name.clone())
319+
.any(|name| name.canonical_name().as_str() == "core"),
311320
RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
312321
RustLibSrcWorkspace::Empty => true,
313322
};

src/tools/rust-analyzer/crates/project-model/src/workspace.rs

Lines changed: 113 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub struct ProjectWorkspace {
6969
}
7070

7171
#[derive(Clone)]
72+
#[allow(clippy::large_enum_variant)]
7273
pub enum ProjectWorkspaceKind {
7374
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
7475
Cargo {
@@ -400,20 +401,17 @@ impl ProjectWorkspace {
400401
}
401402

402403
pub fn load_inline(
403-
project_json: ProjectJson,
404+
mut project_json: ProjectJson,
404405
config: &CargoConfig,
405406
progress: &dyn Fn(String),
406407
) -> ProjectWorkspace {
407408
progress("Discovering sysroot".to_owned());
408409
let mut sysroot =
409410
Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone());
410-
let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::Stitched);
411-
if let Some(loaded_sysroot) = loaded_sysroot {
412-
sysroot.set_workspace(loaded_sysroot);
413-
}
414411

415412
tracing::info!(workspace = %project_json.manifest_or_root(), src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
416413
progress("Querying project metadata".to_owned());
414+
let sysroot_project = project_json.sysroot_project.take();
417415
let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
418416
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
419417
.unwrap_or_default();
@@ -435,14 +433,31 @@ impl ProjectWorkspace {
435433
&config.extra_env,
436434
)
437435
});
438-
thread::Result::Ok((toolchain.join()?, rustc_cfg.join()?, data_layout.join()?))
436+
let loaded_sysroot = s.spawn(|| {
437+
if let Some(sysroot_project) = sysroot_project {
438+
sysroot.load_workspace(&RustSourceWorkspaceConfig::Json(*sysroot_project))
439+
} else {
440+
sysroot.load_workspace(&RustSourceWorkspaceConfig::Stitched)
441+
}
442+
});
443+
444+
thread::Result::Ok((
445+
toolchain.join()?,
446+
rustc_cfg.join()?,
447+
data_layout.join()?,
448+
loaded_sysroot.join()?,
449+
))
439450
});
440451

441-
let (toolchain, rustc_cfg, target_layout) = match join {
452+
let (toolchain, rustc_cfg, target_layout, loaded_sysroot) = match join {
442453
Ok(it) => it,
443454
Err(e) => std::panic::resume_unwind(e),
444455
};
445456

457+
if let Some(loaded_sysroot) = loaded_sysroot {
458+
sysroot.set_workspace(loaded_sysroot);
459+
}
460+
446461
ProjectWorkspace {
447462
kind: ProjectWorkspaceKind::Json(project_json),
448463
sysroot,
@@ -667,6 +682,14 @@ impl ProjectWorkspace {
667682
Some(PackageRoot { is_local: false, include, exclude })
668683
})
669684
.collect(),
685+
RustLibSrcWorkspace::Json(project_json) => project_json
686+
.crates()
687+
.map(|(_, krate)| PackageRoot {
688+
is_local: false,
689+
include: krate.include.clone(),
690+
exclude: krate.exclude.clone(),
691+
})
692+
.collect(),
670693
RustLibSrcWorkspace::Stitched(_) | RustLibSrcWorkspace::Empty => vec![],
671694
};
672695

@@ -1490,6 +1513,65 @@ impl SysrootPublicDeps {
14901513
}
14911514
}
14921515

1516+
fn extend_crate_graph_with_sysroot(
1517+
crate_graph: &mut CrateGraph,
1518+
mut sysroot_crate_graph: CrateGraph,
1519+
mut sysroot_proc_macros: ProcMacroPaths,
1520+
) -> (SysrootPublicDeps, Option<CrateId>) {
1521+
let mut pub_deps = vec![];
1522+
let mut libproc_macro = None;
1523+
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
1524+
for (cid, c) in sysroot_crate_graph.iter_mut() {
1525+
// uninject `test` flag so `core` keeps working.
1526+
Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
1527+
// patch the origin
1528+
if c.origin.is_local() {
1529+
let lang_crate = LangCrateOrigin::from(
1530+
c.display_name.as_ref().map_or("", |it| it.canonical_name().as_str()),
1531+
);
1532+
c.origin = CrateOrigin::Lang(lang_crate);
1533+
match lang_crate {
1534+
LangCrateOrigin::Test
1535+
| LangCrateOrigin::Alloc
1536+
| LangCrateOrigin::Core
1537+
| LangCrateOrigin::Std => pub_deps.push((
1538+
CrateName::normalize_dashes(&lang_crate.to_string()),
1539+
cid,
1540+
!matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
1541+
)),
1542+
LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
1543+
LangCrateOrigin::Other => (),
1544+
}
1545+
}
1546+
}
1547+
1548+
let mut marker_set = vec![];
1549+
for &(_, cid, _) in pub_deps.iter() {
1550+
marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
1551+
}
1552+
if let Some(cid) = libproc_macro {
1553+
marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
1554+
}
1555+
1556+
marker_set.sort();
1557+
marker_set.dedup();
1558+
1559+
// Remove all crates except the ones we are interested in to keep the sysroot graph small.
1560+
let removed_mapping = sysroot_crate_graph.remove_crates_except(&marker_set);
1561+
let mapping = crate_graph.extend(sysroot_crate_graph, &mut sysroot_proc_macros);
1562+
1563+
// Map the id through the removal mapping first, then through the crate graph extension mapping.
1564+
pub_deps.iter_mut().for_each(|(_, cid, _)| {
1565+
*cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
1566+
});
1567+
if let Some(libproc_macro) = &mut libproc_macro {
1568+
*libproc_macro =
1569+
mapping[&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
1570+
}
1571+
1572+
(SysrootPublicDeps { deps: pub_deps }, libproc_macro)
1573+
}
1574+
14931575
fn sysroot_to_crate_graph(
14941576
crate_graph: &mut CrateGraph,
14951577
sysroot: &Sysroot,
@@ -1499,7 +1581,7 @@ fn sysroot_to_crate_graph(
14991581
let _p = tracing::info_span!("sysroot_to_crate_graph").entered();
15001582
match sysroot.workspace() {
15011583
RustLibSrcWorkspace::Workspace(cargo) => {
1502-
let (mut cg, mut pm) = cargo_to_crate_graph(
1584+
let (cg, pm) = cargo_to_crate_graph(
15031585
load,
15041586
None,
15051587
cargo,
@@ -1520,58 +1602,30 @@ fn sysroot_to_crate_graph(
15201602
false,
15211603
);
15221604

1523-
let mut pub_deps = vec![];
1524-
let mut libproc_macro = None;
1525-
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
1526-
for (cid, c) in cg.iter_mut() {
1527-
// uninject `test` flag so `core` keeps working.
1528-
Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
1529-
// patch the origin
1530-
if c.origin.is_local() {
1531-
let lang_crate = LangCrateOrigin::from(
1532-
c.display_name.as_ref().map_or("", |it| it.canonical_name().as_str()),
1533-
);
1534-
c.origin = CrateOrigin::Lang(lang_crate);
1535-
match lang_crate {
1536-
LangCrateOrigin::Test
1537-
| LangCrateOrigin::Alloc
1538-
| LangCrateOrigin::Core
1539-
| LangCrateOrigin::Std => pub_deps.push((
1540-
CrateName::normalize_dashes(&lang_crate.to_string()),
1541-
cid,
1542-
!matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
1543-
)),
1544-
LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
1545-
LangCrateOrigin::Other => (),
1546-
}
1547-
}
1548-
}
1549-
1550-
let mut marker_set = vec![];
1551-
for &(_, cid, _) in pub_deps.iter() {
1552-
marker_set.extend(cg.transitive_deps(cid));
1553-
}
1554-
if let Some(cid) = libproc_macro {
1555-
marker_set.extend(cg.transitive_deps(cid));
1556-
}
1557-
1558-
marker_set.sort();
1559-
marker_set.dedup();
1560-
1561-
// Remove all crates except the ones we are interested in to keep the sysroot graph small.
1562-
let removed_mapping = cg.remove_crates_except(&marker_set);
1563-
let mapping = crate_graph.extend(cg, &mut pm);
1564-
1565-
// Map the id through the removal mapping first, then through the crate graph extension mapping.
1566-
pub_deps.iter_mut().for_each(|(_, cid, _)| {
1567-
*cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
1568-
});
1569-
if let Some(libproc_macro) = &mut libproc_macro {
1570-
*libproc_macro = mapping
1571-
[&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
1572-
}
1605+
extend_crate_graph_with_sysroot(crate_graph, cg, pm)
1606+
}
1607+
RustLibSrcWorkspace::Json(project_json) => {
1608+
let (cg, pm) = project_json_to_crate_graph(
1609+
rustc_cfg,
1610+
load,
1611+
project_json,
1612+
&Sysroot::empty(),
1613+
&FxHashMap::default(),
1614+
&CfgOverrides {
1615+
global: CfgDiff::new(
1616+
vec![
1617+
CfgAtom::Flag(sym::debug_assertions.clone()),
1618+
CfgAtom::Flag(sym::miri.clone()),
1619+
],
1620+
vec![],
1621+
)
1622+
.unwrap(),
1623+
..Default::default()
1624+
},
1625+
false,
1626+
);
15731627

1574-
(SysrootPublicDeps { deps: pub_deps }, libproc_macro)
1628+
extend_crate_graph_with_sysroot(crate_graph, cg, pm)
15751629
}
15761630
RustLibSrcWorkspace::Stitched(stitched) => {
15771631
let cfg_options = Arc::new({

0 commit comments

Comments
 (0)