1 //! Handles lowering of build-system specific workspace information (`cargo
2 //! metadata` or `rust-project.json`) into representation stored in the salsa
3 //! database -- `CrateGraph`.
5 use std::{collections::VecDeque, convert::TryFrom, fmt, fs, process::Command};
7 use anyhow::{format_err, Context, Result};
8 use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9 use cfg::{CfgDiff, CfgOptions};
10 use paths::{AbsPath, AbsPathBuf};
11 use rustc_hash::{FxHashMap, FxHashSet};
15 build_scripts::BuildScriptOutput,
16 cargo_workspace::{DepKind, PackageData, RustcSource},
19 sysroot::SysrootCrate,
20 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
21 TargetKind, WorkspaceBuildScripts,
24 /// A set of cfg-overrides per crate.
26 /// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
27 /// without having to first obtain a list of all crates.
28 #[derive(Debug, Clone, Eq, PartialEq)]
29 pub enum CfgOverrides {
30 /// A single global set of overrides matching all crates.
32 /// A set of overrides matching specific crates.
33 Selective(FxHashMap<String, CfgDiff>),
36 impl Default for CfgOverrides {
37 fn default() -> Self {
38 Self::Selective(FxHashMap::default())
43 pub fn len(&self) -> usize {
45 CfgOverrides::Wildcard(_) => 1,
46 CfgOverrides::Selective(hash_map) => hash_map.len(),
51 /// `PackageRoot` describes a package root folder.
52 /// Which may be an external dependency, or a member of
53 /// the current workspace.
54 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
55 pub struct PackageRoot {
56 /// Is from the local filesystem and may be edited
58 pub include: Vec<AbsPathBuf>,
59 pub exclude: Vec<AbsPathBuf>,
62 #[derive(Clone, Eq, PartialEq)]
63 pub enum ProjectWorkspace {
64 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
66 cargo: CargoWorkspace,
67 build_scripts: WorkspaceBuildScripts,
68 sysroot: Option<Sysroot>,
69 rustc: Option<CargoWorkspace>,
70 /// Holds cfg flags for the current target. We get those by running
71 /// `rustc --print cfg`.
73 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
75 rustc_cfg: Vec<CfgFlag>,
76 cfg_overrides: CfgOverrides,
78 /// Project workspace was manually specified using a `rust-project.json` file.
79 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
81 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
82 // That's not the end user experience we should strive for.
83 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
84 // That needs some changes on the salsa-level though.
85 // 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).
86 // 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.
87 // 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.
89 /// Project with a set of disjoint files, not belonging to any particular workspace.
90 /// Backed by basic sysroot crates for basic completion and highlighting.
91 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
94 impl fmt::Debug for ProjectWorkspace {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 // Make sure this isn't too verbose.
98 ProjectWorkspace::Cargo {
106 .debug_struct("Cargo")
107 .field("root", &cargo.workspace_root().file_name())
108 .field("n_packages", &cargo.packages().len())
109 .field("sysroot", &sysroot.is_some())
111 "n_rustc_compiler_crates",
112 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
114 .field("n_rustc_cfg", &rustc_cfg.len())
115 .field("n_cfg_overrides", &cfg_overrides.len())
117 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
118 let mut debug_struct = f.debug_struct("Json");
119 debug_struct.field("n_crates", &project.n_crates());
120 if let Some(sysroot) = sysroot {
121 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
123 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
124 debug_struct.finish()
126 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
127 .debug_struct("DetachedFiles")
128 .field("n_files", &files.len())
129 .field("n_sysroot_crates", &sysroot.crates().len())
130 .field("n_rustc_cfg", &rustc_cfg.len())
136 impl ProjectWorkspace {
138 manifest: ProjectManifest,
139 config: &CargoConfig,
140 progress: &dyn Fn(String),
141 ) -> Result<ProjectWorkspace> {
142 let res = match manifest {
143 ProjectManifest::ProjectJson(project_json) => {
144 let file = fs::read_to_string(&project_json).with_context(|| {
145 format!("Failed to read json file {}", project_json.display())
147 let data = serde_json::from_str(&file).with_context(|| {
148 format!("Failed to deserialize json file {}", project_json.display())
150 let project_location = project_json.parent().to_path_buf();
151 let project_json = ProjectJson::new(&project_location, data);
152 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
154 ProjectManifest::CargoToml(cargo_toml) => {
155 let cargo_version = utf8_stdout({
156 let mut cmd = Command::new(toolchain::cargo());
157 cmd.arg("--version");
161 let meta = CargoWorkspace::fetch_metadata(&cargo_toml, config, progress)
164 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
165 cargo_toml.display(),
169 let cargo = CargoWorkspace::new(meta);
171 let sysroot = if config.no_sysroot {
174 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
176 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
182 let rustc_dir = match &config.rustc_source {
183 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
184 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
188 let rustc = match rustc_dir {
189 Some(rustc_dir) => Some({
190 let meta = CargoWorkspace::fetch_metadata(&rustc_dir, config, progress)
192 "Failed to read Cargo metadata for Rust sources".to_string()
194 CargoWorkspace::new(meta)
199 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
201 let cfg_overrides = config.cfg_overrides();
202 ProjectWorkspace::Cargo {
204 build_scripts: WorkspaceBuildScripts::default(),
217 project_json: ProjectJson,
218 target: Option<&str>,
219 ) -> Result<ProjectWorkspace> {
220 let sysroot = match &project_json.sysroot_src {
221 Some(path) => Some(Sysroot::load(path.clone())?),
224 let rustc_cfg = rustc_cfg::get(None, target);
225 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
228 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
229 let sysroot = Sysroot::discover(
232 .and_then(|it| it.parent())
233 .ok_or_else(|| format_err!("No detached files to load"))?,
235 let rustc_cfg = rustc_cfg::get(None, None);
236 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
239 pub fn run_build_scripts(
241 config: &CargoConfig,
242 progress: &dyn Fn(String),
243 ) -> Result<WorkspaceBuildScripts> {
245 ProjectWorkspace::Cargo { cargo, .. } => {
246 WorkspaceBuildScripts::run(config, cargo, progress)
248 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
249 Ok(WorkspaceBuildScripts::default())
254 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
256 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
258 always!(bs == WorkspaceBuildScripts::default());
263 /// Returns the roots for the current `ProjectWorkspace`
264 /// The return type contains the path and whether or not
265 /// the root is a member of the current workspace
266 pub fn to_roots(&self) -> Vec<PackageRoot> {
268 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
270 .map(|(_, krate)| PackageRoot {
271 is_local: krate.is_workspace_member,
272 include: krate.include.clone(),
273 exclude: krate.exclude.clone(),
275 .collect::<FxHashSet<_>>()
277 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
278 sysroot.crates().map(move |krate| PackageRoot {
280 include: vec![sysroot[krate].root.parent().to_path_buf()],
284 .collect::<Vec<_>>(),
285 ProjectWorkspace::Cargo {
296 let is_local = cargo[pkg].is_local;
297 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
299 let mut include = vec![pkg_root.clone()];
301 build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
304 // In case target's path is manually set in Cargo.toml to be
305 // outside the package root, add its parent as an extra include.
306 // An example of this situation would look like this:
310 // path = "../../src/lib.rs"
312 let extra_targets = cargo[pkg]
315 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
316 .filter_map(|&tgt| cargo[tgt].root.parent())
317 .map(|tgt| tgt.normalize().to_path_buf())
318 .filter(|path| !path.starts_with(&pkg_root));
319 include.extend(extra_targets);
321 let mut exclude = vec![pkg_root.join(".git")];
323 exclude.push(pkg_root.join("target"));
325 exclude.push(pkg_root.join("tests"));
326 exclude.push(pkg_root.join("examples"));
327 exclude.push(pkg_root.join("benches"));
329 PackageRoot { is_local, include, exclude }
331 .chain(sysroot.iter().map(|sysroot| PackageRoot {
333 include: vec![sysroot.root().to_path_buf()],
336 .chain(rustc.iter().flat_map(|rustc| {
337 rustc.packages().map(move |krate| PackageRoot {
339 include: vec![rustc[krate].manifest.parent().to_path_buf()],
345 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
347 .map(|detached_file| PackageRoot {
349 include: vec![detached_file.clone()],
352 .chain(sysroot.crates().map(|krate| PackageRoot {
354 include: vec![sysroot[krate].root.parent().to_path_buf()],
361 pub fn n_packages(&self) -> usize {
363 ProjectWorkspace::Json { project, .. } => project.n_crates(),
364 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
365 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
366 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
367 cargo.packages().len() + sysroot_package_len + rustc_package_len
369 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
370 sysroot.crates().len() + files.len()
375 pub fn to_crate_graph(
377 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
378 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
380 let _p = profile::span("ProjectWorkspace::to_crate_graph");
382 let mut crate_graph = match self {
383 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
390 ProjectWorkspace::Cargo {
397 } => cargo_to_crate_graph(
407 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
408 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
411 if crate_graph.patch_cfg_if() {
412 tracing::debug!("Patched std to depend on cfg-if")
414 tracing::debug!("Did not patch std to depend on cfg-if")
420 fn project_json_to_crate_graph(
421 rustc_cfg: Vec<CfgFlag>,
422 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
423 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
424 project: &ProjectJson,
425 sysroot: &Option<Sysroot>,
427 let mut crate_graph = CrateGraph::default();
428 let sysroot_deps = sysroot
430 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
432 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
433 let crates: FxHashMap<CrateId, CrateId> = project
435 .filter_map(|(crate_id, krate)| {
436 let file_path = &krate.root_module;
437 let file_id = load(file_path)?;
438 Some((crate_id, krate, file_id))
440 .map(|(crate_id, krate, file_id)| {
441 let env = krate.env.clone().into_iter().collect();
442 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| load_proc_macro(&it));
444 let target_cfgs = match krate.target.as_deref() {
446 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
451 let mut cfg_options = CfgOptions::default();
452 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
455 crate_graph.add_crate_root(
458 krate.display_name.clone(),
462 proc_macro.unwrap_or_default(),
468 for (from, krate) in project.crates() {
469 if let Some(&from) = crates.get(&from) {
470 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
471 for (name, to) in public_deps.iter() {
472 add_dep(&mut crate_graph, from, name.clone(), *to)
474 if krate.is_proc_macro {
475 if let Some(proc_macro) = libproc_macro {
479 CrateName::new("proc_macro").unwrap(),
486 for dep in &krate.deps {
487 if let Some(&to) = crates.get(&dep.crate_id) {
488 add_dep(&mut crate_graph, from, dep.name.clone(), to)
496 fn cargo_to_crate_graph(
497 rustc_cfg: Vec<CfgFlag>,
498 override_cfg: &CfgOverrides,
499 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
500 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
501 cargo: &CargoWorkspace,
502 build_scripts: &WorkspaceBuildScripts,
503 sysroot: Option<&Sysroot>,
504 rustc: &Option<CargoWorkspace>,
506 let _p = profile::span("cargo_to_crate_graph");
507 let mut crate_graph = CrateGraph::default();
508 let (public_deps, libproc_macro) = match sysroot {
509 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
510 None => (Vec::new(), None),
513 let mut cfg_options = CfgOptions::default();
514 cfg_options.extend(rustc_cfg);
516 let mut pkg_to_lib_crate = FxHashMap::default();
518 // Add test cfg for non-sysroot crates
519 cfg_options.insert_atom("test".into());
520 cfg_options.insert_atom("debug_assertions".into());
522 let mut pkg_crates = FxHashMap::default();
523 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
524 let mut has_private = false;
525 // Next, create crates for each package, target pair
526 for pkg in cargo.packages() {
527 let mut cfg_options = &cfg_options;
528 let mut replaced_cfg_options;
530 let overrides = match override_cfg {
531 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
532 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
535 if let Some(overrides) = overrides {
536 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
537 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
538 // working on rust-lang/rust as that's the only time it appears outside sysroot).
540 // A more ideal solution might be to reanalyze crates based on where the cursor is and
541 // figure out the set of cfgs that would have to apply to make it active.
543 replaced_cfg_options = cfg_options.clone();
544 replaced_cfg_options.apply_diff(overrides.clone());
545 cfg_options = &replaced_cfg_options;
548 has_private |= cargo[pkg].metadata.rustc_private;
549 let mut lib_tgt = None;
550 for &tgt in cargo[pkg].targets.iter() {
551 if let Some(file_id) = load(&cargo[tgt].root) {
552 let crate_id = add_target_crate_root(
555 build_scripts.outputs.get(pkg),
561 if cargo[tgt].kind == TargetKind::Lib {
562 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
563 pkg_to_lib_crate.insert(pkg, crate_id);
565 if let Some(proc_macro) = libproc_macro {
569 CrateName::new("proc_macro").unwrap(),
574 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
578 // Set deps to the core, std and to the lib target of the current package
579 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
580 if let Some((to, name)) = lib_tgt.clone() {
581 if to != *from && *kind != TargetKind::BuildScript {
582 // (build script can not depend on its library target)
584 // For root projects with dashes in their name,
585 // cargo metadata does not do any normalization,
586 // so we do it ourselves currently
587 let name = CrateName::normalize_dashes(&name);
588 add_dep(&mut crate_graph, *from, name, to);
591 for (name, krate) in public_deps.iter() {
592 add_dep(&mut crate_graph, *from, name.clone(), *krate);
597 // Now add a dep edge from all targets of upstream to the lib
598 // target of downstream.
599 for pkg in cargo.packages() {
600 for dep in cargo[pkg].dependencies.iter() {
601 let name = CrateName::new(&dep.name).unwrap();
602 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
603 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
604 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
605 // Only build scripts may depend on build dependencies.
608 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
609 // Build scripts may only depend on build dependencies.
613 add_dep(&mut crate_graph, *from, name.clone(), to)
620 // If the user provided a path to rustc sources, we add all the rustc_private crates
621 // and create dependencies on them for the crates which opt-in to that
622 if let Some(rustc_workspace) = rustc {
629 &mut pkg_to_lib_crate,
639 fn detached_files_to_crate_graph(
640 rustc_cfg: Vec<CfgFlag>,
641 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
642 detached_files: &[AbsPathBuf],
645 let _p = profile::span("detached_files_to_crate_graph");
646 let mut crate_graph = CrateGraph::default();
647 let (public_deps, _libproc_macro) =
648 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
650 let mut cfg_options = CfgOptions::default();
651 cfg_options.extend(rustc_cfg);
653 for detached_file in detached_files {
654 let file_id = match load(detached_file) {
655 Some(file_id) => file_id,
657 tracing::error!("Failed to load detached file {:?}", detached_file);
661 let display_name = detached_file
663 .and_then(|os_str| os_str.to_str())
664 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
665 let detached_file_crate = crate_graph.add_crate_root(
675 for (name, krate) in public_deps.iter() {
676 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
682 fn handle_rustc_crates(
683 rustc_workspace: &CargoWorkspace,
684 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
685 crate_graph: &mut CrateGraph,
686 cfg_options: &CfgOptions,
687 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
688 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
689 public_deps: &[(CrateName, CrateId)],
690 cargo: &CargoWorkspace,
691 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
693 let mut rustc_pkg_crates = FxHashMap::default();
694 // The root package of the rustc-dev component is rustc_driver, so we match that
696 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
697 // The rustc workspace might be incomplete (such as if rustc-dev is not
698 // installed for the current toolchain) and `rustcSource` is set to discover.
699 if let Some(root_pkg) = root_pkg {
700 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
701 let mut queue = VecDeque::new();
702 queue.push_back(root_pkg);
703 while let Some(pkg) = queue.pop_front() {
704 // Don't duplicate packages if they are dependended on a diamond pattern
705 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
706 // which is not ideal
707 if rustc_pkg_crates.contains_key(&pkg) {
710 for dep in &rustc_workspace[pkg].dependencies {
711 queue.push_back(dep.pkg);
713 for &tgt in rustc_workspace[pkg].targets.iter() {
714 if rustc_workspace[tgt].kind != TargetKind::Lib {
717 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
718 let crate_id = add_target_crate_root(
720 &rustc_workspace[pkg],
725 &rustc_workspace[tgt].name,
727 pkg_to_lib_crate.insert(pkg, crate_id);
728 // Add dependencies on core / std / alloc for this crate
729 for (name, krate) in public_deps.iter() {
730 add_dep(crate_graph, crate_id, name.clone(), *krate);
732 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
737 // Now add a dep edge from all targets of upstream to the lib
738 // target of downstream.
739 for pkg in rustc_pkg_crates.keys().copied() {
740 for dep in rustc_workspace[pkg].dependencies.iter() {
741 let name = CrateName::new(&dep.name).unwrap();
742 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
743 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
744 add_dep(crate_graph, from, name.clone(), to);
749 // Add a dependency on the rustc_private crates for all targets of each package
751 for dep in rustc_workspace.packages() {
752 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
754 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
755 for pkg in cargo.packages() {
756 let package = &cargo[pkg];
757 if !package.metadata.rustc_private {
760 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
761 // Avoid creating duplicate dependencies
762 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
763 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
764 // instead of the one from `crates.io`
765 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
766 add_dep(crate_graph, *from, name.clone(), to);
774 fn add_target_crate_root(
775 crate_graph: &mut CrateGraph,
777 build_data: Option<&BuildScriptOutput>,
778 cfg_options: &CfgOptions,
779 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
783 let edition = pkg.edition;
785 let mut opts = cfg_options.clone();
786 for feature in pkg.active_features.iter() {
787 opts.insert_key_value("feature".into(), feature.into());
789 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
790 opts.extend(cfgs.iter().cloned());
795 let mut env = Env::default();
796 inject_cargo_env(pkg, &mut env);
798 if let Some(envs) = build_data.map(|it| &it.envs) {
800 env.set(k, v.clone());
804 let proc_macro = build_data
806 .and_then(|it| it.proc_macro_dylib_path.as_ref())
807 .map(|it| load_proc_macro(it))
808 .unwrap_or_default();
810 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
811 let mut potential_cfg_options = cfg_options.clone();
812 potential_cfg_options.extend(
815 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
818 crate_graph.add_crate_root(
823 potential_cfg_options,
829 fn sysroot_to_crate_graph(
830 crate_graph: &mut CrateGraph,
832 rustc_cfg: Vec<CfgFlag>,
833 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
834 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
835 let _p = profile::span("sysroot_to_crate_graph");
836 let mut cfg_options = CfgOptions::default();
837 cfg_options.extend(rustc_cfg);
838 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
840 .filter_map(|krate| {
841 let file_id = load(&sysroot[krate].root)?;
843 let env = Env::default();
844 let proc_macro = vec![];
845 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
846 let crate_id = crate_graph.add_crate_root(
855 Some((krate, crate_id))
859 for from in sysroot.crates() {
860 for &to in sysroot[from].deps.iter() {
861 let name = CrateName::new(&sysroot[to].name).unwrap();
862 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
863 add_dep(crate_graph, from, name, to);
868 let public_deps = sysroot
870 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
871 .collect::<Vec<_>>();
873 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
874 (public_deps, libproc_macro)
877 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
878 if let Err(err) = graph.add_dep(from, name, to) {
879 tracing::error!("{}", err)
883 /// Recreates the compile-time environment variables that Cargo sets.
885 /// Should be synced with
886 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
888 /// FIXME: ask Cargo to provide this data instead of re-deriving.
889 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
890 // FIXME: Missing variables:
891 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
893 let manifest_dir = package.manifest.parent();
894 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
896 // Not always right, but works for common cases.
897 env.set("CARGO", "cargo".into());
899 env.set("CARGO_PKG_VERSION", package.version.to_string());
900 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
901 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
902 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
903 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
905 env.set("CARGO_PKG_AUTHORS", String::new());
907 env.set("CARGO_PKG_NAME", package.name.clone());
908 // FIXME: This isn't really correct (a package can have many crates with different names), but
909 // it's better than leaving the variable unset.
910 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
911 env.set("CARGO_PKG_DESCRIPTION", String::new());
912 env.set("CARGO_PKG_HOMEPAGE", String::new());
913 env.set("CARGO_PKG_REPOSITORY", String::new());
914 env.set("CARGO_PKG_LICENSE", String::new());
916 env.set("CARGO_PKG_LICENSE_FILE", String::new());