aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model')
-rw-r--r--crates/project_model/src/cargo_workspace.rs3
-rw-r--r--crates/project_model/src/lib.rs273
2 files changed, 209 insertions, 67 deletions
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index d5f6a4025..608a031d4 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -64,6 +64,9 @@ pub struct CargoConfig {
64 64
65 /// rustc target 65 /// rustc target
66 pub target: Option<String>, 66 pub target: Option<String>,
67
68 /// rustc private crate source
69 pub rustc_source: Option<AbsPathBuf>,
67} 70}
68 71
69pub type Package = Idx<PackageData>; 72pub type Package = Idx<PackageData>;
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index e92cfea59..40eb05f70 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -9,6 +9,7 @@ use std::{
9 fmt, 9 fmt,
10 fs::{self, read_dir, ReadDir}, 10 fs::{self, read_dir, ReadDir},
11 io, 11 io,
12 path::Component,
12 process::Command, 13 process::Command,
13}; 14};
14 15
@@ -31,7 +32,7 @@ pub use proc_macro_api::ProcMacroClient;
31#[derive(Clone, Eq, PartialEq)] 32#[derive(Clone, Eq, PartialEq)]
32pub enum ProjectWorkspace { 33pub enum ProjectWorkspace {
33 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 34 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
34 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 35 Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> },
35 /// Project workspace was manually specified using a `rust-project.json` file. 36 /// Project workspace was manually specified using a `rust-project.json` file.
36 Json { project: ProjectJson, sysroot: Option<Sysroot> }, 37 Json { project: ProjectJson, sysroot: Option<Sysroot> },
37} 38}
@@ -39,10 +40,14 @@ pub enum ProjectWorkspace {
39impl fmt::Debug for ProjectWorkspace { 40impl fmt::Debug for ProjectWorkspace {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self { 42 match self {
42 ProjectWorkspace::Cargo { cargo, sysroot } => f 43 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f
43 .debug_struct("Cargo") 44 .debug_struct("Cargo")
44 .field("n_packages", &cargo.packages().len()) 45 .field("n_packages", &cargo.packages().len())
45 .field("n_sysroot_crates", &sysroot.crates().len()) 46 .field("n_sysroot_crates", &sysroot.crates().len())
47 .field(
48 "n_rustc_compiler_crates",
49 &rustc.as_ref().map(|rc| rc.packages().len()).unwrap_or(0),
50 )
46 .finish(), 51 .finish(),
47 ProjectWorkspace::Json { project, sysroot } => { 52 ProjectWorkspace::Json { project, sysroot } => {
48 let mut debug_struct = f.debug_struct("Json"); 53 let mut debug_struct = f.debug_struct("Json");
@@ -200,7 +205,19 @@ impl ProjectWorkspace {
200 } else { 205 } else {
201 Sysroot::default() 206 Sysroot::default()
202 }; 207 };
203 ProjectWorkspace::Cargo { cargo, sysroot } 208
209 let rustc = if let Some(rustc_dir) = &cargo_config.rustc_source {
210 Some(
211 CargoWorkspace::from_cargo_metadata(&rustc_dir, cargo_config)
212 .with_context(|| {
213 format!("Failed to read Cargo metadata for Rust sources")
214 })?,
215 )
216 } else {
217 None
218 };
219
220 ProjectWorkspace::Cargo { cargo, sysroot, rustc }
204 } 221 }
205 }; 222 };
206 223
@@ -238,31 +255,43 @@ impl ProjectWorkspace {
238 }) 255 })
239 })) 256 }))
240 .collect::<Vec<_>>(), 257 .collect::<Vec<_>>(),
241 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 258 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
242 .packages() 259 let roots = cargo
243 .map(|pkg| { 260 .packages()
244 let is_member = cargo[pkg].is_member; 261 .map(|pkg| {
245 let pkg_root = cargo[pkg].root().to_path_buf(); 262 let is_member = cargo[pkg].is_member;
246 263 let pkg_root = cargo[pkg].root().to_path_buf();
247 let mut include = vec![pkg_root.clone()]; 264
248 include.extend(cargo[pkg].out_dir.clone()); 265 let mut include = vec![pkg_root.clone()];
249 266 include.extend(cargo[pkg].out_dir.clone());
250 let mut exclude = vec![pkg_root.join(".git")]; 267
251 if is_member { 268 let mut exclude = vec![pkg_root.join(".git")];
252 exclude.push(pkg_root.join("target")); 269 if is_member {
253 } else { 270 exclude.push(pkg_root.join("target"));
254 exclude.push(pkg_root.join("tests")); 271 } else {
255 exclude.push(pkg_root.join("examples")); 272 exclude.push(pkg_root.join("tests"));
256 exclude.push(pkg_root.join("benches")); 273 exclude.push(pkg_root.join("examples"));
257 } 274 exclude.push(pkg_root.join("benches"));
258 PackageRoot { is_member, include, exclude } 275 }
259 }) 276 PackageRoot { is_member, include, exclude }
260 .chain(sysroot.crates().map(|krate| PackageRoot { 277 })
261 is_member: false, 278 .chain(sysroot.crates().map(|krate| PackageRoot {
262 include: vec![sysroot[krate].root_dir().to_path_buf()], 279 is_member: false,
263 exclude: Vec::new(), 280 include: vec![sysroot[krate].root_dir().to_path_buf()],
264 })) 281 exclude: Vec::new(),
265 .collect(), 282 }));
283 if let Some(rustc_packages) = rustc {
284 roots
285 .chain(rustc_packages.packages().map(|krate| PackageRoot {
286 is_member: false,
287 include: vec![rustc_packages[krate].root().to_path_buf()],
288 exclude: Vec::new(),
289 }))
290 .collect()
291 } else {
292 roots.collect()
293 }
294 }
266 } 295 }
267 } 296 }
268 297
@@ -273,7 +302,7 @@ impl ProjectWorkspace {
273 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref()) 302 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref())
274 .cloned() 303 .cloned()
275 .collect(), 304 .collect(),
276 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo 305 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot, rustc: _rustc_crates } => cargo
277 .packages() 306 .packages()
278 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref()) 307 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref())
279 .cloned() 308 .cloned()
@@ -284,8 +313,10 @@ impl ProjectWorkspace {
284 pub fn n_packages(&self) -> usize { 313 pub fn n_packages(&self) -> usize {
285 match self { 314 match self {
286 ProjectWorkspace::Json { project, .. } => project.n_crates(), 315 ProjectWorkspace::Json { project, .. } => project.n_crates(),
287 ProjectWorkspace::Cargo { cargo, sysroot } => { 316 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
288 cargo.packages().len() + sysroot.crates().len() 317 let rustc_package_len = rustc.as_ref().map(|rc| rc.packages().len()).unwrap_or(0);
318 dbg!(rustc_package_len);
319 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
289 } 320 }
290 } 321 }
291 } 322 }
@@ -365,7 +396,7 @@ impl ProjectWorkspace {
365 } 396 }
366 } 397 }
367 } 398 }
368 ProjectWorkspace::Cargo { cargo, sysroot } => { 399 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
369 let (public_deps, libproc_macro) = 400 let (public_deps, libproc_macro) =
370 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 401 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
371 402
@@ -373,50 +404,88 @@ impl ProjectWorkspace {
373 cfg_options.extend(get_rustc_cfg_options(target)); 404 cfg_options.extend(get_rustc_cfg_options(target));
374 405
375 let mut pkg_to_lib_crate = FxHashMap::default(); 406 let mut pkg_to_lib_crate = FxHashMap::default();
376 let mut pkg_crates = FxHashMap::default();
377 407
378 // Add test cfg for non-sysroot crates 408 // Add test cfg for non-sysroot crates
379 cfg_options.insert_atom("test".into()); 409 cfg_options.insert_atom("test".into());
380 cfg_options.insert_atom("debug_assertions".into()); 410 cfg_options.insert_atom("debug_assertions".into());
381 411
412 let mut rustc_pkg_crates = FxHashMap::default();
413
414 // Add crate roots for rustc_private libs if a path to source is provided
415 if let Some(rustc_workspace) = rustc {
416 for pkg in rustc_workspace.packages() {
417 for &tgt in rustc_workspace[pkg].targets.iter() {
418 if rustc_workspace[tgt].kind != TargetKind::Lib {
419 continue;
420 }
421 // Exclude alloc / core / std
422 if rustc_workspace[tgt]
423 .root
424 .components()
425 .any(|c| c == Component::Normal("library".as_ref()))
426 {
427 continue;
428 }
429
430 if let Some(crate_id) = add_target_crate_root(
431 &mut crate_graph,
432 &rustc_workspace[pkg],
433 &rustc_workspace[tgt],
434 &cfg_options,
435 proc_macro_client,
436 load,
437 ) {
438 pkg_to_lib_crate.insert(pkg, crate_id);
439 // Add dependencies on the core / std / alloc for rustc
440 for (name, krate) in public_deps.iter() {
441 if let Err(_) =
442 crate_graph.add_dep(crate_id, name.clone(), *krate)
443 {
444 log::error!(
445 "cyclic dependency on {} for {}",
446 name,
447 &cargo[pkg].name
448 )
449 }
450 }
451 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
452 }
453 }
454 }
455 // Now add a dep edge from all targets of upstream to the lib
456 // target of downstream.
457 for pkg in rustc_workspace.packages() {
458 for dep in rustc_workspace[pkg].dependencies.iter() {
459 let name = CrateName::new(&dep.name).unwrap();
460 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
461 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
462 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
463 log::error!(
464 "cyclic dependency {} -> {}",
465 &rustc_workspace[pkg].name,
466 &rustc_workspace[dep.pkg].name
467 )
468 }
469 }
470 }
471 }
472 }
473 };
474
475 let mut pkg_crates = FxHashMap::default();
476
382 // Next, create crates for each package, target pair 477 // Next, create crates for each package, target pair
383 for pkg in cargo.packages() { 478 for pkg in cargo.packages() {
384 let mut lib_tgt = None; 479 let mut lib_tgt = None;
385 for &tgt in cargo[pkg].targets.iter() { 480 for &tgt in cargo[pkg].targets.iter() {
386 let root = cargo[tgt].root.as_path(); 481 if let Some(crate_id) = add_target_crate_root(
387 if let Some(file_id) = load(root) { 482 &mut crate_graph,
388 let edition = cargo[pkg].edition; 483 &cargo[pkg],
389 let cfg_options = { 484 &cargo[tgt],
390 let mut opts = cfg_options.clone(); 485 &cfg_options,
391 for feature in cargo[pkg].features.iter() { 486 proc_macro_client,
392 opts.insert_key_value("feature".into(), feature.into()); 487 load,
393 } 488 ) {
394 opts.extend(cargo[pkg].cfgs.iter().cloned());
395 opts
396 };
397 let mut env = Env::default();
398 if let Some(out_dir) = &cargo[pkg].out_dir {
399 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
400 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
401 env.set("OUT_DIR", out_dir);
402 }
403 }
404 let proc_macro = cargo[pkg]
405 .proc_macro_dylib_path
406 .as_ref()
407 .map(|it| proc_macro_client.by_dylib_path(&it))
408 .unwrap_or_default();
409
410 let display_name =
411 CrateDisplayName::from_canonical_name(cargo[pkg].name.clone());
412 let crate_id = crate_graph.add_crate_root(
413 file_id,
414 edition,
415 Some(display_name),
416 cfg_options,
417 env,
418 proc_macro.clone(),
419 );
420 if cargo[tgt].kind == TargetKind::Lib { 489 if cargo[tgt].kind == TargetKind::Lib {
421 lib_tgt = Some((crate_id, cargo[tgt].name.clone())); 490 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
422 pkg_to_lib_crate.insert(pkg, crate_id); 491 pkg_to_lib_crate.insert(pkg, crate_id);
@@ -466,6 +535,30 @@ impl ProjectWorkspace {
466 } 535 }
467 } 536 }
468 537
538 // If we have access to the rust sources, create dependencies onto rustc_private libraries from all targets
539 // that are members of the current workspace
540 if let Some(rustc_workspace) = rustc {
541 for dep in rustc_workspace.packages() {
542 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
543
544 if let Some(&from) = pkg_to_lib_crate.get(&dep) {
545 for pkg in cargo.packages() {
546 if !cargo[pkg].is_member {
547 continue;
548 }
549 for &to in pkg_crates.get(&pkg).into_iter().flatten() {
550 if let Err(_) = crate_graph.add_dep(to, name.clone(), from) {
551 log::error!(
552 "cyclic dependency22 {} -> {}",
553 &cargo[pkg].name,
554 &rustc_workspace[dep].name
555 )
556 }
557 }
558 }
559 }
560 }
561 }
469 // Now add a dep edge from all targets of upstream to the lib 562 // Now add a dep edge from all targets of upstream to the lib
470 // target of downstream. 563 // target of downstream.
471 for pkg in cargo.packages() { 564 for pkg in cargo.packages() {
@@ -537,6 +630,52 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
537 Ok(stdout.trim().to_string()) 630 Ok(stdout.trim().to_string())
538} 631}
539 632
633fn add_target_crate_root(
634 crate_graph: &mut CrateGraph,
635 pkg: &cargo_workspace::PackageData,
636 tgt: &cargo_workspace::TargetData,
637 cfg_options: &CfgOptions,
638 proc_macro_client: &ProcMacroClient,
639 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
640) -> Option<CrateId> {
641 let root = tgt.root.as_path();
642 if let Some(file_id) = load(root) {
643 let edition = pkg.edition;
644 let cfg_options = {
645 let mut opts = cfg_options.clone();
646 for feature in pkg.features.iter() {
647 opts.insert_key_value("feature".into(), feature.into());
648 }
649 opts.extend(pkg.cfgs.iter().cloned());
650 opts
651 };
652 let mut env = Env::default();
653 if let Some(out_dir) = &pkg.out_dir {
654 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
655 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
656 env.set("OUT_DIR", out_dir);
657 }
658 }
659 let proc_macro = pkg
660 .proc_macro_dylib_path
661 .as_ref()
662 .map(|it| proc_macro_client.by_dylib_path(&it))
663 .unwrap_or_default();
664
665 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
666 let crate_id = crate_graph.add_crate_root(
667 file_id,
668 edition,
669 Some(display_name),
670 cfg_options,
671 env,
672 proc_macro.clone(),
673 );
674
675 return Some(crate_id);
676 }
677 None
678}
540fn sysroot_to_crate_graph( 679fn sysroot_to_crate_graph(
541 crate_graph: &mut CrateGraph, 680 crate_graph: &mut CrateGraph,
542 sysroot: &Sysroot, 681 sysroot: &Sysroot,