mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-25 11:17:13 +00:00
Allow rust-project.json to specify sysroot workspace
This commit is contained in:
parent
1795a85be3
commit
ca5a35e97f
@ -262,6 +262,7 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> {
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RustSourceWorkspaceConfig {
|
||||
CargoMetadata(CargoMetadataConfig),
|
||||
Json(ProjectJson),
|
||||
Stitched,
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,8 @@ pub struct ProjectJson {
|
||||
pub(crate) sysroot: Option<AbsPathBuf>,
|
||||
/// e.g. `path/to/sysroot/lib/rustlib/src/rust/library`
|
||||
pub(crate) sysroot_src: Option<AbsPathBuf>,
|
||||
/// A nested project describing the layout of the sysroot
|
||||
pub(crate) sysroot_project: Option<Box<ProjectJson>>,
|
||||
project_root: AbsPathBuf,
|
||||
/// The path to the rust-project.json file. May be None if this
|
||||
/// data was generated by the discoverConfig command.
|
||||
@ -91,9 +93,16 @@ impl ProjectJson {
|
||||
data: ProjectJsonData,
|
||||
) -> ProjectJson {
|
||||
let absolutize_on_base = |p| base.absolutize(p);
|
||||
let sysroot_src = data.sysroot_src.map(absolutize_on_base);
|
||||
let sysroot_project =
|
||||
data.sysroot_project.zip(sysroot_src.clone()).map(|(sysroot_data, sysroot_src)| {
|
||||
Box::new(ProjectJson::new(None, &sysroot_src, *sysroot_data))
|
||||
});
|
||||
|
||||
ProjectJson {
|
||||
sysroot: data.sysroot.map(absolutize_on_base),
|
||||
sysroot_src: data.sysroot_src.map(absolutize_on_base),
|
||||
sysroot_src,
|
||||
sysroot_project,
|
||||
project_root: base.to_path_buf(),
|
||||
manifest,
|
||||
runnables: data.runnables.into_iter().map(Runnable::from).collect(),
|
||||
@ -330,6 +339,7 @@ pub enum RunnableKind {
|
||||
pub struct ProjectJsonData {
|
||||
sysroot: Option<Utf8PathBuf>,
|
||||
sysroot_src: Option<Utf8PathBuf>,
|
||||
sysroot_project: Option<Box<ProjectJsonData>>,
|
||||
#[serde(default)]
|
||||
cfg_groups: FxHashMap<String, CfgList>,
|
||||
crates: Vec<CrateData>,
|
||||
|
@ -21,7 +21,7 @@ use stdx::format_to;
|
||||
use toolchain::{probe_for_binary, Tool};
|
||||
|
||||
use crate::{
|
||||
cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath,
|
||||
cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath, ProjectJson,
|
||||
RustSourceWorkspaceConfig,
|
||||
};
|
||||
|
||||
@ -36,6 +36,7 @@ pub struct Sysroot {
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum RustLibSrcWorkspace {
|
||||
Workspace(CargoWorkspace),
|
||||
Json(ProjectJson),
|
||||
Stitched(Stitched),
|
||||
Empty,
|
||||
}
|
||||
@ -114,6 +115,7 @@ impl Sysroot {
|
||||
pub fn is_rust_lib_src_empty(&self) -> bool {
|
||||
match &self.workspace {
|
||||
RustLibSrcWorkspace::Workspace(ws) => ws.packages().next().is_none(),
|
||||
RustLibSrcWorkspace::Json(project_json) => project_json.n_crates() == 0,
|
||||
RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.is_empty(),
|
||||
RustLibSrcWorkspace::Empty => true,
|
||||
}
|
||||
@ -126,6 +128,7 @@ impl Sysroot {
|
||||
pub fn num_packages(&self) -> usize {
|
||||
match &self.workspace {
|
||||
RustLibSrcWorkspace::Workspace(ws) => ws.packages().count(),
|
||||
RustLibSrcWorkspace::Json(project_json) => project_json.n_crates(),
|
||||
RustLibSrcWorkspace::Stitched(c) => c.crates().count(),
|
||||
RustLibSrcWorkspace::Empty => 0,
|
||||
}
|
||||
@ -252,6 +255,8 @@ impl Sysroot {
|
||||
return Some(loaded);
|
||||
}
|
||||
}
|
||||
} else if let RustSourceWorkspaceConfig::Json(project_json) = sysroot_source_config {
|
||||
return Some(RustLibSrcWorkspace::Json(project_json.clone()));
|
||||
}
|
||||
tracing::debug!("Stitching sysroot library: {src_root}");
|
||||
|
||||
@ -308,6 +313,10 @@ impl Sysroot {
|
||||
RustLibSrcWorkspace::Workspace(ws) => {
|
||||
ws.packages().any(|p| ws[p].name == "core")
|
||||
}
|
||||
RustLibSrcWorkspace::Json(project_json) => project_json
|
||||
.crates()
|
||||
.filter_map(|(_, krate)| krate.display_name.clone())
|
||||
.any(|name| name.canonical_name().as_str() == "core"),
|
||||
RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
|
||||
RustLibSrcWorkspace::Empty => true,
|
||||
};
|
||||
|
@ -69,6 +69,7 @@ pub struct ProjectWorkspace {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ProjectWorkspaceKind {
|
||||
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
|
||||
Cargo {
|
||||
@ -400,20 +401,17 @@ impl ProjectWorkspace {
|
||||
}
|
||||
|
||||
pub fn load_inline(
|
||||
project_json: ProjectJson,
|
||||
mut project_json: ProjectJson,
|
||||
config: &CargoConfig,
|
||||
progress: &dyn Fn(String),
|
||||
) -> ProjectWorkspace {
|
||||
progress("Discovering sysroot".to_owned());
|
||||
let mut sysroot =
|
||||
Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone());
|
||||
let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::Stitched);
|
||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||
sysroot.set_workspace(loaded_sysroot);
|
||||
}
|
||||
|
||||
tracing::info!(workspace = %project_json.manifest_or_root(), src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
|
||||
progress("Querying project metadata".to_owned());
|
||||
let sysroot_project = project_json.sysroot_project.take();
|
||||
let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
|
||||
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
|
||||
.unwrap_or_default();
|
||||
@ -435,14 +433,31 @@ impl ProjectWorkspace {
|
||||
&config.extra_env,
|
||||
)
|
||||
});
|
||||
thread::Result::Ok((toolchain.join()?, rustc_cfg.join()?, data_layout.join()?))
|
||||
let loaded_sysroot = s.spawn(|| {
|
||||
if let Some(sysroot_project) = sysroot_project {
|
||||
sysroot.load_workspace(&RustSourceWorkspaceConfig::Json(*sysroot_project))
|
||||
} else {
|
||||
sysroot.load_workspace(&RustSourceWorkspaceConfig::Stitched)
|
||||
}
|
||||
});
|
||||
|
||||
thread::Result::Ok((
|
||||
toolchain.join()?,
|
||||
rustc_cfg.join()?,
|
||||
data_layout.join()?,
|
||||
loaded_sysroot.join()?,
|
||||
))
|
||||
});
|
||||
|
||||
let (toolchain, rustc_cfg, target_layout) = match join {
|
||||
let (toolchain, rustc_cfg, target_layout, loaded_sysroot) = match join {
|
||||
Ok(it) => it,
|
||||
Err(e) => std::panic::resume_unwind(e),
|
||||
};
|
||||
|
||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||
sysroot.set_workspace(loaded_sysroot);
|
||||
}
|
||||
|
||||
ProjectWorkspace {
|
||||
kind: ProjectWorkspaceKind::Json(project_json),
|
||||
sysroot,
|
||||
@ -667,6 +682,14 @@ impl ProjectWorkspace {
|
||||
Some(PackageRoot { is_local: false, include, exclude })
|
||||
})
|
||||
.collect(),
|
||||
RustLibSrcWorkspace::Json(project_json) => project_json
|
||||
.crates()
|
||||
.map(|(_, krate)| PackageRoot {
|
||||
is_local: false,
|
||||
include: krate.include.clone(),
|
||||
exclude: krate.exclude.clone(),
|
||||
})
|
||||
.collect(),
|
||||
RustLibSrcWorkspace::Stitched(_) | RustLibSrcWorkspace::Empty => vec![],
|
||||
};
|
||||
|
||||
@ -1490,6 +1513,65 @@ impl SysrootPublicDeps {
|
||||
}
|
||||
}
|
||||
|
||||
fn extend_crate_graph_with_sysroot(
|
||||
crate_graph: &mut CrateGraph,
|
||||
mut sysroot_crate_graph: CrateGraph,
|
||||
mut sysroot_proc_macros: ProcMacroPaths,
|
||||
) -> (SysrootPublicDeps, Option<CrateId>) {
|
||||
let mut pub_deps = vec![];
|
||||
let mut libproc_macro = None;
|
||||
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
|
||||
for (cid, c) in sysroot_crate_graph.iter_mut() {
|
||||
// uninject `test` flag so `core` keeps working.
|
||||
Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
|
||||
// patch the origin
|
||||
if c.origin.is_local() {
|
||||
let lang_crate = LangCrateOrigin::from(
|
||||
c.display_name.as_ref().map_or("", |it| it.canonical_name().as_str()),
|
||||
);
|
||||
c.origin = CrateOrigin::Lang(lang_crate);
|
||||
match lang_crate {
|
||||
LangCrateOrigin::Test
|
||||
| LangCrateOrigin::Alloc
|
||||
| LangCrateOrigin::Core
|
||||
| LangCrateOrigin::Std => pub_deps.push((
|
||||
CrateName::normalize_dashes(&lang_crate.to_string()),
|
||||
cid,
|
||||
!matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
|
||||
)),
|
||||
LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
|
||||
LangCrateOrigin::Other => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut marker_set = vec![];
|
||||
for &(_, cid, _) in pub_deps.iter() {
|
||||
marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
|
||||
}
|
||||
if let Some(cid) = libproc_macro {
|
||||
marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
|
||||
}
|
||||
|
||||
marker_set.sort();
|
||||
marker_set.dedup();
|
||||
|
||||
// Remove all crates except the ones we are interested in to keep the sysroot graph small.
|
||||
let removed_mapping = sysroot_crate_graph.remove_crates_except(&marker_set);
|
||||
let mapping = crate_graph.extend(sysroot_crate_graph, &mut sysroot_proc_macros);
|
||||
|
||||
// Map the id through the removal mapping first, then through the crate graph extension mapping.
|
||||
pub_deps.iter_mut().for_each(|(_, cid, _)| {
|
||||
*cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
|
||||
});
|
||||
if let Some(libproc_macro) = &mut libproc_macro {
|
||||
*libproc_macro =
|
||||
mapping[&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
|
||||
}
|
||||
|
||||
(SysrootPublicDeps { deps: pub_deps }, libproc_macro)
|
||||
}
|
||||
|
||||
fn sysroot_to_crate_graph(
|
||||
crate_graph: &mut CrateGraph,
|
||||
sysroot: &Sysroot,
|
||||
@ -1499,7 +1581,7 @@ fn sysroot_to_crate_graph(
|
||||
let _p = tracing::info_span!("sysroot_to_crate_graph").entered();
|
||||
match sysroot.workspace() {
|
||||
RustLibSrcWorkspace::Workspace(cargo) => {
|
||||
let (mut cg, mut pm) = cargo_to_crate_graph(
|
||||
let (cg, pm) = cargo_to_crate_graph(
|
||||
load,
|
||||
None,
|
||||
cargo,
|
||||
@ -1520,58 +1602,30 @@ fn sysroot_to_crate_graph(
|
||||
false,
|
||||
);
|
||||
|
||||
let mut pub_deps = vec![];
|
||||
let mut libproc_macro = None;
|
||||
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
|
||||
for (cid, c) in cg.iter_mut() {
|
||||
// uninject `test` flag so `core` keeps working.
|
||||
Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
|
||||
// patch the origin
|
||||
if c.origin.is_local() {
|
||||
let lang_crate = LangCrateOrigin::from(
|
||||
c.display_name.as_ref().map_or("", |it| it.canonical_name().as_str()),
|
||||
);
|
||||
c.origin = CrateOrigin::Lang(lang_crate);
|
||||
match lang_crate {
|
||||
LangCrateOrigin::Test
|
||||
| LangCrateOrigin::Alloc
|
||||
| LangCrateOrigin::Core
|
||||
| LangCrateOrigin::Std => pub_deps.push((
|
||||
CrateName::normalize_dashes(&lang_crate.to_string()),
|
||||
cid,
|
||||
!matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
|
||||
)),
|
||||
LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
|
||||
LangCrateOrigin::Other => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
extend_crate_graph_with_sysroot(crate_graph, cg, pm)
|
||||
}
|
||||
RustLibSrcWorkspace::Json(project_json) => {
|
||||
let (cg, pm) = project_json_to_crate_graph(
|
||||
rustc_cfg,
|
||||
load,
|
||||
project_json,
|
||||
&Sysroot::empty(),
|
||||
&FxHashMap::default(),
|
||||
&CfgOverrides {
|
||||
global: CfgDiff::new(
|
||||
vec![
|
||||
CfgAtom::Flag(sym::debug_assertions.clone()),
|
||||
CfgAtom::Flag(sym::miri.clone()),
|
||||
],
|
||||
vec![],
|
||||
)
|
||||
.unwrap(),
|
||||
..Default::default()
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
let mut marker_set = vec![];
|
||||
for &(_, cid, _) in pub_deps.iter() {
|
||||
marker_set.extend(cg.transitive_deps(cid));
|
||||
}
|
||||
if let Some(cid) = libproc_macro {
|
||||
marker_set.extend(cg.transitive_deps(cid));
|
||||
}
|
||||
|
||||
marker_set.sort();
|
||||
marker_set.dedup();
|
||||
|
||||
// Remove all crates except the ones we are interested in to keep the sysroot graph small.
|
||||
let removed_mapping = cg.remove_crates_except(&marker_set);
|
||||
let mapping = crate_graph.extend(cg, &mut pm);
|
||||
|
||||
// Map the id through the removal mapping first, then through the crate graph extension mapping.
|
||||
pub_deps.iter_mut().for_each(|(_, cid, _)| {
|
||||
*cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
|
||||
});
|
||||
if let Some(libproc_macro) = &mut libproc_macro {
|
||||
*libproc_macro = mapping
|
||||
[&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
|
||||
}
|
||||
|
||||
(SysrootPublicDeps { deps: pub_deps }, libproc_macro)
|
||||
extend_crate_graph_with_sysroot(crate_graph, cg, pm)
|
||||
}
|
||||
RustLibSrcWorkspace::Stitched(stitched) => {
|
||||
let cfg_options = Arc::new({
|
||||
|
Loading…
x
Reference in New Issue
Block a user