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.rs173
1 files changed, 110 insertions, 63 deletions
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index aa2e15930c..fa3a79e041 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -2,6 +2,7 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
+use std::thread::Builder;
use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
use anyhow::Context;
@@ -12,7 +13,7 @@ use base_db::{
};
use cfg::{CfgAtom, CfgDiff, CfgOptions};
use intern::{Symbol, sym};
-use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
+use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use span::{Edition, FileId};
@@ -26,7 +27,7 @@ use crate::{
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
WorkspaceBuildScripts,
build_dependencies::{BuildScriptOutput, ProcMacroDylibPath},
- cargo_config_file,
+ cargo_config_file::CargoConfigFile,
cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
project_json::{Crate, CrateArrayIdx},
@@ -267,7 +268,7 @@ impl ProjectWorkspace {
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
progress("querying project metadata".to_owned());
- let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
+ let config_file = CargoConfigFile::load(cargo_toml, extra_env, &sysroot);
let config_file_ = config_file.clone();
let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
let targets =
@@ -301,31 +302,39 @@ impl ProjectWorkspace {
// We can speed up loading a bit by spawning all of these processes in parallel (especially
// on systems were process spawning is delayed)
let join = thread::scope(|s| {
- let rustc_cfg = s.spawn(|| {
- rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
- });
- let target_data = s.spawn(|| {
- target_data::get(
- toolchain_config,
- targets.first().map(Deref::deref),
- extra_env,
- ).inspect_err(|e| {
- tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace")
+ let rustc_cfg = Builder::new()
+ .name("ProjectWorkspace::rustc_cfg".to_owned())
+ .spawn_scoped(s, || {
+ rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
})
- });
-
- let rustc_dir = s.spawn(|| {
- let rustc_dir = match rustc_source {
- Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
- .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
- Some(RustLibSource::Discover) => {
- sysroot.discover_rustc_src().ok_or_else(|| {
- Some("Failed to discover rustc source for sysroot.".to_owned())
+ .expect("failed to spawn thread");
+ let target_data = Builder::new()
+ .name("ProjectWorkspace::target_data".to_owned())
+ .spawn_scoped(s, || {
+ target_data::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
+ .inspect_err(|e| {
+ tracing::error!(%e,
+ "failed fetching data layout for \
+ {cargo_toml:?} workspace"
+ )
})
- }
- None => Err(None),
- };
- rustc_dir.and_then(|rustc_dir| {
+ })
+ .expect("failed to spawn thread");
+
+ let rustc_dir = Builder::new()
+ .name("ProjectWorkspace::rustc_dir".to_owned())
+ .spawn_scoped(s, || {
+ let rustc_dir = match rustc_source {
+ Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
+ .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
+ Some(RustLibSource::Discover) => {
+ sysroot.discover_rustc_src().ok_or_else(|| {
+ Some("Failed to discover rustc source for sysroot.".to_owned())
+ })
+ }
+ None => Err(None),
+ };
+ rustc_dir.and_then(|rustc_dir| {
info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
match FetchMetadata::new(
&rustc_dir,
@@ -359,31 +368,42 @@ impl ProjectWorkspace {
Err(e) => {
tracing::error!(
%e,
- "Failed to read Cargo metadata from rustc source at {rustc_dir}",
+ "Failed to read Cargo metadata from rustc source \
+ at {rustc_dir}",
);
Err(Some(format!(
- "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
+ "Failed to read Cargo metadata from rustc source \
+ at {rustc_dir}: {e}"
)))
}
}
})
- });
-
- let cargo_metadata = s.spawn(|| fetch_metadata.exec(false, progress));
- let loaded_sysroot = s.spawn(|| {
- sysroot.load_workspace(
- &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
- config,
- workspace_dir,
- &targets,
- toolchain.clone(),
- )),
- config.no_deps,
- progress,
- )
- });
- let cargo_env =
- s.spawn(move || cargo_config_env(cargo_toml, &config_file, &config.extra_env));
+ })
+ .expect("failed to spawn thread");
+
+ let cargo_metadata = Builder::new()
+ .name("ProjectWorkspace::cargo_metadata".to_owned())
+ .spawn_scoped(s, || fetch_metadata.exec(false, progress))
+ .expect("failed to spawn thread");
+ let loaded_sysroot = Builder::new()
+ .name("ProjectWorkspace::loaded_sysroot".to_owned())
+ .spawn_scoped(s, || {
+ sysroot.load_workspace(
+ &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
+ config,
+ workspace_dir,
+ &targets,
+ toolchain.clone(),
+ )),
+ config.no_deps,
+ progress,
+ )
+ })
+ .expect("failed to spawn thread");
+ let cargo_env = Builder::new()
+ .name("ProjectWorkspace::cargo_env".to_owned())
+ .spawn_scoped(s, move || cargo_config_env(&config_file, &config.extra_env))
+ .expect("failed to spawn thread");
thread::Result::Ok((
rustc_cfg.join()?,
target_data.join()?,
@@ -526,7 +546,7 @@ impl ProjectWorkspace {
None => Sysroot::empty(),
};
- let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
+ let config_file = CargoConfigFile::load(detached_file, &config.extra_env, &sysroot);
let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
@@ -563,8 +583,7 @@ impl ProjectWorkspace {
config.no_deps,
);
let cargo_script = fetch_metadata.exec(false, &|_| ()).ok().map(|(ws, error)| {
- let cargo_config_extra_env =
- cargo_config_env(detached_file, &config_file, &config.extra_env);
+ let cargo_config_extra_env = cargo_config_env(&config_file, &config.extra_env);
(
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
WorkspaceBuildScripts::default(),
@@ -803,10 +822,16 @@ impl ProjectWorkspace {
// [lib]
// path = "../../src/lib.rs"
// ```
+ //
+ // or
+ //
+ // ```toml
+ // [[bin]]
+ // path = "../bin_folder/main.rs"
+ // ```
let extra_targets = cargo[pkg]
.targets
.iter()
- .filter(|&&tgt| matches!(cargo[tgt].kind, TargetKind::Lib { .. }))
.filter_map(|&tgt| cargo[tgt].root.parent())
.map(|tgt| tgt.normalize().to_path_buf())
.filter(|path| !path.starts_with(&pkg_root));
@@ -822,6 +847,8 @@ impl ProjectWorkspace {
exclude.push(pkg_root.join("examples"));
exclude.push(pkg_root.join("benches"));
}
+ include.sort();
+ include.dedup();
PackageRoot { is_local, include, exclude }
})
.chain(mk_sysroot())
@@ -858,10 +885,16 @@ impl ProjectWorkspace {
// [lib]
// path = "../../src/lib.rs"
// ```
+ //
+ // or
+ //
+ // ```toml
+ // [[bin]]
+ // path = "../bin_folder/main.rs"
+ // ```
let extra_targets = cargo[pkg]
.targets
.iter()
- .filter(|&&tgt| matches!(cargo[tgt].kind, TargetKind::Lib { .. }))
.filter_map(|&tgt| cargo[tgt].root.parent())
.map(|tgt| tgt.normalize().to_path_buf())
.filter(|path| !path.starts_with(&pkg_root));
@@ -877,6 +910,8 @@ impl ProjectWorkspace {
exclude.push(pkg_root.join("examples"));
exclude.push(pkg_root.join("benches"));
}
+ include.sort();
+ include.dedup();
PackageRoot { is_local, include, exclude }
})
}))
@@ -1012,7 +1047,7 @@ impl ProjectWorkspace {
/// Returns `true` if the project workspace is [`Json`].
///
- /// [`Json`]: ProjectWorkspace::Json
+ /// [`Json`]: ProjectWorkspaceKind::Json
#[must_use]
pub fn is_json(&self) -> bool {
matches!(self.kind, ProjectWorkspaceKind::Json { .. })
@@ -1058,6 +1093,7 @@ fn project_json_to_crate_graph(
cfg,
target,
env,
+ crate_attrs,
proc_macro_dylib_path,
is_proc_macro,
repository,
@@ -1128,6 +1164,7 @@ fn project_json_to_crate_graph(
} else {
CrateOrigin::Local { repo: None, name: None }
},
+ crate_attrs.clone(),
*is_proc_macro,
match proc_macro_cwd {
Some(path) => Arc::new(path.clone()),
@@ -1194,6 +1231,7 @@ fn cargo_to_crate_graph(
load,
crate_ws_data.clone(),
);
+ let cargo_path = sysroot.tool_path(Tool::Cargo, cargo.workspace_root(), cargo.env());
let cfg_options = CfgOptions::from_iter(rustc_cfg);
@@ -1268,6 +1306,7 @@ fn cargo_to_crate_graph(
} else {
Arc::new(pkg_data.manifest.parent().to_path_buf())
},
+ &cargo_path,
);
if let TargetKind::Lib { .. } = kind {
lib_tgt = Some((crate_id, name.clone()));
@@ -1375,6 +1414,7 @@ fn cargo_to_crate_graph(
},
// FIXME: This looks incorrect but I don't think this causes problems.
crate_ws_data,
+ &cargo_path,
);
}
}
@@ -1429,6 +1469,7 @@ fn detached_file_to_crate_graph(
repo: None,
name: display_name.map(|n| n.canonical_name().to_owned()),
},
+ Vec::new(),
false,
Arc::new(detached_file.parent().to_path_buf()),
crate_ws_data,
@@ -1453,6 +1494,7 @@ fn handle_rustc_crates(
override_cfg: &CfgOverrides,
build_scripts: &WorkspaceBuildScripts,
crate_ws_data: Arc<CrateWorkspaceData>,
+ cargo_path: &Utf8Path,
) {
let mut rustc_pkg_crates = FxHashMap::default();
// The root package of the rustc-dev component is rustc_driver, so we match that
@@ -1503,6 +1545,7 @@ fn handle_rustc_crates(
} else {
Arc::new(pkg_data.manifest.parent().to_path_buf())
},
+ cargo_path,
);
pkg_to_lib_crate.insert(pkg, crate_id);
// Add dependencies on core / std / alloc for this crate
@@ -1560,11 +1603,12 @@ fn add_target_crate_root(
build_data: Option<(&BuildScriptOutput, bool)>,
cfg_options: CfgOptions,
file_id: FileId,
- cargo_name: &str,
+ cargo_crate_name: &str,
kind: TargetKind,
origin: CrateOrigin,
crate_ws_data: Arc<CrateWorkspaceData>,
proc_macro_cwd: Arc<AbsPathBuf>,
+ cargo_path: &Utf8Path,
) -> CrateBuilderId {
let edition = pkg.edition;
let potential_cfg_options = if pkg.features.is_empty() {
@@ -1591,8 +1635,8 @@ fn add_target_crate_root(
let mut env = cargo.env().clone();
inject_cargo_package_env(&mut env, pkg);
- inject_cargo_env(&mut env);
- inject_rustc_tool_env(&mut env, cargo_name, kind);
+ inject_cargo_env(&mut env, cargo_path);
+ inject_rustc_tool_env(&mut env, cargo_crate_name, kind);
if let Some(envs) = build_data.map(|(it, _)| &it.envs) {
env.extend_from_other(envs);
@@ -1600,12 +1644,13 @@ fn add_target_crate_root(
let crate_id = crate_graph.add_crate_root(
file_id,
edition,
- Some(CrateDisplayName::from_canonical_name(cargo_name)),
+ Some(CrateDisplayName::from_canonical_name(cargo_crate_name)),
Some(pkg.version.to_string()),
cfg_options,
potential_cfg_options,
env,
origin,
+ Vec::new(),
matches!(kind, TargetKind::Lib { is_proc_macro: true }),
proc_macro_cwd,
crate_ws_data,
@@ -1614,7 +1659,9 @@ fn add_target_crate_root(
let proc_macro = match build_data {
Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
match proc_macro_dylib_path {
- ProcMacroDylibPath::Path(path) => Ok((cargo_name.to_owned(), path.clone())),
+ ProcMacroDylibPath::Path(path) => {
+ Ok((cargo_crate_name.to_owned(), path.clone()))
+ }
ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt),
ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound
if has_errors =>
@@ -1787,6 +1834,7 @@ fn sysroot_to_crate_graph(
None,
Env::default(),
CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
+ Vec::new(),
false,
Arc::new(stitched[krate].root.parent().to_path_buf()),
crate_ws_data.clone(),
@@ -1867,20 +1915,19 @@ fn add_dep_inner(graph: &mut CrateGraphBuilder, from: CrateBuilderId, dep: Depen
fn sysroot_metadata_config(
config: &CargoConfig,
- current_dir: &AbsPath,
+ workspace_root: &AbsPath,
targets: &[String],
toolchain_version: Option<Version>,
) -> CargoMetadataConfig {
- // We run `cargo metadata` on sysroot with sysroot dir as a working directory, but still pass
- // the `targets` from the cargo config evaluated from the workspace's `current_dir`.
- // So, we need to *canonicalize* those *might-be-relative-paths-to-custom-target-json-files*.
- //
- // See https://github.com/rust-lang/cargo/blob/f7acf448fc127df9a77c52cc2bba027790ac4931/src/cargo/core/compiler/compile_kind.rs#L171-L192
+ // If the target is a JSON path, prefix it with workspace root directory.
+ // Since `cargo metadata` command for sysroot is run inside sysroots dir, it may fail to
+ // locate the target file if it is given as a relative path.
let targets = targets
.iter()
.map(|target| {
if target.ends_with(".json") {
- current_dir.join(target).to_string()
+ // If `target` is an absolute path, this will replace the whole path.
+ workspace_root.join(target).to_string()
} else {
target.to_owned()
}