1 //! Handles lowering of build-system specific workspace information (`cargo
2 //! metadata` or `rust-project.json`) into representation stored in the salsa
3 //! database -- `CrateGraph`.
5 use std::{collections::VecDeque, fmt, fs, process::Command};
7 use anyhow::{format_err, Context, Result};
9 CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
10 FileId, LangCrateOrigin, ProcMacroLoadResult,
12 use cfg::{CfgDiff, CfgOptions};
13 use paths::{AbsPath, AbsPathBuf};
14 use rustc_hash::{FxHashMap, FxHashSet};
18 build_scripts::BuildScriptOutput,
19 cargo_workspace::{DepKind, PackageData, RustcSource},
22 sysroot::SysrootCrate,
23 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
24 TargetKind, WorkspaceBuildScripts,
27 /// A set of cfg-overrides per crate.
29 /// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
30 /// without having to first obtain a list of all crates.
31 #[derive(Debug, Clone, Eq, PartialEq)]
32 pub enum CfgOverrides {
33 /// A single global set of overrides matching all crates.
35 /// A set of overrides matching specific crates.
36 Selective(FxHashMap<String, CfgDiff>),
39 impl Default for CfgOverrides {
40 fn default() -> Self {
41 Self::Selective(FxHashMap::default())
46 pub fn len(&self) -> usize {
48 CfgOverrides::Wildcard(_) => 1,
49 CfgOverrides::Selective(hash_map) => hash_map.len(),
54 /// `PackageRoot` describes a package root folder.
55 /// Which may be an external dependency, or a member of
56 /// the current workspace.
57 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
58 pub struct PackageRoot {
59 /// Is from the local filesystem and may be edited
61 pub include: Vec<AbsPathBuf>,
62 pub exclude: Vec<AbsPathBuf>,
65 #[derive(Clone, Eq, PartialEq)]
66 pub enum ProjectWorkspace {
67 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
69 cargo: CargoWorkspace,
70 build_scripts: WorkspaceBuildScripts,
71 sysroot: Option<Sysroot>,
72 rustc: Option<CargoWorkspace>,
73 /// Holds cfg flags for the current target. We get those by running
74 /// `rustc --print cfg`.
76 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
78 rustc_cfg: Vec<CfgFlag>,
79 cfg_overrides: CfgOverrides,
81 /// Project workspace was manually specified using a `rust-project.json` file.
82 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
84 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
85 // That's not the end user experience we should strive for.
86 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
87 // That needs some changes on the salsa-level though.
88 // In particular, we should split the unified CrateGraph (which currently has maximal durability) into proper crate graph, and a set of ad hoc roots (with minimal durability).
89 // Then, we need to hide the graph behind the queries such that most queries look only at the proper crate graph, and fall back to ad hoc roots only if there's no results.
90 // After this, we should be able to tweak the logic in reload.rs to add newly opened files, which don't belong to any existing crates, to the set of the detached files.
92 /// Project with a set of disjoint files, not belonging to any particular workspace.
93 /// Backed by basic sysroot crates for basic completion and highlighting.
94 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
97 impl fmt::Debug for ProjectWorkspace {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 // Make sure this isn't too verbose.
101 ProjectWorkspace::Cargo {
109 .debug_struct("Cargo")
110 .field("root", &cargo.workspace_root().file_name())
111 .field("n_packages", &cargo.packages().len())
112 .field("sysroot", &sysroot.is_some())
114 "n_rustc_compiler_crates",
115 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
117 .field("n_rustc_cfg", &rustc_cfg.len())
118 .field("n_cfg_overrides", &cfg_overrides.len())
120 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
121 let mut debug_struct = f.debug_struct("Json");
122 debug_struct.field("n_crates", &project.n_crates());
123 if let Some(sysroot) = sysroot {
124 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
126 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
127 debug_struct.finish()
129 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
130 .debug_struct("DetachedFiles")
131 .field("n_files", &files.len())
132 .field("n_sysroot_crates", &sysroot.crates().len())
133 .field("n_rustc_cfg", &rustc_cfg.len())
139 impl ProjectWorkspace {
141 manifest: ProjectManifest,
142 config: &CargoConfig,
143 progress: &dyn Fn(String),
144 ) -> Result<ProjectWorkspace> {
145 let res = match manifest {
146 ProjectManifest::ProjectJson(project_json) => {
147 let file = fs::read_to_string(&project_json).with_context(|| {
148 format!("Failed to read json file {}", project_json.display())
150 let data = serde_json::from_str(&file).with_context(|| {
151 format!("Failed to deserialize json file {}", project_json.display())
153 let project_location = project_json.parent().to_path_buf();
154 let project_json = ProjectJson::new(&project_location, data);
155 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
157 ProjectManifest::CargoToml(cargo_toml) => {
158 let cargo_version = utf8_stdout({
159 let mut cmd = Command::new(toolchain::cargo());
160 cmd.arg("--version");
164 let meta = CargoWorkspace::fetch_metadata(
172 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
173 cargo_toml.display(),
177 let cargo = CargoWorkspace::new(meta);
179 let sysroot = if config.no_sysroot {
182 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
184 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
190 let rustc_dir = match &config.rustc_source {
191 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
192 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
196 let rustc = match rustc_dir {
197 Some(rustc_dir) => Some({
198 let meta = CargoWorkspace::fetch_metadata(
205 "Failed to read Cargo metadata for Rust sources".to_string()
207 CargoWorkspace::new(meta)
212 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
214 let cfg_overrides = config.cfg_overrides();
215 ProjectWorkspace::Cargo {
217 build_scripts: WorkspaceBuildScripts::default(),
230 project_json: ProjectJson,
231 target: Option<&str>,
232 ) -> Result<ProjectWorkspace> {
233 let sysroot = match &project_json.sysroot_src {
234 Some(path) => Some(Sysroot::load(path.clone())?),
237 let rustc_cfg = rustc_cfg::get(None, target);
238 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
241 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
242 let sysroot = Sysroot::discover(
245 .and_then(|it| it.parent())
246 .ok_or_else(|| format_err!("No detached files to load"))?,
248 let rustc_cfg = rustc_cfg::get(None, None);
249 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
252 pub fn run_build_scripts(
254 config: &CargoConfig,
255 progress: &dyn Fn(String),
256 ) -> Result<WorkspaceBuildScripts> {
258 ProjectWorkspace::Cargo { cargo, .. } => {
259 WorkspaceBuildScripts::run(config, cargo, progress).with_context(|| {
260 format!("Failed to run build scripts for {}", &cargo.workspace_root().display())
263 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
264 Ok(WorkspaceBuildScripts::default())
269 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
271 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
273 always!(bs == WorkspaceBuildScripts::default());
278 /// Returns the roots for the current `ProjectWorkspace`
279 /// The return type contains the path and whether or not
280 /// the root is a member of the current workspace
281 pub fn to_roots(&self) -> Vec<PackageRoot> {
283 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
285 .map(|(_, krate)| PackageRoot {
286 is_local: krate.is_workspace_member,
287 include: krate.include.clone(),
288 exclude: krate.exclude.clone(),
290 .collect::<FxHashSet<_>>()
292 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
293 sysroot.crates().map(move |krate| PackageRoot {
295 include: vec![sysroot[krate].root.parent().to_path_buf()],
299 .collect::<Vec<_>>(),
300 ProjectWorkspace::Cargo {
311 let is_local = cargo[pkg].is_local;
312 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
314 let mut include = vec![pkg_root.clone()];
316 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
317 include.extend(out_dir);
319 // In case target's path is manually set in Cargo.toml to be
320 // outside the package root, add its parent as an extra include.
321 // An example of this situation would look like this:
325 // path = "../../src/lib.rs"
327 let extra_targets = cargo[pkg]
330 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
331 .filter_map(|&tgt| cargo[tgt].root.parent())
332 .map(|tgt| tgt.normalize().to_path_buf())
333 .filter(|path| !path.starts_with(&pkg_root));
334 include.extend(extra_targets);
336 let mut exclude = vec![pkg_root.join(".git")];
338 exclude.push(pkg_root.join("target"));
340 exclude.push(pkg_root.join("tests"));
341 exclude.push(pkg_root.join("examples"));
342 exclude.push(pkg_root.join("benches"));
344 PackageRoot { is_local, include, exclude }
346 .chain(sysroot.iter().map(|sysroot| PackageRoot {
348 include: vec![sysroot.root().to_path_buf()],
351 .chain(rustc.iter().flat_map(|rustc| {
352 rustc.packages().map(move |krate| PackageRoot {
354 include: vec![rustc[krate].manifest.parent().to_path_buf()],
360 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
362 .map(|detached_file| PackageRoot {
364 include: vec![detached_file.clone()],
367 .chain(sysroot.crates().map(|krate| PackageRoot {
369 include: vec![sysroot[krate].root.parent().to_path_buf()],
376 pub fn n_packages(&self) -> usize {
378 ProjectWorkspace::Json { project, .. } => project.n_crates(),
379 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
380 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
381 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
382 cargo.packages().len() + sysroot_package_len + rustc_package_len
384 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
385 sysroot.crates().len() + files.len()
390 pub fn to_crate_graph(
392 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
393 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
395 let _p = profile::span("ProjectWorkspace::to_crate_graph");
397 let mut crate_graph = match self {
398 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
405 ProjectWorkspace::Cargo {
412 } => cargo_to_crate_graph(
422 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
423 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
426 if crate_graph.patch_cfg_if() {
427 tracing::debug!("Patched std to depend on cfg-if")
429 tracing::debug!("Did not patch std to depend on cfg-if")
435 fn project_json_to_crate_graph(
436 rustc_cfg: Vec<CfgFlag>,
437 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
438 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
439 project: &ProjectJson,
440 sysroot: &Option<Sysroot>,
442 let mut crate_graph = CrateGraph::default();
443 let sysroot_deps = sysroot
445 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
447 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
448 let crates: FxHashMap<CrateId, CrateId> = project
450 .filter_map(|(crate_id, krate)| {
451 let file_path = &krate.root_module;
452 let file_id = load(file_path)?;
453 Some((crate_id, krate, file_id))
455 .map(|(crate_id, krate, file_id)| {
456 let env = krate.env.clone().into_iter().collect();
457 let proc_macro = match krate.proc_macro_dylib_path.clone() {
458 Some(it) => load_proc_macro(
459 krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
462 None => Err("no proc macro dylib present".into()),
465 let target_cfgs = match krate.target.as_deref() {
467 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
472 let mut cfg_options = CfgOptions::default();
473 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
476 crate_graph.add_crate_root(
479 krate.display_name.clone(),
480 krate.version.clone(),
486 if krate.display_name.is_some() {
487 CrateOrigin::CratesIo { repo: krate.repository.clone() }
489 CrateOrigin::CratesIo { repo: None }
496 for (from, krate) in project.crates() {
497 if let Some(&from) = crates.get(&from) {
498 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
499 public_deps.add(from, &mut crate_graph);
500 if krate.is_proc_macro {
501 if let Some(proc_macro) = libproc_macro {
505 CrateName::new("proc_macro").unwrap(),
512 for dep in &krate.deps {
513 if let Some(&to) = crates.get(&dep.crate_id) {
514 add_dep(&mut crate_graph, from, dep.name.clone(), to)
522 fn cargo_to_crate_graph(
523 rustc_cfg: Vec<CfgFlag>,
524 override_cfg: &CfgOverrides,
525 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
526 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
527 cargo: &CargoWorkspace,
528 build_scripts: &WorkspaceBuildScripts,
529 sysroot: Option<&Sysroot>,
530 rustc: &Option<CargoWorkspace>,
532 let _p = profile::span("cargo_to_crate_graph");
533 let mut crate_graph = CrateGraph::default();
534 let (public_deps, libproc_macro) = match sysroot {
535 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
536 None => (SysrootPublicDeps::default(), None),
539 let mut cfg_options = CfgOptions::default();
540 cfg_options.extend(rustc_cfg);
542 let mut pkg_to_lib_crate = FxHashMap::default();
544 cfg_options.insert_atom("debug_assertions".into());
546 let mut pkg_crates = FxHashMap::default();
547 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
548 let mut has_private = false;
549 // Next, create crates for each package, target pair
550 for pkg in cargo.packages() {
551 let mut cfg_options = cfg_options.clone();
553 let overrides = match override_cfg {
554 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
555 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
558 // Add test cfg for local crates
559 if cargo[pkg].is_local {
560 cfg_options.insert_atom("test".into());
563 if let Some(overrides) = overrides {
564 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
565 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
566 // working on rust-lang/rust as that's the only time it appears outside sysroot).
568 // A more ideal solution might be to reanalyze crates based on where the cursor is and
569 // figure out the set of cfgs that would have to apply to make it active.
571 cfg_options.apply_diff(overrides.clone());
574 has_private |= cargo[pkg].metadata.rustc_private;
575 let mut lib_tgt = None;
576 for &tgt in cargo[pkg].targets.iter() {
577 if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
578 // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
579 // add any targets except the library target, since those will not work correctly if
580 // they use dev-dependencies.
581 // In fact, they can break quite badly if multiple client workspaces get merged:
582 // https://github.com/rust-lang/rust-analyzer/issues/11300
586 if let Some(file_id) = load(&cargo[tgt].root) {
587 let crate_id = add_target_crate_root(
590 build_scripts.get_output(pkg),
592 &mut |path| load_proc_macro(&cargo[tgt].name, path),
595 cargo[tgt].is_proc_macro,
597 if cargo[tgt].kind == TargetKind::Lib {
598 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
599 pkg_to_lib_crate.insert(pkg, crate_id);
601 if let Some(proc_macro) = libproc_macro {
602 add_dep_with_prelude(
605 CrateName::new("proc_macro").unwrap(),
607 cargo[tgt].is_proc_macro,
611 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
615 // Set deps to the core, std and to the lib target of the current package
616 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
617 // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
618 public_deps.add(*from, &mut crate_graph);
620 if let Some((to, name)) = lib_tgt.clone() {
621 if to != *from && *kind != TargetKind::BuildScript {
622 // (build script can not depend on its library target)
624 // For root projects with dashes in their name,
625 // cargo metadata does not do any normalization,
626 // so we do it ourselves currently
627 let name = CrateName::normalize_dashes(&name);
628 add_dep(&mut crate_graph, *from, name, to);
634 // Now add a dep edge from all targets of upstream to the lib
635 // target of downstream.
636 for pkg in cargo.packages() {
637 for dep in cargo[pkg].dependencies.iter() {
638 let name = CrateName::new(&dep.name).unwrap();
639 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
640 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
641 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
642 // Only build scripts may depend on build dependencies.
645 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
646 // Build scripts may only depend on build dependencies.
650 add_dep(&mut crate_graph, *from, name.clone(), to)
657 // If the user provided a path to rustc sources, we add all the rustc_private crates
658 // and create dependencies on them for the crates which opt-in to that
659 if let Some(rustc_workspace) = rustc {
667 &mut pkg_to_lib_crate,
678 fn detached_files_to_crate_graph(
679 rustc_cfg: Vec<CfgFlag>,
680 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
681 detached_files: &[AbsPathBuf],
684 let _p = profile::span("detached_files_to_crate_graph");
685 let mut crate_graph = CrateGraph::default();
686 let (public_deps, _libproc_macro) =
687 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
689 let mut cfg_options = CfgOptions::default();
690 cfg_options.extend(rustc_cfg);
692 for detached_file in detached_files {
693 let file_id = match load(detached_file) {
694 Some(file_id) => file_id,
696 tracing::error!("Failed to load detached file {:?}", detached_file);
700 let display_name = detached_file
702 .and_then(|os_str| os_str.to_str())
703 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
704 let detached_file_crate = crate_graph.add_crate_root(
714 CrateOrigin::CratesIo { repo: None },
717 public_deps.add(detached_file_crate, &mut crate_graph);
722 fn handle_rustc_crates(
723 rustc_workspace: &CargoWorkspace,
724 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
725 crate_graph: &mut CrateGraph,
726 cfg_options: &CfgOptions,
727 override_cfg: &CfgOverrides,
728 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
729 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
730 public_deps: &SysrootPublicDeps,
731 cargo: &CargoWorkspace,
732 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
733 build_scripts: &WorkspaceBuildScripts,
735 let mut rustc_pkg_crates = FxHashMap::default();
736 // The root package of the rustc-dev component is rustc_driver, so we match that
738 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
739 // The rustc workspace might be incomplete (such as if rustc-dev is not
740 // installed for the current toolchain) and `rustc_source` is set to discover.
741 if let Some(root_pkg) = root_pkg {
742 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
743 let mut queue = VecDeque::new();
744 queue.push_back(root_pkg);
745 while let Some(pkg) = queue.pop_front() {
746 // Don't duplicate packages if they are dependended on a diamond pattern
747 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
748 // which is not ideal
749 if rustc_pkg_crates.contains_key(&pkg) {
752 for dep in &rustc_workspace[pkg].dependencies {
753 queue.push_back(dep.pkg);
756 let mut cfg_options = cfg_options.clone();
758 let overrides = match override_cfg {
759 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
760 CfgOverrides::Selective(cfg_overrides) => {
761 cfg_overrides.get(&rustc_workspace[pkg].name)
765 if let Some(overrides) = overrides {
766 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
767 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
768 // working on rust-lang/rust as that's the only time it appears outside sysroot).
770 // A more ideal solution might be to reanalyze crates based on where the cursor is and
771 // figure out the set of cfgs that would have to apply to make it active.
773 cfg_options.apply_diff(overrides.clone());
776 for &tgt in rustc_workspace[pkg].targets.iter() {
777 if rustc_workspace[tgt].kind != TargetKind::Lib {
780 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
781 let crate_id = add_target_crate_root(
783 &rustc_workspace[pkg],
784 build_scripts.get_output(pkg),
786 &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
788 &rustc_workspace[tgt].name,
789 rustc_workspace[tgt].is_proc_macro,
791 pkg_to_lib_crate.insert(pkg, crate_id);
792 // Add dependencies on core / std / alloc for this crate
793 public_deps.add(crate_id, crate_graph);
794 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
799 // Now add a dep edge from all targets of upstream to the lib
800 // target of downstream.
801 for pkg in rustc_pkg_crates.keys().copied() {
802 for dep in rustc_workspace[pkg].dependencies.iter() {
803 let name = CrateName::new(&dep.name).unwrap();
804 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
805 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
806 add_dep(crate_graph, from, name.clone(), to);
811 // Add a dependency on the rustc_private crates for all targets of each package
813 for dep in rustc_workspace.packages() {
814 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
816 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
817 for pkg in cargo.packages() {
818 let package = &cargo[pkg];
819 if !package.metadata.rustc_private {
822 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
823 // Avoid creating duplicate dependencies
824 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
825 // `rust_analyzer` thinks that it should use the one from the `rustc_source`
826 // instead of the one from `crates.io`
827 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
828 add_dep(crate_graph, *from, name.clone(), to);
836 fn add_target_crate_root(
837 crate_graph: &mut CrateGraph,
839 build_data: Option<&BuildScriptOutput>,
840 cfg_options: CfgOptions,
841 load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
846 let edition = pkg.edition;
847 let mut potential_cfg_options = cfg_options.clone();
848 potential_cfg_options.extend(
851 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
854 let mut opts = cfg_options;
855 for feature in pkg.active_features.iter() {
856 opts.insert_key_value("feature".into(), feature.into());
858 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
859 opts.extend(cfgs.iter().cloned());
864 let mut env = Env::default();
865 inject_cargo_env(pkg, &mut env);
867 if let Some(envs) = build_data.map(|it| &it.envs) {
869 env.set(k, v.clone());
873 let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
874 Some(Some(it)) => load_proc_macro(it),
875 Some(None) => Err("no proc macro dylib present".into()),
876 None => Err("crate has not (yet) been built".into()),
879 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
880 crate_graph.add_crate_root(
884 Some(pkg.version.to_string()),
886 potential_cfg_options,
890 CrateOrigin::CratesIo { repo: pkg.repository.clone() },
895 struct SysrootPublicDeps {
896 deps: Vec<(CrateName, CrateId, bool)>,
899 impl SysrootPublicDeps {
900 /// Makes `from` depend on the public sysroot crates.
901 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
902 for (name, krate, prelude) in &self.deps {
903 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
908 fn sysroot_to_crate_graph(
909 crate_graph: &mut CrateGraph,
911 rustc_cfg: Vec<CfgFlag>,
912 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
913 ) -> (SysrootPublicDeps, Option<CrateId>) {
914 let _p = profile::span("sysroot_to_crate_graph");
915 let mut cfg_options = CfgOptions::default();
916 cfg_options.extend(rustc_cfg);
917 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
919 .filter_map(|krate| {
920 let file_id = load(&sysroot[krate].root)?;
922 let env = Env::default();
923 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
924 let crate_id = crate_graph.add_crate_root(
932 Err("no proc macro loaded for sysroot crate".into()),
934 CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
936 Some((krate, crate_id))
940 for from in sysroot.crates() {
941 for &to in sysroot[from].deps.iter() {
942 let name = CrateName::new(&sysroot[to].name).unwrap();
943 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
944 add_dep(crate_graph, from, name, to);
949 let public_deps = SysrootPublicDeps {
952 .map(|(name, idx, prelude)| {
953 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
955 .collect::<Vec<_>>(),
958 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
959 (public_deps, libproc_macro)
962 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
963 add_dep_inner(graph, from, Dependency::new(name, to))
966 fn add_dep_with_prelude(
967 graph: &mut CrateGraph,
973 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
976 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
977 if let Err(err) = graph.add_dep(from, dep) {
978 tracing::error!("{}", err)
982 /// Recreates the compile-time environment variables that Cargo sets.
984 /// Should be synced with
985 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
987 /// FIXME: ask Cargo to provide this data instead of re-deriving.
988 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
989 // FIXME: Missing variables:
990 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
992 let manifest_dir = package.manifest.parent();
993 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
995 // Not always right, but works for common cases.
996 env.set("CARGO", "cargo".into());
998 env.set("CARGO_PKG_VERSION", package.version.to_string());
999 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
1000 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
1001 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
1002 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
1004 env.set("CARGO_PKG_AUTHORS", String::new());
1006 env.set("CARGO_PKG_NAME", package.name.clone());
1007 // FIXME: This isn't really correct (a package can have many crates with different names), but
1008 // it's better than leaving the variable unset.
1009 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
1010 env.set("CARGO_PKG_DESCRIPTION", String::new());
1011 env.set("CARGO_PKG_HOMEPAGE", String::new());
1012 env.set("CARGO_PKG_REPOSITORY", String::new());
1013 env.set("CARGO_PKG_LICENSE", String::new());
1015 env.set("CARGO_PKG_LICENSE_FILE", String::new());