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, _proc_macro)) = &sysroot_deps {
450 for (name, to) in public_deps.iter() {
451 add_dep(&mut crate_graph, from, name.clone(), *to)
455 for dep in &krate.deps {
456 if let Some(&to) = crates.get(&dep.crate_id) {
457 add_dep(&mut crate_graph, from, dep.name.clone(), to)
465 fn cargo_to_crate_graph(
466 rustc_cfg: Vec<CfgFlag>,
467 override_cfg: &CfgOverrides,
468 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
469 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
470 cargo: &CargoWorkspace,
471 build_scripts: &WorkspaceBuildScripts,
473 rustc: &Option<CargoWorkspace>,
475 let _p = profile::span("cargo_to_crate_graph");
476 let mut crate_graph = CrateGraph::default();
477 let (public_deps, libproc_macro) =
478 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
480 let mut cfg_options = CfgOptions::default();
481 cfg_options.extend(rustc_cfg);
483 let mut pkg_to_lib_crate = FxHashMap::default();
485 // Add test cfg for non-sysroot crates
486 cfg_options.insert_atom("test".into());
487 cfg_options.insert_atom("debug_assertions".into());
489 let mut pkg_crates = FxHashMap::default();
490 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
491 let mut has_private = false;
492 // Next, create crates for each package, target pair
493 for pkg in cargo.packages() {
494 let mut cfg_options = &cfg_options;
495 let mut replaced_cfg_options;
496 if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
497 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
498 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
499 // working on rust-lang/rust as that's the only time it appears outside sysroot).
501 // A more ideal solution might be to reanalyze crates based on where the cursor is and
502 // figure out the set of cfgs that would have to apply to make it active.
504 replaced_cfg_options = cfg_options.clone();
505 replaced_cfg_options.apply_diff(overrides.clone());
506 cfg_options = &replaced_cfg_options;
509 has_private |= cargo[pkg].metadata.rustc_private;
510 let mut lib_tgt = None;
511 for &tgt in cargo[pkg].targets.iter() {
512 if let Some(file_id) = load(&cargo[tgt].root) {
513 let crate_id = add_target_crate_root(
516 build_scripts.outputs.get(pkg),
522 if cargo[tgt].kind == TargetKind::Lib {
523 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
524 pkg_to_lib_crate.insert(pkg, crate_id);
526 if cargo[tgt].is_proc_macro {
527 if let Some(proc_macro) = libproc_macro {
531 CrateName::new("proc_macro").unwrap(),
537 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
541 // Set deps to the core, std and to the lib target of the current package
542 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
543 if let Some((to, name)) = lib_tgt.clone() {
544 if to != *from && *kind != TargetKind::BuildScript {
545 // (build script can not depend on its library target)
547 // For root projects with dashes in their name,
548 // cargo metadata does not do any normalization,
549 // so we do it ourselves currently
550 let name = CrateName::normalize_dashes(&name);
551 add_dep(&mut crate_graph, *from, name, to);
554 for (name, krate) in public_deps.iter() {
555 add_dep(&mut crate_graph, *from, name.clone(), *krate);
560 // Now add a dep edge from all targets of upstream to the lib
561 // target of downstream.
562 for pkg in cargo.packages() {
563 for dep in cargo[pkg].dependencies.iter() {
564 let name = CrateName::new(&dep.name).unwrap();
565 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
566 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
567 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
568 // Only build scripts may depend on build dependencies.
571 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
572 // Build scripts may only depend on build dependencies.
576 add_dep(&mut crate_graph, *from, name.clone(), to)
583 // If the user provided a path to rustc sources, we add all the rustc_private crates
584 // and create dependencies on them for the crates which opt-in to that
585 if let Some(rustc_workspace) = rustc {
592 &mut pkg_to_lib_crate,
602 fn detached_files_to_crate_graph(
603 rustc_cfg: Vec<CfgFlag>,
604 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
605 detached_files: &[AbsPathBuf],
608 let _p = profile::span("detached_files_to_crate_graph");
609 let mut crate_graph = CrateGraph::default();
610 let (public_deps, _libproc_macro) =
611 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
613 let mut cfg_options = CfgOptions::default();
614 cfg_options.extend(rustc_cfg);
616 for detached_file in detached_files {
617 let file_id = match load(detached_file) {
618 Some(file_id) => file_id,
620 log::error!("Failed to load detached file {:?}", detached_file);
624 let display_name = detached_file
626 .and_then(|os_str| os_str.to_str())
627 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
628 let detached_file_crate = crate_graph.add_crate_root(
638 for (name, krate) in public_deps.iter() {
639 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
645 fn handle_rustc_crates(
646 rustc_workspace: &CargoWorkspace,
647 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
648 crate_graph: &mut CrateGraph,
649 cfg_options: &CfgOptions,
650 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
651 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
652 public_deps: &[(CrateName, CrateId)],
653 cargo: &CargoWorkspace,
654 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
656 let mut rustc_pkg_crates = FxHashMap::default();
657 // The root package of the rustc-dev component is rustc_driver, so we match that
659 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
660 // The rustc workspace might be incomplete (such as if rustc-dev is not
661 // installed for the current toolchain) and `rustcSource` is set to discover.
662 if let Some(root_pkg) = root_pkg {
663 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
664 let mut queue = VecDeque::new();
665 queue.push_back(root_pkg);
666 while let Some(pkg) = queue.pop_front() {
667 // Don't duplicate packages if they are dependended on a diamond pattern
668 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
669 // which is not ideal
670 if rustc_pkg_crates.contains_key(&pkg) {
673 for dep in &rustc_workspace[pkg].dependencies {
674 queue.push_back(dep.pkg);
676 for &tgt in rustc_workspace[pkg].targets.iter() {
677 if rustc_workspace[tgt].kind != TargetKind::Lib {
680 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
681 let crate_id = add_target_crate_root(
683 &rustc_workspace[pkg],
688 &rustc_workspace[tgt].name,
690 pkg_to_lib_crate.insert(pkg, crate_id);
691 // Add dependencies on core / std / alloc for this crate
692 for (name, krate) in public_deps.iter() {
693 add_dep(crate_graph, crate_id, name.clone(), *krate);
695 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
700 // Now add a dep edge from all targets of upstream to the lib
701 // target of downstream.
702 for pkg in rustc_pkg_crates.keys().copied() {
703 for dep in rustc_workspace[pkg].dependencies.iter() {
704 let name = CrateName::new(&dep.name).unwrap();
705 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
706 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
707 add_dep(crate_graph, from, name.clone(), to);
712 // Add a dependency on the rustc_private crates for all targets of each package
714 for dep in rustc_workspace.packages() {
715 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
717 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
718 for pkg in cargo.packages() {
719 let package = &cargo[pkg];
720 if !package.metadata.rustc_private {
723 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
724 // Avoid creating duplicate dependencies
725 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
726 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
727 // instead of the one from `crates.io`
728 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
729 add_dep(crate_graph, *from, name.clone(), to);
737 fn add_target_crate_root(
738 crate_graph: &mut CrateGraph,
740 build_data: Option<&BuildScriptOutput>,
741 cfg_options: &CfgOptions,
742 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
746 let edition = pkg.edition;
748 let mut opts = cfg_options.clone();
749 for feature in pkg.active_features.iter() {
750 opts.insert_key_value("feature".into(), feature.into());
752 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
753 opts.extend(cfgs.iter().cloned());
758 let mut env = Env::default();
759 inject_cargo_env(pkg, &mut env);
761 if let Some(envs) = build_data.map(|it| &it.envs) {
763 env.set(k, v.clone());
767 let proc_macro = build_data
769 .and_then(|it| it.proc_macro_dylib_path.as_ref())
770 .map(|it| proc_macro_loader(it))
771 .unwrap_or_default();
773 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
774 let mut potential_cfg_options = cfg_options.clone();
775 potential_cfg_options.extend(
778 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
781 let crate_id = crate_graph.add_crate_root(
786 potential_cfg_options,
794 fn sysroot_to_crate_graph(
795 crate_graph: &mut CrateGraph,
797 rustc_cfg: Vec<CfgFlag>,
798 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
799 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
800 let _p = profile::span("sysroot_to_crate_graph");
801 let mut cfg_options = CfgOptions::default();
802 cfg_options.extend(rustc_cfg);
803 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
805 .filter_map(|krate| {
806 let file_id = load(&sysroot[krate].root)?;
808 let env = Env::default();
809 let proc_macro = vec![];
810 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
811 let crate_id = crate_graph.add_crate_root(
820 Some((krate, crate_id))
824 for from in sysroot.crates() {
825 for &to in sysroot[from].deps.iter() {
826 let name = CrateName::new(&sysroot[to].name).unwrap();
827 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
828 add_dep(crate_graph, from, name, to);
833 let public_deps = sysroot
835 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
836 .collect::<Vec<_>>();
838 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
839 (public_deps, libproc_macro)
842 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
843 if let Err(err) = graph.add_dep(from, name, to) {
844 log::error!("{}", err)
848 /// Recreates the compile-time environment variables that Cargo sets.
850 /// Should be synced with
851 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
853 /// FIXME: ask Cargo to provide this data instead of re-deriving.
854 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
855 // FIXME: Missing variables:
856 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
858 let manifest_dir = package.manifest.parent();
859 env.set("CARGO_MANIFEST_DIR".into(), manifest_dir.as_os_str().to_string_lossy().into_owned());
861 // Not always right, but works for common cases.
862 env.set("CARGO".into(), "cargo".into());
864 env.set("CARGO_PKG_VERSION".into(), package.version.to_string());
865 env.set("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string());
866 env.set("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string());
867 env.set("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string());
868 env.set("CARGO_PKG_VERSION_PRE".into(), package.version.pre.to_string());
870 env.set("CARGO_PKG_AUTHORS".into(), String::new());
872 env.set("CARGO_PKG_NAME".into(), package.name.clone());
873 // FIXME: This isn't really correct (a package can have many crates with different names), but
874 // it's better than leaving the variable unset.
875 env.set("CARGO_CRATE_NAME".into(), CrateName::normalize_dashes(&package.name).to_string());
876 env.set("CARGO_PKG_DESCRIPTION".into(), String::new());
877 env.set("CARGO_PKG_HOMEPAGE".into(), String::new());
878 env.set("CARGO_PKG_REPOSITORY".into(), String::new());
879 env.set("CARGO_PKG_LICENSE".into(), String::new());
881 env.set("CARGO_PKG_LICENSE_FILE".into(), String::new());