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.rs | 173 |
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() } |