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