@@ -3,13 +3,18 @@ use crate::rust_analyzer::path_to_file_id;
33use crate :: trap:: TrapId ;
44use anyhow:: Context ;
55use archive:: Archiver ;
6+ use itertools:: Itertools ;
67use log:: { info, warn} ;
8+ use ra_ap_base_db:: { CrateId , SourceDatabase } ;
9+ use ra_ap_cfg:: CfgAtom ;
710use ra_ap_hir:: Semantics ;
811use ra_ap_ide_db:: line_index:: { LineCol , LineIndex } ;
912use ra_ap_ide_db:: RootDatabase ;
1013use ra_ap_project_model:: { CargoConfig , ProjectManifest } ;
11- use ra_ap_vfs:: Vfs ;
14+ use ra_ap_vfs:: { Vfs , VfsPath } ;
1215use rust_analyzer:: { ParseResult , RustAnalyzer } ;
16+ use std:: cmp:: Ordering ;
17+ use std:: hash:: { Hash , Hasher } ;
1318use std:: time:: Instant ;
1419use std:: {
1520 collections:: HashMap ,
@@ -159,6 +164,83 @@ impl<'a> Extractor<'a> {
159164 trap. commit ( ) ?;
160165 Ok ( ( ) )
161166 }
167+
168+ fn extract_crate_graph ( & self , db : & RootDatabase , vfs : & Vfs ) {
169+ let crate_graph = db. crate_graph ( ) ;
170+
171+ // According to the documentation of `CrateGraph`:
172+ // Each crate is defined by the `FileId` of its root module, the set of enabled
173+ // `cfg` flags and the set of dependencies.
174+ let mut crate_id_map = HashMap :: < CrateId , ( & VfsPath , u64 ) > :: new ( ) ;
175+ for krate_id in crate_graph. crates_in_topological_order ( ) {
176+ let krate = & crate_graph[ krate_id] ;
177+ let root_module_file = vfs. file_path ( krate. root_file_id ) ;
178+ let mut hasher = std:: hash:: DefaultHasher :: new ( ) ;
179+ krate
180+ . cfg_options
181+ . as_ref ( )
182+ . into_iter ( )
183+ . sorted_by ( cmp_flag)
184+ . for_each ( |x| format ! ( "{x}" ) . hash ( & mut hasher) ) ;
185+
186+ krate
187+ . dependencies
188+ . iter ( )
189+ . flat_map ( |d| crate_id_map. get ( & d. crate_id ) )
190+ . sorted ( )
191+ . for_each ( |x| x. hash ( & mut hasher) ) ;
192+ let hash = hasher. finish ( ) ;
193+ crate_id_map. insert ( krate_id, ( root_module_file, hash) ) ;
194+ }
195+ for krate_id in crate_graph. iter ( ) {
196+ let ( root_module_file, hash) = crate_id_map. get ( & krate_id) . unwrap ( ) ;
197+ let path: & Path = root_module_file. as_path ( ) . unwrap ( ) . as_ref ( ) ;
198+ let path = path. join ( format ! ( "{hash:0>16x}" ) ) ;
199+ let mut trap = self . traps . create ( "crates" , path. as_path ( ) ) ;
200+ if trap. path . exists ( ) {
201+ continue ;
202+ }
203+ let krate = & crate_graph[ krate_id] ;
204+ let element = generated:: Crate {
205+ id : trap:: TrapId :: Key ( format ! ( "crate:{root_module_file}:{hash}" ) ) ,
206+ name : krate
207+ . display_name
208+ . as_ref ( )
209+ . map ( |x| x. canonical_name ( ) . to_string ( ) ) ,
210+ version : krate. version . to_owned ( ) ,
211+ cfg_options : krate
212+ . cfg_options
213+ . as_ref ( )
214+ . into_iter ( )
215+ . map ( |x| format ! ( "{x}" ) )
216+ . collect ( ) ,
217+ dependencies : krate
218+ . dependencies
219+ . iter ( )
220+ . flat_map ( |x| crate_id_map. get ( & x. crate_id ) )
221+ . map ( |( module, hash) | trap. label ( format ! ( "crate:{module}:{hash}" ) . into ( ) ) )
222+ . collect ( ) ,
223+ } ;
224+ trap. emit ( element) ;
225+ trap. commit ( ) ;
226+ }
227+ }
228+ }
229+
230+ fn cmp_flag ( a : & & CfgAtom , b : & & CfgAtom ) -> Ordering {
231+ match ( a, b) {
232+ ( CfgAtom :: Flag ( a) , CfgAtom :: Flag ( b) ) => a. as_str ( ) . cmp ( b. as_str ( ) ) ,
233+ ( CfgAtom :: Flag ( a) , CfgAtom :: KeyValue { key : b, value : _ } ) => {
234+ a. as_str ( ) . cmp ( b. as_str ( ) ) . then ( Ordering :: Less )
235+ }
236+ ( CfgAtom :: KeyValue { key : a, value : _ } , CfgAtom :: Flag ( b) ) => {
237+ a. as_str ( ) . cmp ( b. as_str ( ) ) . then ( Ordering :: Greater )
238+ }
239+ ( CfgAtom :: KeyValue { key : a, value : av } , CfgAtom :: KeyValue { key : b, value : bv } ) => a
240+ . as_str ( )
241+ . cmp ( b. as_str ( ) )
242+ . then ( av. as_str ( ) . cmp ( bv. as_str ( ) ) ) ,
243+ }
162244}
163245
164246fn main ( ) -> anyhow:: Result < ( ) > {
@@ -204,6 +286,7 @@ fn main() -> anyhow::Result<()> {
204286 let cargo_config = cfg. to_cargo_config ( ) ;
205287 for ( manifest, files) in map. values ( ) . filter ( |( _, files) | !files. is_empty ( ) ) {
206288 if let Some ( ( ref db, ref vfs) ) = extractor. load_manifest ( manifest, & cargo_config) {
289+ extractor. extract_crate_graph ( db, vfs) ;
207290 let semantics = Semantics :: new ( db) ;
208291 for file in files {
209292 match extractor. load_source ( file, & semantics, vfs) {
0 commit comments