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};
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(&cargo_toml, config, progress)
166 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
167 cargo_toml.display(),
171 let cargo = CargoWorkspace::new(meta);
173 let sysroot = if config.no_sysroot {
176 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
178 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
184 let rustc_dir = match &config.rustc_source {
185 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
186 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
190 let rustc = match rustc_dir {
191 Some(rustc_dir) => Some({
192 let meta = CargoWorkspace::fetch_metadata(&rustc_dir, config, progress)
194 "Failed to read Cargo metadata for Rust sources".to_string()
196 CargoWorkspace::new(meta)
201 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
203 let cfg_overrides = config.cfg_overrides();
204 ProjectWorkspace::Cargo {
206 build_scripts: WorkspaceBuildScripts::default(),
219 project_json: ProjectJson,
220 target: Option<&str>,
221 ) -> Result<ProjectWorkspace> {
222 let sysroot = match &project_json.sysroot_src {
223 Some(path) => Some(Sysroot::load(path.clone())?),
226 let rustc_cfg = rustc_cfg::get(None, target);
227 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
230 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
231 let sysroot = Sysroot::discover(
234 .and_then(|it| it.parent())
235 .ok_or_else(|| format_err!("No detached files to load"))?,
237 let rustc_cfg = rustc_cfg::get(None, None);
238 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
241 pub fn run_build_scripts(
243 config: &CargoConfig,
244 progress: &dyn Fn(String),
245 ) -> Result<WorkspaceBuildScripts> {
247 ProjectWorkspace::Cargo { cargo, .. } => {
248 WorkspaceBuildScripts::run(config, cargo, progress)
250 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
251 Ok(WorkspaceBuildScripts::default())
256 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
258 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
260 always!(bs == WorkspaceBuildScripts::default());
265 /// Returns the roots for the current `ProjectWorkspace`
266 /// The return type contains the path and whether or not
267 /// the root is a member of the current workspace
268 pub fn to_roots(&self) -> Vec<PackageRoot> {
270 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
272 .map(|(_, krate)| PackageRoot {
273 is_local: krate.is_workspace_member,
274 include: krate.include.clone(),
275 exclude: krate.exclude.clone(),
277 .collect::<FxHashSet<_>>()
279 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
280 sysroot.crates().map(move |krate| PackageRoot {
282 include: vec![sysroot[krate].root.parent().to_path_buf()],
286 .collect::<Vec<_>>(),
287 ProjectWorkspace::Cargo {
298 let is_local = cargo[pkg].is_local;
299 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
301 let mut include = vec![pkg_root.clone()];
303 build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
306 // In case target's path is manually set in Cargo.toml to be
307 // outside the package root, add its parent as an extra include.
308 // An example of this situation would look like this:
312 // path = "../../src/lib.rs"
314 let extra_targets = cargo[pkg]
317 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
318 .filter_map(|&tgt| cargo[tgt].root.parent())
319 .map(|tgt| tgt.normalize().to_path_buf())
320 .filter(|path| !path.starts_with(&pkg_root));
321 include.extend(extra_targets);
323 let mut exclude = vec![pkg_root.join(".git")];
325 exclude.push(pkg_root.join("target"));
327 exclude.push(pkg_root.join("tests"));
328 exclude.push(pkg_root.join("examples"));
329 exclude.push(pkg_root.join("benches"));
331 PackageRoot { is_local, include, exclude }
333 .chain(sysroot.iter().map(|sysroot| PackageRoot {
335 include: vec![sysroot.root().to_path_buf()],
338 .chain(rustc.iter().flat_map(|rustc| {
339 rustc.packages().map(move |krate| PackageRoot {
341 include: vec![rustc[krate].manifest.parent().to_path_buf()],
347 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
349 .map(|detached_file| PackageRoot {
351 include: vec![detached_file.clone()],
354 .chain(sysroot.crates().map(|krate| PackageRoot {
356 include: vec![sysroot[krate].root.parent().to_path_buf()],
363 pub fn n_packages(&self) -> usize {
365 ProjectWorkspace::Json { project, .. } => project.n_crates(),
366 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
367 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
368 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
369 cargo.packages().len() + sysroot_package_len + rustc_package_len
371 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
372 sysroot.crates().len() + files.len()
377 pub fn to_crate_graph(
379 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
380 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
382 let _p = profile::span("ProjectWorkspace::to_crate_graph");
384 let mut crate_graph = match self {
385 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
392 ProjectWorkspace::Cargo {
399 } => cargo_to_crate_graph(
409 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
410 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
413 if crate_graph.patch_cfg_if() {
414 tracing::debug!("Patched std to depend on cfg-if")
416 tracing::debug!("Did not patch std to depend on cfg-if")
422 fn project_json_to_crate_graph(
423 rustc_cfg: Vec<CfgFlag>,
424 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
425 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
426 project: &ProjectJson,
427 sysroot: &Option<Sysroot>,
429 let mut crate_graph = CrateGraph::default();
430 let sysroot_deps = sysroot
432 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
434 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
435 let crates: FxHashMap<CrateId, CrateId> = project
437 .filter_map(|(crate_id, krate)| {
438 let file_path = &krate.root_module;
439 let file_id = load(file_path)?;
440 Some((crate_id, krate, file_id))
442 .map(|(crate_id, krate, file_id)| {
443 let env = krate.env.clone().into_iter().collect();
444 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| load_proc_macro(&it));
446 let target_cfgs = match krate.target.as_deref() {
448 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
453 let mut cfg_options = CfgOptions::default();
454 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
457 crate_graph.add_crate_root(
460 krate.display_name.clone(),
464 proc_macro.unwrap_or_default(),
470 for (from, krate) in project.crates() {
471 if let Some(&from) = crates.get(&from) {
472 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
473 public_deps.add(from, &mut crate_graph);
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 => (SysrootPublicDeps::default(), 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 {
566 add_dep_with_prelude(
569 CrateName::new("proc_macro").unwrap(),
571 cargo[tgt].is_proc_macro,
575 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
579 // Set deps to the core, std and to the lib target of the current package
580 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
581 if let Some((to, name)) = lib_tgt.clone() {
582 if to != *from && *kind != TargetKind::BuildScript {
583 // (build script can not depend on its library target)
585 // For root projects with dashes in their name,
586 // cargo metadata does not do any normalization,
587 // so we do it ourselves currently
588 let name = CrateName::normalize_dashes(&name);
589 add_dep(&mut crate_graph, *from, name, to);
592 public_deps.add(*from, &mut crate_graph);
596 // Now add a dep edge from all targets of upstream to the lib
597 // target of downstream.
598 for pkg in cargo.packages() {
599 for dep in cargo[pkg].dependencies.iter() {
600 let name = CrateName::new(&dep.name).unwrap();
601 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
602 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
603 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
604 // Only build scripts may depend on build dependencies.
607 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
608 // Build scripts may only depend on build dependencies.
612 add_dep(&mut crate_graph, *from, name.clone(), to)
619 // If the user provided a path to rustc sources, we add all the rustc_private crates
620 // and create dependencies on them for the crates which opt-in to that
621 if let Some(rustc_workspace) = rustc {
628 &mut pkg_to_lib_crate,
638 fn detached_files_to_crate_graph(
639 rustc_cfg: Vec<CfgFlag>,
640 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
641 detached_files: &[AbsPathBuf],
644 let _p = profile::span("detached_files_to_crate_graph");
645 let mut crate_graph = CrateGraph::default();
646 let (public_deps, _libproc_macro) =
647 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
649 let mut cfg_options = CfgOptions::default();
650 cfg_options.extend(rustc_cfg);
652 for detached_file in detached_files {
653 let file_id = match load(detached_file) {
654 Some(file_id) => file_id,
656 tracing::error!("Failed to load detached file {:?}", detached_file);
660 let display_name = detached_file
662 .and_then(|os_str| os_str.to_str())
663 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
664 let detached_file_crate = crate_graph.add_crate_root(
674 public_deps.add(detached_file_crate, &mut crate_graph);
679 fn handle_rustc_crates(
680 rustc_workspace: &CargoWorkspace,
681 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
682 crate_graph: &mut CrateGraph,
683 cfg_options: &CfgOptions,
684 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
685 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
686 public_deps: &SysrootPublicDeps,
687 cargo: &CargoWorkspace,
688 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
690 let mut rustc_pkg_crates = FxHashMap::default();
691 // The root package of the rustc-dev component is rustc_driver, so we match that
693 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
694 // The rustc workspace might be incomplete (such as if rustc-dev is not
695 // installed for the current toolchain) and `rustcSource` is set to discover.
696 if let Some(root_pkg) = root_pkg {
697 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
698 let mut queue = VecDeque::new();
699 queue.push_back(root_pkg);
700 while let Some(pkg) = queue.pop_front() {
701 // Don't duplicate packages if they are dependended on a diamond pattern
702 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
703 // which is not ideal
704 if rustc_pkg_crates.contains_key(&pkg) {
707 for dep in &rustc_workspace[pkg].dependencies {
708 queue.push_back(dep.pkg);
710 for &tgt in rustc_workspace[pkg].targets.iter() {
711 if rustc_workspace[tgt].kind != TargetKind::Lib {
714 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
715 let crate_id = add_target_crate_root(
717 &rustc_workspace[pkg],
722 &rustc_workspace[tgt].name,
724 pkg_to_lib_crate.insert(pkg, crate_id);
725 // Add dependencies on core / std / alloc for this crate
726 public_deps.add(crate_id, crate_graph);
727 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
732 // Now add a dep edge from all targets of upstream to the lib
733 // target of downstream.
734 for pkg in rustc_pkg_crates.keys().copied() {
735 for dep in rustc_workspace[pkg].dependencies.iter() {
736 let name = CrateName::new(&dep.name).unwrap();
737 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
738 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
739 add_dep(crate_graph, from, name.clone(), to);
744 // Add a dependency on the rustc_private crates for all targets of each package
746 for dep in rustc_workspace.packages() {
747 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
749 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
750 for pkg in cargo.packages() {
751 let package = &cargo[pkg];
752 if !package.metadata.rustc_private {
755 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
756 // Avoid creating duplicate dependencies
757 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
758 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
759 // instead of the one from `crates.io`
760 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
761 add_dep(crate_graph, *from, name.clone(), to);
769 fn add_target_crate_root(
770 crate_graph: &mut CrateGraph,
772 build_data: Option<&BuildScriptOutput>,
773 cfg_options: &CfgOptions,
774 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
778 let edition = pkg.edition;
780 let mut opts = cfg_options.clone();
781 for feature in pkg.active_features.iter() {
782 opts.insert_key_value("feature".into(), feature.into());
784 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
785 opts.extend(cfgs.iter().cloned());
790 let mut env = Env::default();
791 inject_cargo_env(pkg, &mut env);
793 if let Some(envs) = build_data.map(|it| &it.envs) {
795 env.set(k, v.clone());
799 let proc_macro = build_data
801 .and_then(|it| it.proc_macro_dylib_path.as_ref())
802 .map(|it| load_proc_macro(it))
803 .unwrap_or_default();
805 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
806 let mut potential_cfg_options = cfg_options.clone();
807 potential_cfg_options.extend(
810 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
813 crate_graph.add_crate_root(
818 potential_cfg_options,
825 struct SysrootPublicDeps {
826 deps: Vec<(CrateName, CrateId, bool)>,
829 impl SysrootPublicDeps {
830 /// Makes `from` depend on the public sysroot crates.
831 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
832 for (name, krate, prelude) in &self.deps {
833 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
838 fn sysroot_to_crate_graph(
839 crate_graph: &mut CrateGraph,
841 rustc_cfg: Vec<CfgFlag>,
842 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
843 ) -> (SysrootPublicDeps, Option<CrateId>) {
844 let _p = profile::span("sysroot_to_crate_graph");
845 let mut cfg_options = CfgOptions::default();
846 cfg_options.extend(rustc_cfg);
847 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
849 .filter_map(|krate| {
850 let file_id = load(&sysroot[krate].root)?;
852 let env = Env::default();
853 let proc_macro = vec![];
854 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
855 let crate_id = crate_graph.add_crate_root(
864 Some((krate, crate_id))
868 for from in sysroot.crates() {
869 for &to in sysroot[from].deps.iter() {
870 let name = CrateName::new(&sysroot[to].name).unwrap();
871 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
872 add_dep(crate_graph, from, name, to);
877 let public_deps = SysrootPublicDeps {
880 .map(|(name, idx, prelude)| {
881 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
883 .collect::<Vec<_>>(),
886 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
887 (public_deps, libproc_macro)
890 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
891 add_dep_inner(graph, from, Dependency::new(name, to))
894 fn add_dep_with_prelude(
895 graph: &mut CrateGraph,
901 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
904 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
905 if let Err(err) = graph.add_dep(from, dep) {
906 tracing::error!("{}", err)
910 /// Recreates the compile-time environment variables that Cargo sets.
912 /// Should be synced with
913 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
915 /// FIXME: ask Cargo to provide this data instead of re-deriving.
916 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
917 // FIXME: Missing variables:
918 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
920 let manifest_dir = package.manifest.parent();
921 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
923 // Not always right, but works for common cases.
924 env.set("CARGO", "cargo".into());
926 env.set("CARGO_PKG_VERSION", package.version.to_string());
927 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
928 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
929 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
930 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
932 env.set("CARGO_PKG_AUTHORS", String::new());
934 env.set("CARGO_PKG_NAME", package.name.clone());
935 // FIXME: This isn't really correct (a package can have many crates with different names), but
936 // it's better than leaving the variable unset.
937 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
938 env.set("CARGO_PKG_DESCRIPTION", String::new());
939 env.set("CARGO_PKG_HOMEPAGE", String::new());
940 env.set("CARGO_PKG_REPOSITORY", String::new());
941 env.set("CARGO_PKG_LICENSE", String::new());
943 env.set("CARGO_PKG_LICENSE_FILE", String::new());