Skip to content

Commit 8716087

Browse files
committed
Add support for loading rustc private crates
1 parent 5c06e82 commit 8716087

File tree

6 files changed

+3608
-79
lines changed

6 files changed

+3608
-79
lines changed

crates/project_model/src/cargo_workspace.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ pub struct CargoConfig {
6464

6565
/// rustc target
6666
pub target: Option<String>,
67+
68+
/// rustc private crate source
69+
pub rustc_source: Option<AbsPathBuf>,
6770
}
6871

6972
pub type Package = Idx<PackageData>;

crates/project_model/src/lib.rs

Lines changed: 206 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{
99
fmt,
1010
fs::{self, read_dir, ReadDir},
1111
io,
12+
path::Component,
1213
process::Command,
1314
};
1415

@@ -31,18 +32,22 @@ pub use proc_macro_api::ProcMacroClient;
3132
#[derive(Clone, Eq, PartialEq)]
3233
pub enum ProjectWorkspace {
3334
/// 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> },
3536
/// Project workspace was manually specified using a `rust-project.json` file.
3637
Json { project: ProjectJson, sysroot: Option<Sysroot> },
3738
}
3839

3940
impl fmt::Debug for ProjectWorkspace {
4041
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4142
match self {
42-
ProjectWorkspace::Cargo { cargo, sysroot } => f
43+
ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f
4344
.debug_struct("Cargo")
4445
.field("n_packages", &cargo.packages().len())
4546
.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+
)
4651
.finish(),
4752
ProjectWorkspace::Json { project, sysroot } => {
4853
let mut debug_struct = f.debug_struct("Json");
@@ -200,7 +205,19 @@ impl ProjectWorkspace {
200205
} else {
201206
Sysroot::default()
202207
};
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 }
204221
}
205222
};
206223

@@ -238,31 +255,43 @@ impl ProjectWorkspace {
238255
})
239256
}))
240257
.collect::<Vec<_>>(),
241-
ProjectWorkspace::Cargo { cargo, sysroot } => cargo
242-
.packages()
243-
.map(|pkg| {
244-
let is_member = cargo[pkg].is_member;
245-
let pkg_root = cargo[pkg].root().to_path_buf();
246-
247-
let mut include = vec![pkg_root.clone()];
248-
include.extend(cargo[pkg].out_dir.clone());
249-
250-
let mut exclude = vec![pkg_root.join(".git")];
251-
if is_member {
252-
exclude.push(pkg_root.join("target"));
253-
} else {
254-
exclude.push(pkg_root.join("tests"));
255-
exclude.push(pkg_root.join("examples"));
256-
exclude.push(pkg_root.join("benches"));
257-
}
258-
PackageRoot { is_member, include, exclude }
259-
})
260-
.chain(sysroot.crates().map(|krate| PackageRoot {
261-
is_member: false,
262-
include: vec![sysroot[krate].root_dir().to_path_buf()],
263-
exclude: Vec::new(),
264-
}))
265-
.collect(),
258+
ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
259+
let roots = cargo
260+
.packages()
261+
.map(|pkg| {
262+
let is_member = cargo[pkg].is_member;
263+
let pkg_root = cargo[pkg].root().to_path_buf();
264+
265+
let mut include = vec![pkg_root.clone()];
266+
include.extend(cargo[pkg].out_dir.clone());
267+
268+
let mut exclude = vec![pkg_root.join(".git")];
269+
if is_member {
270+
exclude.push(pkg_root.join("target"));
271+
} else {
272+
exclude.push(pkg_root.join("tests"));
273+
exclude.push(pkg_root.join("examples"));
274+
exclude.push(pkg_root.join("benches"));
275+
}
276+
PackageRoot { is_member, include, exclude }
277+
})
278+
.chain(sysroot.crates().map(|krate| PackageRoot {
279+
is_member: false,
280+
include: vec![sysroot[krate].root_dir().to_path_buf()],
281+
exclude: Vec::new(),
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+
}
266295
}
267296
}
268297

@@ -273,7 +302,7 @@ impl ProjectWorkspace {
273302
.filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref())
274303
.cloned()
275304
.collect(),
276-
ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
305+
ProjectWorkspace::Cargo { cargo, sysroot: _sysroot, rustc: _rustc_crates } => cargo
277306
.packages()
278307
.filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref())
279308
.cloned()
@@ -284,8 +313,10 @@ impl ProjectWorkspace {
284313
pub fn n_packages(&self) -> usize {
285314
match self {
286315
ProjectWorkspace::Json { project, .. } => project.n_crates(),
287-
ProjectWorkspace::Cargo { cargo, sysroot } => {
288-
cargo.packages().len() + sysroot.crates().len()
316+
ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
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
289320
}
290321
}
291322
}
@@ -365,58 +396,96 @@ impl ProjectWorkspace {
365396
}
366397
}
367398
}
368-
ProjectWorkspace::Cargo { cargo, sysroot } => {
399+
ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
369400
let (public_deps, libproc_macro) =
370401
sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
371402

372403
let mut cfg_options = CfgOptions::default();
373404
cfg_options.extend(get_rustc_cfg_options(target));
374405

375406
let mut pkg_to_lib_crate = FxHashMap::default();
376-
let mut pkg_crates = FxHashMap::default();
377407

378408
// Add test cfg for non-sysroot crates
379409
cfg_options.insert_atom("test".into());
380410
cfg_options.insert_atom("debug_assertions".into());
381411

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+
382477
// Next, create crates for each package, target pair
383478
for pkg in cargo.packages() {
384479
let mut lib_tgt = None;
385480
for &tgt in cargo[pkg].targets.iter() {
386-
let root = cargo[tgt].root.as_path();
387-
if let Some(file_id) = load(root) {
388-
let edition = cargo[pkg].edition;
389-
let cfg_options = {
390-
let mut opts = cfg_options.clone();
391-
for feature in cargo[pkg].features.iter() {
392-
opts.insert_key_value("feature".into(), feature.into());
393-
}
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-
);
481+
if let Some(crate_id) = add_target_crate_root(
482+
&mut crate_graph,
483+
&cargo[pkg],
484+
&cargo[tgt],
485+
&cfg_options,
486+
proc_macro_client,
487+
load,
488+
) {
420489
if cargo[tgt].kind == TargetKind::Lib {
421490
lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
422491
pkg_to_lib_crate.insert(pkg, crate_id);
@@ -466,6 +535,30 @@ impl ProjectWorkspace {
466535
}
467536
}
468537

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+
}
469562
// Now add a dep edge from all targets of upstream to the lib
470563
// target of downstream.
471564
for pkg in cargo.packages() {
@@ -537,6 +630,52 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
537630
Ok(stdout.trim().to_string())
538631
}
539632

633+
fn 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+
}
540679
fn sysroot_to_crate_graph(
541680
crate_graph: &mut CrateGraph,
542681
sysroot: &Sysroot,

0 commit comments

Comments
 (0)