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,
44 sysroot: Option<Sysroot>,
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("sysroot", &sysroot.is_some())
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 Some(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.clone())?),
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.into_iter().map(|sysroot| PackageRoot {
309 include: vec![sysroot.root().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, |it| it.packages().len());
342 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
343 cargo.packages().len() + sysroot_package_len + rustc_package_len
345 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
346 sysroot.crates().len() + files.len()
351 pub fn to_crate_graph(
353 proc_macro_client: Option<&ProcMacroClient>,
354 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
356 let _p = profile::span("ProjectWorkspace::to_crate_graph");
357 let proc_macro_loader = |path: &AbsPath| match proc_macro_client {
358 Some(client) => client.by_dylib_path(path),
362 let mut crate_graph = match self {
363 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
370 ProjectWorkspace::Cargo {
377 } => cargo_to_crate_graph(
387 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
388 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
391 if crate_graph.patch_cfg_if() {
392 log::debug!("Patched std to depend on cfg-if")
394 log::debug!("Did not patch std to depend on cfg-if")
400 fn project_json_to_crate_graph(
401 rustc_cfg: Vec<CfgFlag>,
402 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
403 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
404 project: &ProjectJson,
405 sysroot: &Option<Sysroot>,
407 let mut crate_graph = CrateGraph::default();
408 let sysroot_deps = sysroot
410 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
412 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
413 let crates: FxHashMap<CrateId, CrateId> = project
415 .filter_map(|(crate_id, krate)| {
416 let file_path = &krate.root_module;
417 let file_id = load(file_path)?;
418 Some((crate_id, krate, file_id))
420 .map(|(crate_id, krate, file_id)| {
421 let env = krate.env.clone().into_iter().collect();
422 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
424 let target_cfgs = match krate.target.as_deref() {
426 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
431 let mut cfg_options = CfgOptions::default();
432 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
435 crate_graph.add_crate_root(
438 krate.display_name.clone(),
442 proc_macro.unwrap_or_default(),
448 for (from, krate) in project.crates() {
449 if let Some(&from) = crates.get(&from) {
450 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
451 for (name, to) in public_deps.iter() {
452 add_dep(&mut crate_graph, from, name.clone(), *to)
454 if krate.is_proc_macro {
455 if let Some(proc_macro) = libproc_macro {
459 CrateName::new("proc_macro").unwrap(),
466 for dep in &krate.deps {
467 if let Some(&to) = crates.get(&dep.crate_id) {
468 add_dep(&mut crate_graph, from, dep.name.clone(), to)
476 fn cargo_to_crate_graph(
477 rustc_cfg: Vec<CfgFlag>,
478 override_cfg: &CfgOverrides,
479 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
480 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
481 cargo: &CargoWorkspace,
482 build_scripts: &WorkspaceBuildScripts,
483 sysroot: Option<&Sysroot>,
484 rustc: &Option<CargoWorkspace>,
486 let _p = profile::span("cargo_to_crate_graph");
487 let mut crate_graph = CrateGraph::default();
488 let (public_deps, libproc_macro) = match sysroot {
489 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
490 None => (Vec::new(), None),
493 let mut cfg_options = CfgOptions::default();
494 cfg_options.extend(rustc_cfg);
496 let mut pkg_to_lib_crate = FxHashMap::default();
498 // Add test cfg for non-sysroot crates
499 cfg_options.insert_atom("test".into());
500 cfg_options.insert_atom("debug_assertions".into());
502 let mut pkg_crates = FxHashMap::default();
503 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
504 let mut has_private = false;
505 // Next, create crates for each package, target pair
506 for pkg in cargo.packages() {
507 let mut cfg_options = &cfg_options;
508 let mut replaced_cfg_options;
509 if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
510 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
511 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
512 // working on rust-lang/rust as that's the only time it appears outside sysroot).
514 // A more ideal solution might be to reanalyze crates based on where the cursor is and
515 // figure out the set of cfgs that would have to apply to make it active.
517 replaced_cfg_options = cfg_options.clone();
518 replaced_cfg_options.apply_diff(overrides.clone());
519 cfg_options = &replaced_cfg_options;
522 has_private |= cargo[pkg].metadata.rustc_private;
523 let mut lib_tgt = None;
524 for &tgt in cargo[pkg].targets.iter() {
525 if let Some(file_id) = load(&cargo[tgt].root) {
526 let crate_id = add_target_crate_root(
529 build_scripts.outputs.get(pkg),
535 if cargo[tgt].kind == TargetKind::Lib {
536 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
537 pkg_to_lib_crate.insert(pkg, crate_id);
539 if let Some(proc_macro) = libproc_macro {
543 CrateName::new("proc_macro").unwrap(),
548 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
552 // Set deps to the core, std and to the lib target of the current package
553 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
554 if let Some((to, name)) = lib_tgt.clone() {
555 if to != *from && *kind != TargetKind::BuildScript {
556 // (build script can not depend on its library target)
558 // For root projects with dashes in their name,
559 // cargo metadata does not do any normalization,
560 // so we do it ourselves currently
561 let name = CrateName::normalize_dashes(&name);
562 add_dep(&mut crate_graph, *from, name, to);
565 for (name, krate) in public_deps.iter() {
566 add_dep(&mut crate_graph, *from, name.clone(), *krate);
571 // Now add a dep edge from all targets of upstream to the lib
572 // target of downstream.
573 for pkg in cargo.packages() {
574 for dep in cargo[pkg].dependencies.iter() {
575 let name = CrateName::new(&dep.name).unwrap();
576 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
577 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
578 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
579 // Only build scripts may depend on build dependencies.
582 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
583 // Build scripts may only depend on build dependencies.
587 add_dep(&mut crate_graph, *from, name.clone(), to)
594 // If the user provided a path to rustc sources, we add all the rustc_private crates
595 // and create dependencies on them for the crates which opt-in to that
596 if let Some(rustc_workspace) = rustc {
603 &mut pkg_to_lib_crate,
613 fn detached_files_to_crate_graph(
614 rustc_cfg: Vec<CfgFlag>,
615 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
616 detached_files: &[AbsPathBuf],
619 let _p = profile::span("detached_files_to_crate_graph");
620 let mut crate_graph = CrateGraph::default();
621 let (public_deps, _libproc_macro) =
622 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
624 let mut cfg_options = CfgOptions::default();
625 cfg_options.extend(rustc_cfg);
627 for detached_file in detached_files {
628 let file_id = match load(detached_file) {
629 Some(file_id) => file_id,
631 log::error!("Failed to load detached file {:?}", detached_file);
635 let display_name = detached_file
637 .and_then(|os_str| os_str.to_str())
638 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
639 let detached_file_crate = crate_graph.add_crate_root(
649 for (name, krate) in public_deps.iter() {
650 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
656 fn handle_rustc_crates(
657 rustc_workspace: &CargoWorkspace,
658 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
659 crate_graph: &mut CrateGraph,
660 cfg_options: &CfgOptions,
661 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
662 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
663 public_deps: &[(CrateName, CrateId)],
664 cargo: &CargoWorkspace,
665 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
667 let mut rustc_pkg_crates = FxHashMap::default();
668 // The root package of the rustc-dev component is rustc_driver, so we match that
670 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
671 // The rustc workspace might be incomplete (such as if rustc-dev is not
672 // installed for the current toolchain) and `rustcSource` is set to discover.
673 if let Some(root_pkg) = root_pkg {
674 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
675 let mut queue = VecDeque::new();
676 queue.push_back(root_pkg);
677 while let Some(pkg) = queue.pop_front() {
678 // Don't duplicate packages if they are dependended on a diamond pattern
679 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
680 // which is not ideal
681 if rustc_pkg_crates.contains_key(&pkg) {
684 for dep in &rustc_workspace[pkg].dependencies {
685 queue.push_back(dep.pkg);
687 for &tgt in rustc_workspace[pkg].targets.iter() {
688 if rustc_workspace[tgt].kind != TargetKind::Lib {
691 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
692 let crate_id = add_target_crate_root(
694 &rustc_workspace[pkg],
699 &rustc_workspace[tgt].name,
701 pkg_to_lib_crate.insert(pkg, crate_id);
702 // Add dependencies on core / std / alloc for this crate
703 for (name, krate) in public_deps.iter() {
704 add_dep(crate_graph, crate_id, name.clone(), *krate);
706 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
711 // Now add a dep edge from all targets of upstream to the lib
712 // target of downstream.
713 for pkg in rustc_pkg_crates.keys().copied() {
714 for dep in rustc_workspace[pkg].dependencies.iter() {
715 let name = CrateName::new(&dep.name).unwrap();
716 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
717 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
718 add_dep(crate_graph, from, name.clone(), to);
723 // Add a dependency on the rustc_private crates for all targets of each package
725 for dep in rustc_workspace.packages() {
726 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
728 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
729 for pkg in cargo.packages() {
730 let package = &cargo[pkg];
731 if !package.metadata.rustc_private {
734 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
735 // Avoid creating duplicate dependencies
736 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
737 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
738 // instead of the one from `crates.io`
739 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
740 add_dep(crate_graph, *from, name.clone(), to);
748 fn add_target_crate_root(
749 crate_graph: &mut CrateGraph,
751 build_data: Option<&BuildScriptOutput>,
752 cfg_options: &CfgOptions,
753 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
757 let edition = pkg.edition;
759 let mut opts = cfg_options.clone();
760 for feature in pkg.active_features.iter() {
761 opts.insert_key_value("feature".into(), feature.into());
763 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
764 opts.extend(cfgs.iter().cloned());
769 let mut env = Env::default();
770 inject_cargo_env(pkg, &mut env);
772 if let Some(envs) = build_data.map(|it| &it.envs) {
774 env.set(k, v.clone());
778 let proc_macro = build_data
780 .and_then(|it| it.proc_macro_dylib_path.as_ref())
781 .map(|it| proc_macro_loader(it))
782 .unwrap_or_default();
784 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
785 let mut potential_cfg_options = cfg_options.clone();
786 potential_cfg_options.extend(
789 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
792 let crate_id = crate_graph.add_crate_root(
797 potential_cfg_options,
805 fn sysroot_to_crate_graph(
806 crate_graph: &mut CrateGraph,
808 rustc_cfg: Vec<CfgFlag>,
809 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
810 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
811 let _p = profile::span("sysroot_to_crate_graph");
812 let mut cfg_options = CfgOptions::default();
813 cfg_options.extend(rustc_cfg);
814 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
816 .filter_map(|krate| {
817 let file_id = load(&sysroot[krate].root)?;
819 let env = Env::default();
820 let proc_macro = vec![];
821 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
822 let crate_id = crate_graph.add_crate_root(
831 Some((krate, crate_id))
835 for from in sysroot.crates() {
836 for &to in sysroot[from].deps.iter() {
837 let name = CrateName::new(&sysroot[to].name).unwrap();
838 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
839 add_dep(crate_graph, from, name, to);
844 let public_deps = sysroot
846 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
847 .collect::<Vec<_>>();
849 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
850 (public_deps, libproc_macro)
853 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
854 if let Err(err) = graph.add_dep(from, name, to) {
855 log::error!("{}", err)
859 /// Recreates the compile-time environment variables that Cargo sets.
861 /// Should be synced with
862 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
864 /// FIXME: ask Cargo to provide this data instead of re-deriving.
865 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
866 // FIXME: Missing variables:
867 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
869 let manifest_dir = package.manifest.parent();
870 env.set("CARGO_MANIFEST_DIR".into(), manifest_dir.as_os_str().to_string_lossy().into_owned());
872 // Not always right, but works for common cases.
873 env.set("CARGO".into(), "cargo".into());
875 env.set("CARGO_PKG_VERSION".into(), package.version.to_string());
876 env.set("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string());
877 env.set("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string());
878 env.set("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string());
879 env.set("CARGO_PKG_VERSION_PRE".into(), package.version.pre.to_string());
881 env.set("CARGO_PKG_AUTHORS".into(), String::new());
883 env.set("CARGO_PKG_NAME".into(), package.name.clone());
884 // FIXME: This isn't really correct (a package can have many crates with different names), but
885 // it's better than leaving the variable unset.
886 env.set("CARGO_CRATE_NAME".into(), CrateName::normalize_dashes(&package.name).to_string());
887 env.set("CARGO_PKG_DESCRIPTION".into(), String::new());
888 env.set("CARGO_PKG_HOMEPAGE".into(), String::new());
889 env.set("CARGO_PKG_REPOSITORY".into(), String::new());
890 env.set("CARGO_PKG_LICENSE".into(), String::new());
892 env.set("CARGO_PKG_LICENSE_FILE".into(), String::new());