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 cargo[tgt].is_proc_macro {
537 if let Some(proc_macro) = libproc_macro {
541 CrateName::new("proc_macro").unwrap(),
547 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
551 // Set deps to the core, std and to the lib target of the current package
552 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
553 if let Some((to, name)) = lib_tgt.clone() {
554 if to != *from && *kind != TargetKind::BuildScript {
555 // (build script can not depend on its library target)
557 // For root projects with dashes in their name,
558 // cargo metadata does not do any normalization,
559 // so we do it ourselves currently
560 let name = CrateName::normalize_dashes(&name);
561 add_dep(&mut crate_graph, *from, name, to);
564 for (name, krate) in public_deps.iter() {
565 add_dep(&mut crate_graph, *from, name.clone(), *krate);
570 // Now add a dep edge from all targets of upstream to the lib
571 // target of downstream.
572 for pkg in cargo.packages() {
573 for dep in cargo[pkg].dependencies.iter() {
574 let name = CrateName::new(&dep.name).unwrap();
575 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
576 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
577 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
578 // Only build scripts may depend on build dependencies.
581 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
582 // Build scripts may only depend on build dependencies.
586 add_dep(&mut crate_graph, *from, name.clone(), to)
593 // If the user provided a path to rustc sources, we add all the rustc_private crates
594 // and create dependencies on them for the crates which opt-in to that
595 if let Some(rustc_workspace) = rustc {
602 &mut pkg_to_lib_crate,
612 fn detached_files_to_crate_graph(
613 rustc_cfg: Vec<CfgFlag>,
614 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
615 detached_files: &[AbsPathBuf],
618 let _p = profile::span("detached_files_to_crate_graph");
619 let mut crate_graph = CrateGraph::default();
620 let (public_deps, _libproc_macro) =
621 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
623 let mut cfg_options = CfgOptions::default();
624 cfg_options.extend(rustc_cfg);
626 for detached_file in detached_files {
627 let file_id = match load(detached_file) {
628 Some(file_id) => file_id,
630 log::error!("Failed to load detached file {:?}", detached_file);
634 let display_name = detached_file
636 .and_then(|os_str| os_str.to_str())
637 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
638 let detached_file_crate = crate_graph.add_crate_root(
648 for (name, krate) in public_deps.iter() {
649 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
655 fn handle_rustc_crates(
656 rustc_workspace: &CargoWorkspace,
657 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
658 crate_graph: &mut CrateGraph,
659 cfg_options: &CfgOptions,
660 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
661 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
662 public_deps: &[(CrateName, CrateId)],
663 cargo: &CargoWorkspace,
664 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
666 let mut rustc_pkg_crates = FxHashMap::default();
667 // The root package of the rustc-dev component is rustc_driver, so we match that
669 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
670 // The rustc workspace might be incomplete (such as if rustc-dev is not
671 // installed for the current toolchain) and `rustcSource` is set to discover.
672 if let Some(root_pkg) = root_pkg {
673 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
674 let mut queue = VecDeque::new();
675 queue.push_back(root_pkg);
676 while let Some(pkg) = queue.pop_front() {
677 // Don't duplicate packages if they are dependended on a diamond pattern
678 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
679 // which is not ideal
680 if rustc_pkg_crates.contains_key(&pkg) {
683 for dep in &rustc_workspace[pkg].dependencies {
684 queue.push_back(dep.pkg);
686 for &tgt in rustc_workspace[pkg].targets.iter() {
687 if rustc_workspace[tgt].kind != TargetKind::Lib {
690 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
691 let crate_id = add_target_crate_root(
693 &rustc_workspace[pkg],
698 &rustc_workspace[tgt].name,
700 pkg_to_lib_crate.insert(pkg, crate_id);
701 // Add dependencies on core / std / alloc for this crate
702 for (name, krate) in public_deps.iter() {
703 add_dep(crate_graph, crate_id, name.clone(), *krate);
705 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
710 // Now add a dep edge from all targets of upstream to the lib
711 // target of downstream.
712 for pkg in rustc_pkg_crates.keys().copied() {
713 for dep in rustc_workspace[pkg].dependencies.iter() {
714 let name = CrateName::new(&dep.name).unwrap();
715 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
716 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
717 add_dep(crate_graph, from, name.clone(), to);
722 // Add a dependency on the rustc_private crates for all targets of each package
724 for dep in rustc_workspace.packages() {
725 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
727 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
728 for pkg in cargo.packages() {
729 let package = &cargo[pkg];
730 if !package.metadata.rustc_private {
733 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
734 // Avoid creating duplicate dependencies
735 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
736 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
737 // instead of the one from `crates.io`
738 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
739 add_dep(crate_graph, *from, name.clone(), to);
747 fn add_target_crate_root(
748 crate_graph: &mut CrateGraph,
750 build_data: Option<&BuildScriptOutput>,
751 cfg_options: &CfgOptions,
752 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
756 let edition = pkg.edition;
758 let mut opts = cfg_options.clone();
759 for feature in pkg.active_features.iter() {
760 opts.insert_key_value("feature".into(), feature.into());
762 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
763 opts.extend(cfgs.iter().cloned());
768 let mut env = Env::default();
769 inject_cargo_env(pkg, &mut env);
771 if let Some(envs) = build_data.map(|it| &it.envs) {
773 env.set(k, v.clone());
777 let proc_macro = build_data
779 .and_then(|it| it.proc_macro_dylib_path.as_ref())
780 .map(|it| proc_macro_loader(it))
781 .unwrap_or_default();
783 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
784 let mut potential_cfg_options = cfg_options.clone();
785 potential_cfg_options.extend(
788 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
791 let crate_id = crate_graph.add_crate_root(
796 potential_cfg_options,
804 fn sysroot_to_crate_graph(
805 crate_graph: &mut CrateGraph,
807 rustc_cfg: Vec<CfgFlag>,
808 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
809 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
810 let _p = profile::span("sysroot_to_crate_graph");
811 let mut cfg_options = CfgOptions::default();
812 cfg_options.extend(rustc_cfg);
813 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
815 .filter_map(|krate| {
816 let file_id = load(&sysroot[krate].root)?;
818 let env = Env::default();
819 let proc_macro = vec![];
820 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
821 let crate_id = crate_graph.add_crate_root(
830 Some((krate, crate_id))
834 for from in sysroot.crates() {
835 for &to in sysroot[from].deps.iter() {
836 let name = CrateName::new(&sysroot[to].name).unwrap();
837 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
838 add_dep(crate_graph, from, name, to);
843 let public_deps = sysroot
845 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
846 .collect::<Vec<_>>();
848 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
849 (public_deps, libproc_macro)
852 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
853 if let Err(err) = graph.add_dep(from, name, to) {
854 log::error!("{}", err)
858 /// Recreates the compile-time environment variables that Cargo sets.
860 /// Should be synced with
861 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
863 /// FIXME: ask Cargo to provide this data instead of re-deriving.
864 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
865 // FIXME: Missing variables:
866 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
868 let manifest_dir = package.manifest.parent();
869 env.set("CARGO_MANIFEST_DIR".into(), manifest_dir.as_os_str().to_string_lossy().into_owned());
871 // Not always right, but works for common cases.
872 env.set("CARGO".into(), "cargo".into());
874 env.set("CARGO_PKG_VERSION".into(), package.version.to_string());
875 env.set("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string());
876 env.set("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string());
877 env.set("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string());
878 env.set("CARGO_PKG_VERSION_PRE".into(), package.version.pre.to_string());
880 env.set("CARGO_PKG_AUTHORS".into(), String::new());
882 env.set("CARGO_PKG_NAME".into(), package.name.clone());
883 // FIXME: This isn't really correct (a package can have many crates with different names), but
884 // it's better than leaving the variable unset.
885 env.set("CARGO_CRATE_NAME".into(), CrateName::normalize_dashes(&package.name).to_string());
886 env.set("CARGO_PKG_DESCRIPTION".into(), String::new());
887 env.set("CARGO_PKG_HOMEPAGE".into(), String::new());
888 env.set("CARGO_PKG_REPOSITORY".into(), String::new());
889 env.set("CARGO_PKG_LICENSE".into(), String::new());
891 env.set("CARGO_PKG_LICENSE_FILE".into(), String::new());