@@ -26,6 +26,7 @@ use syntax::attr::AttrMetaMethods;
2626
2727use std:: c_str:: ToCStr ;
2828use std:: cast;
29+ use std:: hashmap:: { HashMap , HashSet } ;
2930use std:: cmp;
3031use std:: io;
3132use std:: os:: consts:: { macos, freebsd, linux, android, win32} ;
@@ -69,6 +70,7 @@ impl Context {
6970 match self . find_library_crate ( ) {
7071 Some ( t) => t,
7172 None => {
73+ self . sess . abort_if_errors ( ) ;
7274 let message = match root_ident {
7375 None => format ! ( "can't find crate for `{}`" , self . ident) ,
7476 Some ( c) => format ! ( "can't find crate for `{}` which `{}` depends on" ,
@@ -82,78 +84,107 @@ impl Context {
8284
8385 fn find_library_crate ( & self ) -> Option < Library > {
8486 let filesearch = self . sess . filesearch ;
85- let crate_name = self . name . clone ( ) ;
8687 let ( dyprefix, dysuffix) = self . dylibname ( ) ;
8788
8889 // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
89- let dylib_prefix = format ! ( "{}{}-" , dyprefix, crate_name ) ;
90- let rlib_prefix = format ! ( "lib{}-" , crate_name ) ;
90+ let dylib_prefix = format ! ( "{}{}-" , dyprefix, self . name ) ;
91+ let rlib_prefix = format ! ( "lib{}-" , self . name ) ;
9192
92- let mut matches = ~[ ] ;
93- filesearch. search ( |path| {
94- match path. filename_str ( ) {
95- None => FileDoesntMatch ,
96- Some ( file) => {
97- let ( candidate, existing) = if file. starts_with ( rlib_prefix) &&
98- file. ends_with ( ".rlib" ) {
99- debug ! ( "{} is an rlib candidate" , path. display( ) ) ;
100- ( true , self . add_existing_rlib ( matches, path, file) )
101- } else if file. starts_with ( dylib_prefix) &&
102- file. ends_with ( dysuffix) {
103- debug ! ( "{} is a dylib candidate" , path. display( ) ) ;
104- ( true , self . add_existing_dylib ( matches, path, file) )
105- } else {
106- ( false , false )
107- } ;
93+ let mut candidates = HashMap :: new ( ) ;
10894
109- if candidate && existing {
95+ // First, find all possible candidate rlibs and dylibs purely based on
96+ // the name of the files themselves. We're trying to match against an
97+ // exact crate_id and a possibly an exact hash.
98+ //
99+ // During this step, we can filter all found libraries based on the
100+ // name and id found in the crate id (we ignore the path portion for
101+ // filename matching), as well as the exact hash (if specified). If we
102+ // end up having many candidates, we must look at the metadata to
103+ // perform exact matches against hashes/crate ids. Note that opening up
104+ // the metadata is where we do an exact match against the full contents
105+ // of the crate id (path/name/id).
106+ //
107+ // The goal of this step is to look at as little metadata as possible.
108+ filesearch. search ( |path| {
109+ let file = match path. filename_str ( ) {
110+ None => return FileDoesntMatch ,
111+ Some ( file) => file,
112+ } ;
113+ if file. starts_with ( rlib_prefix) && file. ends_with ( ".rlib" ) {
114+ info ! ( "rlib candidate: {}" , path. display( ) ) ;
115+ match self . try_match ( file, rlib_prefix, ".rlib" ) {
116+ Some ( hash) => {
117+ info ! ( "rlib accepted, hash: {}" , hash) ;
118+ let slot = candidates. find_or_insert_with ( hash, |_| {
119+ ( HashSet :: new ( ) , HashSet :: new ( ) )
120+ } ) ;
121+ let ( ref mut rlibs, _) = * slot;
122+ rlibs. insert ( path. clone ( ) ) ;
110123 FileMatches
111- } else if candidate {
112- match get_metadata_section ( self . os , path) {
113- Some ( cvec) =>
114- if crate_matches ( cvec. as_slice ( ) ,
115- self . name . clone ( ) ,
116- self . version . clone ( ) ,
117- self . hash . clone ( ) ) {
118- debug ! ( "found {} with matching crate_id" ,
119- path. display( ) ) ;
120- let ( rlib, dylib) = if file. ends_with ( ".rlib" ) {
121- ( Some ( path. clone ( ) ) , None )
122- } else {
123- ( None , Some ( path. clone ( ) ) )
124- } ;
125- matches. push ( Library {
126- rlib : rlib,
127- dylib : dylib,
128- metadata : cvec,
129- } ) ;
130- FileMatches
131- } else {
132- debug ! ( "skipping {}, crate_id doesn't match" ,
133- path. display( ) ) ;
134- FileDoesntMatch
135- } ,
136- _ => {
137- debug ! ( "could not load metadata for {}" ,
138- path. display( ) ) ;
139- FileDoesntMatch
140- }
141- }
142- } else {
124+ }
125+ None => {
126+ info ! ( "rlib rejected" ) ;
143127 FileDoesntMatch
144128 }
145129 }
130+ } else if file. starts_with ( dylib_prefix) && file. ends_with ( dysuffix) {
131+ info ! ( "dylib candidate: {}" , path. display( ) ) ;
132+ match self . try_match ( file, dylib_prefix, dysuffix) {
133+ Some ( hash) => {
134+ info ! ( "dylib accepted, hash: {}" , hash) ;
135+ let slot = candidates. find_or_insert_with ( hash, |_| {
136+ ( HashSet :: new ( ) , HashSet :: new ( ) )
137+ } ) ;
138+ let ( _, ref mut dylibs) = * slot;
139+ dylibs. insert ( path. clone ( ) ) ;
140+ FileMatches
141+ }
142+ None => {
143+ info ! ( "dylib rejected" ) ;
144+ FileDoesntMatch
145+ }
146+ }
147+ } else {
148+ FileDoesntMatch
146149 }
147150 } ) ;
148151
149- match matches. len ( ) {
152+ // We have now collected all known libraries into a set of candidates
153+ // keyed of the filename hash listed. For each filename, we also have a
154+ // list of rlibs/dylibs that apply. Here, we map each of these lists
155+ // (per hash), to a Library candidate for returning.
156+ //
157+ // A Library candidate is created if the metadata for the set of
158+ // libraries corresponds to the crate id and hash criteria that this
159+ // serach is being performed for.
160+ let mut libraries = ~[ ] ;
161+ for ( _hash, ( rlibs, dylibs) ) in candidates. move_iter ( ) {
162+ let mut metadata = None ;
163+ let rlib = self . extract_one ( rlibs, "rlib" , & mut metadata) ;
164+ let dylib = self . extract_one ( dylibs, "dylib" , & mut metadata) ;
165+ match metadata {
166+ Some ( metadata) => {
167+ libraries. push ( Library {
168+ dylib : dylib,
169+ rlib : rlib,
170+ metadata : metadata,
171+ } )
172+ }
173+ None => { }
174+ }
175+ }
176+
177+ // Having now translated all relevant found hashes into libraries, see
178+ // what we've got and figure out if we found multiple candidates for
179+ // libraries or not.
180+ match libraries. len ( ) {
150181 0 => None ,
151- 1 => Some ( matches [ 0 ] ) ,
182+ 1 => Some ( libraries [ 0 ] ) ,
152183 _ => {
153184 self . sess . span_err ( self . span ,
154- format ! ( "multiple matching crates for `{}`" , crate_name ) ) ;
185+ format ! ( "multiple matching crates for `{}`" , self . name ) ) ;
155186 self . sess . note ( "candidates:" ) ;
156- for lib in matches . iter ( ) {
187+ for lib in libraries . iter ( ) {
157188 match lib. dylib {
158189 Some ( ref p) => {
159190 self . sess . note ( format ! ( "path: {}" , p. display( ) ) ) ;
@@ -175,50 +206,90 @@ impl Context {
175206 }
176207 }
177208 }
178- self . sess . abort_if_errors ( ) ;
179209 None
180210 }
181211 }
182212 }
183213
184- fn add_existing_rlib ( & self , libs : & mut [ Library ] ,
185- path : & Path , file : & str ) -> bool {
186- let ( prefix, suffix) = self . dylibname ( ) ;
187- let file = file. slice_from ( 3 ) ; // chop off 'lib'
188- let file = file. slice_to ( file. len ( ) - 5 ) ; // chop off '.rlib'
189- let file = format ! ( "{}{}{}" , prefix, file, suffix) ;
190-
191- for lib in libs. mut_iter ( ) {
192- match lib. dylib {
193- Some ( ref p) if p. filename_str ( ) == Some ( file. as_slice ( ) ) => {
194- assert ! ( lib. rlib. is_none( ) ) ; // FIXME: legit compiler error
195- lib. rlib = Some ( path. clone ( ) ) ;
196- return true ;
197- }
198- Some ( ..) | None => { }
199- }
214+ // Attempts to match the requested version of a library against the file
215+ // specified. The prefix/suffix are specified (disambiguates between
216+ // rlib/dylib).
217+ //
218+ // The return value is `None` if `file` doesn't look like a rust-generated
219+ // library, or if a specific version was requested and it doens't match the
220+ // apparent file's version.
221+ //
222+ // If everything checks out, then `Some(hash)` is returned where `hash` is
223+ // the listed hash in the filename itself.
224+ fn try_match ( & self , file : & str , prefix : & str , suffix : & str ) -> Option < ~str > {
225+ let middle = file. slice ( prefix. len ( ) , file. len ( ) - suffix. len ( ) ) ;
226+ debug ! ( "matching -- {}, middle: {}" , file, middle) ;
227+ let mut parts = middle. splitn ( '-' , 1 ) ;
228+ let hash = match parts. next ( ) { Some ( h) => h, None => return None } ;
229+ debug ! ( "matching -- {}, hash: {}" , file, hash) ;
230+ let vers = match parts. next ( ) { Some ( v) => v, None => return None } ;
231+ debug ! ( "matching -- {}, vers: {}" , file, vers) ;
232+ if !self . version . is_empty ( ) && self . version . as_slice ( ) != vers {
233+ return None
234+ }
235+ debug ! ( "matching -- {}, vers ok (requested {})" , file,
236+ self . version) ;
237+ // hashes in filenames are prefixes of the "true hash"
238+ if self . hash . is_empty ( ) || self . hash . starts_with ( hash) {
239+ debug ! ( "matching -- {}, hash ok (requested {})" , file, self . hash) ;
240+ Some ( hash. to_owned ( ) )
241+ } else {
242+ None
200243 }
201- return false ;
202244 }
203245
204- fn add_existing_dylib ( & self , libs : & mut [ Library ] ,
205- path : & Path , file : & str ) -> bool {
206- let ( prefix, suffix) = self . dylibname ( ) ;
207- let file = file. slice_from ( prefix. len ( ) ) ;
208- let file = file. slice_to ( file. len ( ) - suffix. len ( ) ) ;
209- let file = format ! ( "lib{}.rlib" , file) ;
246+ // Attempts to extract *one* library from the set `m`. If the set has no
247+ // elements, `None` is returned. If the set has more than one element, then
248+ // the errors and notes are emitted about the set of libraries.
249+ //
250+ // With only one library in the set, this function will extract it, and then
251+ // read the metadata from it if `*slot` is `None`. If the metadata couldn't
252+ // be read, it is assumed that the file isn't a valid rust library (no
253+ // errors are emitted).
254+ //
255+ // FIXME(#10786): for an optimization, we only read one of the library's
256+ // metadata sections. In theory we should read both, but
257+ // reading dylib metadata is quite slow.
258+ fn extract_one ( & self , m : HashSet < Path > , flavor : & str ,
259+ slot : & mut Option < MetadataBlob > ) -> Option < Path > {
260+ if m. len ( ) == 0 { return None }
261+ if m. len ( ) > 1 {
262+ self . sess . span_err ( self . span ,
263+ format ! ( "multiple {} candidates for `{}` \
264+ found", flavor, self . name) ) ;
265+ for ( i, path) in m. iter ( ) . enumerate ( ) {
266+ self . sess . span_note ( self . span ,
267+ format ! ( r"candidate \#{}: {}" , i + 1 ,
268+ path. display( ) ) ) ;
269+ }
270+ return None
271+ }
210272
211- for lib in libs. mut_iter ( ) {
212- match lib. rlib {
213- Some ( ref p) if p. filename_str ( ) == Some ( file. as_slice ( ) ) => {
214- assert ! ( lib. dylib. is_none( ) ) ; // FIXME: legit compiler error
215- lib. dylib = Some ( path. clone ( ) ) ;
216- return true ;
273+ let lib = m. move_iter ( ) . next ( ) . unwrap ( ) ;
274+ if slot. is_none ( ) {
275+ info ! ( "{} reading meatadata from: {}" , flavor, lib. display( ) ) ;
276+ match get_metadata_section ( self . os , & lib) {
277+ Some ( blob) => {
278+ if crate_matches ( blob. as_slice ( ) , self . name ,
279+ self . version , self . hash ) {
280+ * slot = Some ( blob) ;
281+ } else {
282+ info ! ( "metadata mismatch" ) ;
283+ return None ;
284+ }
285+ }
286+ None => {
287+ info ! ( "no metadata found" ) ;
288+ return None
217289 }
218- Some ( ..) | None => { }
219290 }
220291 }
221- return false ;
292+ return Some ( lib ) ;
222293 }
223294
224295 // Returns the corresponding (prefix, suffix) that files need to have for
@@ -239,16 +310,16 @@ pub fn note_crateid_attr(diag: @SpanHandler, crateid: &CrateId) {
239310}
240311
241312fn crate_matches ( crate_data : & [ u8 ] ,
242- name : ~ str ,
243- version : ~ str ,
244- hash : ~ str ) -> bool {
313+ name : & str ,
314+ version : & str ,
315+ hash : & str ) -> bool {
245316 let attrs = decoder:: get_crate_attributes ( crate_data) ;
246317 match attr:: find_crateid ( attrs) {
247318 None => false ,
248319 Some ( crateid) => {
249320 if !hash. is_empty ( ) {
250321 let chash = decoder:: get_crate_hash ( crate_data) ;
251- if chash != hash { return false ; }
322+ if chash. as_slice ( ) != hash { return false ; }
252323 }
253324 name == crateid. name &&
254325 ( version. is_empty ( ) ||
@@ -383,7 +454,9 @@ pub fn read_meta_section_name(os: Os) -> &'static str {
383454pub fn list_file_metadata ( os : Os , path : & Path ,
384455 out : & mut io:: Writer ) -> io:: IoResult < ( ) > {
385456 match get_metadata_section ( os, path) {
386- Some ( bytes) => decoder:: list_crate_metadata ( bytes. as_slice ( ) , out) ,
387- None => write ! ( out, "could not find metadata in {}.\n " , path. display( ) )
457+ Some ( bytes) => decoder:: list_crate_metadata ( bytes. as_slice ( ) , out) ,
458+ None => {
459+ write ! ( out, "could not find metadata in {}.\n " , path. display( ) )
460+ }
388461 }
389462}
0 commit comments