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