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.rs586
1 files changed, 323 insertions, 263 deletions
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index b8c5885108..98c5a02dcd 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -2,7 +2,7 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
-use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync};
+use std::{collections::VecDeque, fmt, fs, iter, sync};
use anyhow::{format_err, Context};
use base_db::{
@@ -21,8 +21,9 @@ use triomphe::Arc;
use crate::{
build_scripts::BuildScriptOutput,
cargo_workspace::{DepKind, PackageData, RustLibSource},
- cfg_flag::CfgFlag,
- project_json::Crate,
+ cfg::{CfgFlag, CfgOverrides},
+ env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
+ project_json::{Crate, CrateArrayIdx},
rustc_cfg::{self, RustcCfgConfig},
sysroot::{SysrootCrate, SysrootMode},
target_data_layout::{self, RustcDataLayoutConfig},
@@ -30,20 +31,7 @@ use crate::{
ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};
-/// A set of cfg-overrides per crate.
-#[derive(Default, Debug, Clone, Eq, PartialEq)]
-pub struct CfgOverrides {
- /// A global set of overrides matching all crates.
- pub global: CfgDiff,
- /// A set of overrides matching specific crates.
- pub selective: FxHashMap<String, CfgDiff>,
-}
-
-impl CfgOverrides {
- pub fn len(&self) -> usize {
- self.global.len() + self.selective.values().map(|it| it.len()).sum::<usize>()
- }
-}
+pub type FileLoader<'a> = &'a mut dyn for<'b> FnMut(&'b AbsPath) -> Option<FileId>;
/// `PackageRoot` describes a package root folder.
/// Which may be an external dependency, or a member of
@@ -60,30 +48,46 @@ pub struct PackageRoot {
pub enum ProjectWorkspace {
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
Cargo {
+ /// The workspace as returned by `cargo metadata`.
cargo: CargoWorkspace,
+ /// The build script results for the workspace.
build_scripts: WorkspaceBuildScripts,
+ /// The sysroot loaded for this workspace.
sysroot: Result<Sysroot, Option<String>>,
+ /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been
+ /// disabled or was otherwise not requested.
rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>,
/// Holds cfg flags for the current target. We get those by running
/// `rustc --print cfg`.
- ///
- /// FIXME: make this a per-crate map, as, eg, build.rs might have a
- /// different target.
+ // FIXME: make this a per-crate map, as, eg, build.rs might have a
+ // different target.
rustc_cfg: Vec<CfgFlag>,
+ /// A set of cfg overrides for this workspace.
cfg_overrides: CfgOverrides,
+ /// The toolchain version used by this workspace.
toolchain: Option<Version>,
+ /// The target data layout queried for workspace.
target_layout: TargetLayoutLoadResult,
+ /// Environment variables set in the `.cargo/config` file.
cargo_config_extra_env: FxHashMap<String, String>,
},
/// Project workspace was manually specified using a `rust-project.json` file.
Json {
+ /// The loaded project json file.
project: ProjectJson,
+ /// The sysroot loaded for this workspace.
sysroot: Result<Sysroot, Option<String>>,
/// Holds cfg flags for the current target. We get those by running
/// `rustc --print cfg`.
+ // FIXME: make this a per-crate map, as, eg, build.rs might have a
+ // different target.
rustc_cfg: Vec<CfgFlag>,
+ /// The toolchain version used by this workspace.
toolchain: Option<Version>,
+ /// The target data layout queried for workspace.
target_layout: TargetLayoutLoadResult,
+ /// A set of cfg overrides for this workspace.
+ cfg_overrides: CfgOverrides,
},
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for.
@@ -95,14 +99,24 @@ pub enum ProjectWorkspace {
// //
/// Project with a set of disjoint files, not belonging to any particular workspace.
/// Backed by basic sysroot crates for basic completion and highlighting.
- DetachedFiles {
- files: Vec<AbsPathBuf>,
+ DetachedFile {
+ /// The file in question.
+ file: AbsPathBuf,
+ /// The sysroot loaded for this workspace.
sysroot: Result<Sysroot, Option<String>>,
/// Holds cfg flags for the current target. We get those by running
/// `rustc --print cfg`.
+ // FIXME: make this a per-crate map, as, eg, build.rs might have a
+ // different target.
rustc_cfg: Vec<CfgFlag>,
+ /// The toolchain version used by this workspace.
toolchain: Option<Version>,
+ /// The target data layout queried for workspace.
target_layout: TargetLayoutLoadResult,
+ /// A set of cfg overrides for the files.
+ cfg_overrides: CfgOverrides,
+ /// Is this file a cargo script file?
+ cargo_script: Option<CargoWorkspace>,
},
}
@@ -141,6 +155,7 @@ impl fmt::Debug for ProjectWorkspace {
rustc_cfg,
toolchain,
target_layout: data_layout,
+ cfg_overrides,
} => {
let mut debug_struct = f.debug_struct("Json");
debug_struct.field("n_crates", &project.n_crates());
@@ -150,22 +165,28 @@ impl fmt::Debug for ProjectWorkspace {
debug_struct
.field("n_rustc_cfg", &rustc_cfg.len())
.field("toolchain", &toolchain)
- .field("data_layout", &data_layout);
+ .field("data_layout", &data_layout)
+ .field("n_cfg_overrides", &cfg_overrides.len());
debug_struct.finish()
}
- ProjectWorkspace::DetachedFiles {
- files,
+ ProjectWorkspace::DetachedFile {
+ file,
sysroot,
rustc_cfg,
toolchain,
target_layout,
+ cfg_overrides,
+ cargo_script,
} => f
.debug_struct("DetachedFiles")
- .field("n_files", &files.len())
+ .field("file", &file)
+ .field("cargo_script", &cargo_script.is_some())
.field("sysroot", &sysroot.is_ok())
+ .field("cargo_script", &cargo_script.is_some())
.field("n_rustc_cfg", &rustc_cfg.len())
.field("toolchain", &toolchain)
.field("data_layout", &target_layout)
+ .field("n_cfg_overrides", &cfg_overrides.len())
.finish(),
}
}
@@ -219,6 +240,7 @@ impl ProjectWorkspace {
project_json,
config.target.as_deref(),
&config.extra_env,
+ &config.cfg_overrides,
)
}
ProjectManifest::CargoToml(cargo_toml) => {
@@ -360,6 +382,7 @@ impl ProjectWorkspace {
project_json: ProjectJson,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ cfg_overrides: &CfgOverrides,
) -> ProjectWorkspace {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
(Some(sysroot), Some(sysroot_src)) => {
@@ -406,57 +429,86 @@ impl ProjectWorkspace {
rustc_cfg,
toolchain,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+ cfg_overrides: cfg_overrides.clone(),
}
}
pub fn load_detached_files(
detached_files: Vec<AbsPathBuf>,
config: &CargoConfig,
- ) -> anyhow::Result<ProjectWorkspace> {
- let dir = detached_files
- .first()
- .and_then(|it| it.parent())
- .ok_or_else(|| format_err!("No detached files to load"))?;
- let sysroot = match &config.sysroot {
- 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) => 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),
- };
+ ) -> Vec<anyhow::Result<ProjectWorkspace>> {
+ detached_files
+ .into_iter()
+ .map(|detached_file| {
+ let dir = detached_file
+ .parent()
+ .ok_or_else(|| format_err!("detached file has no parent"))?;
+ let sysroot = match &config.sysroot {
+ 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) => {
+ 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),
+ };
- let sysroot_ref = sysroot.as_ref().ok();
- let toolchain =
- match get_toolchain_version(dir, sysroot_ref, Tool::Rustc, &config.extra_env, "rustc ")
- {
- Ok(it) => it,
- Err(e) => {
- tracing::error!("{e}");
- None
- }
- };
+ let sysroot_ref = sysroot.as_ref().ok();
+ let toolchain = match get_toolchain_version(
+ dir,
+ sysroot_ref,
+ Tool::Rustc,
+ &config.extra_env,
+ "rustc ",
+ ) {
+ Ok(it) => it,
+ Err(e) => {
+ tracing::error!("{e}");
+ None
+ }
+ };
- let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
- let data_layout = target_data_layout::get(
- RustcDataLayoutConfig::Rustc(sysroot_ref),
- None,
- &config.extra_env,
- );
- Ok(ProjectWorkspace::DetachedFiles {
- files: detached_files,
- sysroot,
- rustc_cfg,
- toolchain,
- target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
- })
+ let rustc_cfg =
+ rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
+ let data_layout = target_data_layout::get(
+ RustcDataLayoutConfig::Rustc(sysroot_ref),
+ None,
+ &config.extra_env,
+ );
+
+ let cargo_script = ManifestPath::try_from(detached_file.clone())
+ .ok()
+ .and_then(|file| {
+ CargoWorkspace::fetch_metadata(
+ &file,
+ file.parent(),
+ config,
+ sysroot_ref,
+ &|_| (),
+ )
+ .ok()
+ })
+ .map(CargoWorkspace::new);
+
+ Ok(ProjectWorkspace::DetachedFile {
+ file: detached_file,
+ sysroot,
+ rustc_cfg,
+ toolchain,
+ target_layout: data_layout
+ .map(Arc::from)
+ .map_err(|it| Arc::from(it.to_string())),
+ cfg_overrides: config.cfg_overrides.clone(),
+ cargo_script,
+ })
+ })
+ .collect()
}
/// Runs the build scripts for this [`ProjectWorkspace`].
@@ -466,7 +518,13 @@ impl ProjectWorkspace {
progress: &dyn Fn(String),
) -> anyhow::Result<WorkspaceBuildScripts> {
match self {
- ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => {
+ ProjectWorkspace::DetachedFile {
+ cargo_script: Some(cargo),
+ toolchain,
+ sysroot,
+ ..
+ }
+ | ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => {
WorkspaceBuildScripts::run_for_workspace(
config,
cargo,
@@ -478,9 +536,8 @@ impl ProjectWorkspace {
format!("Failed to run build scripts for {}", cargo.workspace_root())
})
}
- ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
- Ok(WorkspaceBuildScripts::default())
- }
+ ProjectWorkspace::DetachedFile { cargo_script: None, .. }
+ | ProjectWorkspace::Json { .. } => Ok(WorkspaceBuildScripts::default()),
}
}
@@ -536,11 +593,11 @@ impl ProjectWorkspace {
}
}
- pub fn workspace_definition_path(&self) -> Option<&AbsPath> {
+ pub fn workspace_definition_path(&self) -> &AbsPath {
match self {
- ProjectWorkspace::Cargo { cargo, .. } => Some(cargo.workspace_root()),
- ProjectWorkspace::Json { project, .. } => Some(project.path()),
- ProjectWorkspace::DetachedFiles { .. } => None,
+ ProjectWorkspace::Cargo { cargo, .. } => cargo.workspace_root(),
+ ProjectWorkspace::Json { project, .. } => project.path(),
+ ProjectWorkspace::DetachedFile { file, .. } => file,
}
}
@@ -548,10 +605,10 @@ impl ProjectWorkspace {
match self {
ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
| ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
- | ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
+ | ProjectWorkspace::DetachedFile { sysroot: Ok(sysroot), .. } => {
sysroot.discover_proc_macro_srv()
}
- ProjectWorkspace::DetachedFiles { .. } => {
+ ProjectWorkspace::DetachedFile { .. } => {
Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found"))
}
ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::format_err!(
@@ -611,6 +668,7 @@ impl ProjectWorkspace {
rustc_cfg: _,
toolchain: _,
target_layout: _,
+ cfg_overrides: _,
} => project
.crates()
.map(|(_, krate)| PackageRoot {
@@ -681,15 +739,50 @@ impl ProjectWorkspace {
}))
.collect()
}
- ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
- .iter()
- .map(|detached_file| PackageRoot {
+ ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => {
+ iter::once(PackageRoot {
is_local: true,
- include: vec![detached_file.clone()],
+ include: vec![file.clone()],
exclude: Vec::new(),
})
+ .chain(cargo_script.iter().flat_map(|cargo| {
+ cargo.packages().map(|pkg| {
+ let is_local = cargo[pkg].is_local;
+ let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
+
+ let mut include = vec![pkg_root.clone()];
+
+ // In case target's path is manually set in Cargo.toml to be
+ // outside the package root, add its parent as an extra include.
+ // An example of this situation would look like this:
+ //
+ // ```toml
+ // [lib]
+ // path = "../../src/lib.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));
+ include.extend(extra_targets);
+
+ let mut exclude = vec![pkg_root.join(".git")];
+ if is_local {
+ exclude.push(pkg_root.join("target"));
+ } else {
+ exclude.push(pkg_root.join("tests"));
+ exclude.push(pkg_root.join("examples"));
+ exclude.push(pkg_root.join("benches"));
+ }
+ PackageRoot { is_local, include, exclude }
+ })
+ }))
.chain(mk_sysroot(sysroot.as_ref()))
- .collect(),
+ .collect()
+ }
}
}
@@ -705,16 +798,17 @@ impl ProjectWorkspace {
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, .. } => {
+ ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => {
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
- sysroot_package_len + files.len()
+ sysroot_package_len
+ + cargo_script.as_ref().map_or(1, |cargo| cargo.packages().len())
}
}
}
pub fn to_crate_graph(
&self,
- load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ load: FileLoader<'_>,
extra_env: &FxHashMap<String, String>,
) -> (CrateGraph, ProcMacroPaths) {
let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered();
@@ -726,6 +820,7 @@ impl ProjectWorkspace {
rustc_cfg,
toolchain: _,
target_layout: _,
+ cfg_overrides,
} => (
project_json_to_crate_graph(
rustc_cfg.clone(),
@@ -733,6 +828,7 @@ impl ProjectWorkspace {
project,
sysroot.as_ref().ok(),
extra_env,
+ cfg_overrides,
),
sysroot,
),
@@ -758,24 +854,39 @@ impl ProjectWorkspace {
),
sysroot,
),
- ProjectWorkspace::DetachedFiles {
- files,
+ ProjectWorkspace::DetachedFile {
+ file,
sysroot,
rustc_cfg,
toolchain: _,
target_layout: _,
+ cfg_overrides,
+ cargo_script,
} => (
- detached_files_to_crate_graph(
- rustc_cfg.clone(),
- load,
- files,
- sysroot.as_ref().ok(),
- ),
+ if let Some(cargo) = cargo_script {
+ cargo_to_crate_graph(
+ &mut |path| load(path),
+ None,
+ cargo,
+ sysroot.as_ref().ok(),
+ rustc_cfg.clone(),
+ cfg_overrides,
+ &WorkspaceBuildScripts::default(),
+ )
+ } else {
+ detached_file_to_crate_graph(
+ rustc_cfg.clone(),
+ load,
+ file,
+ sysroot.as_ref().ok(),
+ cfg_overrides,
+ )
+ },
sysroot,
),
};
- if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Workspace(_)))
+ if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Stitched(_)))
&& crate_graph.patch_cfg_if()
{
tracing::debug!("Patched std to depend on cfg-if")
@@ -820,35 +931,56 @@ impl ProjectWorkspace {
&& cargo_config_extra_env == o_cargo_config_extra_env
}
(
- Self::Json { project, sysroot, rustc_cfg, toolchain, target_layout: _ },
+ Self::Json {
+ project,
+ sysroot,
+ rustc_cfg,
+ toolchain,
+ target_layout: _,
+ cfg_overrides,
+ },
Self::Json {
project: o_project,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
toolchain: o_toolchain,
target_layout: _,
+ cfg_overrides: o_cfg_overrides,
},
) => {
project == o_project
&& rustc_cfg == o_rustc_cfg
&& sysroot == o_sysroot
&& toolchain == o_toolchain
+ && cfg_overrides == o_cfg_overrides
}
(
- Self::DetachedFiles { files, sysroot, rustc_cfg, toolchain, target_layout },
- Self::DetachedFiles {
- files: o_files,
+ Self::DetachedFile {
+ file,
+ sysroot,
+ rustc_cfg,
+ cargo_script,
+ toolchain,
+ target_layout,
+ cfg_overrides,
+ },
+ Self::DetachedFile {
+ file: o_file,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
+ cargo_script: o_cargo_script,
toolchain: o_toolchain,
target_layout: o_target_layout,
+ cfg_overrides: o_cfg_overrides,
},
) => {
- files == o_files
+ file == o_file
&& sysroot == o_sysroot
&& rustc_cfg == o_rustc_cfg
&& toolchain == o_toolchain
&& target_layout == o_target_layout
+ && cfg_overrides == o_cfg_overrides
+ && cargo_script == o_cargo_script
}
_ => false,
}
@@ -865,10 +997,11 @@ impl ProjectWorkspace {
fn project_json_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
- load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ load: FileLoader<'_>,
project: &ProjectJson,
sysroot: Option<&Sysroot>,
extra_env: &FxHashMap<String, String>,
+ override_cfg: &CfgOverrides,
) -> (CrateGraph, ProcMacroPaths) {
let mut res = (CrateGraph::default(), ProcMacroPaths::default());
let (crate_graph, proc_macros) = &mut res;
@@ -878,12 +1011,13 @@ fn project_json_to_crate_graph(
let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned());
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
- let crates: FxHashMap<CrateId, CrateId> = project
+
+ let idx_to_crate_id: FxHashMap<CrateArrayIdx, CrateId> = project
.crates()
- .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?)))
+ .filter_map(|(idx, krate)| Some((idx, krate, load(&krate.root_module)?)))
.map(
|(
- crate_id,
+ idx,
Crate {
display_name,
edition,
@@ -907,17 +1041,22 @@ fn project_json_to_crate_graph(
None => &rustc_cfg,
};
+ let mut cfg_options = target_cfgs
+ .iter()
+ .chain(cfg.iter())
+ .chain(iter::once(&r_a_cfg_flag))
+ .cloned()
+ .collect();
+ override_cfg.apply(
+ &mut cfg_options,
+ display_name.as_ref().map(|it| it.canonical_name()).unwrap_or_default(),
+ );
let crate_graph_crate_id = crate_graph.add_crate_root(
file_id,
*edition,
display_name.clone(),
version.clone(),
- target_cfgs
- .iter()
- .chain(cfg.iter())
- .chain(iter::once(&r_a_cfg_flag))
- .cloned()
- .collect(),
+ Arc::new(cfg_options),
None,
env,
*is_proc_macro,
@@ -939,13 +1078,13 @@ fn project_json_to_crate_graph(
proc_macros.insert(crate_graph_crate_id, node);
}
}
- (crate_id, crate_graph_crate_id)
+ (idx, crate_graph_crate_id)
},
)
.collect();
- for (from, krate) in project.crates() {
- if let Some(&from) = crates.get(&from) {
+ for (from_idx, krate) in project.crates() {
+ if let Some(&from) = idx_to_crate_id.get(&from_idx) {
if let Some((public_deps, libproc_macro)) = &sysroot_deps {
public_deps.add_to_crate_graph(crate_graph, from);
if let Some(proc_macro) = libproc_macro {
@@ -954,8 +1093,8 @@ fn project_json_to_crate_graph(
}
for dep in &krate.deps {
- if let Some(&to) = crates.get(&dep.crate_id) {
- add_dep(crate_graph, from, dep.name.clone(), to)
+ if let Some(&to) = idx_to_crate_id.get(&dep.krate) {
+ add_dep(crate_graph, from, dep.name.clone(), to);
}
}
}
@@ -964,7 +1103,7 @@ fn project_json_to_crate_graph(
}
fn cargo_to_crate_graph(
- load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ load: FileLoader<'_>,
rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
cargo: &CargoWorkspace,
sysroot: Option<&Sysroot>,
@@ -981,7 +1120,7 @@ fn cargo_to_crate_graph(
None => (SysrootPublicDeps::default(), None),
};
- let cfg_options = create_cfg_options(rustc_cfg);
+ let cfg_options = CfgOptions::from_iter(rustc_cfg);
// Mapping of a package to its library target
let mut pkg_to_lib_crate = FxHashMap::default();
@@ -996,25 +1135,13 @@ fn cargo_to_crate_graph(
let cfg_options = {
let mut cfg_options = cfg_options.clone();
- // Add test cfg for local crates
if cargo[pkg].is_local {
+ // Add test cfg for local crates
cfg_options.insert_atom("test".into());
cfg_options.insert_atom("rust_analyzer".into());
}
- if !override_cfg.global.is_empty() {
- cfg_options.apply_diff(override_cfg.global.clone());
- };
- if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) {
- // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
- // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
- // working on rust-lang/rust as that's the only time it appears outside sysroot).
- //
- // A more ideal solution might be to reanalyze crates based on where the cursor is and
- // figure out the set of cfgs that would have to apply to make it active.
-
- cfg_options.apply_diff(diff.clone());
- };
+ override_cfg.apply(&mut cfg_options, &cargo[pkg].name);
cfg_options
};
@@ -1150,6 +1277,7 @@ fn cargo_to_crate_graph(
&pkg_crates,
&cfg_options,
override_cfg,
+ // FIXME: Remove this once rustc switched over to rust-project.json
if rustc_workspace.workspace_root() == cargo.workspace_root() {
// the rustc workspace does not use the installed toolchain's proc-macro server
// so we need to make sure we don't use the pre compiled proc-macros there either
@@ -1163,11 +1291,12 @@ fn cargo_to_crate_graph(
res
}
-fn detached_files_to_crate_graph(
+fn detached_file_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
- load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
- detached_files: &[AbsPathBuf],
+ load: FileLoader<'_>,
+ detached_file: &AbsPathBuf,
sysroot: Option<&Sysroot>,
+ override_cfg: &CfgOverrides,
) -> (CrateGraph, ProcMacroPaths) {
let _p = tracing::span!(tracing::Level::INFO, "detached_files_to_crate_graph").entered();
let mut crate_graph = CrateGraph::default();
@@ -1176,37 +1305,38 @@ fn detached_files_to_crate_graph(
None => (SysrootPublicDeps::default(), None),
};
- let mut cfg_options = create_cfg_options(rustc_cfg);
+ let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
+ cfg_options.insert_atom("test".into());
cfg_options.insert_atom("rust_analyzer".into());
+ override_cfg.apply(&mut cfg_options, "");
+ let cfg_options = Arc::new(cfg_options);
+
+ let file_id = match load(detached_file) {
+ Some(file_id) => file_id,
+ None => {
+ tracing::error!("Failed to load detached file {:?}", detached_file);
+ return (crate_graph, FxHashMap::default());
+ }
+ };
+ let display_name = detached_file
+ .file_stem()
+ .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
+ let detached_file_crate = crate_graph.add_crate_root(
+ file_id,
+ Edition::CURRENT,
+ display_name.clone(),
+ None,
+ cfg_options.clone(),
+ None,
+ Env::default(),
+ false,
+ CrateOrigin::Local {
+ repo: None,
+ name: display_name.map(|n| n.canonical_name().to_owned()),
+ },
+ );
- for detached_file in detached_files {
- let file_id = match load(detached_file) {
- Some(file_id) => file_id,
- None => {
- tracing::error!("Failed to load detached file {:?}", detached_file);
- continue;
- }
- };
- let display_name = detached_file
- .file_stem()
- .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
- let detached_file_crate = crate_graph.add_crate_root(
- file_id,
- Edition::CURRENT,
- display_name.clone(),
- None,
- cfg_options.clone(),
- None,
- Env::default(),
- false,
- CrateOrigin::Local {
- repo: None,
- name: display_name.map(|n| n.canonical_name().to_owned()),
- },
- );
-
- public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
- }
+ public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
(crate_graph, FxHashMap::default())
}
@@ -1214,7 +1344,7 @@ fn handle_rustc_crates(
crate_graph: &mut CrateGraph,
proc_macros: &mut ProcMacroPaths,
pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
- load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ load: FileLoader<'_>,
rustc_workspace: &CargoWorkspace,
cargo: &CargoWorkspace,
public_deps: &SysrootPublicDeps,
@@ -1246,20 +1376,7 @@ fn handle_rustc_crates(
}
let mut cfg_options = cfg_options.clone();
-
- if !override_cfg.global.is_empty() {
- cfg_options.apply_diff(override_cfg.global.clone());
- };
- if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) {
- // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
- // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
- // working on rust-lang/rust as that's the only time it appears outside sysroot).
- //
- // A more ideal solution might be to reanalyze crates based on where the cursor is and
- // figure out the set of cfgs that would have to apply to make it active.
-
- cfg_options.apply_diff(diff.clone());
- };
+ override_cfg.apply(&mut cfg_options, &rustc_workspace[pkg].name);
for &tgt in rustc_workspace[pkg].targets.iter() {
let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else {
@@ -1361,26 +1478,22 @@ fn add_target_crate_root(
};
let mut env = Env::default();
- inject_cargo_env(pkg, &mut env);
- if let Ok(cname) = String::from_str(cargo_name) {
- // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark.
- env.set("CARGO_CRATE_NAME", cname.replace('-', "_"));
- }
+ inject_cargo_package_env(&mut env, pkg);
+ inject_cargo_env(&mut env);
+ inject_rustc_tool_env(&mut env, cargo_name, kind);
if let Some(envs) = build_data.map(|it| &it.envs) {
for (k, v) in envs {
env.set(k, v.clone());
}
}
-
- let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_owned());
let crate_id = crate_graph.add_crate_root(
file_id,
edition,
- Some(display_name),
+ Some(CrateDisplayName::from_canonical_name(cargo_name.to_owned())),
Some(pkg.version.to_string()),
- cfg_options,
- potential_cfg_options,
+ Arc::new(cfg_options),
+ potential_cfg_options.map(Arc::new),
env,
matches!(kind, TargetKind::Lib { is_proc_macro: true }),
origin,
@@ -1416,7 +1529,7 @@ fn sysroot_to_crate_graph(
crate_graph: &mut CrateGraph,
sysroot: &Sysroot,
rustc_cfg: Vec<CfgFlag>,
- load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ load: FileLoader<'_>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = tracing::span!(tracing::Level::INFO, "sysroot_to_crate_graph").entered();
match sysroot.mode() {
@@ -1427,7 +1540,17 @@ fn sysroot_to_crate_graph(
cargo,
None,
rustc_cfg,
- &CfgOverrides::default(),
+ &CfgOverrides {
+ global: CfgDiff::new(
+ vec![
+ CfgAtom::Flag("debug_assertions".into()),
+ CfgAtom::Flag("miri".into()),
+ ],
+ vec![],
+ )
+ .unwrap(),
+ ..Default::default()
+ },
&WorkspaceBuildScripts::default(),
);
@@ -1436,7 +1559,7 @@ fn sysroot_to_crate_graph(
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());
+ Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
// patch the origin
if c.origin.is_local() {
let lang_crate = LangCrateOrigin::from(
@@ -1485,13 +1608,18 @@ fn sysroot_to_crate_graph(
(SysrootPublicDeps { deps: pub_deps }, libproc_macro)
}
SysrootMode::Stitched(stitched) => {
- let cfg_options = create_cfg_options(rustc_cfg);
+ let cfg_options = Arc::new({
+ let mut cfg_options = CfgOptions::default();
+ cfg_options.extend(rustc_cfg);
+ cfg_options.insert_atom("debug_assertions".into());
+ cfg_options.insert_atom("miri".into());
+ cfg_options
+ });
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched
.crates()
.filter_map(|krate| {
let file_id = load(&stitched[krate].root)?;
- let env = Env::default();
let display_name =
CrateDisplayName::from_canonical_name(stitched[krate].name.clone());
let crate_id = crate_graph.add_crate_root(
@@ -1501,7 +1629,7 @@ fn sysroot_to_crate_graph(
None,
cfg_options.clone(),
None,
- env,
+ Env::default(),
false,
CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
);
@@ -1559,71 +1687,3 @@ fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
tracing::error!("{}", err)
}
}
-
-/// Recreates the compile-time environment variables that Cargo sets.
-///
-/// Should be synced with
-/// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
-///
-/// FIXME: ask Cargo to provide this data instead of re-deriving.
-fn inject_cargo_env(package: &PackageData, env: &mut Env) {
- // FIXME: Missing variables:
- // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
-
- let manifest_dir = package.manifest.parent();
- env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str().to_owned());
-
- // Not always right, but works for common cases.
- env.set("CARGO", "cargo".into());
-
- env.set("CARGO_PKG_VERSION", package.version.to_string());
- env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
- env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
- env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
- env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
-
- env.set("CARGO_PKG_AUTHORS", String::new());
-
- env.set("CARGO_PKG_NAME", package.name.clone());
- // FIXME: This isn't really correct (a package can have many crates with different names), but
- // it's better than leaving the variable unset.
- env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
- env.set("CARGO_PKG_DESCRIPTION", String::new());
- env.set("CARGO_PKG_HOMEPAGE", String::new());
- env.set("CARGO_PKG_REPOSITORY", String::new());
- env.set("CARGO_PKG_LICENSE", String::new());
-
- env.set("CARGO_PKG_LICENSE_FILE", String::new());
-}
-
-fn create_cfg_options(rustc_cfg: Vec<CfgFlag>) -> CfgOptions {
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg);
- cfg_options.insert_atom("debug_assertions".into());
- cfg_options
-}
-
-fn cargo_config_env(
- cargo_toml: &ManifestPath,
- extra_env: &FxHashMap<String, String>,
- sysroot: Option<&Sysroot>,
-) -> FxHashMap<String, String> {
- let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo);
- cargo_config.envs(extra_env);
- cargo_config
- .current_dir(cargo_toml.parent())
- .args(["-Z", "unstable-options", "config", "get", "env"])
- .env("RUSTC_BOOTSTRAP", "1");
- // if successful we receive `env.key.value = "value" per entry
- tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
- utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default()
-}
-
-fn parse_output_cargo_config_env(stdout: String) -> FxHashMap<String, String> {
- stdout
- .lines()
- .filter_map(|l| l.strip_prefix("env."))
- .filter_map(|l| l.split_once(".value = "))
- .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned()))
- .collect()
-}