@@ -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 ) ]
3233pub 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
3940impl 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_or ( 0 , |rc| rc. packages ( ) . len ( ) ) ,
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,9 @@ 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_or ( 0 , |rc| rc. packages ( ) . len ( ) ) ;
318+ cargo. packages ( ) . len ( ) + sysroot. crates ( ) . len ( ) + rustc_package_len
289319 }
290320 }
291321 }
@@ -365,58 +395,33 @@ impl ProjectWorkspace {
365395 }
366396 }
367397 }
368- ProjectWorkspace :: Cargo { cargo, sysroot } => {
398+ ProjectWorkspace :: Cargo { cargo, sysroot, rustc } => {
369399 let ( public_deps, libproc_macro) =
370400 sysroot_to_crate_graph ( & mut crate_graph, sysroot, target, load) ;
371401
372402 let mut cfg_options = CfgOptions :: default ( ) ;
373403 cfg_options. extend ( get_rustc_cfg_options ( target) ) ;
374404
375405 let mut pkg_to_lib_crate = FxHashMap :: default ( ) ;
376- let mut pkg_crates = FxHashMap :: default ( ) ;
377406
378407 // Add test cfg for non-sysroot crates
379408 cfg_options. insert_atom ( "test" . into ( ) ) ;
380409 cfg_options. insert_atom ( "debug_assertions" . into ( ) ) ;
381410
411+ let mut pkg_crates = FxHashMap :: default ( ) ;
412+
382413 // Next, create crates for each package, target pair
383414 for pkg in cargo. packages ( ) {
384415 let mut lib_tgt = None ;
385416 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- ) ;
417+ if let Some ( crate_id) = add_target_crate_root (
418+ & mut crate_graph,
419+ & cargo[ pkg] ,
420+ & cargo[ tgt] ,
421+ & cfg_options,
422+ proc_macro_client,
423+ load,
424+ ) {
420425 if cargo[ tgt] . kind == TargetKind :: Lib {
421426 lib_tgt = Some ( ( crate_id, cargo[ tgt] . name . clone ( ) ) ) ;
422427 pkg_to_lib_crate. insert ( pkg, crate_id) ;
@@ -484,6 +489,92 @@ impl ProjectWorkspace {
484489 }
485490 }
486491 }
492+
493+ let mut rustc_pkg_crates = FxHashMap :: default ( ) ;
494+
495+ // If the user provided a path to rustc sources, we add all the rustc_private crates
496+ // and create dependencies on them for the crates in the current workspace
497+ if let Some ( rustc_workspace) = rustc {
498+ for pkg in rustc_workspace. packages ( ) {
499+ for & tgt in rustc_workspace[ pkg] . targets . iter ( ) {
500+ if rustc_workspace[ tgt] . kind != TargetKind :: Lib {
501+ continue ;
502+ }
503+ // Exclude alloc / core / std
504+ if rustc_workspace[ tgt]
505+ . root
506+ . components ( )
507+ . any ( |c| c == Component :: Normal ( "library" . as_ref ( ) ) )
508+ {
509+ continue ;
510+ }
511+
512+ if let Some ( crate_id) = add_target_crate_root (
513+ & mut crate_graph,
514+ & rustc_workspace[ pkg] ,
515+ & rustc_workspace[ tgt] ,
516+ & cfg_options,
517+ proc_macro_client,
518+ load,
519+ ) {
520+ pkg_to_lib_crate. insert ( pkg, crate_id) ;
521+ // Add dependencies on the core / std / alloc for rustc
522+ for ( name, krate) in public_deps. iter ( ) {
523+ if let Err ( _) =
524+ crate_graph. add_dep ( crate_id, name. clone ( ) , * krate)
525+ {
526+ log:: error!(
527+ "cyclic dependency on {} for {}" ,
528+ name,
529+ & cargo[ pkg] . name
530+ )
531+ }
532+ }
533+ rustc_pkg_crates. entry ( pkg) . or_insert_with ( Vec :: new) . push ( crate_id) ;
534+ }
535+ }
536+ }
537+ // Now add a dep edge from all targets of upstream to the lib
538+ // target of downstream.
539+ for pkg in rustc_workspace. packages ( ) {
540+ for dep in rustc_workspace[ pkg] . dependencies . iter ( ) {
541+ let name = CrateName :: new ( & dep. name ) . unwrap ( ) ;
542+ if let Some ( & to) = pkg_to_lib_crate. get ( & dep. pkg ) {
543+ for & from in rustc_pkg_crates. get ( & pkg) . into_iter ( ) . flatten ( ) {
544+ if let Err ( _) = crate_graph. add_dep ( from, name. clone ( ) , to) {
545+ log:: error!(
546+ "cyclic dependency {} -> {}" ,
547+ & rustc_workspace[ pkg] . name,
548+ & rustc_workspace[ dep. pkg] . name
549+ )
550+ }
551+ }
552+ }
553+ }
554+ }
555+
556+ // Add dependencies for all the crates of the current workspace to rustc_private libraries
557+ for dep in rustc_workspace. packages ( ) {
558+ let name = CrateName :: normalize_dashes ( & rustc_workspace[ dep] . name ) ;
559+
560+ if let Some ( & to) = pkg_to_lib_crate. get ( & dep) {
561+ for pkg in cargo. packages ( ) {
562+ if !cargo[ pkg] . is_member {
563+ continue ;
564+ }
565+ for & from in pkg_crates. get ( & pkg) . into_iter ( ) . flatten ( ) {
566+ if let Err ( _) = crate_graph. add_dep ( from, name. clone ( ) , to) {
567+ log:: error!(
568+ "cyclic dependency {} -> {}" ,
569+ & cargo[ pkg] . name,
570+ & rustc_workspace[ dep] . name
571+ )
572+ }
573+ }
574+ }
575+ }
576+ }
577+ }
487578 }
488579 }
489580 if crate_graph. patch_cfg_if ( ) {
@@ -537,6 +628,52 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
537628 Ok ( stdout. trim ( ) . to_string ( ) )
538629}
539630
631+ fn add_target_crate_root (
632+ crate_graph : & mut CrateGraph ,
633+ pkg : & cargo_workspace:: PackageData ,
634+ tgt : & cargo_workspace:: TargetData ,
635+ cfg_options : & CfgOptions ,
636+ proc_macro_client : & ProcMacroClient ,
637+ load : & mut dyn FnMut ( & AbsPath ) -> Option < FileId > ,
638+ ) -> Option < CrateId > {
639+ let root = tgt. root . as_path ( ) ;
640+ if let Some ( file_id) = load ( root) {
641+ let edition = pkg. edition ;
642+ let cfg_options = {
643+ let mut opts = cfg_options. clone ( ) ;
644+ for feature in pkg. features . iter ( ) {
645+ opts. insert_key_value ( "feature" . into ( ) , feature. into ( ) ) ;
646+ }
647+ opts. extend ( pkg. cfgs . iter ( ) . cloned ( ) ) ;
648+ opts
649+ } ;
650+ let mut env = Env :: default ( ) ;
651+ if let Some ( out_dir) = & pkg. out_dir {
652+ // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
653+ if let Some ( out_dir) = out_dir. to_str ( ) . map ( |s| s. to_owned ( ) ) {
654+ env. set ( "OUT_DIR" , out_dir) ;
655+ }
656+ }
657+ let proc_macro = pkg
658+ . proc_macro_dylib_path
659+ . as_ref ( )
660+ . map ( |it| proc_macro_client. by_dylib_path ( & it) )
661+ . unwrap_or_default ( ) ;
662+
663+ let display_name = CrateDisplayName :: from_canonical_name ( pkg. name . clone ( ) ) ;
664+ let crate_id = crate_graph. add_crate_root (
665+ file_id,
666+ edition,
667+ Some ( display_name) ,
668+ cfg_options,
669+ env,
670+ proc_macro. clone ( ) ,
671+ ) ;
672+
673+ return Some ( crate_id) ;
674+ }
675+ None
676+ }
540677fn sysroot_to_crate_graph (
541678 crate_graph : & mut CrateGraph ,
542679 sysroot : & Sysroot ,
0 commit comments