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, fmt, fs, process::Command};
7 use anyhow::{format_err, Context, Result};
9 CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacro,
11 use cfg::{CfgDiff, CfgOptions};
12 use paths::{AbsPath, AbsPathBuf};
13 use rustc_hash::{FxHashMap, FxHashSet};
17 build_scripts::BuildScriptOutput,
18 cargo_workspace::{DepKind, PackageData, RustcSource},
21 sysroot::SysrootCrate,
22 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
23 TargetKind, WorkspaceBuildScripts,
26 /// A set of cfg-overrides per crate.
28 /// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
29 /// without having to first obtain a list of all crates.
30 #[derive(Debug, Clone, Eq, PartialEq)]
31 pub enum CfgOverrides {
32 /// A single global set of overrides matching all crates.
34 /// A set of overrides matching specific crates.
35 Selective(FxHashMap<String, CfgDiff>),
38 impl Default for CfgOverrides {
39 fn default() -> Self {
40 Self::Selective(FxHashMap::default())
45 pub fn len(&self) -> usize {
47 CfgOverrides::Wildcard(_) => 1,
48 CfgOverrides::Selective(hash_map) => hash_map.len(),
53 /// `PackageRoot` describes a package root folder.
54 /// Which may be an external dependency, or a member of
55 /// the current workspace.
56 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
57 pub struct PackageRoot {
58 /// Is from the local filesystem and may be edited
60 pub include: Vec<AbsPathBuf>,
61 pub exclude: Vec<AbsPathBuf>,
64 #[derive(Clone, Eq, PartialEq)]
65 pub enum ProjectWorkspace {
66 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
68 cargo: CargoWorkspace,
69 build_scripts: WorkspaceBuildScripts,
70 sysroot: Option<Sysroot>,
71 rustc: Option<CargoWorkspace>,
72 /// Holds cfg flags for the current target. We get those by running
73 /// `rustc --print cfg`.
75 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
77 rustc_cfg: Vec<CfgFlag>,
78 cfg_overrides: CfgOverrides,
80 /// Project workspace was manually specified using a `rust-project.json` file.
81 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
83 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
84 // That's not the end user experience we should strive for.
85 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
86 // That needs some changes on the salsa-level though.
87 // 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).
88 // 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.
89 // 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.
91 /// Project with a set of disjoint files, not belonging to any particular workspace.
92 /// Backed by basic sysroot crates for basic completion and highlighting.
93 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
96 impl fmt::Debug for ProjectWorkspace {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 // Make sure this isn't too verbose.
100 ProjectWorkspace::Cargo {
108 .debug_struct("Cargo")
109 .field("root", &cargo.workspace_root().file_name())
110 .field("n_packages", &cargo.packages().len())
111 .field("sysroot", &sysroot.is_some())
113 "n_rustc_compiler_crates",
114 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
116 .field("n_rustc_cfg", &rustc_cfg.len())
117 .field("n_cfg_overrides", &cfg_overrides.len())
119 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
120 let mut debug_struct = f.debug_struct("Json");
121 debug_struct.field("n_crates", &project.n_crates());
122 if let Some(sysroot) = sysroot {
123 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
125 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
126 debug_struct.finish()
128 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
129 .debug_struct("DetachedFiles")
130 .field("n_files", &files.len())
131 .field("n_sysroot_crates", &sysroot.crates().len())
132 .field("n_rustc_cfg", &rustc_cfg.len())
138 impl ProjectWorkspace {
140 manifest: ProjectManifest,
141 config: &CargoConfig,
142 progress: &dyn Fn(String),
143 ) -> Result<ProjectWorkspace> {
144 let res = match manifest {
145 ProjectManifest::ProjectJson(project_json) => {
146 let file = fs::read_to_string(&project_json).with_context(|| {
147 format!("Failed to read json file {}", project_json.display())
149 let data = serde_json::from_str(&file).with_context(|| {
150 format!("Failed to deserialize json file {}", project_json.display())
152 let project_location = project_json.parent().to_path_buf();
153 let project_json = ProjectJson::new(&project_location, data);
154 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
156 ProjectManifest::CargoToml(cargo_toml) => {
157 let cargo_version = utf8_stdout({
158 let mut cmd = Command::new(toolchain::cargo());
159 cmd.arg("--version");
163 let meta = CargoWorkspace::fetch_metadata(
171 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
172 cargo_toml.display(),
176 let cargo = CargoWorkspace::new(meta);
178 let sysroot = if config.no_sysroot {
181 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
183 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
189 let rustc_dir = match &config.rustc_source {
190 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
191 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
195 let rustc = match rustc_dir {
196 Some(rustc_dir) => Some({
197 let meta = CargoWorkspace::fetch_metadata(
204 "Failed to read Cargo metadata for Rust sources".to_string()
206 CargoWorkspace::new(meta)
211 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
213 let cfg_overrides = config.cfg_overrides();
214 ProjectWorkspace::Cargo {
216 build_scripts: WorkspaceBuildScripts::default(),
229 project_json: ProjectJson,
230 target: Option<&str>,
231 ) -> Result<ProjectWorkspace> {
232 let sysroot = match &project_json.sysroot_src {
233 Some(path) => Some(Sysroot::load(path.clone())?),
236 let rustc_cfg = rustc_cfg::get(None, target);
237 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
240 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
241 let sysroot = Sysroot::discover(
244 .and_then(|it| it.parent())
245 .ok_or_else(|| format_err!("No detached files to load"))?,
247 let rustc_cfg = rustc_cfg::get(None, None);
248 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
251 pub fn run_build_scripts(
253 config: &CargoConfig,
254 progress: &dyn Fn(String),
255 ) -> Result<WorkspaceBuildScripts> {
257 ProjectWorkspace::Cargo { cargo, .. } => {
258 WorkspaceBuildScripts::run(config, cargo, progress)
260 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
261 Ok(WorkspaceBuildScripts::default())
266 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
268 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
270 always!(bs == WorkspaceBuildScripts::default());
275 /// Returns the roots for the current `ProjectWorkspace`
276 /// The return type contains the path and whether or not
277 /// the root is a member of the current workspace
278 pub fn to_roots(&self) -> Vec<PackageRoot> {
280 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
282 .map(|(_, krate)| PackageRoot {
283 is_local: krate.is_workspace_member,
284 include: krate.include.clone(),
285 exclude: krate.exclude.clone(),
287 .collect::<FxHashSet<_>>()
289 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
290 sysroot.crates().map(move |krate| PackageRoot {
292 include: vec![sysroot[krate].root.parent().to_path_buf()],
296 .collect::<Vec<_>>(),
297 ProjectWorkspace::Cargo {
308 let is_local = cargo[pkg].is_local;
309 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
311 let mut include = vec![pkg_root.clone()];
313 build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
316 // In case target's path is manually set in Cargo.toml to be
317 // outside the package root, add its parent as an extra include.
318 // An example of this situation would look like this:
322 // path = "../../src/lib.rs"
324 let extra_targets = cargo[pkg]
327 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
328 .filter_map(|&tgt| cargo[tgt].root.parent())
329 .map(|tgt| tgt.normalize().to_path_buf())
330 .filter(|path| !path.starts_with(&pkg_root));
331 include.extend(extra_targets);
333 let mut exclude = vec![pkg_root.join(".git")];
335 exclude.push(pkg_root.join("target"));
337 exclude.push(pkg_root.join("tests"));
338 exclude.push(pkg_root.join("examples"));
339 exclude.push(pkg_root.join("benches"));
341 PackageRoot { is_local, include, exclude }
343 .chain(sysroot.iter().map(|sysroot| PackageRoot {
345 include: vec![sysroot.root().to_path_buf()],
348 .chain(rustc.iter().flat_map(|rustc| {
349 rustc.packages().map(move |krate| PackageRoot {
351 include: vec![rustc[krate].manifest.parent().to_path_buf()],
357 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
359 .map(|detached_file| PackageRoot {
361 include: vec![detached_file.clone()],
364 .chain(sysroot.crates().map(|krate| PackageRoot {
366 include: vec![sysroot[krate].root.parent().to_path_buf()],
373 pub fn n_packages(&self) -> usize {
375 ProjectWorkspace::Json { project, .. } => project.n_crates(),
376 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
377 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
378 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
379 cargo.packages().len() + sysroot_package_len + rustc_package_len
381 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
382 sysroot.crates().len() + files.len()
387 pub fn to_crate_graph(
389 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
390 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
392 let _p = profile::span("ProjectWorkspace::to_crate_graph");
394 let mut crate_graph = match self {
395 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
402 ProjectWorkspace::Cargo {
409 } => cargo_to_crate_graph(
419 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
420 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
423 if crate_graph.patch_cfg_if() {
424 tracing::debug!("Patched std to depend on cfg-if")
426 tracing::debug!("Did not patch std to depend on cfg-if")
432 fn project_json_to_crate_graph(
433 rustc_cfg: Vec<CfgFlag>,
434 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
435 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
436 project: &ProjectJson,
437 sysroot: &Option<Sysroot>,
439 let mut crate_graph = CrateGraph::default();
440 let sysroot_deps = sysroot
442 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
444 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
445 let crates: FxHashMap<CrateId, CrateId> = project
447 .filter_map(|(crate_id, krate)| {
448 let file_path = &krate.root_module;
449 let file_id = load(file_path)?;
450 Some((crate_id, krate, file_id))
452 .map(|(crate_id, krate, file_id)| {
453 let env = krate.env.clone().into_iter().collect();
454 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| load_proc_macro(&it));
456 let target_cfgs = match krate.target.as_deref() {
458 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
463 let mut cfg_options = CfgOptions::default();
464 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
467 crate_graph.add_crate_root(
470 krate.display_name.clone(),
471 krate.version.clone(),
475 proc_macro.unwrap_or_default(),
481 for (from, krate) in project.crates() {
482 if let Some(&from) = crates.get(&from) {
483 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
484 public_deps.add(from, &mut crate_graph);
485 if krate.is_proc_macro {
486 if let Some(proc_macro) = libproc_macro {
490 CrateName::new("proc_macro").unwrap(),
497 for dep in &krate.deps {
498 if let Some(&to) = crates.get(&dep.crate_id) {
499 add_dep(&mut crate_graph, from, dep.name.clone(), to)
507 fn cargo_to_crate_graph(
508 rustc_cfg: Vec<CfgFlag>,
509 override_cfg: &CfgOverrides,
510 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
511 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
512 cargo: &CargoWorkspace,
513 build_scripts: &WorkspaceBuildScripts,
514 sysroot: Option<&Sysroot>,
515 rustc: &Option<CargoWorkspace>,
517 let _p = profile::span("cargo_to_crate_graph");
518 let mut crate_graph = CrateGraph::default();
519 let (public_deps, libproc_macro) = match sysroot {
520 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
521 None => (SysrootPublicDeps::default(), None),
524 let mut cfg_options = CfgOptions::default();
525 cfg_options.extend(rustc_cfg);
527 let mut pkg_to_lib_crate = FxHashMap::default();
529 // Add test cfg for non-sysroot crates
530 cfg_options.insert_atom("test".into());
531 cfg_options.insert_atom("debug_assertions".into());
533 let mut pkg_crates = FxHashMap::default();
534 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
535 let mut has_private = false;
536 // Next, create crates for each package, target pair
537 for pkg in cargo.packages() {
538 let mut cfg_options = &cfg_options;
539 let mut replaced_cfg_options;
541 let overrides = match override_cfg {
542 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
543 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
546 if let Some(overrides) = overrides {
547 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
548 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
549 // working on rust-lang/rust as that's the only time it appears outside sysroot).
551 // A more ideal solution might be to reanalyze crates based on where the cursor is and
552 // figure out the set of cfgs that would have to apply to make it active.
554 replaced_cfg_options = cfg_options.clone();
555 replaced_cfg_options.apply_diff(overrides.clone());
556 cfg_options = &replaced_cfg_options;
559 has_private |= cargo[pkg].metadata.rustc_private;
560 let mut lib_tgt = None;
561 for &tgt in cargo[pkg].targets.iter() {
562 if let Some(file_id) = load(&cargo[tgt].root) {
563 let crate_id = add_target_crate_root(
566 build_scripts.outputs.get(pkg),
572 if cargo[tgt].kind == TargetKind::Lib {
573 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
574 pkg_to_lib_crate.insert(pkg, crate_id);
576 if let Some(proc_macro) = libproc_macro {
577 add_dep_with_prelude(
580 CrateName::new("proc_macro").unwrap(),
582 cargo[tgt].is_proc_macro,
586 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
590 // Set deps to the core, std and to the lib target of the current package
591 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
592 if let Some((to, name)) = lib_tgt.clone() {
593 if to != *from && *kind != TargetKind::BuildScript {
594 // (build script can not depend on its library target)
596 // For root projects with dashes in their name,
597 // cargo metadata does not do any normalization,
598 // so we do it ourselves currently
599 let name = CrateName::normalize_dashes(&name);
600 add_dep(&mut crate_graph, *from, name, to);
603 public_deps.add(*from, &mut crate_graph);
607 // Now add a dep edge from all targets of upstream to the lib
608 // target of downstream.
609 for pkg in cargo.packages() {
610 for dep in cargo[pkg].dependencies.iter() {
611 let name = CrateName::new(&dep.name).unwrap();
612 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
613 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
614 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
615 // Only build scripts may depend on build dependencies.
618 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
619 // Build scripts may only depend on build dependencies.
623 add_dep(&mut crate_graph, *from, name.clone(), to)
630 // If the user provided a path to rustc sources, we add all the rustc_private crates
631 // and create dependencies on them for the crates which opt-in to that
632 if let Some(rustc_workspace) = rustc {
639 &mut pkg_to_lib_crate,
649 fn detached_files_to_crate_graph(
650 rustc_cfg: Vec<CfgFlag>,
651 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
652 detached_files: &[AbsPathBuf],
655 let _p = profile::span("detached_files_to_crate_graph");
656 let mut crate_graph = CrateGraph::default();
657 let (public_deps, _libproc_macro) =
658 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
660 let mut cfg_options = CfgOptions::default();
661 cfg_options.extend(rustc_cfg);
663 for detached_file in detached_files {
664 let file_id = match load(detached_file) {
665 Some(file_id) => file_id,
667 tracing::error!("Failed to load detached file {:?}", detached_file);
671 let display_name = detached_file
673 .and_then(|os_str| os_str.to_str())
674 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
675 let detached_file_crate = crate_graph.add_crate_root(
686 public_deps.add(detached_file_crate, &mut crate_graph);
691 fn handle_rustc_crates(
692 rustc_workspace: &CargoWorkspace,
693 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
694 crate_graph: &mut CrateGraph,
695 cfg_options: &CfgOptions,
696 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
697 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
698 public_deps: &SysrootPublicDeps,
699 cargo: &CargoWorkspace,
700 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
702 let mut rustc_pkg_crates = FxHashMap::default();
703 // The root package of the rustc-dev component is rustc_driver, so we match that
705 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
706 // The rustc workspace might be incomplete (such as if rustc-dev is not
707 // installed for the current toolchain) and `rustcSource` is set to discover.
708 if let Some(root_pkg) = root_pkg {
709 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
710 let mut queue = VecDeque::new();
711 queue.push_back(root_pkg);
712 while let Some(pkg) = queue.pop_front() {
713 // Don't duplicate packages if they are dependended on a diamond pattern
714 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
715 // which is not ideal
716 if rustc_pkg_crates.contains_key(&pkg) {
719 for dep in &rustc_workspace[pkg].dependencies {
720 queue.push_back(dep.pkg);
722 for &tgt in rustc_workspace[pkg].targets.iter() {
723 if rustc_workspace[tgt].kind != TargetKind::Lib {
726 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
727 let crate_id = add_target_crate_root(
729 &rustc_workspace[pkg],
734 &rustc_workspace[tgt].name,
736 pkg_to_lib_crate.insert(pkg, crate_id);
737 // Add dependencies on core / std / alloc for this crate
738 public_deps.add(crate_id, crate_graph);
739 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
744 // Now add a dep edge from all targets of upstream to the lib
745 // target of downstream.
746 for pkg in rustc_pkg_crates.keys().copied() {
747 for dep in rustc_workspace[pkg].dependencies.iter() {
748 let name = CrateName::new(&dep.name).unwrap();
749 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
750 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
751 add_dep(crate_graph, from, name.clone(), to);
756 // Add a dependency on the rustc_private crates for all targets of each package
758 for dep in rustc_workspace.packages() {
759 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
761 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
762 for pkg in cargo.packages() {
763 let package = &cargo[pkg];
764 if !package.metadata.rustc_private {
767 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
768 // Avoid creating duplicate dependencies
769 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
770 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
771 // instead of the one from `crates.io`
772 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
773 add_dep(crate_graph, *from, name.clone(), to);
781 fn add_target_crate_root(
782 crate_graph: &mut CrateGraph,
784 build_data: Option<&BuildScriptOutput>,
785 cfg_options: &CfgOptions,
786 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
790 let edition = pkg.edition;
792 let mut opts = cfg_options.clone();
793 for feature in pkg.active_features.iter() {
794 opts.insert_key_value("feature".into(), feature.into());
796 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
797 opts.extend(cfgs.iter().cloned());
802 let mut env = Env::default();
803 inject_cargo_env(pkg, &mut env);
805 if let Some(envs) = build_data.map(|it| &it.envs) {
807 env.set(k, v.clone());
811 let proc_macro = build_data
813 .and_then(|it| it.proc_macro_dylib_path.as_ref())
814 .map(|it| load_proc_macro(it))
815 .unwrap_or_default();
817 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
818 let mut potential_cfg_options = cfg_options.clone();
819 potential_cfg_options.extend(
822 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
825 crate_graph.add_crate_root(
829 Some(pkg.version.to_string()),
831 potential_cfg_options,
838 struct SysrootPublicDeps {
839 deps: Vec<(CrateName, CrateId, bool)>,
842 impl SysrootPublicDeps {
843 /// Makes `from` depend on the public sysroot crates.
844 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
845 for (name, krate, prelude) in &self.deps {
846 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
851 fn sysroot_to_crate_graph(
852 crate_graph: &mut CrateGraph,
854 rustc_cfg: Vec<CfgFlag>,
855 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
856 ) -> (SysrootPublicDeps, Option<CrateId>) {
857 let _p = profile::span("sysroot_to_crate_graph");
858 let mut cfg_options = CfgOptions::default();
859 cfg_options.extend(rustc_cfg);
860 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
862 .filter_map(|krate| {
863 let file_id = load(&sysroot[krate].root)?;
865 let env = Env::default();
866 let proc_macro = vec![];
867 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
868 let crate_id = crate_graph.add_crate_root(
878 Some((krate, crate_id))
882 for from in sysroot.crates() {
883 for &to in sysroot[from].deps.iter() {
884 let name = CrateName::new(&sysroot[to].name).unwrap();
885 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
886 add_dep(crate_graph, from, name, to);
891 let public_deps = SysrootPublicDeps {
894 .map(|(name, idx, prelude)| {
895 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
897 .collect::<Vec<_>>(),
900 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
901 (public_deps, libproc_macro)
904 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
905 add_dep_inner(graph, from, Dependency::new(name, to))
908 fn add_dep_with_prelude(
909 graph: &mut CrateGraph,
915 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
918 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
919 if let Err(err) = graph.add_dep(from, dep) {
920 tracing::error!("{}", err)
924 /// Recreates the compile-time environment variables that Cargo sets.
926 /// Should be synced with
927 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
929 /// FIXME: ask Cargo to provide this data instead of re-deriving.
930 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
931 // FIXME: Missing variables:
932 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
934 let manifest_dir = package.manifest.parent();
935 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
937 // Not always right, but works for common cases.
938 env.set("CARGO", "cargo".into());
940 env.set("CARGO_PKG_VERSION", package.version.to_string());
941 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
942 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
943 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
944 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
946 env.set("CARGO_PKG_AUTHORS", String::new());
948 env.set("CARGO_PKG_NAME", package.name.clone());
949 // FIXME: This isn't really correct (a package can have many crates with different names), but
950 // it's better than leaving the variable unset.
951 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
952 env.set("CARGO_PKG_DESCRIPTION", String::new());
953 env.set("CARGO_PKG_HOMEPAGE", String::new());
954 env.set("CARGO_PKG_REPOSITORY", String::new());
955 env.set("CARGO_PKG_LICENSE", String::new());
957 env.set("CARGO_PKG_LICENSE_FILE", String::new());