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 rustc_hash::{FxHashMap, FxHashSet};
15 build_scripts::BuildScriptOutput,
16 cargo_workspace::{DepKind, PackageData, RustcSource},
19 sysroot::SysrootCrate,
20 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
21 TargetKind, WorkspaceBuildScripts,
24 pub type CfgOverrides = FxHashMap<String, CfgDiff>;
26 /// `PackageRoot` describes a package root folder.
27 /// Which may be an external dependency, or a member of
28 /// the current workspace.
29 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
30 pub struct PackageRoot {
31 /// Is a member of the current workspace
33 pub include: Vec<AbsPathBuf>,
34 pub exclude: Vec<AbsPathBuf>,
37 #[derive(Clone, Eq, PartialEq)]
38 pub enum ProjectWorkspace {
39 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
41 cargo: CargoWorkspace,
42 build_scripts: WorkspaceBuildScripts,
43 sysroot: Option<Sysroot>,
44 rustc: Option<CargoWorkspace>,
45 /// Holds cfg flags for the current target. We get those by running
46 /// `rustc --print cfg`.
48 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
50 rustc_cfg: Vec<CfgFlag>,
51 cfg_overrides: CfgOverrides,
53 /// Project workspace was manually specified using a `rust-project.json` file.
54 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
56 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
57 // That's not the end user experience we should strive for.
58 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
59 // That needs some changes on the salsa-level though.
60 // 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).
61 // 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.
62 // 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.
64 /// Project with a set of disjoint files, not belonging to any particular workspace.
65 /// Backed by basic sysroot crates for basic completion and highlighting.
66 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
69 impl fmt::Debug for ProjectWorkspace {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 // Make sure this isn't too verbose.
73 ProjectWorkspace::Cargo {
81 .debug_struct("Cargo")
82 .field("root", &cargo.workspace_root().file_name())
83 .field("n_packages", &cargo.packages().len())
84 .field("sysroot", &sysroot.is_some())
86 "n_rustc_compiler_crates",
87 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
89 .field("n_rustc_cfg", &rustc_cfg.len())
90 .field("n_cfg_overrides", &cfg_overrides.len())
92 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
93 let mut debug_struct = f.debug_struct("Json");
94 debug_struct.field("n_crates", &project.n_crates());
95 if let Some(sysroot) = sysroot {
96 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
98 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
101 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
102 .debug_struct("DetachedFiles")
103 .field("n_files", &files.len())
104 .field("n_sysroot_crates", &sysroot.crates().len())
105 .field("n_rustc_cfg", &rustc_cfg.len())
111 impl ProjectWorkspace {
113 manifest: ProjectManifest,
114 config: &CargoConfig,
115 progress: &dyn Fn(String),
116 ) -> Result<ProjectWorkspace> {
117 let res = match manifest {
118 ProjectManifest::ProjectJson(project_json) => {
119 let file = fs::read_to_string(&project_json).with_context(|| {
120 format!("Failed to read json file {}", project_json.display())
122 let data = serde_json::from_str(&file).with_context(|| {
123 format!("Failed to deserialize json file {}", project_json.display())
125 let project_location = project_json.parent().to_path_buf();
126 let project_json = ProjectJson::new(&project_location, data);
127 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
129 ProjectManifest::CargoToml(cargo_toml) => {
130 let cargo_version = utf8_stdout({
131 let mut cmd = Command::new(toolchain::cargo());
132 cmd.arg("--version");
136 let meta = CargoWorkspace::fetch_metadata(&cargo_toml, config, progress)
139 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
140 cargo_toml.display(),
144 let cargo = CargoWorkspace::new(meta);
146 let sysroot = if config.no_sysroot {
149 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
151 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
157 let rustc_dir = match &config.rustc_source {
158 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
159 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
163 let rustc = match rustc_dir {
164 Some(rustc_dir) => Some({
165 let meta = CargoWorkspace::fetch_metadata(&rustc_dir, config, progress)
167 format!("Failed to read Cargo metadata for Rust sources")
169 CargoWorkspace::new(meta)
174 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
176 let cfg_overrides = config.cfg_overrides();
177 ProjectWorkspace::Cargo {
179 build_scripts: WorkspaceBuildScripts::default(),
192 project_json: ProjectJson,
193 target: Option<&str>,
194 ) -> Result<ProjectWorkspace> {
195 let sysroot = match &project_json.sysroot_src {
196 Some(path) => Some(Sysroot::load(path.clone())?),
199 let rustc_cfg = rustc_cfg::get(None, target);
200 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
203 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
204 let sysroot = Sysroot::discover(
207 .and_then(|it| it.parent())
208 .ok_or_else(|| format_err!("No detached files to load"))?,
210 let rustc_cfg = rustc_cfg::get(None, None);
211 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
214 pub fn run_build_scripts(
216 config: &CargoConfig,
217 progress: &dyn Fn(String),
218 ) -> Result<WorkspaceBuildScripts> {
220 ProjectWorkspace::Cargo { cargo, .. } => {
221 WorkspaceBuildScripts::run(config, cargo, progress)
223 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
224 Ok(WorkspaceBuildScripts::default())
229 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
231 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
233 always!(bs == WorkspaceBuildScripts::default());
238 /// Returns the roots for the current `ProjectWorkspace`
239 /// The return type contains the path and whether or not
240 /// the root is a member of the current workspace
241 pub fn to_roots(&self) -> Vec<PackageRoot> {
243 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
245 .map(|(_, krate)| PackageRoot {
246 is_member: krate.is_workspace_member,
247 include: krate.include.clone(),
248 exclude: krate.exclude.clone(),
250 .collect::<FxHashSet<_>>()
252 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
253 sysroot.crates().map(move |krate| PackageRoot {
255 include: vec![sysroot[krate].root.parent().to_path_buf()],
259 .collect::<Vec<_>>(),
260 ProjectWorkspace::Cargo {
271 let is_member = cargo[pkg].is_member;
272 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
274 let mut include = vec![pkg_root.clone()];
276 build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
279 // In case target's path is manually set in Cargo.toml to be
280 // outside the package root, add its parent as an extra include.
281 // An example of this situation would look like this:
285 // path = "../../src/lib.rs"
287 let extra_targets = cargo[pkg]
290 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
291 .filter_map(|&tgt| cargo[tgt].root.parent())
292 .map(|tgt| tgt.normalize().to_path_buf())
293 .filter(|path| !path.starts_with(&pkg_root));
294 include.extend(extra_targets);
296 let mut exclude = vec![pkg_root.join(".git")];
298 exclude.push(pkg_root.join("target"));
300 exclude.push(pkg_root.join("tests"));
301 exclude.push(pkg_root.join("examples"));
302 exclude.push(pkg_root.join("benches"));
304 PackageRoot { is_member, include, exclude }
306 .chain(sysroot.into_iter().map(|sysroot| PackageRoot {
308 include: vec![sysroot.root().to_path_buf()],
311 .chain(rustc.into_iter().flat_map(|rustc| {
312 rustc.packages().map(move |krate| PackageRoot {
314 include: vec![rustc[krate].manifest.parent().to_path_buf()],
320 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
322 .map(|detached_file| PackageRoot {
324 include: vec![detached_file.clone()],
327 .chain(sysroot.crates().map(|krate| PackageRoot {
329 include: vec![sysroot[krate].root.parent().to_path_buf()],
336 pub fn n_packages(&self) -> usize {
338 ProjectWorkspace::Json { project, .. } => project.n_crates(),
339 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
340 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
341 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
342 cargo.packages().len() + sysroot_package_len + rustc_package_len
344 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
345 sysroot.crates().len() + files.len()
350 pub fn to_crate_graph(
352 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
353 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
355 let _p = profile::span("ProjectWorkspace::to_crate_graph");
357 let mut crate_graph = match self {
358 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
365 ProjectWorkspace::Cargo {
372 } => cargo_to_crate_graph(
382 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
383 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
386 if crate_graph.patch_cfg_if() {
387 log::debug!("Patched std to depend on cfg-if")
389 log::debug!("Did not patch std to depend on cfg-if")
395 fn project_json_to_crate_graph(
396 rustc_cfg: Vec<CfgFlag>,
397 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
398 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
399 project: &ProjectJson,
400 sysroot: &Option<Sysroot>,
402 let mut crate_graph = CrateGraph::default();
403 let sysroot_deps = sysroot
405 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
407 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
408 let crates: FxHashMap<CrateId, CrateId> = project
410 .filter_map(|(crate_id, krate)| {
411 let file_path = &krate.root_module;
412 let file_id = load(file_path)?;
413 Some((crate_id, krate, file_id))
415 .map(|(crate_id, krate, file_id)| {
416 let env = krate.env.clone().into_iter().collect();
417 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| load_proc_macro(&it));
419 let target_cfgs = match krate.target.as_deref() {
421 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
426 let mut cfg_options = CfgOptions::default();
427 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
430 crate_graph.add_crate_root(
433 krate.display_name.clone(),
437 proc_macro.unwrap_or_default(),
443 for (from, krate) in project.crates() {
444 if let Some(&from) = crates.get(&from) {
445 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
446 for (name, to) in public_deps.iter() {
447 add_dep(&mut crate_graph, from, name.clone(), *to)
449 if krate.is_proc_macro {
450 if let Some(proc_macro) = libproc_macro {
454 CrateName::new("proc_macro").unwrap(),
461 for dep in &krate.deps {
462 if let Some(&to) = crates.get(&dep.crate_id) {
463 add_dep(&mut crate_graph, from, dep.name.clone(), to)
471 fn cargo_to_crate_graph(
472 rustc_cfg: Vec<CfgFlag>,
473 override_cfg: &CfgOverrides,
474 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
475 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
476 cargo: &CargoWorkspace,
477 build_scripts: &WorkspaceBuildScripts,
478 sysroot: Option<&Sysroot>,
479 rustc: &Option<CargoWorkspace>,
481 let _p = profile::span("cargo_to_crate_graph");
482 let mut crate_graph = CrateGraph::default();
483 let (public_deps, libproc_macro) = match sysroot {
484 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
485 None => (Vec::new(), None),
488 let mut cfg_options = CfgOptions::default();
489 cfg_options.extend(rustc_cfg);
491 let mut pkg_to_lib_crate = FxHashMap::default();
493 // Add test cfg for non-sysroot crates
494 cfg_options.insert_atom("test".into());
495 cfg_options.insert_atom("debug_assertions".into());
497 let mut pkg_crates = FxHashMap::default();
498 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
499 let mut has_private = false;
500 // Next, create crates for each package, target pair
501 for pkg in cargo.packages() {
502 let mut cfg_options = &cfg_options;
503 let mut replaced_cfg_options;
504 if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
505 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
506 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
507 // working on rust-lang/rust as that's the only time it appears outside sysroot).
509 // A more ideal solution might be to reanalyze crates based on where the cursor is and
510 // figure out the set of cfgs that would have to apply to make it active.
512 replaced_cfg_options = cfg_options.clone();
513 replaced_cfg_options.apply_diff(overrides.clone());
514 cfg_options = &replaced_cfg_options;
517 has_private |= cargo[pkg].metadata.rustc_private;
518 let mut lib_tgt = None;
519 for &tgt in cargo[pkg].targets.iter() {
520 if let Some(file_id) = load(&cargo[tgt].root) {
521 let crate_id = add_target_crate_root(
524 build_scripts.outputs.get(pkg),
530 if cargo[tgt].kind == TargetKind::Lib {
531 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
532 pkg_to_lib_crate.insert(pkg, crate_id);
534 if let Some(proc_macro) = libproc_macro {
538 CrateName::new("proc_macro").unwrap(),
543 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
547 // Set deps to the core, std and to the lib target of the current package
548 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
549 if let Some((to, name)) = lib_tgt.clone() {
550 if to != *from && *kind != TargetKind::BuildScript {
551 // (build script can not depend on its library target)
553 // For root projects with dashes in their name,
554 // cargo metadata does not do any normalization,
555 // so we do it ourselves currently
556 let name = CrateName::normalize_dashes(&name);
557 add_dep(&mut crate_graph, *from, name, to);
560 for (name, krate) in public_deps.iter() {
561 add_dep(&mut crate_graph, *from, name.clone(), *krate);
566 // Now add a dep edge from all targets of upstream to the lib
567 // target of downstream.
568 for pkg in cargo.packages() {
569 for dep in cargo[pkg].dependencies.iter() {
570 let name = CrateName::new(&dep.name).unwrap();
571 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
572 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
573 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
574 // Only build scripts may depend on build dependencies.
577 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
578 // Build scripts may only depend on build dependencies.
582 add_dep(&mut crate_graph, *from, name.clone(), to)
589 // If the user provided a path to rustc sources, we add all the rustc_private crates
590 // and create dependencies on them for the crates which opt-in to that
591 if let Some(rustc_workspace) = rustc {
598 &mut pkg_to_lib_crate,
608 fn detached_files_to_crate_graph(
609 rustc_cfg: Vec<CfgFlag>,
610 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
611 detached_files: &[AbsPathBuf],
614 let _p = profile::span("detached_files_to_crate_graph");
615 let mut crate_graph = CrateGraph::default();
616 let (public_deps, _libproc_macro) =
617 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
619 let mut cfg_options = CfgOptions::default();
620 cfg_options.extend(rustc_cfg);
622 for detached_file in detached_files {
623 let file_id = match load(detached_file) {
624 Some(file_id) => file_id,
626 log::error!("Failed to load detached file {:?}", detached_file);
630 let display_name = detached_file
632 .and_then(|os_str| os_str.to_str())
633 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
634 let detached_file_crate = crate_graph.add_crate_root(
644 for (name, krate) in public_deps.iter() {
645 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
651 fn handle_rustc_crates(
652 rustc_workspace: &CargoWorkspace,
653 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
654 crate_graph: &mut CrateGraph,
655 cfg_options: &CfgOptions,
656 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
657 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
658 public_deps: &[(CrateName, CrateId)],
659 cargo: &CargoWorkspace,
660 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
662 let mut rustc_pkg_crates = FxHashMap::default();
663 // The root package of the rustc-dev component is rustc_driver, so we match that
665 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
666 // The rustc workspace might be incomplete (such as if rustc-dev is not
667 // installed for the current toolchain) and `rustcSource` is set to discover.
668 if let Some(root_pkg) = root_pkg {
669 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
670 let mut queue = VecDeque::new();
671 queue.push_back(root_pkg);
672 while let Some(pkg) = queue.pop_front() {
673 // Don't duplicate packages if they are dependended on a diamond pattern
674 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
675 // which is not ideal
676 if rustc_pkg_crates.contains_key(&pkg) {
679 for dep in &rustc_workspace[pkg].dependencies {
680 queue.push_back(dep.pkg);
682 for &tgt in rustc_workspace[pkg].targets.iter() {
683 if rustc_workspace[tgt].kind != TargetKind::Lib {
686 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
687 let crate_id = add_target_crate_root(
689 &rustc_workspace[pkg],
694 &rustc_workspace[tgt].name,
696 pkg_to_lib_crate.insert(pkg, crate_id);
697 // Add dependencies on core / std / alloc for this crate
698 for (name, krate) in public_deps.iter() {
699 add_dep(crate_graph, crate_id, name.clone(), *krate);
701 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
706 // Now add a dep edge from all targets of upstream to the lib
707 // target of downstream.
708 for pkg in rustc_pkg_crates.keys().copied() {
709 for dep in rustc_workspace[pkg].dependencies.iter() {
710 let name = CrateName::new(&dep.name).unwrap();
711 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
712 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
713 add_dep(crate_graph, from, name.clone(), to);
718 // Add a dependency on the rustc_private crates for all targets of each package
720 for dep in rustc_workspace.packages() {
721 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
723 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
724 for pkg in cargo.packages() {
725 let package = &cargo[pkg];
726 if !package.metadata.rustc_private {
729 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
730 // Avoid creating duplicate dependencies
731 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
732 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
733 // instead of the one from `crates.io`
734 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
735 add_dep(crate_graph, *from, name.clone(), to);
743 fn add_target_crate_root(
744 crate_graph: &mut CrateGraph,
746 build_data: Option<&BuildScriptOutput>,
747 cfg_options: &CfgOptions,
748 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
752 let edition = pkg.edition;
754 let mut opts = cfg_options.clone();
755 for feature in pkg.active_features.iter() {
756 opts.insert_key_value("feature".into(), feature.into());
758 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
759 opts.extend(cfgs.iter().cloned());
764 let mut env = Env::default();
765 inject_cargo_env(pkg, &mut env);
767 if let Some(envs) = build_data.map(|it| &it.envs) {
769 env.set(k, v.clone());
773 let proc_macro = build_data
775 .and_then(|it| it.proc_macro_dylib_path.as_ref())
776 .map(|it| load_proc_macro(it))
777 .unwrap_or_default();
779 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
780 let mut potential_cfg_options = cfg_options.clone();
781 potential_cfg_options.extend(
784 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
787 let crate_id = crate_graph.add_crate_root(
792 potential_cfg_options,
800 fn sysroot_to_crate_graph(
801 crate_graph: &mut CrateGraph,
803 rustc_cfg: Vec<CfgFlag>,
804 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
805 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
806 let _p = profile::span("sysroot_to_crate_graph");
807 let mut cfg_options = CfgOptions::default();
808 cfg_options.extend(rustc_cfg);
809 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
811 .filter_map(|krate| {
812 let file_id = load(&sysroot[krate].root)?;
814 let env = Env::default();
815 let proc_macro = vec![];
816 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
817 let crate_id = crate_graph.add_crate_root(
826 Some((krate, crate_id))
830 for from in sysroot.crates() {
831 for &to in sysroot[from].deps.iter() {
832 let name = CrateName::new(&sysroot[to].name).unwrap();
833 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
834 add_dep(crate_graph, from, name, to);
839 let public_deps = sysroot
841 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
842 .collect::<Vec<_>>();
844 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
845 (public_deps, libproc_macro)
848 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
849 if let Err(err) = graph.add_dep(from, name, to) {
850 log::error!("{}", err)
854 /// Recreates the compile-time environment variables that Cargo sets.
856 /// Should be synced with
857 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
859 /// FIXME: ask Cargo to provide this data instead of re-deriving.
860 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
861 // FIXME: Missing variables:
862 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
864 let manifest_dir = package.manifest.parent();
865 env.set("CARGO_MANIFEST_DIR".into(), manifest_dir.as_os_str().to_string_lossy().into_owned());
867 // Not always right, but works for common cases.
868 env.set("CARGO".into(), "cargo".into());
870 env.set("CARGO_PKG_VERSION".into(), package.version.to_string());
871 env.set("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string());
872 env.set("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string());
873 env.set("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string());
874 env.set("CARGO_PKG_VERSION_PRE".into(), package.version.pre.to_string());
876 env.set("CARGO_PKG_AUTHORS".into(), String::new());
878 env.set("CARGO_PKG_NAME".into(), package.name.clone());
879 // FIXME: This isn't really correct (a package can have many crates with different names), but
880 // it's better than leaving the variable unset.
881 env.set("CARGO_CRATE_NAME".into(), CrateName::normalize_dashes(&package.name).to_string());
882 env.set("CARGO_PKG_DESCRIPTION".into(), String::new());
883 env.set("CARGO_PKG_HOMEPAGE".into(), String::new());
884 env.set("CARGO_PKG_REPOSITORY".into(), String::new());
885 env.set("CARGO_PKG_LICENSE".into(), String::new());
887 env.set("CARGO_PKG_LICENSE_FILE".into(), String::new());