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, convert::TryFrom, fmt, fs, process::Command};
7 use anyhow::{format_err, Context, Result};
8 use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9 use cfg::{CfgDiff, CfgOptions};
10 use paths::{AbsPath, AbsPathBuf};
11 use proc_macro_api::ProcMacroClient;
12 use rustc_hash::{FxHashMap, FxHashSet};
16 build_scripts::BuildScriptOutput,
17 cargo_workspace::{DepKind, PackageData, RustcSource},
20 sysroot::SysrootCrate,
21 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
22 TargetKind, WorkspaceBuildScripts,
25 pub type CfgOverrides = FxHashMap<String, CfgDiff>;
27 /// `PackageRoot` describes a package root folder.
28 /// Which may be an external dependency, or a member of
29 /// the current workspace.
30 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
31 pub struct PackageRoot {
32 /// Is a member of the current workspace
34 pub include: Vec<AbsPathBuf>,
35 pub exclude: Vec<AbsPathBuf>,
38 #[derive(Clone, Eq, PartialEq)]
39 pub enum ProjectWorkspace {
40 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
42 cargo: CargoWorkspace,
43 build_scripts: WorkspaceBuildScripts,
45 rustc: Option<CargoWorkspace>,
46 /// Holds cfg flags for the current target. We get those by running
47 /// `rustc --print cfg`.
49 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
51 rustc_cfg: Vec<CfgFlag>,
52 cfg_overrides: CfgOverrides,
54 /// Project workspace was manually specified using a `rust-project.json` file.
55 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
57 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
58 // That's not the end user experience we should strive for.
59 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
60 // That needs some changes on the salsa-level though.
61 // 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).
62 // 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.
63 // 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.
65 /// Project with a set of disjoint files, not belonging to any particular workspace.
66 /// Backed by basic sysroot crates for basic completion and highlighting.
67 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
70 impl fmt::Debug for ProjectWorkspace {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 // Make sure this isn't too verbose.
74 ProjectWorkspace::Cargo {
82 .debug_struct("Cargo")
83 .field("root", &cargo.workspace_root().file_name())
84 .field("n_packages", &cargo.packages().len())
85 .field("n_sysroot_crates", &sysroot.crates().len())
87 "n_rustc_compiler_crates",
88 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
90 .field("n_rustc_cfg", &rustc_cfg.len())
91 .field("n_cfg_overrides", &cfg_overrides.len())
93 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
94 let mut debug_struct = f.debug_struct("Json");
95 debug_struct.field("n_crates", &project.n_crates());
96 if let Some(sysroot) = sysroot {
97 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
99 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
100 debug_struct.finish()
102 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
103 .debug_struct("DetachedFiles")
104 .field("n_files", &files.len())
105 .field("n_sysroot_crates", &sysroot.crates().len())
106 .field("n_rustc_cfg", &rustc_cfg.len())
112 impl ProjectWorkspace {
114 manifest: ProjectManifest,
115 config: &CargoConfig,
116 progress: &dyn Fn(String),
117 ) -> Result<ProjectWorkspace> {
118 let res = match manifest {
119 ProjectManifest::ProjectJson(project_json) => {
120 let file = fs::read_to_string(&project_json).with_context(|| {
121 format!("Failed to read json file {}", project_json.display())
123 let data = serde_json::from_str(&file).with_context(|| {
124 format!("Failed to deserialize json file {}", project_json.display())
126 let project_location = project_json.parent().to_path_buf();
127 let project_json = ProjectJson::new(&project_location, data);
128 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
130 ProjectManifest::CargoToml(cargo_toml) => {
131 let cargo_version = utf8_stdout({
132 let mut cmd = Command::new(toolchain::cargo());
133 cmd.arg("--version");
137 let meta = CargoWorkspace::fetch_metadata(&cargo_toml, config, progress)
140 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
141 cargo_toml.display(),
145 let cargo = CargoWorkspace::new(meta);
147 let sysroot = if config.no_sysroot {
150 Sysroot::discover(cargo_toml.parent()).with_context(|| {
152 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
158 let rustc_dir = match &config.rustc_source {
159 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
160 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
164 let rustc = match rustc_dir {
165 Some(rustc_dir) => Some({
166 let meta = CargoWorkspace::fetch_metadata(&rustc_dir, config, progress)
168 format!("Failed to read Cargo metadata for Rust sources")
170 CargoWorkspace::new(meta)
175 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
177 let cfg_overrides = config.cfg_overrides();
178 ProjectWorkspace::Cargo {
180 build_scripts: WorkspaceBuildScripts::default(),
193 project_json: ProjectJson,
194 target: Option<&str>,
195 ) -> Result<ProjectWorkspace> {
196 let sysroot = match &project_json.sysroot_src {
197 Some(path) => Some(Sysroot::load(path)?),
200 let rustc_cfg = rustc_cfg::get(None, target);
201 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
204 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
205 let sysroot = Sysroot::discover(
208 .and_then(|it| it.parent())
209 .ok_or_else(|| format_err!("No detached files to load"))?,
211 let rustc_cfg = rustc_cfg::get(None, None);
212 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
215 pub fn run_build_scripts(
217 config: &CargoConfig,
218 progress: &dyn Fn(String),
219 ) -> Result<WorkspaceBuildScripts> {
221 ProjectWorkspace::Cargo { cargo, .. } => {
222 WorkspaceBuildScripts::run(config, cargo, progress)
224 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
225 Ok(WorkspaceBuildScripts::default())
230 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
232 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
234 always!(bs == WorkspaceBuildScripts::default());
239 /// Returns the roots for the current `ProjectWorkspace`
240 /// The return type contains the path and whether or not
241 /// the root is a member of the current workspace
242 pub fn to_roots(&self) -> Vec<PackageRoot> {
244 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
246 .map(|(_, krate)| PackageRoot {
247 is_member: krate.is_workspace_member,
248 include: krate.include.clone(),
249 exclude: krate.exclude.clone(),
251 .collect::<FxHashSet<_>>()
253 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
254 sysroot.crates().map(move |krate| PackageRoot {
256 include: vec![sysroot[krate].root.parent().to_path_buf()],
260 .collect::<Vec<_>>(),
261 ProjectWorkspace::Cargo {
272 let is_member = cargo[pkg].is_member;
273 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
275 let mut include = vec![pkg_root.clone()];
277 build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
280 // In case target's path is manually set in Cargo.toml to be
281 // outside the package root, add its parent as an extra include.
282 // An example of this situation would look like this:
286 // path = "../../src/lib.rs"
288 let extra_targets = cargo[pkg]
291 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
292 .filter_map(|&tgt| cargo[tgt].root.parent())
293 .map(|tgt| tgt.normalize().to_path_buf())
294 .filter(|path| !path.starts_with(&pkg_root));
295 include.extend(extra_targets);
297 let mut exclude = vec![pkg_root.join(".git")];
299 exclude.push(pkg_root.join("target"));
301 exclude.push(pkg_root.join("tests"));
302 exclude.push(pkg_root.join("examples"));
303 exclude.push(pkg_root.join("benches"));
305 PackageRoot { is_member, include, exclude }
307 .chain(sysroot.crates().map(|krate| PackageRoot {
309 include: vec![sysroot[krate].root.parent().to_path_buf()],
312 .chain(rustc.into_iter().flat_map(|rustc| {
313 rustc.packages().map(move |krate| PackageRoot {
315 include: vec![rustc[krate].manifest.parent().to_path_buf()],
321 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
323 .map(|detached_file| PackageRoot {
325 include: vec![detached_file.clone()],
328 .chain(sysroot.crates().map(|krate| PackageRoot {
330 include: vec![sysroot[krate].root.parent().to_path_buf()],
337 pub fn n_packages(&self) -> usize {
339 ProjectWorkspace::Json { project, .. } => project.n_crates(),
340 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
341 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
342 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
344 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
345 sysroot.crates().len() + files.len()
350 pub fn to_crate_graph(
352 proc_macro_client: Option<&ProcMacroClient>,
353 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
355 let _p = profile::span("ProjectWorkspace::to_crate_graph");
356 let proc_macro_loader = |path: &AbsPath| match proc_macro_client {
357 Some(client) => client.by_dylib_path(path),
361 let mut crate_graph = match self {
362 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
369 ProjectWorkspace::Cargo {
376 } => cargo_to_crate_graph(
386 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
387 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
390 if crate_graph.patch_cfg_if() {
391 log::debug!("Patched std to depend on cfg-if")
393 log::debug!("Did not patch std to depend on cfg-if")
399 fn project_json_to_crate_graph(
400 rustc_cfg: Vec<CfgFlag>,
401 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
402 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
403 project: &ProjectJson,
404 sysroot: &Option<Sysroot>,
406 let mut crate_graph = CrateGraph::default();
407 let sysroot_deps = sysroot
409 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
411 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
412 let crates: FxHashMap<CrateId, CrateId> = project
414 .filter_map(|(crate_id, krate)| {
415 let file_path = &krate.root_module;
416 let file_id = load(file_path)?;
417 Some((crate_id, krate, file_id))
419 .map(|(crate_id, krate, file_id)| {
420 let env = krate.env.clone().into_iter().collect();
421 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
423 let target_cfgs = match krate.target.as_deref() {
425 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
430 let mut cfg_options = CfgOptions::default();
431 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
434 crate_graph.add_crate_root(
437 krate.display_name.clone(),
441 proc_macro.unwrap_or_default(),
447 for (from, krate) in project.crates() {
448 if let Some(&from) = crates.get(&from) {
449 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
450 for (name, to) in public_deps.iter() {
451 add_dep(&mut crate_graph, from, name.clone(), *to)
453 if krate.is_proc_macro {
454 if let Some(proc_macro) = libproc_macro {
458 CrateName::new("proc_macro").unwrap(),
465 for dep in &krate.deps {
466 if let Some(&to) = crates.get(&dep.crate_id) {
467 add_dep(&mut crate_graph, from, dep.name.clone(), to)
475 fn cargo_to_crate_graph(
476 rustc_cfg: Vec<CfgFlag>,
477 override_cfg: &CfgOverrides,
478 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
479 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
480 cargo: &CargoWorkspace,
481 build_scripts: &WorkspaceBuildScripts,
483 rustc: &Option<CargoWorkspace>,
485 let _p = profile::span("cargo_to_crate_graph");
486 let mut crate_graph = CrateGraph::default();
487 let (public_deps, libproc_macro) =
488 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
490 let mut cfg_options = CfgOptions::default();
491 cfg_options.extend(rustc_cfg);
493 let mut pkg_to_lib_crate = FxHashMap::default();
495 // Add test cfg for non-sysroot crates
496 cfg_options.insert_atom("test".into());
497 cfg_options.insert_atom("debug_assertions".into());
499 let mut pkg_crates = FxHashMap::default();
500 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
501 let mut has_private = false;
502 // Next, create crates for each package, target pair
503 for pkg in cargo.packages() {
504 let mut cfg_options = &cfg_options;
505 let mut replaced_cfg_options;
506 if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
507 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
508 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
509 // working on rust-lang/rust as that's the only time it appears outside sysroot).
511 // A more ideal solution might be to reanalyze crates based on where the cursor is and
512 // figure out the set of cfgs that would have to apply to make it active.
514 replaced_cfg_options = cfg_options.clone();
515 replaced_cfg_options.apply_diff(overrides.clone());
516 cfg_options = &replaced_cfg_options;
519 has_private |= cargo[pkg].metadata.rustc_private;
520 let mut lib_tgt = None;
521 for &tgt in cargo[pkg].targets.iter() {
522 if let Some(file_id) = load(&cargo[tgt].root) {
523 let crate_id = add_target_crate_root(
526 build_scripts.outputs.get(pkg),
532 if cargo[tgt].kind == TargetKind::Lib {
533 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
534 pkg_to_lib_crate.insert(pkg, crate_id);
536 if let Some(proc_macro) = libproc_macro {
540 CrateName::new("proc_macro").unwrap(),
545 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
549 // Set deps to the core, std and to the lib target of the current package
550 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
551 if let Some((to, name)) = lib_tgt.clone() {
552 if to != *from && *kind != TargetKind::BuildScript {
553 // (build script can not depend on its library target)
555 // For root projects with dashes in their name,
556 // cargo metadata does not do any normalization,
557 // so we do it ourselves currently
558 let name = CrateName::normalize_dashes(&name);
559 add_dep(&mut crate_graph, *from, name, to);
562 for (name, krate) in public_deps.iter() {
563 add_dep(&mut crate_graph, *from, name.clone(), *krate);
568 // Now add a dep edge from all targets of upstream to the lib
569 // target of downstream.
570 for pkg in cargo.packages() {
571 for dep in cargo[pkg].dependencies.iter() {
572 let name = CrateName::new(&dep.name).unwrap();
573 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
574 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
575 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
576 // Only build scripts may depend on build dependencies.
579 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
580 // Build scripts may only depend on build dependencies.
584 add_dep(&mut crate_graph, *from, name.clone(), to)
591 // If the user provided a path to rustc sources, we add all the rustc_private crates
592 // and create dependencies on them for the crates which opt-in to that
593 if let Some(rustc_workspace) = rustc {
600 &mut pkg_to_lib_crate,
610 fn detached_files_to_crate_graph(
611 rustc_cfg: Vec<CfgFlag>,
612 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
613 detached_files: &[AbsPathBuf],
616 let _p = profile::span("detached_files_to_crate_graph");
617 let mut crate_graph = CrateGraph::default();
618 let (public_deps, _libproc_macro) =
619 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
621 let mut cfg_options = CfgOptions::default();
622 cfg_options.extend(rustc_cfg);
624 for detached_file in detached_files {
625 let file_id = match load(detached_file) {
626 Some(file_id) => file_id,
628 log::error!("Failed to load detached file {:?}", detached_file);
632 let display_name = detached_file
634 .and_then(|os_str| os_str.to_str())
635 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
636 let detached_file_crate = crate_graph.add_crate_root(
646 for (name, krate) in public_deps.iter() {
647 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
653 fn handle_rustc_crates(
654 rustc_workspace: &CargoWorkspace,
655 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
656 crate_graph: &mut CrateGraph,
657 cfg_options: &CfgOptions,
658 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
659 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
660 public_deps: &[(CrateName, CrateId)],
661 cargo: &CargoWorkspace,
662 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
664 let mut rustc_pkg_crates = FxHashMap::default();
665 // The root package of the rustc-dev component is rustc_driver, so we match that
667 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
668 // The rustc workspace might be incomplete (such as if rustc-dev is not
669 // installed for the current toolchain) and `rustcSource` is set to discover.
670 if let Some(root_pkg) = root_pkg {
671 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
672 let mut queue = VecDeque::new();
673 queue.push_back(root_pkg);
674 while let Some(pkg) = queue.pop_front() {
675 // Don't duplicate packages if they are dependended on a diamond pattern
676 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
677 // which is not ideal
678 if rustc_pkg_crates.contains_key(&pkg) {
681 for dep in &rustc_workspace[pkg].dependencies {
682 queue.push_back(dep.pkg);
684 for &tgt in rustc_workspace[pkg].targets.iter() {
685 if rustc_workspace[tgt].kind != TargetKind::Lib {
688 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
689 let crate_id = add_target_crate_root(
691 &rustc_workspace[pkg],
696 &rustc_workspace[tgt].name,
698 pkg_to_lib_crate.insert(pkg, crate_id);
699 // Add dependencies on core / std / alloc for this crate
700 for (name, krate) in public_deps.iter() {
701 add_dep(crate_graph, crate_id, name.clone(), *krate);
703 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
708 // Now add a dep edge from all targets of upstream to the lib
709 // target of downstream.
710 for pkg in rustc_pkg_crates.keys().copied() {
711 for dep in rustc_workspace[pkg].dependencies.iter() {
712 let name = CrateName::new(&dep.name).unwrap();
713 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
714 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
715 add_dep(crate_graph, from, name.clone(), to);
720 // Add a dependency on the rustc_private crates for all targets of each package
722 for dep in rustc_workspace.packages() {
723 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
725 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
726 for pkg in cargo.packages() {
727 let package = &cargo[pkg];
728 if !package.metadata.rustc_private {
731 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
732 // Avoid creating duplicate dependencies
733 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
734 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
735 // instead of the one from `crates.io`
736 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
737 add_dep(crate_graph, *from, name.clone(), to);
745 fn add_target_crate_root(
746 crate_graph: &mut CrateGraph,
748 build_data: Option<&BuildScriptOutput>,
749 cfg_options: &CfgOptions,
750 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
754 let edition = pkg.edition;
756 let mut opts = cfg_options.clone();
757 for feature in pkg.active_features.iter() {
758 opts.insert_key_value("feature".into(), feature.into());
760 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
761 opts.extend(cfgs.iter().cloned());
766 let mut env = Env::default();
767 inject_cargo_env(pkg, &mut env);
769 if let Some(envs) = build_data.map(|it| &it.envs) {
771 env.set(k, v.clone());
775 let proc_macro = build_data
777 .and_then(|it| it.proc_macro_dylib_path.as_ref())
778 .map(|it| proc_macro_loader(it))
779 .unwrap_or_default();
781 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
782 let mut potential_cfg_options = cfg_options.clone();
783 potential_cfg_options.extend(
786 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
789 let crate_id = crate_graph.add_crate_root(
794 potential_cfg_options,
802 fn sysroot_to_crate_graph(
803 crate_graph: &mut CrateGraph,
805 rustc_cfg: Vec<CfgFlag>,
806 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
807 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
808 let _p = profile::span("sysroot_to_crate_graph");
809 let mut cfg_options = CfgOptions::default();
810 cfg_options.extend(rustc_cfg);
811 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
813 .filter_map(|krate| {
814 let file_id = load(&sysroot[krate].root)?;
816 let env = Env::default();
817 let proc_macro = vec![];
818 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
819 let crate_id = crate_graph.add_crate_root(
828 Some((krate, crate_id))
832 for from in sysroot.crates() {
833 for &to in sysroot[from].deps.iter() {
834 let name = CrateName::new(&sysroot[to].name).unwrap();
835 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
836 add_dep(crate_graph, from, name, to);
841 let public_deps = sysroot
843 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
844 .collect::<Vec<_>>();
846 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
847 (public_deps, libproc_macro)
850 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
851 if let Err(err) = graph.add_dep(from, name, to) {
852 log::error!("{}", err)
856 /// Recreates the compile-time environment variables that Cargo sets.
858 /// Should be synced with
859 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
861 /// FIXME: ask Cargo to provide this data instead of re-deriving.
862 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
863 // FIXME: Missing variables:
864 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
866 let manifest_dir = package.manifest.parent();
867 env.set("CARGO_MANIFEST_DIR".into(), manifest_dir.as_os_str().to_string_lossy().into_owned());
869 // Not always right, but works for common cases.
870 env.set("CARGO".into(), "cargo".into());
872 env.set("CARGO_PKG_VERSION".into(), package.version.to_string());
873 env.set("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string());
874 env.set("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string());
875 env.set("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string());
876 env.set("CARGO_PKG_VERSION_PRE".into(), package.version.pre.to_string());
878 env.set("CARGO_PKG_AUTHORS".into(), String::new());
880 env.set("CARGO_PKG_NAME".into(), package.name.clone());
881 // FIXME: This isn't really correct (a package can have many crates with different names), but
882 // it's better than leaving the variable unset.
883 env.set("CARGO_CRATE_NAME".into(), CrateName::normalize_dashes(&package.name).to_string());
884 env.set("CARGO_PKG_DESCRIPTION".into(), String::new());
885 env.set("CARGO_PKG_HOMEPAGE".into(), String::new());
886 env.set("CARGO_PKG_REPOSITORY".into(), String::new());
887 env.set("CARGO_PKG_LICENSE".into(), String::new());
889 env.set("CARGO_PKG_LICENSE_FILE".into(), String::new());