Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/project-model/src/workspace.rs')
-rw-r--r--crates/project-model/src/workspace.rs340
1 files changed, 189 insertions, 151 deletions
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index c04eddc56f..88974e889e 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -9,7 +9,7 @@ use base_db::{
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, DependencyKind,
Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
};
-use cfg::{CfgDiff, CfgOptions};
+use cfg::{CfgAtom, CfgDiff, CfgOptions};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
@@ -22,7 +22,7 @@ use crate::{
cfg_flag::CfgFlag,
project_json::Crate,
rustc_cfg::{self, RustcCfgConfig},
- sysroot::SysrootCrate,
+ sysroot::{SysrootCrate, SysrootMode},
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};
@@ -38,7 +38,7 @@ pub struct CfgOverrides {
impl CfgOverrides {
pub fn len(&self) -> usize {
- self.global.len() + self.selective.iter().map(|(_, it)| it.len()).sum::<usize>()
+ self.global.len() + self.selective.values().map(|it| it.len()).sum::<usize>()
}
}
@@ -130,7 +130,7 @@ impl fmt::Debug for ProjectWorkspace {
let mut debug_struct = f.debug_struct("Json");
debug_struct.field("n_crates", &project.n_crates());
if let Ok(sysroot) = sysroot {
- debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
+ debug_struct.field("n_sysroot_crates", &sysroot.num_packages());
}
debug_struct.field("toolchain", &toolchain);
debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
@@ -177,7 +177,7 @@ impl ProjectWorkspace {
};
let res = match manifest {
ProjectManifest::ProjectJson(project_json) => {
- let file = fs::read_to_string(&project_json)
+ let file = fs::read_to_string(project_json)
.with_context(|| format!("Failed to read json file {project_json}"))?;
let data = serde_json::from_str(&file)
.with_context(|| format!("Failed to deserialize json file {project_json}"))?;
@@ -194,7 +194,7 @@ impl ProjectWorkspace {
ProjectManifest::CargoToml(cargo_toml) => {
let toolchain = version(cargo_toml.parent(), toolchain::cargo(), "cargo ")?;
let meta = CargoWorkspace::fetch_metadata(
- &cargo_toml,
+ cargo_toml,
cargo_toml.parent(),
config,
progress,
@@ -208,23 +208,23 @@ impl ProjectWorkspace {
let sysroot = match (&config.sysroot, &config.sysroot_src) {
(Some(RustLibSource::Path(path)), None) => {
- Sysroot::with_sysroot_dir(path.clone()).map_err(|e| {
+ Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata).map_err(|e| {
Some(format!("Failed to find sysroot at {path}:{e}"))
})
}
(Some(RustLibSource::Discover), None) => {
- Sysroot::discover(cargo_toml.parent(), &config.extra_env).map_err(|e| {
+ Sysroot::discover(cargo_toml.parent(), &config.extra_env, config.sysroot_query_metadata).map_err(|e| {
Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}"))
})
}
(Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
- Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone()))
+ Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone(), config.sysroot_query_metadata))
}
(Some(RustLibSource::Discover), Some(sysroot_src)) => {
Sysroot::discover_with_src_override(
cargo_toml.parent(),
&config.extra_env,
- sysroot_src.clone(),
+ sysroot_src.clone(), config.sysroot_query_metadata,
).map_err(|e| {
Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}"))
})
@@ -241,7 +241,7 @@ impl ProjectWorkspace {
.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.")),
+ || Some("Failed to discover rustc source for sysroot.".to_string()),
)
}
None => Err(None),
@@ -287,7 +287,7 @@ impl ProjectWorkspace {
let cfg_overrides = config.cfg_overrides.clone();
let data_layout = target_data_layout::get(
- Some(&cargo_toml),
+ Some(cargo_toml),
config.target.as_deref(),
&config.extra_env,
);
@@ -317,12 +317,12 @@ impl ProjectWorkspace {
toolchain: Option<Version>,
) -> ProjectWorkspace {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
- (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)),
+ (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src, false)),
(Some(sysroot), None) => {
// assume sysroot is structured like rustup's and guess `sysroot_src`
let sysroot_src =
sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
- Ok(Sysroot::load(sysroot, sysroot_src))
+ Ok(Sysroot::load(sysroot, sysroot_src, false))
}
(None, Some(sysroot_src)) => {
// assume sysroot is structured like rustup's and guess `sysroot`
@@ -330,7 +330,7 @@ impl ProjectWorkspace {
for _ in 0..5 {
sysroot.pop();
}
- Ok(Sysroot::load(sysroot, sysroot_src))
+ Ok(Sysroot::load(sysroot, sysroot_src, false))
}
(None, None) => Err(None),
};
@@ -354,16 +354,22 @@ impl ProjectWorkspace {
config: &CargoConfig,
) -> anyhow::Result<ProjectWorkspace> {
let sysroot = match &config.sysroot {
- Some(RustLibSource::Path(path)) => Sysroot::with_sysroot_dir(path.clone())
- .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))),
+ Some(RustLibSource::Path(path)) => {
+ Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
+ .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
+ }
Some(RustLibSource::Discover) => {
let dir = &detached_files
.first()
.and_then(|it| it.parent())
.ok_or_else(|| format_err!("No detached files to load"))?;
- Sysroot::discover(dir, &config.extra_env).map_err(|e| {
- Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}"))
- })
+ Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata).map_err(
+ |e| {
+ Some(format!(
+ "Failed to find sysroot for {dir}. Is rust-src installed? {e}"
+ ))
+ },
+ )
}
None => Err(None),
};
@@ -407,6 +413,7 @@ impl ProjectWorkspace {
workspaces: &[ProjectWorkspace],
config: &CargoConfig,
progress: &dyn Fn(String),
+ workspace_root: &AbsPathBuf,
) -> Vec<anyhow::Result<WorkspaceBuildScripts>> {
if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace)
|| config.run_build_script_command.is_none()
@@ -421,11 +428,13 @@ impl ProjectWorkspace {
_ => None,
})
.collect();
- let outputs = &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) {
- Ok(it) => Ok(it.into_iter()),
- // io::Error is not Clone?
- Err(e) => Err(sync::Arc::new(e)),
- };
+ let outputs =
+ &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress, workspace_root)
+ {
+ Ok(it) => Ok(it.into_iter()),
+ // io::Error is not Clone?
+ Err(e) => Err(sync::Arc::new(e)),
+ };
workspaces
.iter()
@@ -494,13 +503,43 @@ impl ProjectWorkspace {
/// The return type contains the path and whether or not
/// the root is a member of the current workspace
pub fn to_roots(&self) -> Vec<PackageRoot> {
- let mk_sysroot = |sysroot: Result<&Sysroot, _>, project_root: Option<&AbsPath>| {
- sysroot.map(|sysroot| PackageRoot {
- // mark the sysroot as mutable if it is located inside of the project
- is_local: project_root
- .map_or(false, |project_root| sysroot.src_root().starts_with(project_root)),
- include: vec![sysroot.src_root().to_path_buf()],
- exclude: Vec::new(),
+ let mk_sysroot = |sysroot: Result<_, _>, project_root: Option<&AbsPath>| {
+ let project_root = project_root.map(ToOwned::to_owned);
+ sysroot.into_iter().flat_map(move |sysroot: &Sysroot| {
+ let mut r = match sysroot.mode() {
+ SysrootMode::Workspace(ws) => ws
+ .packages()
+ .filter_map(|pkg| {
+ if ws[pkg].is_local {
+ // the local ones are included in the main `PackageRoot`` below
+ return None;
+ }
+ let pkg_root = ws[pkg].manifest.parent().to_path_buf();
+
+ let include = vec![pkg_root.clone()];
+
+ let exclude = vec![
+ pkg_root.join(".git"),
+ pkg_root.join("target"),
+ pkg_root.join("tests"),
+ pkg_root.join("examples"),
+ pkg_root.join("benches"),
+ ];
+ Some(PackageRoot { is_local: false, include, exclude })
+ })
+ .collect(),
+ SysrootMode::Stitched(_) => vec![],
+ };
+
+ r.push(PackageRoot {
+ // mark the sysroot as mutable if it is located inside of the project
+ is_local: project_root
+ .as_ref()
+ .map_or(false, |project_root| sysroot.src_root().starts_with(project_root)),
+ include: vec![sysroot.src_root().to_path_buf()],
+ exclude: Vec::new(),
+ });
+ r
})
};
match self {
@@ -588,16 +627,16 @@ impl ProjectWorkspace {
pub fn n_packages(&self) -> usize {
match self {
ProjectWorkspace::Json { project, sysroot, .. } => {
- let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
+ let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
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 sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
+ let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
cargo.packages().len() + sysroot_package_len + rustc_package_len
}
ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
- let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
+ let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
sysroot_package_len + files.len()
}
}
@@ -638,7 +677,6 @@ impl ProjectWorkspace {
sysroot.as_ref().ok(),
rustc_cfg.clone(),
cfg_overrides,
- None,
build_scripts,
match target_layout.as_ref() {
Ok(it) => Ok(Arc::from(it.as_str())),
@@ -849,8 +887,6 @@ fn cargo_to_crate_graph(
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
- // Don't compute cfg and use this if present, only used for the sysroot experiment hack
- forced_cfg: Option<CfgOptions>,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
toolchain: Option<&Version>,
@@ -883,7 +919,7 @@ fn cargo_to_crate_graph(
for pkg in cargo.packages() {
has_private |= cargo[pkg].metadata.rustc_private;
- let cfg_options = forced_cfg.clone().unwrap_or_else(|| {
+ let cfg_options = {
let mut cfg_options = cfg_options.clone();
// Add test cfg for local crates
@@ -908,7 +944,7 @@ fn cargo_to_crate_graph(
cfg_options.apply_diff(diff.clone());
};
cfg_options
- });
+ };
let mut lib_tgt = None;
for &tgt in cargo[pkg].targets.iter() {
@@ -1349,124 +1385,126 @@ fn sysroot_to_crate_graph(
toolchain: Option<&Version>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = profile::span("sysroot_to_crate_graph");
- let cfg_options = create_cfg_options(rustc_cfg.clone());
- let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace {
- Some(cargo) => handle_hack_cargo_workspace(
- load,
- cargo,
- rustc_cfg,
- cfg_options,
- target_layout,
- toolchain,
- crate_graph,
- sysroot,
- ),
- None => sysroot
- .crates()
- .filter_map(|krate| {
- let file_id = load(&sysroot[krate].root)?;
-
- let env = Env::default();
- let display_name =
- CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
- let crate_id = crate_graph.add_crate_root(
- file_id,
- Edition::CURRENT,
- Some(display_name),
- None,
- cfg_options.clone(),
- None,
- env,
- false,
- CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
- target_layout.clone(),
- toolchain.cloned(),
- );
- Some((krate, crate_id))
- })
- .collect(),
- };
- for from in sysroot.crates() {
- for &to in sysroot[from].deps.iter() {
- let name = CrateName::new(&sysroot[to].name).unwrap();
- if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
- add_dep(crate_graph, from, name, to, DependencyKind::Normal);
+ match sysroot.mode() {
+ SysrootMode::Workspace(cargo) => {
+ let (mut cg, mut pm) = cargo_to_crate_graph(
+ load,
+ None,
+ cargo,
+ None,
+ rustc_cfg,
+ &CfgOverrides::default(),
+ &WorkspaceBuildScripts::default(),
+ target_layout,
+ toolchain,
+ );
+
+ let mut pub_deps = vec![];
+ let mut libproc_macro = None;
+ let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap();
+ for (cid, c) in cg.iter_mut() {
+ // uninject `test` flag so `core` keeps working.
+ 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()),
+ );
+ 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::ProcMacro => libproc_macro = Some(cid),
+ LangCrateOrigin::Other => (),
+ }
+ }
}
+
+ 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);
+
+ crate_graph.extend(cg, &mut pm, |mapping| {
+ // 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)
}
- }
+ SysrootMode::Stitched(stitched) => {
+ let cfg_options = create_cfg_options(rustc_cfg.clone());
+ let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched
+ .crates()
+ .filter_map(|krate| {
+ let file_id = load(&stitched[krate].root)?;
- let public_deps = SysrootPublicDeps {
- deps: sysroot
- .public_deps()
- .filter_map(|(name, idx, prelude)| Some((name, *sysroot_crates.get(&idx)?, prelude)))
- .collect::<Vec<_>>(),
- };
+ let env = Env::default();
+ let display_name =
+ CrateDisplayName::from_canonical_name(stitched[krate].name.clone());
+ let crate_id = crate_graph.add_crate_root(
+ file_id,
+ Edition::CURRENT,
+ Some(display_name),
+ None,
+ cfg_options.clone(),
+ None,
+ env,
+ false,
+ CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
+ target_layout.clone(),
+ toolchain.cloned(),
+ );
+ Some((krate, crate_id))
+ })
+ .collect();
+
+ for from in stitched.crates() {
+ for &to in stitched[from].deps.iter() {
+ let name = CrateName::new(&stitched[to].name).unwrap();
+ if let (Some(&from), Some(&to)) =
+ (sysroot_crates.get(&from), sysroot_crates.get(&to))
+ {
+ add_dep(crate_graph, from, name, to, DependencyKind::Normal);
+ }
+ }
+ }
- let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
- (public_deps, libproc_macro)
-}
+ let public_deps = SysrootPublicDeps {
+ deps: stitched
+ .public_deps()
+ .filter_map(|(name, idx, prelude)| {
+ Some((name, *sysroot_crates.get(&idx)?, prelude))
+ })
+ .collect::<Vec<_>>(),
+ };
-fn handle_hack_cargo_workspace(
- load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
- cargo: &CargoWorkspace,
- rustc_cfg: Vec<CfgFlag>,
- cfg_options: CfgOptions,
- target_layout: Result<Arc<str>, Arc<str>>,
- toolchain: Option<&Version>,
- crate_graph: &mut CrateGraph,
- sysroot: &Sysroot,
-) -> FxHashMap<SysrootCrate, CrateId> {
- let (cg, mut pm) = cargo_to_crate_graph(
- load,
- None,
- cargo,
- None,
- rustc_cfg,
- &CfgOverrides::default(),
- Some(cfg_options),
- &WorkspaceBuildScripts::default(),
- target_layout,
- toolchain,
- );
- crate_graph.extend(cg, &mut pm);
- for crate_name in ["std", "alloc", "core"] {
- let original = crate_graph
- .iter()
- .find(|x| {
- crate_graph[*x]
- .display_name
- .as_ref()
- .map(|x| x.canonical_name() == crate_name)
- .unwrap_or(false)
- })
- .unwrap();
- let fake_crate_name = format!("rustc-std-workspace-{}", crate_name);
- let fake = crate_graph
- .iter()
- .find(|x| {
- crate_graph[*x]
- .display_name
- .as_ref()
- .map(|x| x.canonical_name() == fake_crate_name)
- .unwrap_or(false)
- })
- .unwrap();
- crate_graph.remove_and_replace(fake, original).unwrap();
- }
- for (_, c) in crate_graph.iter_mut() {
- if c.origin.is_local() {
- // LangCrateOrigin::Other is good enough for a hack.
- c.origin = CrateOrigin::Lang(LangCrateOrigin::Other);
+ let libproc_macro =
+ stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
+ (public_deps, libproc_macro)
}
}
- sysroot
- .crates()
- .filter_map(|krate| {
- let file_id = load(&sysroot[krate].root)?;
- let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
- Some((krate, crate_id))
- })
- .collect()
}
fn add_dep(