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, CrateOrigin, Dependency, Edition, Env,
12 use cfg::{CfgDiff, CfgOptions};
13 use paths::{AbsPath, AbsPathBuf};
14 use rustc_hash::{FxHashMap, FxHashSet};
18 build_scripts::BuildScriptOutput,
19 cargo_workspace::{DepKind, PackageData, RustcSource},
22 sysroot::SysrootCrate,
23 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
24 TargetKind, WorkspaceBuildScripts,
27 /// A set of cfg-overrides per crate.
29 /// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
30 /// without having to first obtain a list of all crates.
31 #[derive(Debug, Clone, Eq, PartialEq)]
32 pub enum CfgOverrides {
33 /// A single global set of overrides matching all crates.
35 /// A set of overrides matching specific crates.
36 Selective(FxHashMap<String, CfgDiff>),
39 impl Default for CfgOverrides {
40 fn default() -> Self {
41 Self::Selective(FxHashMap::default())
46 pub fn len(&self) -> usize {
48 CfgOverrides::Wildcard(_) => 1,
49 CfgOverrides::Selective(hash_map) => hash_map.len(),
54 /// `PackageRoot` describes a package root folder.
55 /// Which may be an external dependency, or a member of
56 /// the current workspace.
57 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
58 pub struct PackageRoot {
59 /// Is from the local filesystem and may be edited
61 pub include: Vec<AbsPathBuf>,
62 pub exclude: Vec<AbsPathBuf>,
65 #[derive(Clone, Eq, PartialEq)]
66 pub enum ProjectWorkspace {
67 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
69 cargo: CargoWorkspace,
70 build_scripts: WorkspaceBuildScripts,
71 sysroot: Option<Sysroot>,
72 rustc: Option<CargoWorkspace>,
73 /// Holds cfg flags for the current target. We get those by running
74 /// `rustc --print cfg`.
76 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
78 rustc_cfg: Vec<CfgFlag>,
79 cfg_overrides: CfgOverrides,
81 /// Project workspace was manually specified using a `rust-project.json` file.
82 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
84 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
85 // That's not the end user experience we should strive for.
86 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
87 // That needs some changes on the salsa-level though.
88 // 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).
89 // 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.
90 // 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.
92 /// Project with a set of disjoint files, not belonging to any particular workspace.
93 /// Backed by basic sysroot crates for basic completion and highlighting.
94 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
97 impl fmt::Debug for ProjectWorkspace {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 // Make sure this isn't too verbose.
101 ProjectWorkspace::Cargo {
109 .debug_struct("Cargo")
110 .field("root", &cargo.workspace_root().file_name())
111 .field("n_packages", &cargo.packages().len())
112 .field("sysroot", &sysroot.is_some())
114 "n_rustc_compiler_crates",
115 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
117 .field("n_rustc_cfg", &rustc_cfg.len())
118 .field("n_cfg_overrides", &cfg_overrides.len())
120 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
121 let mut debug_struct = f.debug_struct("Json");
122 debug_struct.field("n_crates", &project.n_crates());
123 if let Some(sysroot) = sysroot {
124 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
126 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
127 debug_struct.finish()
129 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
130 .debug_struct("DetachedFiles")
131 .field("n_files", &files.len())
132 .field("n_sysroot_crates", &sysroot.crates().len())
133 .field("n_rustc_cfg", &rustc_cfg.len())
139 impl ProjectWorkspace {
141 manifest: ProjectManifest,
142 config: &CargoConfig,
143 progress: &dyn Fn(String),
144 ) -> Result<ProjectWorkspace> {
145 let res = match manifest {
146 ProjectManifest::ProjectJson(project_json) => {
147 let file = fs::read_to_string(&project_json).with_context(|| {
148 format!("Failed to read json file {}", project_json.display())
150 let data = serde_json::from_str(&file).with_context(|| {
151 format!("Failed to deserialize json file {}", project_json.display())
153 let project_location = project_json.parent().to_path_buf();
154 let project_json = ProjectJson::new(&project_location, data);
155 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
157 ProjectManifest::CargoToml(cargo_toml) => {
158 let cargo_version = utf8_stdout({
159 let mut cmd = Command::new(toolchain::cargo());
160 cmd.arg("--version");
164 let meta = CargoWorkspace::fetch_metadata(
172 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
173 cargo_toml.display(),
177 let cargo = CargoWorkspace::new(meta);
179 let sysroot = if config.no_sysroot {
182 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
184 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
190 let rustc_dir = match &config.rustc_source {
191 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
192 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
196 let rustc = match rustc_dir {
197 Some(rustc_dir) => Some({
198 let meta = CargoWorkspace::fetch_metadata(
205 "Failed to read Cargo metadata for Rust sources".to_string()
207 CargoWorkspace::new(meta)
212 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
214 let cfg_overrides = config.cfg_overrides();
215 ProjectWorkspace::Cargo {
217 build_scripts: WorkspaceBuildScripts::default(),
230 project_json: ProjectJson,
231 target: Option<&str>,
232 ) -> Result<ProjectWorkspace> {
233 let sysroot = match &project_json.sysroot_src {
234 Some(path) => Some(Sysroot::load(path.clone())?),
237 let rustc_cfg = rustc_cfg::get(None, target);
238 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
241 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
242 let sysroot = Sysroot::discover(
245 .and_then(|it| it.parent())
246 .ok_or_else(|| format_err!("No detached files to load"))?,
248 let rustc_cfg = rustc_cfg::get(None, None);
249 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
252 pub fn run_build_scripts(
254 config: &CargoConfig,
255 progress: &dyn Fn(String),
256 ) -> Result<WorkspaceBuildScripts> {
258 ProjectWorkspace::Cargo { cargo, .. } => {
259 WorkspaceBuildScripts::run(config, cargo, progress)
261 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
262 Ok(WorkspaceBuildScripts::default())
267 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
269 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
271 always!(bs == WorkspaceBuildScripts::default());
276 /// Returns the roots for the current `ProjectWorkspace`
277 /// The return type contains the path and whether or not
278 /// the root is a member of the current workspace
279 pub fn to_roots(&self) -> Vec<PackageRoot> {
281 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
283 .map(|(_, krate)| PackageRoot {
284 is_local: krate.is_workspace_member,
285 include: krate.include.clone(),
286 exclude: krate.exclude.clone(),
288 .collect::<FxHashSet<_>>()
290 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
291 sysroot.crates().map(move |krate| PackageRoot {
293 include: vec![sysroot[krate].root.parent().to_path_buf()],
297 .collect::<Vec<_>>(),
298 ProjectWorkspace::Cargo {
309 let is_local = cargo[pkg].is_local;
310 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
312 let mut include = vec![pkg_root.clone()];
314 build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
317 // In case target's path is manually set in Cargo.toml to be
318 // outside the package root, add its parent as an extra include.
319 // An example of this situation would look like this:
323 // path = "../../src/lib.rs"
325 let extra_targets = cargo[pkg]
328 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
329 .filter_map(|&tgt| cargo[tgt].root.parent())
330 .map(|tgt| tgt.normalize().to_path_buf())
331 .filter(|path| !path.starts_with(&pkg_root));
332 include.extend(extra_targets);
334 let mut exclude = vec![pkg_root.join(".git")];
336 exclude.push(pkg_root.join("target"));
338 exclude.push(pkg_root.join("tests"));
339 exclude.push(pkg_root.join("examples"));
340 exclude.push(pkg_root.join("benches"));
342 PackageRoot { is_local, include, exclude }
344 .chain(sysroot.iter().map(|sysroot| PackageRoot {
346 include: vec![sysroot.root().to_path_buf()],
349 .chain(rustc.iter().flat_map(|rustc| {
350 rustc.packages().map(move |krate| PackageRoot {
352 include: vec![rustc[krate].manifest.parent().to_path_buf()],
358 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
360 .map(|detached_file| PackageRoot {
362 include: vec![detached_file.clone()],
365 .chain(sysroot.crates().map(|krate| PackageRoot {
367 include: vec![sysroot[krate].root.parent().to_path_buf()],
374 pub fn n_packages(&self) -> usize {
376 ProjectWorkspace::Json { project, .. } => project.n_crates(),
377 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
378 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
379 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
380 cargo.packages().len() + sysroot_package_len + rustc_package_len
382 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
383 sysroot.crates().len() + files.len()
388 pub fn to_crate_graph(
390 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
391 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
393 let _p = profile::span("ProjectWorkspace::to_crate_graph");
395 let mut crate_graph = match self {
396 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
403 ProjectWorkspace::Cargo {
410 } => cargo_to_crate_graph(
420 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
421 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
424 if crate_graph.patch_cfg_if() {
425 tracing::debug!("Patched std to depend on cfg-if")
427 tracing::debug!("Did not patch std to depend on cfg-if")
433 fn project_json_to_crate_graph(
434 rustc_cfg: Vec<CfgFlag>,
435 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
436 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
437 project: &ProjectJson,
438 sysroot: &Option<Sysroot>,
440 let mut crate_graph = CrateGraph::default();
441 let sysroot_deps = sysroot
443 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
445 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
446 let crates: FxHashMap<CrateId, CrateId> = project
448 .filter_map(|(crate_id, krate)| {
449 let file_path = &krate.root_module;
450 let file_id = load(file_path)?;
451 Some((crate_id, krate, file_id))
453 .map(|(crate_id, krate, file_id)| {
454 let env = krate.env.clone().into_iter().collect();
455 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| load_proc_macro(&it));
457 let target_cfgs = match krate.target.as_deref() {
459 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
464 let mut cfg_options = CfgOptions::default();
465 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
468 crate_graph.add_crate_root(
471 krate.display_name.clone(),
472 krate.version.clone(),
476 proc_macro.unwrap_or_default(),
477 if let Some(name) = &krate.display_name {
478 CrateOrigin::CratesIo {
479 repo: krate.repository.clone(),
480 name: name.crate_name().to_string(),
481 version: krate.version.clone().unwrap_or_else(|| "".to_string()),
491 for (from, krate) in project.crates() {
492 if let Some(&from) = crates.get(&from) {
493 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
494 public_deps.add(from, &mut crate_graph);
495 if krate.is_proc_macro {
496 if let Some(proc_macro) = libproc_macro {
500 CrateName::new("proc_macro").unwrap(),
507 for dep in &krate.deps {
508 if let Some(&to) = crates.get(&dep.crate_id) {
509 add_dep(&mut crate_graph, from, dep.name.clone(), to)
517 fn cargo_to_crate_graph(
518 rustc_cfg: Vec<CfgFlag>,
519 override_cfg: &CfgOverrides,
520 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
521 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
522 cargo: &CargoWorkspace,
523 build_scripts: &WorkspaceBuildScripts,
524 sysroot: Option<&Sysroot>,
525 rustc: &Option<CargoWorkspace>,
527 let _p = profile::span("cargo_to_crate_graph");
528 let mut crate_graph = CrateGraph::default();
529 let (public_deps, libproc_macro) = match sysroot {
530 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
531 None => (SysrootPublicDeps::default(), None),
534 let mut cfg_options = CfgOptions::default();
535 cfg_options.extend(rustc_cfg);
537 let mut pkg_to_lib_crate = FxHashMap::default();
539 // Add test cfg for non-sysroot crates
540 cfg_options.insert_atom("test".into());
541 cfg_options.insert_atom("debug_assertions".into());
543 let mut pkg_crates = FxHashMap::default();
544 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
545 let mut has_private = false;
546 // Next, create crates for each package, target pair
547 for pkg in cargo.packages() {
548 let mut cfg_options = &cfg_options;
549 let mut replaced_cfg_options;
551 let overrides = match override_cfg {
552 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
553 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
556 if let Some(overrides) = overrides {
557 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
558 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
559 // working on rust-lang/rust as that's the only time it appears outside sysroot).
561 // A more ideal solution might be to reanalyze crates based on where the cursor is and
562 // figure out the set of cfgs that would have to apply to make it active.
564 replaced_cfg_options = cfg_options.clone();
565 replaced_cfg_options.apply_diff(overrides.clone());
566 cfg_options = &replaced_cfg_options;
569 has_private |= cargo[pkg].metadata.rustc_private;
570 let mut lib_tgt = None;
571 for &tgt in cargo[pkg].targets.iter() {
572 if let Some(file_id) = load(&cargo[tgt].root) {
573 let crate_id = add_target_crate_root(
576 build_scripts.outputs.get(pkg),
582 if cargo[tgt].kind == TargetKind::Lib {
583 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
584 pkg_to_lib_crate.insert(pkg, crate_id);
586 if let Some(proc_macro) = libproc_macro {
587 add_dep_with_prelude(
590 CrateName::new("proc_macro").unwrap(),
592 cargo[tgt].is_proc_macro,
596 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
600 // Set deps to the core, std and to the lib target of the current package
601 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
602 if let Some((to, name)) = lib_tgt.clone() {
603 if to != *from && *kind != TargetKind::BuildScript {
604 // (build script can not depend on its library target)
606 // For root projects with dashes in their name,
607 // cargo metadata does not do any normalization,
608 // so we do it ourselves currently
609 let name = CrateName::normalize_dashes(&name);
610 add_dep(&mut crate_graph, *from, name, to);
613 public_deps.add(*from, &mut crate_graph);
617 // Now add a dep edge from all targets of upstream to the lib
618 // target of downstream.
619 for pkg in cargo.packages() {
620 for dep in cargo[pkg].dependencies.iter() {
621 let name = CrateName::new(&dep.name).unwrap();
622 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
623 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
624 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
625 // Only build scripts may depend on build dependencies.
628 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
629 // Build scripts may only depend on build dependencies.
633 add_dep(&mut crate_graph, *from, name.clone(), to)
640 // If the user provided a path to rustc sources, we add all the rustc_private crates
641 // and create dependencies on them for the crates which opt-in to that
642 if let Some(rustc_workspace) = rustc {
649 &mut pkg_to_lib_crate,
659 fn detached_files_to_crate_graph(
660 rustc_cfg: Vec<CfgFlag>,
661 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
662 detached_files: &[AbsPathBuf],
665 let _p = profile::span("detached_files_to_crate_graph");
666 let mut crate_graph = CrateGraph::default();
667 let (public_deps, _libproc_macro) =
668 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
670 let mut cfg_options = CfgOptions::default();
671 cfg_options.extend(rustc_cfg);
673 for detached_file in detached_files {
674 let file_id = match load(detached_file) {
675 Some(file_id) => file_id,
677 tracing::error!("Failed to load detached file {:?}", detached_file);
681 let display_name = detached_file
683 .and_then(|os_str| os_str.to_str())
684 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
685 let detached_file_crate = crate_graph.add_crate_root(
694 CrateOrigin::Unknown,
697 public_deps.add(detached_file_crate, &mut crate_graph);
702 fn handle_rustc_crates(
703 rustc_workspace: &CargoWorkspace,
704 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
705 crate_graph: &mut CrateGraph,
706 cfg_options: &CfgOptions,
707 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
708 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
709 public_deps: &SysrootPublicDeps,
710 cargo: &CargoWorkspace,
711 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
713 let mut rustc_pkg_crates = FxHashMap::default();
714 // The root package of the rustc-dev component is rustc_driver, so we match that
716 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
717 // The rustc workspace might be incomplete (such as if rustc-dev is not
718 // installed for the current toolchain) and `rustcSource` is set to discover.
719 if let Some(root_pkg) = root_pkg {
720 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
721 let mut queue = VecDeque::new();
722 queue.push_back(root_pkg);
723 while let Some(pkg) = queue.pop_front() {
724 // Don't duplicate packages if they are dependended on a diamond pattern
725 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
726 // which is not ideal
727 if rustc_pkg_crates.contains_key(&pkg) {
730 for dep in &rustc_workspace[pkg].dependencies {
731 queue.push_back(dep.pkg);
733 for &tgt in rustc_workspace[pkg].targets.iter() {
734 if rustc_workspace[tgt].kind != TargetKind::Lib {
737 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
738 let crate_id = add_target_crate_root(
740 &rustc_workspace[pkg],
745 &rustc_workspace[tgt].name,
747 pkg_to_lib_crate.insert(pkg, crate_id);
748 // Add dependencies on core / std / alloc for this crate
749 public_deps.add(crate_id, crate_graph);
750 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
755 // Now add a dep edge from all targets of upstream to the lib
756 // target of downstream.
757 for pkg in rustc_pkg_crates.keys().copied() {
758 for dep in rustc_workspace[pkg].dependencies.iter() {
759 let name = CrateName::new(&dep.name).unwrap();
760 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
761 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
762 add_dep(crate_graph, from, name.clone(), to);
767 // Add a dependency on the rustc_private crates for all targets of each package
769 for dep in rustc_workspace.packages() {
770 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
772 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
773 for pkg in cargo.packages() {
774 let package = &cargo[pkg];
775 if !package.metadata.rustc_private {
778 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
779 // Avoid creating duplicate dependencies
780 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
781 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
782 // instead of the one from `crates.io`
783 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
784 add_dep(crate_graph, *from, name.clone(), to);
792 fn add_target_crate_root(
793 crate_graph: &mut CrateGraph,
795 build_data: Option<&BuildScriptOutput>,
796 cfg_options: &CfgOptions,
797 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
801 let edition = pkg.edition;
803 let mut opts = cfg_options.clone();
804 for feature in pkg.active_features.iter() {
805 opts.insert_key_value("feature".into(), feature.into());
807 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
808 opts.extend(cfgs.iter().cloned());
813 let mut env = Env::default();
814 inject_cargo_env(pkg, &mut env);
816 if let Some(envs) = build_data.map(|it| &it.envs) {
818 env.set(k, v.clone());
822 let proc_macro = build_data
824 .and_then(|it| it.proc_macro_dylib_path.as_ref())
825 .map(|it| load_proc_macro(it))
826 .unwrap_or_default();
828 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
829 let mut potential_cfg_options = cfg_options.clone();
830 potential_cfg_options.extend(
833 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
835 let crate_name = display_name.crate_name().to_string();
836 crate_graph.add_crate_root(
840 Some(pkg.version.to_string()),
842 potential_cfg_options,
845 CrateOrigin::CratesIo {
847 repo: pkg.repository.clone(),
848 version: pkg.version.to_string(),
854 struct SysrootPublicDeps {
855 deps: Vec<(CrateName, CrateId, bool)>,
858 impl SysrootPublicDeps {
859 /// Makes `from` depend on the public sysroot crates.
860 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
861 for (name, krate, prelude) in &self.deps {
862 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
867 fn sysroot_to_crate_graph(
868 crate_graph: &mut CrateGraph,
870 rustc_cfg: Vec<CfgFlag>,
871 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
872 ) -> (SysrootPublicDeps, Option<CrateId>) {
873 let _p = profile::span("sysroot_to_crate_graph");
874 let mut cfg_options = CfgOptions::default();
875 cfg_options.extend(rustc_cfg);
876 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
878 .filter_map(|krate| {
879 let file_id = load(&sysroot[krate].root)?;
881 let env = Env::default();
882 let proc_macro = vec![];
883 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
884 let crate_id = crate_graph.add_crate_root(
893 CrateOrigin::Lang(sysroot[krate].name.clone()),
895 Some((krate, crate_id))
899 for from in sysroot.crates() {
900 for &to in sysroot[from].deps.iter() {
901 let name = CrateName::new(&sysroot[to].name).unwrap();
902 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
903 add_dep(crate_graph, from, name, to);
908 let public_deps = SysrootPublicDeps {
911 .map(|(name, idx, prelude)| {
912 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
914 .collect::<Vec<_>>(),
917 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
918 (public_deps, libproc_macro)
921 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
922 add_dep_inner(graph, from, Dependency::new(name, to))
925 fn add_dep_with_prelude(
926 graph: &mut CrateGraph,
932 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
935 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
936 if let Err(err) = graph.add_dep(from, dep) {
937 tracing::error!("{}", err)
941 /// Recreates the compile-time environment variables that Cargo sets.
943 /// Should be synced with
944 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
946 /// FIXME: ask Cargo to provide this data instead of re-deriving.
947 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
948 // FIXME: Missing variables:
949 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
951 let manifest_dir = package.manifest.parent();
952 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
954 // Not always right, but works for common cases.
955 env.set("CARGO", "cargo".into());
957 env.set("CARGO_PKG_VERSION", package.version.to_string());
958 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
959 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
960 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
961 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
963 env.set("CARGO_PKG_AUTHORS", String::new());
965 env.set("CARGO_PKG_NAME", package.name.clone());
966 // FIXME: This isn't really correct (a package can have many crates with different names), but
967 // it's better than leaving the variable unset.
968 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
969 env.set("CARGO_PKG_DESCRIPTION", String::new());
970 env.set("CARGO_PKG_HOMEPAGE", String::new());
971 env.set("CARGO_PKG_REPOSITORY", String::new());
972 env.set("CARGO_PKG_LICENSE", String::new());
974 env.set("CARGO_PKG_LICENSE_FILE", String::new());