1use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
6
7use anyhow::Context;
8use base_db::{
9 CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin,
10 CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError,
11 ProcMacroPaths, TargetLayoutLoadResult,
12};
13use cfg::{CfgAtom, CfgDiff, CfgOptions};
14use intern::{Symbol, sym};
15use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
16use rustc_hash::{FxHashMap, FxHashSet};
17use semver::Version;
18use span::{Edition, FileId};
19use toolchain::Tool;
20use tracing::instrument;
21use triomphe::Arc;
22
23use crate::{
24 CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package,
25 ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
26 WorkspaceBuildScripts,
27 build_dependencies::{BuildScriptOutput, ProcMacroDylibPath},
28 cargo_config_file,
29 cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
30 env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
31 project_json::{Crate, CrateArrayIdx},
32 sysroot::RustLibSrcWorkspace,
33 toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
34 utf8_stdout,
35};
36use tracing::{debug, error, info};
37
38pub type FileLoader<'a> = &'a mut dyn for<'b> FnMut(&'b AbsPath) -> Option<FileId>;
39
40#[derive(Debug, Clone, Eq, PartialEq, Hash)]
44pub struct PackageRoot {
45 pub is_local: bool,
47 pub include: Vec<AbsPathBuf>,
49 pub exclude: Vec<AbsPathBuf>,
51}
52
53#[derive(Clone)]
54pub struct ProjectWorkspace {
55 pub kind: ProjectWorkspaceKind,
56 pub sysroot: Sysroot,
58 pub rustc_cfg: Vec<CfgAtom>,
63 pub toolchain: Option<Version>,
65 pub target_layout: TargetLayoutLoadResult,
67 pub cfg_overrides: CfgOverrides,
69 pub extra_includes: Vec<AbsPathBuf>,
71 pub set_test: bool,
73}
74
75#[derive(Clone)]
76#[allow(clippy::large_enum_variant)]
77pub enum ProjectWorkspaceKind {
78 Cargo {
80 cargo: CargoWorkspace,
82 error: Option<Arc<anyhow::Error>>,
84 build_scripts: WorkspaceBuildScripts,
86 rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>,
89 },
90 Json(ProjectJson),
92 DetachedFile {
103 file: ManifestPath,
105 cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option<Arc<anyhow::Error>>)>,
107 },
108}
109
110impl fmt::Debug for ProjectWorkspace {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 let Self {
114 kind,
115 sysroot,
116 rustc_cfg,
117 toolchain,
118 target_layout,
119 cfg_overrides,
120 extra_includes,
121 set_test,
122 } = self;
123 match kind {
124 ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc } => f
125 .debug_struct("Cargo")
126 .field("root", &cargo.workspace_root().file_name())
127 .field("n_packages", &cargo.packages().len())
128 .field("n_sysroot_crates", &sysroot.num_packages())
129 .field(
130 "n_rustc_compiler_crates",
131 &rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(rc, _)| rc.packages().len()),
132 )
133 .field("n_rustc_cfg", &rustc_cfg.len())
134 .field("n_cfg_overrides", &cfg_overrides.len())
135 .field("n_extra_includes", &extra_includes.len())
136 .field("toolchain", &toolchain)
137 .field("data_layout", &target_layout)
138 .field("set_test", set_test)
139 .field("build_scripts", &build_scripts.error().unwrap_or("ok"))
140 .finish(),
141 ProjectWorkspaceKind::Json(project) => {
142 let mut debug_struct = f.debug_struct("Json");
143 debug_struct
144 .field("n_crates", &project.n_crates())
145 .field("n_sysroot_crates", &sysroot.num_packages())
146 .field("n_rustc_cfg", &rustc_cfg.len())
147 .field("toolchain", &toolchain)
148 .field("data_layout", &target_layout)
149 .field("n_cfg_overrides", &cfg_overrides.len())
150 .field("n_extra_includes", &extra_includes.len())
151 .field("set_test", set_test);
152
153 debug_struct.finish()
154 }
155 ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script } => f
156 .debug_struct("DetachedFiles")
157 .field("file", &file)
158 .field("cargo_script", &cargo_script.is_some())
159 .field("n_sysroot_crates", &sysroot.num_packages())
160 .field("n_rustc_cfg", &rustc_cfg.len())
161 .field("toolchain", &toolchain)
162 .field("data_layout", &target_layout)
163 .field("n_cfg_overrides", &cfg_overrides.len())
164 .field("n_extra_includes", &extra_includes.len())
165 .field("set_test", set_test)
166 .finish(),
167 }
168 }
169}
170
171impl ProjectWorkspace {
172 pub fn load(
173 manifest: ProjectManifest,
174 config: &CargoConfig,
175 progress: &(dyn Fn(String) + Sync),
176 ) -> anyhow::Result<ProjectWorkspace> {
177 ProjectWorkspace::load_inner(&manifest, config, progress)
178 .with_context(|| format!("Failed to load the project at {manifest}"))
179 }
180
181 fn load_inner(
182 manifest: &ProjectManifest,
183 config: &CargoConfig,
184 progress: &(dyn Fn(String) + Sync),
185 ) -> anyhow::Result<ProjectWorkspace> {
186 let res = match manifest {
187 ProjectManifest::ProjectJson(project_json) => {
188 let file = fs::read_to_string(project_json)
189 .with_context(|| format!("Failed to read json file {project_json}"))?;
190 let data = serde_json::from_str(&file)
191 .with_context(|| format!("Failed to deserialize json file {project_json}"))?;
192 let project_location = project_json.parent().to_path_buf();
193 let project_json: ProjectJson =
194 ProjectJson::new(Some(project_json.clone()), &project_location, data);
195 ProjectWorkspace::load_inline(project_json, config, progress)
196 }
197 ProjectManifest::CargoScript(rust_file) => {
198 ProjectWorkspace::load_detached_file(rust_file, config)?
199 }
200 ProjectManifest::CargoToml(cargo_toml) => {
201 ProjectWorkspace::load_cargo(cargo_toml, config, progress)?
202 }
203 };
204
205 Ok(res)
206 }
207
208 fn load_cargo(
209 cargo_toml: &ManifestPath,
210 config: &CargoConfig,
211 progress: &(dyn Fn(String) + Sync),
212 ) -> Result<ProjectWorkspace, anyhow::Error> {
213 progress("discovering sysroot".to_owned());
214 let CargoConfig {
215 features,
216 rustc_source,
217 extra_args,
218 extra_env,
219 set_test,
220 cfg_overrides,
221 extra_includes,
222 sysroot,
223 sysroot_src,
224 target,
225 no_deps,
226 ..
227 } = config;
228 let workspace_dir = cargo_toml.parent();
229 let mut sysroot = match (sysroot, sysroot_src) {
230 (Some(RustLibSource::Discover), None) => Sysroot::discover(workspace_dir, extra_env),
231 (Some(RustLibSource::Discover), Some(sysroot_src)) => {
232 Sysroot::discover_with_src_override(workspace_dir, extra_env, sysroot_src.clone())
233 }
234 (Some(RustLibSource::Path(path)), None) => {
235 Sysroot::discover_rust_lib_src_dir(path.clone())
236 }
237 (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
238 Sysroot::new(Some(sysroot.clone()), Some(sysroot_src.clone()))
239 }
240 (None, _) => Sysroot::empty(),
241 };
242
243 let mut cmd = sysroot.tool(Tool::Cargo, workspace_dir, extra_env);
245 cmd.args(["locate-project", "--workspace", "--manifest-path", cargo_toml.as_str()]);
246 let cargo_toml = &match utf8_stdout(&mut cmd) {
247 Ok(output) => {
248 #[derive(serde_derive::Deserialize)]
249 struct Root {
250 root: Utf8PathBuf,
251 }
252 match serde_json::from_str::<Root>(&output) {
253 Ok(object) => ManifestPath::try_from(AbsPathBuf::assert(object.root))
254 .expect("manifest path should be absolute"),
255 Err(e) => {
256 tracing::error!(%e, %cargo_toml, "failed fetching cargo workspace root");
257 cargo_toml.clone()
258 }
259 }
260 }
261 Err(e) => {
262 tracing::error!(%e, %cargo_toml, "failed fetching cargo workspace root");
263 cargo_toml.clone()
264 }
265 };
266 let workspace_dir = cargo_toml.parent();
267
268 tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
269 progress("querying project metadata".to_owned());
270 let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
271 let config_file_ = config_file.clone();
272 let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
273 let targets =
274 target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
275 let toolchain = version::get(toolchain_config, extra_env)
276 .inspect_err(|e| {
277 tracing::error!(%e,
278 "failed fetching toolchain version for {cargo_toml:?} workspace"
279 )
280 })
281 .ok()
282 .flatten();
283
284 let fetch_metadata = FetchMetadata::new(
285 cargo_toml,
286 workspace_dir,
287 &CargoMetadataConfig {
288 features: features.clone(),
289 targets: targets.clone(),
290 extra_args: extra_args.clone(),
291 extra_env: extra_env.clone(),
292 toolchain_version: toolchain.clone(),
293 kind: "workspace",
294 },
295 &sysroot,
296 *no_deps,
297 );
298 let target_dir = config
299 .target_dir
300 .clone()
301 .or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
302 .unwrap_or_else(|| workspace_dir.join("target").into());
303
304 let join = thread::scope(|s| {
309 let rustc_cfg = s.spawn(|| {
310 rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
311 });
312 let data_layout = s.spawn(|| {
313 target_data_layout::get(
314 toolchain_config,
315 targets.first().map(Deref::deref),
316 extra_env,
317 ).inspect_err(|e| {
318 tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace")
319 })
320 });
321
322 let rustc_dir = s.spawn(|| {
323 let rustc_dir = match rustc_source {
324 Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
325 .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
326 Some(RustLibSource::Discover) => {
327 sysroot.discover_rustc_src().ok_or_else(|| {
328 Some("Failed to discover rustc source for sysroot.".to_owned())
329 })
330 }
331 None => Err(None),
332 };
333 rustc_dir.and_then(|rustc_dir| {
334 info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
335 match FetchMetadata::new(
336 &rustc_dir,
337 workspace_dir,
338 &CargoMetadataConfig {
339 features: crate::CargoFeatures::default(),
340 targets: targets.clone(),
341 extra_args: extra_args.clone(),
342 extra_env: extra_env.clone(),
343 toolchain_version: toolchain.clone(),
344 kind: "rustc-dev"
345 },
346 &sysroot,
347 *no_deps,
348 ).exec(&target_dir, true, progress) {
349 Ok((meta, _error)) => {
350 let workspace = CargoWorkspace::new(
351 meta,
352 cargo_toml.clone(),
353 Env::default(),
354 false,
355 );
356 let build_scripts = WorkspaceBuildScripts::rustc_crates(
357 &workspace,
358 workspace_dir,
359 extra_env,
360 &sysroot,
361 );
362 Ok(Box::new((workspace, build_scripts)))
363 }
364 Err(e) => {
365 tracing::error!(
366 %e,
367 "Failed to read Cargo metadata from rustc source at {rustc_dir}",
368 );
369 Err(Some(format!(
370 "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
371 )))
372 }
373 }
374 })
375 });
376
377 let cargo_metadata = s.spawn(|| fetch_metadata.exec(&target_dir, false, progress));
378 let loaded_sysroot = s.spawn(|| {
379 sysroot.load_workspace(
380 &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
381 config,
382 &targets,
383 toolchain.clone(),
384 )),
385 config.no_deps,
386 workspace_dir,
387 &target_dir,
388 progress,
389 )
390 });
391 let cargo_config_extra_env =
392 s.spawn(move || cargo_config_env(cargo_toml, &config_file));
393 thread::Result::Ok((
394 rustc_cfg.join()?,
395 data_layout.join()?,
396 rustc_dir.join()?,
397 loaded_sysroot.join()?,
398 cargo_metadata.join()?,
399 cargo_config_extra_env.join()?,
400 ))
401 });
402
403 let (
404 rustc_cfg,
405 data_layout,
406 mut rustc,
407 loaded_sysroot,
408 cargo_metadata,
409 cargo_config_extra_env,
410 ) = match join {
411 Ok(it) => it,
412 Err(e) => std::panic::resume_unwind(e),
413 };
414
415 let (meta, error) = cargo_metadata.with_context(|| {
416 format!(
417 "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
418 )
419 })?;
420 let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env, false);
421 if let Some(loaded_sysroot) = loaded_sysroot {
422 tracing::info!(src_root = ?sysroot.rust_lib_src_root(), root = %loaded_sysroot, "Loaded sysroot");
423 sysroot.set_workspace(loaded_sysroot);
424 }
425
426 if !cargo.requires_rustc_private()
427 && let Err(e) = &mut rustc
428 {
429 _ = e.take();
432 }
433
434 Ok(ProjectWorkspace {
435 kind: ProjectWorkspaceKind::Cargo {
436 cargo,
437 build_scripts: WorkspaceBuildScripts::default(),
438 rustc,
439 error: error.map(Arc::new),
440 },
441 sysroot,
442 rustc_cfg,
443 cfg_overrides: cfg_overrides.clone(),
444 toolchain,
445 target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
446 extra_includes: extra_includes.clone(),
447 set_test: *set_test,
448 })
449 }
450
451 pub fn load_inline(
452 mut project_json: ProjectJson,
453 config: &CargoConfig,
454 progress: &(dyn Fn(String) + Sync),
455 ) -> ProjectWorkspace {
456 progress("discovering sysroot".to_owned());
457 let mut sysroot =
458 Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone());
459
460 tracing::info!(workspace = %project_json.manifest_or_root(), src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
461 progress("querying project metadata".to_owned());
462 let sysroot_project = project_json.sysroot_project.take();
463 let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
464 let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
465 .unwrap_or_default();
466 let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
467 let project_root = project_json.project_root();
468 let target_dir = config
469 .target_dir
470 .clone()
471 .or_else(|| cargo_target_dir(project_json.manifest()?, &config.extra_env, &sysroot))
472 .unwrap_or_else(|| project_root.join("target").into());
473
474 let join = thread::scope(|s| {
479 let rustc_cfg = s.spawn(|| {
480 rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
481 });
482 let data_layout = s.spawn(|| {
483 target_data_layout::get(
484 query_config,
485 targets.first().map(Deref::deref),
486 &config.extra_env,
487 )
488 });
489 let loaded_sysroot = s.spawn(|| {
490 if let Some(sysroot_project) = sysroot_project {
491 sysroot.load_workspace(
492 &RustSourceWorkspaceConfig::Json(*sysroot_project),
493 config.no_deps,
494 project_root,
495 &target_dir,
496 progress,
497 )
498 } else {
499 sysroot.load_workspace(
500 &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
501 config,
502 &targets,
503 toolchain.clone(),
504 )),
505 config.no_deps,
506 project_root,
507 &target_dir,
508 progress,
509 )
510 }
511 });
512
513 thread::Result::Ok((rustc_cfg.join()?, data_layout.join()?, loaded_sysroot.join()?))
514 });
515
516 let (rustc_cfg, target_layout, loaded_sysroot) = match join {
517 Ok(it) => it,
518 Err(e) => std::panic::resume_unwind(e),
519 };
520
521 if let Some(loaded_sysroot) = loaded_sysroot {
522 sysroot.set_workspace(loaded_sysroot);
523 }
524
525 ProjectWorkspace {
526 kind: ProjectWorkspaceKind::Json(project_json),
527 sysroot,
528 rustc_cfg,
529 toolchain,
530 target_layout: target_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
531 cfg_overrides: config.cfg_overrides.clone(),
532 extra_includes: config.extra_includes.clone(),
533 set_test: config.set_test,
534 }
535 }
536
537 pub fn load_detached_file(
538 detached_file: &ManifestPath,
539 config: &CargoConfig,
540 ) -> anyhow::Result<ProjectWorkspace> {
541 let dir = detached_file.parent();
542 let mut sysroot = match &config.sysroot {
543 Some(RustLibSource::Path(path)) => Sysroot::discover_rust_lib_src_dir(path.clone()),
544 Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env),
545 None => Sysroot::empty(),
546 };
547
548 let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
549 let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
550 let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
551 let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
552 .unwrap_or_default();
553 let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
554 let data_layout = target_data_layout::get(query_config, None, &config.extra_env);
555 let target_dir = config
556 .target_dir
557 .clone()
558 .or_else(|| cargo_target_dir(detached_file, &config.extra_env, &sysroot))
559 .unwrap_or_else(|| dir.join("target").into());
560
561 let loaded_sysroot = sysroot.load_workspace(
562 &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
563 config,
564 &targets,
565 toolchain.clone(),
566 )),
567 config.no_deps,
568 dir,
569 &target_dir,
570 &|_| (),
571 );
572 if let Some(loaded_sysroot) = loaded_sysroot {
573 sysroot.set_workspace(loaded_sysroot);
574 }
575
576 let fetch_metadata = FetchMetadata::new(
577 detached_file,
578 dir,
579 &CargoMetadataConfig {
580 features: config.features.clone(),
581 targets,
582 extra_args: config.extra_args.clone(),
583 extra_env: config.extra_env.clone(),
584 toolchain_version: toolchain.clone(),
585 kind: "detached-file",
586 },
587 &sysroot,
588 config.no_deps,
589 );
590 let target_dir = config
591 .target_dir
592 .clone()
593 .or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
594 .unwrap_or_else(|| dir.join("target").into());
595 let cargo_script =
596 fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| {
597 let cargo_config_extra_env = cargo_config_env(detached_file, &config_file);
598 (
599 CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
600 WorkspaceBuildScripts::default(),
601 error.map(Arc::new),
602 )
603 });
604
605 Ok(ProjectWorkspace {
606 kind: ProjectWorkspaceKind::DetachedFile {
607 file: detached_file.to_owned(),
608 cargo: cargo_script,
609 },
610 sysroot,
611 rustc_cfg,
612 toolchain,
613 target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
614 cfg_overrides: config.cfg_overrides.clone(),
615 extra_includes: config.extra_includes.clone(),
616 set_test: config.set_test,
617 })
618 }
619
620 pub fn load_detached_files(
621 detached_files: Vec<ManifestPath>,
622 config: &CargoConfig,
623 ) -> Vec<anyhow::Result<ProjectWorkspace>> {
624 detached_files.into_iter().map(|file| Self::load_detached_file(&file, config)).collect()
625 }
626
627 pub fn run_build_scripts(
629 &self,
630 config: &CargoConfig,
631 progress: &dyn Fn(String),
632 ) -> anyhow::Result<WorkspaceBuildScripts> {
633 match &self.kind {
634 ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, None)), .. }
635 | ProjectWorkspaceKind::Cargo { cargo, error: None, .. } => {
636 WorkspaceBuildScripts::run_for_workspace(
637 config,
638 cargo,
639 progress,
640 &self.sysroot,
641 self.toolchain.as_ref(),
642 )
643 .with_context(|| {
644 format!("Failed to run build scripts for {}", cargo.workspace_root())
645 })
646 }
647 _ => Ok(WorkspaceBuildScripts::default()),
648 }
649 }
650
651 pub fn run_all_build_scripts(
654 workspaces: &[ProjectWorkspace],
655 config: &CargoConfig,
656 progress: &dyn Fn(String),
657 working_directory: &AbsPathBuf,
658 ) -> Vec<anyhow::Result<WorkspaceBuildScripts>> {
659 if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace)
660 || config.run_build_script_command.is_none()
661 {
662 return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect();
663 }
664
665 let cargo_ws: Vec<_> = workspaces
666 .iter()
667 .filter_map(|it| match &it.kind {
668 ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo),
669 _ => None,
670 })
671 .collect();
672 let outputs = &mut match WorkspaceBuildScripts::run_once(
673 config,
674 &cargo_ws,
675 progress,
676 working_directory,
677 ) {
678 Ok(it) => Ok(it.into_iter()),
679 Err(e) => Err(sync::Arc::new(e)),
681 };
682
683 workspaces
684 .iter()
685 .map(|it| match &it.kind {
686 ProjectWorkspaceKind::Cargo { cargo, .. } => match outputs {
687 Ok(outputs) => Ok(outputs.next().unwrap()),
688 Err(e) => Err(e.clone()).with_context(|| {
689 format!("Failed to run build scripts for {}", cargo.workspace_root())
690 }),
691 },
692 _ => Ok(WorkspaceBuildScripts::default()),
693 })
694 .collect()
695 }
696
697 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
698 match &mut self.kind {
699 ProjectWorkspaceKind::Cargo { build_scripts, .. }
700 | ProjectWorkspaceKind::DetachedFile { cargo: Some((_, build_scripts, _)), .. } => {
701 *build_scripts = bs
702 }
703 _ => assert_eq!(bs, WorkspaceBuildScripts::default()),
704 }
705 }
706
707 pub fn manifest_or_root(&self) -> &AbsPath {
708 match &self.kind {
709 ProjectWorkspaceKind::Cargo { cargo, .. } => cargo.manifest_path(),
710 ProjectWorkspaceKind::Json(project) => project.manifest_or_root(),
711 ProjectWorkspaceKind::DetachedFile { file, .. } => file,
712 }
713 }
714
715 pub fn workspace_root(&self) -> &AbsPath {
716 match &self.kind {
717 ProjectWorkspaceKind::Cargo { cargo, .. } => cargo.workspace_root(),
718 ProjectWorkspaceKind::Json(project) => project.project_root(),
719 ProjectWorkspaceKind::DetachedFile { file, .. } => file.parent(),
720 }
721 }
722
723 pub fn manifest(&self) -> Option<&ManifestPath> {
724 match &self.kind {
725 ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo.manifest_path()),
726 ProjectWorkspaceKind::Json(project) => project.manifest(),
727 ProjectWorkspaceKind::DetachedFile { cargo, .. } => {
728 Some(cargo.as_ref()?.0.manifest_path())
729 }
730 }
731 }
732
733 pub fn buildfiles(&self) -> Vec<AbsPathBuf> {
734 match &self.kind {
735 ProjectWorkspaceKind::Json(project) => project
736 .crates()
737 .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone()))
738 .map(|build_file| self.workspace_root().join(build_file))
739 .collect(),
740 _ => vec![],
741 }
742 }
743
744 pub fn find_sysroot_proc_macro_srv(&self) -> Option<anyhow::Result<AbsPathBuf>> {
745 self.sysroot.discover_proc_macro_srv()
746 }
747
748 pub fn to_roots(&self) -> Vec<PackageRoot> {
752 let mk_sysroot = || {
753 let mut r = match self.sysroot.workspace() {
754 RustLibSrcWorkspace::Workspace(ws) => ws
755 .packages()
756 .filter_map(|pkg| {
757 if ws[pkg].is_local {
758 return None;
760 }
761 let pkg_root = ws[pkg].manifest.parent().to_path_buf();
762
763 let include = vec![pkg_root.clone()];
764
765 let exclude = vec![
766 pkg_root.join(".git"),
767 pkg_root.join("target"),
768 pkg_root.join("tests"),
769 pkg_root.join("examples"),
770 pkg_root.join("benches"),
771 ];
772 Some(PackageRoot { is_local: false, include, exclude })
773 })
774 .collect(),
775 RustLibSrcWorkspace::Json(project_json) => project_json
776 .crates()
777 .map(|(_, krate)| PackageRoot {
778 is_local: false,
779 include: krate.include.clone(),
780 exclude: krate.exclude.clone(),
781 })
782 .collect(),
783 RustLibSrcWorkspace::Stitched(_) => vec![],
784 RustLibSrcWorkspace::Empty => vec![],
785 };
786
787 r.push(PackageRoot {
788 is_local: false,
789 include: self
790 .sysroot
791 .rust_lib_src_root()
792 .map(|it| it.to_path_buf())
793 .into_iter()
794 .collect(),
795 exclude: Vec::new(),
796 });
797 r
798 };
799 match &self.kind {
800 ProjectWorkspaceKind::Json(project) => project
801 .crates()
802 .map(|(_, krate)| PackageRoot {
803 is_local: krate.is_workspace_member,
804 include: krate
805 .include
806 .iter()
807 .cloned()
808 .chain(self.extra_includes.iter().cloned())
809 .collect(),
810 exclude: krate.exclude.clone(),
811 })
812 .collect::<FxHashSet<_>>()
813 .into_iter()
814 .chain(mk_sysroot())
815 .collect::<Vec<_>>(),
816 ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _ } => {
817 cargo
818 .packages()
819 .map(|pkg| {
820 let is_local = cargo[pkg].is_local;
821 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
822
823 let mut include = vec![pkg_root.clone()];
824 let out_dir =
825 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
826 include.extend(out_dir);
827
828 let extra_targets = cargo[pkg]
837 .targets
838 .iter()
839 .filter(|&&tgt| matches!(cargo[tgt].kind, TargetKind::Lib { .. }))
840 .filter_map(|&tgt| cargo[tgt].root.parent())
841 .map(|tgt| tgt.normalize().to_path_buf())
842 .filter(|path| !path.starts_with(&pkg_root));
843 include.extend(extra_targets);
844
845 let mut exclude = vec![pkg_root.join(".git")];
846 if is_local {
847 include.extend(self.extra_includes.iter().cloned());
848
849 exclude.push(pkg_root.join("target"));
850 } else {
851 exclude.push(pkg_root.join("tests"));
852 exclude.push(pkg_root.join("examples"));
853 exclude.push(pkg_root.join("benches"));
854 }
855 PackageRoot { is_local, include, exclude }
856 })
857 .chain(mk_sysroot())
858 .chain(rustc.iter().map(|a| a.as_ref()).flat_map(|(rustc, _)| {
859 rustc.packages().map(move |krate| PackageRoot {
860 is_local: false,
861 include: vec![rustc[krate].manifest.parent().to_path_buf()],
862 exclude: Vec::new(),
863 })
864 }))
865 .collect()
866 }
867 ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
868 iter::once(PackageRoot {
869 is_local: true,
870 include: vec![file.to_path_buf()],
871 exclude: Vec::new(),
872 })
873 .chain(cargo_script.iter().flat_map(|(cargo, build_scripts, _)| {
874 cargo.packages().map(|pkg| {
875 let is_local = cargo[pkg].is_local;
876 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
877
878 let mut include = vec![pkg_root.clone()];
879 let out_dir =
880 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
881 include.extend(out_dir);
882
883 let extra_targets = cargo[pkg]
892 .targets
893 .iter()
894 .filter(|&&tgt| matches!(cargo[tgt].kind, TargetKind::Lib { .. }))
895 .filter_map(|&tgt| cargo[tgt].root.parent())
896 .map(|tgt| tgt.normalize().to_path_buf())
897 .filter(|path| !path.starts_with(&pkg_root));
898 include.extend(extra_targets);
899
900 let mut exclude = vec![pkg_root.join(".git")];
901 if is_local {
902 include.extend(self.extra_includes.iter().cloned());
903
904 exclude.push(pkg_root.join("target"));
905 } else {
906 exclude.push(pkg_root.join("tests"));
907 exclude.push(pkg_root.join("examples"));
908 exclude.push(pkg_root.join("benches"));
909 }
910 PackageRoot { is_local, include, exclude }
911 })
912 }))
913 .chain(mk_sysroot())
914 .collect()
915 }
916 }
917 }
918
919 pub fn n_packages(&self) -> usize {
920 let sysroot_package_len = self.sysroot.num_packages();
921 match &self.kind {
922 ProjectWorkspaceKind::Json(project) => sysroot_package_len + project.n_crates(),
923 ProjectWorkspaceKind::Cargo { cargo, rustc, .. } => {
924 let rustc_package_len =
925 rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(it, _)| it.packages().len());
926 cargo.packages().len() + sysroot_package_len + rustc_package_len
927 }
928 ProjectWorkspaceKind::DetachedFile { cargo: cargo_script, .. } => {
929 sysroot_package_len
930 + cargo_script.as_ref().map_or(1, |(cargo, _, _)| cargo.packages().len())
931 }
932 }
933 }
934
935 pub fn to_crate_graph(
936 &self,
937 load: FileLoader<'_>,
938 extra_env: &FxHashMap<String, Option<String>>,
939 ) -> (CrateGraphBuilder, ProcMacroPaths) {
940 let _p = tracing::info_span!("ProjectWorkspace::to_crate_graph").entered();
941
942 let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self;
943 let crate_ws_data = Arc::new(CrateWorkspaceData {
944 toolchain: self.toolchain.clone(),
945 data_layout: self.target_layout.clone(),
946 });
947 let (crate_graph, proc_macros) = match kind {
948 ProjectWorkspaceKind::Json(project) => project_json_to_crate_graph(
949 rustc_cfg.clone(),
950 load,
951 project,
952 sysroot,
953 extra_env,
954 cfg_overrides,
955 self.set_test,
956 false,
957 crate_ws_data,
958 ),
959 ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _ } => {
960 cargo_to_crate_graph(
961 load,
962 rustc.as_ref().map(|a| a.as_ref()).ok(),
963 cargo,
964 sysroot,
965 rustc_cfg.clone(),
966 cfg_overrides,
967 build_scripts,
968 self.set_test,
969 crate_ws_data,
970 )
971 }
972 ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
973 if let Some((cargo, build_scripts, _)) = cargo_script {
974 cargo_to_crate_graph(
975 &mut |path| load(path),
976 None,
977 cargo,
978 sysroot,
979 rustc_cfg.clone(),
980 cfg_overrides,
981 build_scripts,
982 self.set_test,
983 crate_ws_data,
984 )
985 } else {
986 detached_file_to_crate_graph(
987 rustc_cfg.clone(),
988 load,
989 file,
990 sysroot,
991 cfg_overrides,
992 self.set_test,
993 crate_ws_data,
994 )
995 }
996 }
997 };
998
999 (crate_graph, proc_macros)
1000 }
1001
1002 pub fn eq_ignore_build_data(&self, other: &Self) -> bool {
1003 let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides, .. } = self;
1004 let Self {
1005 kind: o_kind,
1006 sysroot: o_sysroot,
1007 rustc_cfg: o_rustc_cfg,
1008 toolchain: o_toolchain,
1009 target_layout: o_target_layout,
1010 cfg_overrides: o_cfg_overrides,
1011 ..
1012 } = other;
1013 (match (kind, o_kind) {
1014 (
1015 ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts: _, error: _ },
1016 ProjectWorkspaceKind::Cargo {
1017 cargo: o_cargo,
1018 rustc: o_rustc,
1019 build_scripts: _,
1020 error: _,
1021 },
1022 ) => cargo == o_cargo && rustc == o_rustc,
1023 (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => {
1024 project == o_project
1025 }
1026 (
1027 ProjectWorkspaceKind::DetachedFile { file, cargo: Some((cargo_script, _, _)) },
1028 ProjectWorkspaceKind::DetachedFile {
1029 file: o_file,
1030 cargo: Some((o_cargo_script, _, _)),
1031 },
1032 ) => file == o_file && cargo_script == o_cargo_script,
1033 _ => return false,
1034 }) && sysroot == o_sysroot
1035 && rustc_cfg == o_rustc_cfg
1036 && toolchain == o_toolchain
1037 && target_layout == o_target_layout
1038 && cfg_overrides == o_cfg_overrides
1039 }
1040
1041 #[must_use]
1045 pub fn is_json(&self) -> bool {
1046 matches!(self.kind, ProjectWorkspaceKind::Json { .. })
1047 }
1048}
1049
1050#[instrument(skip_all)]
1051fn project_json_to_crate_graph(
1052 rustc_cfg: Vec<CfgAtom>,
1053 load: FileLoader<'_>,
1054 project: &ProjectJson,
1055 sysroot: &Sysroot,
1056 extra_env: &FxHashMap<String, Option<String>>,
1057 override_cfg: &CfgOverrides,
1058 set_test: bool,
1059 is_sysroot: bool,
1060 crate_ws_data: Arc<CrateWorkspaceData>,
1061) -> (CrateGraphBuilder, ProcMacroPaths) {
1062 let mut res = (CrateGraphBuilder::default(), ProcMacroPaths::default());
1063 let (crate_graph, proc_macros) = &mut res;
1064 let (public_deps, libproc_macro) = sysroot_to_crate_graph(
1065 crate_graph,
1066 sysroot,
1067 rustc_cfg.clone(),
1068 load,
1069 crate_ws_data.clone(),
1071 );
1072
1073 let mut cfg_cache: FxHashMap<&str, Vec<CfgAtom>> = FxHashMap::default();
1074 let project_root = Arc::new(project.project_root().to_path_buf());
1075
1076 let idx_to_crate_id: FxHashMap<CrateArrayIdx, _> = project
1077 .crates()
1078 .filter_map(|(idx, krate)| Some((idx, krate, load(&krate.root_module)?)))
1079 .map(
1080 |(
1081 idx,
1082 Crate {
1083 display_name,
1084 edition,
1085 version,
1086 cfg,
1087 target,
1088 env,
1089 proc_macro_dylib_path,
1090 is_proc_macro,
1091 repository,
1092 is_workspace_member,
1093 proc_macro_cwd,
1094 ..
1095 },
1096 file_id,
1097 )| {
1098 let env = env.clone().into_iter().collect();
1099
1100 let target_cfgs = match target.as_deref() {
1101 Some(target) => cfg_cache.entry(target).or_insert_with(|| {
1102 rustc_cfg::get(
1103 QueryConfig::Rustc(sysroot, project.project_root().as_ref()),
1104 Some(target),
1105 extra_env,
1106 )
1107 }),
1108 None => &rustc_cfg,
1109 };
1110
1111 let cfg_options = {
1112 let mut cfg_options: CfgOptions =
1113 target_cfgs.iter().chain(cfg.iter()).cloned().collect();
1114
1115 if *is_workspace_member {
1116 if set_test && !is_sysroot {
1117 cfg_options.insert_atom(sym::test);
1119 }
1120 cfg_options.insert_atom(sym::rust_analyzer);
1121 }
1122
1123 override_cfg.apply(
1124 &mut cfg_options,
1125 display_name
1126 .as_ref()
1127 .map(|it| it.canonical_name().as_str())
1128 .unwrap_or_default(),
1129 );
1130 cfg_options
1131 };
1132
1133 let crate_graph_crate_id = crate_graph.add_crate_root(
1134 file_id,
1135 *edition,
1136 display_name.clone(),
1137 version.clone(),
1138 cfg_options,
1139 None,
1140 env,
1141 if let Some(name) = display_name.clone() {
1142 if is_sysroot {
1143 CrateOrigin::Lang(LangCrateOrigin::from(name.canonical_name().as_str()))
1144 } else {
1145 CrateOrigin::Local {
1146 repo: repository.clone(),
1147 name: Some(name.canonical_name().to_owned()),
1148 }
1149 }
1150 } else {
1151 CrateOrigin::Local { repo: None, name: None }
1152 },
1153 *is_proc_macro,
1154 match proc_macro_cwd {
1155 Some(path) => Arc::new(path.clone()),
1156 None => project_root.clone(),
1157 },
1158 crate_ws_data.clone(),
1159 );
1160 debug!(
1161 ?crate_graph_crate_id,
1162 crate = display_name.as_ref().map(|name| name.canonical_name().as_str()),
1163 "added root to crate graph"
1164 );
1165 if *is_proc_macro && let Some(path) = proc_macro_dylib_path.clone() {
1166 let node = Ok((
1167 display_name
1168 .as_ref()
1169 .map(|it| it.canonical_name().as_str().to_owned())
1170 .unwrap_or_else(|| format!("crate{}", idx.0)),
1171 path,
1172 ));
1173 proc_macros.insert(crate_graph_crate_id, node);
1174 }
1175 (idx, crate_graph_crate_id)
1176 },
1177 )
1178 .collect();
1179
1180 debug!(map = ?idx_to_crate_id);
1181 for (from_idx, krate) in project.crates() {
1182 if let Some(&from) = idx_to_crate_id.get(&from_idx) {
1183 public_deps.add_to_crate_graph(crate_graph, from);
1184 if let Some(proc_macro) = libproc_macro {
1185 add_proc_macro_dep(crate_graph, from, proc_macro, krate.is_proc_macro);
1186 }
1187
1188 for dep in &krate.deps {
1189 if let Some(&to) = idx_to_crate_id.get(&dep.krate) {
1190 add_dep(crate_graph, from, dep.name.clone(), to);
1191 }
1192 }
1193 }
1194 }
1195 res
1196}
1197
1198fn cargo_to_crate_graph(
1199 load: FileLoader<'_>,
1200 rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
1201 cargo: &CargoWorkspace,
1202 sysroot: &Sysroot,
1203 rustc_cfg: Vec<CfgAtom>,
1204 override_cfg: &CfgOverrides,
1205 build_scripts: &WorkspaceBuildScripts,
1206 set_test: bool,
1207 crate_ws_data: Arc<CrateWorkspaceData>,
1208) -> (CrateGraphBuilder, ProcMacroPaths) {
1209 let _p = tracing::info_span!("cargo_to_crate_graph").entered();
1210 let mut res = (CrateGraphBuilder::default(), ProcMacroPaths::default());
1211 let (crate_graph, proc_macros) = &mut res;
1212 let (public_deps, libproc_macro) = sysroot_to_crate_graph(
1213 crate_graph,
1214 sysroot,
1215 rustc_cfg.clone(),
1216 load,
1217 crate_ws_data.clone(),
1218 );
1219
1220 let cfg_options = CfgOptions::from_iter(rustc_cfg);
1221
1222 let mut pkg_to_lib_crate = FxHashMap::default();
1224 let mut pkg_crates = FxHashMap::default();
1225 let workspace_proc_macro_cwd = Arc::new(cargo.workspace_root().to_path_buf());
1226
1227 for pkg in cargo.packages() {
1229 let cfg_options = {
1230 let mut cfg_options = cfg_options.clone();
1231
1232 if cargo[pkg].is_local {
1233 if set_test && !cargo.is_sysroot() {
1234 cfg_options.insert_atom(sym::test);
1236 }
1237 cfg_options.insert_atom(sym::rust_analyzer);
1238 }
1239
1240 override_cfg.apply(&mut cfg_options, &cargo[pkg].name);
1241 cfg_options
1242 };
1243
1244 let mut lib_tgt = None;
1245 for &tgt in cargo[pkg].targets.iter() {
1246 let pkg_data = &cargo[pkg];
1247 if !matches!(cargo[tgt].kind, TargetKind::Lib { .. })
1248 && (!pkg_data.is_member || cargo.is_sysroot())
1249 {
1250 continue;
1256 }
1257 let &TargetData { ref name, kind, ref root, .. } = &cargo[tgt];
1258
1259 let Some(file_id) = load(root) else { continue };
1260
1261 let build_data = build_scripts.get_output(pkg);
1262 let crate_id = add_target_crate_root(
1263 crate_graph,
1264 proc_macros,
1265 cargo,
1266 pkg_data,
1267 build_data.zip(Some(build_scripts.error().is_some())),
1268 cfg_options.clone(),
1269 file_id,
1270 name,
1271 kind,
1272 if pkg_data.is_local {
1273 if cargo.is_sysroot() {
1274 CrateOrigin::Lang(LangCrateOrigin::from(&*pkg_data.name))
1275 } else {
1276 CrateOrigin::Local {
1277 repo: pkg_data.repository.clone(),
1278 name: Some(Symbol::intern(&pkg_data.name)),
1279 }
1280 }
1281 } else {
1282 CrateOrigin::Library {
1283 repo: pkg_data.repository.clone(),
1284 name: Symbol::intern(&pkg_data.name),
1285 }
1286 },
1287 crate_ws_data.clone(),
1288 if pkg_data.is_member {
1289 workspace_proc_macro_cwd.clone()
1290 } else {
1291 Arc::new(pkg_data.manifest.parent().to_path_buf())
1292 },
1293 );
1294 if let TargetKind::Lib { .. } = kind {
1295 lib_tgt = Some((crate_id, name.clone()));
1296 pkg_to_lib_crate.insert(pkg, crate_id);
1297 }
1298 if let Some(proc_macro) = libproc_macro {
1301 add_proc_macro_dep(
1302 crate_graph,
1303 crate_id,
1304 proc_macro,
1305 matches!(kind, TargetKind::Lib { is_proc_macro: true }),
1306 );
1307 }
1308
1309 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
1310 }
1311
1312 for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
1314 public_deps.add_to_crate_graph(crate_graph, from);
1316
1317 if let Some((to, name)) = lib_tgt.clone()
1319 && to != from
1320 && kind != TargetKind::BuildScript
1321 {
1322 let name = CrateName::normalize_dashes(&name);
1328 add_dep(crate_graph, from, name, to);
1329 }
1330 }
1331 }
1332
1333 let mut delayed_dev_deps = vec![];
1334
1335 for pkg in cargo.packages() {
1338 for dep in &cargo[pkg].dependencies {
1339 let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) else { continue };
1340 let Some(targets) = pkg_crates.get(&pkg) else { continue };
1341
1342 let name = CrateName::new(&dep.name).unwrap();
1343 for &(from, kind) in targets {
1344 if (dep.kind == DepKind::Build) != (kind == TargetKind::BuildScript) {
1346 continue;
1347 }
1348
1349 if dep.kind == DepKind::Dev
1357 && matches!(kind, TargetKind::Lib { .. })
1358 && cargo[dep.pkg].is_member
1359 && cargo[pkg].is_member
1360 {
1361 delayed_dev_deps.push((from, name.clone(), to));
1362 continue;
1363 }
1364
1365 add_dep(crate_graph, from, name.clone(), to)
1366 }
1367 }
1368 }
1369
1370 for (from, name, to) in delayed_dev_deps {
1371 add_dep(crate_graph, from, name, to);
1372 }
1373
1374 if cargo.requires_rustc_private() {
1375 if let Some((rustc_workspace, rustc_build_scripts)) = rustc {
1378 handle_rustc_crates(
1379 crate_graph,
1380 proc_macros,
1381 &mut pkg_to_lib_crate,
1382 load,
1383 rustc_workspace,
1384 cargo,
1385 &public_deps,
1386 libproc_macro,
1387 &pkg_crates,
1388 &cfg_options,
1389 override_cfg,
1390 if rustc_workspace.workspace_root() == cargo.workspace_root() {
1392 build_scripts
1395 } else {
1396 rustc_build_scripts
1397 },
1398 crate_ws_data,
1400 );
1401 }
1402 }
1403 res
1404}
1405
1406fn detached_file_to_crate_graph(
1407 rustc_cfg: Vec<CfgAtom>,
1408 load: FileLoader<'_>,
1409 detached_file: &ManifestPath,
1410 sysroot: &Sysroot,
1411 override_cfg: &CfgOverrides,
1412 set_test: bool,
1413 crate_ws_data: Arc<CrateWorkspaceData>,
1414) -> (CrateGraphBuilder, ProcMacroPaths) {
1415 let _p = tracing::info_span!("detached_file_to_crate_graph").entered();
1416 let mut crate_graph = CrateGraphBuilder::default();
1417 let (public_deps, _libproc_macro) = sysroot_to_crate_graph(
1418 &mut crate_graph,
1419 sysroot,
1420 rustc_cfg.clone(),
1421 load,
1422 crate_ws_data.clone(),
1424 );
1425
1426 let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
1427 if set_test {
1428 cfg_options.insert_atom(sym::test);
1429 }
1430 cfg_options.insert_atom(sym::rust_analyzer);
1431 override_cfg.apply(&mut cfg_options, "");
1432 let cfg_options = cfg_options;
1433
1434 let file_id = match load(detached_file) {
1435 Some(file_id) => file_id,
1436 None => {
1437 error!("Failed to load detached file {:?}", detached_file);
1438 return (crate_graph, FxHashMap::default());
1439 }
1440 };
1441 let display_name = detached_file.file_stem().map(CrateDisplayName::from_canonical_name);
1442 let detached_file_crate = crate_graph.add_crate_root(
1443 file_id,
1444 Edition::CURRENT,
1445 display_name.clone(),
1446 None,
1447 cfg_options,
1448 None,
1449 Env::default(),
1450 CrateOrigin::Local {
1451 repo: None,
1452 name: display_name.map(|n| n.canonical_name().to_owned()),
1453 },
1454 false,
1455 Arc::new(detached_file.parent().to_path_buf()),
1456 crate_ws_data,
1457 );
1458
1459 public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
1460 (crate_graph, FxHashMap::default())
1461}
1462
1463fn handle_rustc_crates(
1465 crate_graph: &mut CrateGraphBuilder,
1466 proc_macros: &mut ProcMacroPaths,
1467 pkg_to_lib_crate: &mut FxHashMap<Package, CrateBuilderId>,
1468 load: FileLoader<'_>,
1469 rustc_workspace: &CargoWorkspace,
1470 cargo: &CargoWorkspace,
1471 public_deps: &SysrootPublicDeps,
1472 libproc_macro: Option<CrateBuilderId>,
1473 pkg_crates: &FxHashMap<Package, Vec<(CrateBuilderId, TargetKind)>>,
1474 cfg_options: &CfgOptions,
1475 override_cfg: &CfgOverrides,
1476 build_scripts: &WorkspaceBuildScripts,
1477 crate_ws_data: Arc<CrateWorkspaceData>,
1478) {
1479 let mut rustc_pkg_crates = FxHashMap::default();
1480 let root_pkg =
1482 rustc_workspace.packages().find(|&package| rustc_workspace[package].name == "rustc_driver");
1483 let workspace_proc_macro_cwd = Arc::new(cargo.workspace_root().to_path_buf());
1484 if let Some(root_pkg) = root_pkg {
1487 let mut queue = VecDeque::new();
1489 queue.push_back(root_pkg);
1490 while let Some(pkg) = queue.pop_front() {
1491 if rustc_pkg_crates.contains_key(&pkg) {
1495 continue;
1496 }
1497 let pkg_data = &rustc_workspace[pkg];
1498 for dep in &pkg_data.dependencies {
1499 queue.push_back(dep.pkg);
1500 }
1501
1502 let mut cfg_options = cfg_options.clone();
1503 override_cfg.apply(&mut cfg_options, &pkg_data.name);
1504
1505 for &tgt in pkg_data.targets.iter() {
1506 let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else {
1507 continue;
1508 };
1509 let pkg_crates = &mut rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new);
1510 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
1511 let crate_id = add_target_crate_root(
1512 crate_graph,
1513 proc_macros,
1514 rustc_workspace,
1515 pkg_data,
1516 build_scripts.get_output(pkg).zip(Some(build_scripts.error().is_some())),
1517 cfg_options.clone(),
1518 file_id,
1519 &rustc_workspace[tgt].name,
1520 kind,
1521 CrateOrigin::Rustc { name: Symbol::intern(&pkg_data.name) },
1522 crate_ws_data.clone(),
1523 if pkg_data.is_member {
1524 workspace_proc_macro_cwd.clone()
1525 } else {
1526 Arc::new(pkg_data.manifest.parent().to_path_buf())
1527 },
1528 );
1529 pkg_to_lib_crate.insert(pkg, crate_id);
1530 public_deps.add_to_crate_graph(crate_graph, crate_id);
1532 if let Some(proc_macro) = libproc_macro {
1533 add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro);
1534 }
1535 pkg_crates.push(crate_id);
1536 }
1537 }
1538 }
1539 }
1540 for pkg in rustc_pkg_crates.keys().copied() {
1543 for dep in rustc_workspace[pkg].dependencies.iter() {
1544 let name = CrateName::new(&dep.name).unwrap();
1545 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
1546 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
1547 add_dep(crate_graph, from, name.clone(), to);
1548 }
1549 }
1550 }
1551 }
1552 for dep in rustc_workspace.packages() {
1555 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
1556
1557 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
1558 for pkg in cargo.packages() {
1559 let package = &cargo[pkg];
1560 if !package.metadata.rustc_private {
1561 continue;
1562 }
1563 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
1564 if !crate_graph[*from].basic.dependencies.iter().any(|d| d.name == name) {
1569 add_dep(crate_graph, *from, name.clone(), to);
1570 }
1571 }
1572 }
1573 }
1574 }
1575}
1576
1577fn add_target_crate_root(
1578 crate_graph: &mut CrateGraphBuilder,
1579 proc_macros: &mut ProcMacroPaths,
1580 cargo: &CargoWorkspace,
1581 pkg: &PackageData,
1582 build_data: Option<(&BuildScriptOutput, bool)>,
1583 cfg_options: CfgOptions,
1584 file_id: FileId,
1585 cargo_name: &str,
1586 kind: TargetKind,
1587 origin: CrateOrigin,
1588 crate_ws_data: Arc<CrateWorkspaceData>,
1589 proc_macro_cwd: Arc<AbsPathBuf>,
1590) -> CrateBuilderId {
1591 let edition = pkg.edition;
1592 let potential_cfg_options = if pkg.features.is_empty() {
1593 None
1594 } else {
1595 let mut potential_cfg_options = cfg_options.clone();
1596 potential_cfg_options.extend(
1597 pkg.features
1598 .iter()
1599 .map(|feat| CfgAtom::KeyValue { key: sym::feature, value: Symbol::intern(feat.0) }),
1600 );
1601 Some(potential_cfg_options)
1602 };
1603 let cfg_options = {
1604 let mut opts = cfg_options;
1605 for feature in pkg.active_features.iter() {
1606 opts.insert_key_value(sym::feature, Symbol::intern(feature));
1607 }
1608 if let Some(cfgs) = build_data.map(|(it, _)| &it.cfgs) {
1609 opts.extend(cfgs.iter().cloned());
1610 }
1611 opts
1612 };
1613
1614 let mut env = cargo.env().clone();
1615 inject_cargo_package_env(&mut env, pkg);
1616 inject_cargo_env(&mut env);
1617 inject_rustc_tool_env(&mut env, cargo_name, kind);
1618
1619 if let Some(envs) = build_data.map(|(it, _)| &it.envs) {
1620 env.extend_from_other(envs);
1621 }
1622 let crate_id = crate_graph.add_crate_root(
1623 file_id,
1624 edition,
1625 Some(CrateDisplayName::from_canonical_name(cargo_name)),
1626 Some(pkg.version.to_string()),
1627 cfg_options,
1628 potential_cfg_options,
1629 env,
1630 origin,
1631 matches!(kind, TargetKind::Lib { is_proc_macro: true }),
1632 proc_macro_cwd,
1633 crate_ws_data,
1634 );
1635 if let TargetKind::Lib { is_proc_macro: true } = kind {
1636 let proc_macro = match build_data {
1637 Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
1638 match proc_macro_dylib_path {
1639 ProcMacroDylibPath::Path(path) => Ok((cargo_name.to_owned(), path.clone())),
1640 ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt),
1641 ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound
1642 if has_errors =>
1643 {
1644 Err(ProcMacroLoadingError::FailedToBuild)
1645 }
1646 ProcMacroDylibPath::NotProcMacro => {
1647 Err(ProcMacroLoadingError::ExpectedProcMacroArtifact)
1648 }
1649 ProcMacroDylibPath::DylibNotFound => {
1650 Err(ProcMacroLoadingError::MissingDylibPath)
1651 }
1652 }
1653 }
1654 None => Err(ProcMacroLoadingError::NotYetBuilt),
1655 };
1656 proc_macros.insert(crate_id, proc_macro);
1657 }
1658
1659 crate_id
1660}
1661
1662#[derive(Default, Debug)]
1663struct SysrootPublicDeps {
1664 deps: Vec<(CrateName, CrateBuilderId, bool)>,
1665}
1666
1667impl SysrootPublicDeps {
1668 fn add_to_crate_graph(&self, crate_graph: &mut CrateGraphBuilder, from: CrateBuilderId) {
1670 for (name, krate, prelude) in &self.deps {
1671 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude, true);
1672 }
1673 }
1674}
1675
1676fn extend_crate_graph_with_sysroot(
1677 crate_graph: &mut CrateGraphBuilder,
1678 mut sysroot_crate_graph: CrateGraphBuilder,
1679 mut sysroot_proc_macros: ProcMacroPaths,
1680) -> (SysrootPublicDeps, Option<CrateBuilderId>) {
1681 let mut pub_deps = vec![];
1682 let mut libproc_macro = None;
1683 for cid in sysroot_crate_graph.iter() {
1684 if let CrateOrigin::Lang(lang_crate) = sysroot_crate_graph[cid].basic.origin {
1685 match lang_crate {
1686 LangCrateOrigin::Test
1687 | LangCrateOrigin::Alloc
1688 | LangCrateOrigin::Core
1689 | LangCrateOrigin::Std => pub_deps.push((
1690 CrateName::normalize_dashes(&lang_crate.to_string()),
1691 cid,
1692 !matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
1693 )),
1694 LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
1695 LangCrateOrigin::Other => (),
1696 }
1697 }
1698 }
1699
1700 let mut marker_set = vec![];
1701 for &(_, cid, _) in pub_deps.iter() {
1702 marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
1703 }
1704 if let Some(cid) = libproc_macro {
1705 marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
1706 }
1707
1708 marker_set.sort();
1709 marker_set.dedup();
1710
1711 let removed_mapping = sysroot_crate_graph.remove_crates_except(&marker_set);
1713 sysroot_proc_macros = sysroot_proc_macros
1714 .into_iter()
1715 .filter_map(|(k, v)| Some((removed_mapping[k.into_raw().into_u32() as usize]?, v)))
1716 .collect();
1717 let mapping = crate_graph.extend(sysroot_crate_graph, &mut sysroot_proc_macros);
1718
1719 pub_deps.iter_mut().for_each(|(_, cid, _)| {
1721 *cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
1722 });
1723 if let Some(libproc_macro) = &mut libproc_macro {
1724 *libproc_macro =
1725 mapping[&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
1726 }
1727
1728 (SysrootPublicDeps { deps: pub_deps }, libproc_macro)
1729}
1730
1731fn sysroot_to_crate_graph(
1732 crate_graph: &mut CrateGraphBuilder,
1733 sysroot: &Sysroot,
1734 rustc_cfg: Vec<CfgAtom>,
1735 load: FileLoader<'_>,
1736 crate_ws_data: Arc<CrateWorkspaceData>,
1737) -> (SysrootPublicDeps, Option<CrateBuilderId>) {
1738 let _p = tracing::info_span!("sysroot_to_crate_graph").entered();
1739 match sysroot.workspace() {
1740 RustLibSrcWorkspace::Workspace(cargo) => {
1741 let (sysroot_cg, sysroot_pm) = cargo_to_crate_graph(
1742 load,
1743 None,
1744 cargo,
1745 &Sysroot::empty(),
1746 rustc_cfg,
1747 &CfgOverrides {
1748 global: CfgDiff::new(
1749 vec![
1750 CfgAtom::Flag(sym::debug_assertions),
1751 CfgAtom::Flag(sym::miri),
1752 CfgAtom::Flag(sym::bootstrap),
1753 ],
1754 vec![CfgAtom::Flag(sym::test)],
1755 ),
1756 ..Default::default()
1757 },
1758 &WorkspaceBuildScripts::default(),
1759 false,
1760 crate_ws_data,
1761 );
1762
1763 extend_crate_graph_with_sysroot(crate_graph, sysroot_cg, sysroot_pm)
1764 }
1765 RustLibSrcWorkspace::Json(project_json) => {
1766 let (sysroot_cg, sysroot_pm) = project_json_to_crate_graph(
1767 rustc_cfg,
1768 load,
1769 project_json,
1770 &Sysroot::empty(),
1771 &FxHashMap::default(),
1772 &CfgOverrides {
1773 global: CfgDiff::new(
1774 vec![CfgAtom::Flag(sym::debug_assertions), CfgAtom::Flag(sym::miri)],
1775 vec![],
1776 ),
1777 ..Default::default()
1778 },
1779 false,
1780 true,
1781 crate_ws_data,
1782 );
1783
1784 extend_crate_graph_with_sysroot(crate_graph, sysroot_cg, sysroot_pm)
1785 }
1786 RustLibSrcWorkspace::Stitched(stitched) => {
1787 let cfg_options = {
1788 let mut cfg_options = CfgOptions::default();
1789 cfg_options.extend(rustc_cfg);
1790 cfg_options.insert_atom(sym::debug_assertions);
1791 cfg_options.insert_atom(sym::miri);
1792 cfg_options
1793 };
1794 let sysroot_crates: FxHashMap<
1795 crate::sysroot::stitched::RustLibSrcCrate,
1796 CrateBuilderId,
1797 > = stitched
1798 .crates()
1799 .filter_map(|krate| {
1800 let file_id = load(&stitched[krate].root)?;
1801
1802 let display_name = CrateDisplayName::from_canonical_name(&stitched[krate].name);
1803 let crate_id = crate_graph.add_crate_root(
1804 file_id,
1805 Edition::CURRENT_FIXME,
1806 Some(display_name),
1807 None,
1808 cfg_options.clone(),
1809 None,
1810 Env::default(),
1811 CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
1812 false,
1813 Arc::new(stitched[krate].root.parent().to_path_buf()),
1814 crate_ws_data.clone(),
1815 );
1816 Some((krate, crate_id))
1817 })
1818 .collect();
1819
1820 for from in stitched.crates() {
1821 for &to in stitched[from].deps.iter() {
1822 let name = CrateName::new(&stitched[to].name).unwrap();
1823 if let (Some(&from), Some(&to)) =
1824 (sysroot_crates.get(&from), sysroot_crates.get(&to))
1825 {
1826 add_dep(crate_graph, from, name, to);
1827 }
1828 }
1829 }
1830
1831 let public_deps = SysrootPublicDeps {
1832 deps: stitched
1833 .public_deps()
1834 .filter_map(|(name, idx, prelude)| {
1835 Some((name, *sysroot_crates.get(&idx)?, prelude))
1836 })
1837 .collect::<Vec<_>>(),
1838 };
1839
1840 let libproc_macro =
1841 stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
1842 (public_deps, libproc_macro)
1843 }
1844 RustLibSrcWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None),
1845 }
1846}
1847
1848fn add_dep(
1849 graph: &mut CrateGraphBuilder,
1850 from: CrateBuilderId,
1851 name: CrateName,
1852 to: CrateBuilderId,
1853) {
1854 add_dep_inner(graph, from, DependencyBuilder::new(name, to))
1855}
1856
1857fn add_dep_with_prelude(
1858 graph: &mut CrateGraphBuilder,
1859 from: CrateBuilderId,
1860 name: CrateName,
1861 to: CrateBuilderId,
1862 prelude: bool,
1863 sysroot: bool,
1864) {
1865 add_dep_inner(graph, from, DependencyBuilder::with_prelude(name, to, prelude, sysroot))
1866}
1867
1868fn add_proc_macro_dep(
1869 crate_graph: &mut CrateGraphBuilder,
1870 from: CrateBuilderId,
1871 to: CrateBuilderId,
1872 prelude: bool,
1873) {
1874 add_dep_with_prelude(
1875 crate_graph,
1876 from,
1877 CrateName::new("proc_macro").unwrap(),
1878 to,
1879 prelude,
1880 true,
1881 );
1882}
1883
1884fn add_dep_inner(graph: &mut CrateGraphBuilder, from: CrateBuilderId, dep: DependencyBuilder) {
1885 if let Err(err) = graph.add_dep(from, dep) {
1886 tracing::warn!("{}", err)
1887 }
1888}
1889
1890fn sysroot_metadata_config(
1891 config: &CargoConfig,
1892 targets: &[String],
1893 toolchain_version: Option<Version>,
1894) -> CargoMetadataConfig {
1895 CargoMetadataConfig {
1896 features: Default::default(),
1897 targets: targets.to_vec(),
1898 extra_args: Default::default(),
1899 extra_env: config.extra_env.clone(),
1900 toolchain_version,
1901 kind: "sysroot",
1902 }
1903}
1904
1905fn cargo_target_dir(
1906 manifest: &ManifestPath,
1907 extra_env: &FxHashMap<String, Option<String>>,
1908 sysroot: &Sysroot,
1909) -> Option<Utf8PathBuf> {
1910 let cargo = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
1911 let mut meta = cargo_metadata::MetadataCommand::new();
1912 meta.cargo_path(cargo.get_program());
1913 meta.manifest_path(manifest);
1914 meta.no_deps();
1917 let mut other_options = vec![];
1918 if manifest.is_rust_manifest() {
1919 meta.env("RUSTC_BOOTSTRAP", "1");
1920 other_options.push("-Zscript".to_owned());
1921 }
1922 meta.other_options(other_options);
1923 meta.exec().map(|m| m.target_directory).ok()
1924}