@@ -26,6 +26,7 @@ use syntax::attr::AttrMetaMethods;
26
26
27
27
use std:: c_str:: ToCStr ;
28
28
use std:: cast;
29
+ use std:: hashmap:: { HashMap , HashSet } ;
29
30
use std:: cmp;
30
31
use std:: io;
31
32
use std:: os:: consts:: { macos, freebsd, linux, android, win32} ;
@@ -69,6 +70,7 @@ impl Context {
69
70
match self . find_library_crate ( ) {
70
71
Some ( t) => t,
71
72
None => {
73
+ self . sess . abort_if_errors ( ) ;
72
74
let message = match root_ident {
73
75
None => format ! ( "can't find crate for `{}`" , self . ident) ,
74
76
Some ( c) => format ! ( "can't find crate for `{}` which `{}` depends on" ,
@@ -82,78 +84,107 @@ impl Context {
82
84
83
85
fn find_library_crate ( & self ) -> Option < Library > {
84
86
let filesearch = self . sess . filesearch ;
85
- let crate_name = self . name . clone ( ) ;
86
87
let ( dyprefix, dysuffix) = self . dylibname ( ) ;
87
88
88
89
// 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 ) ;
91
92
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 ( ) ;
108
94
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 ( ) ) ;
110
123
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" ) ;
143
127
FileDoesntMatch
144
128
}
145
129
}
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
146
149
}
147
150
} ) ;
148
151
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 ( ) {
150
181
0 => None ,
151
- 1 => Some ( matches [ 0 ] ) ,
182
+ 1 => Some ( libraries [ 0 ] ) ,
152
183
_ => {
153
184
self . sess . span_err ( self . span ,
154
- format ! ( "multiple matching crates for `{}`" , crate_name ) ) ;
185
+ format ! ( "multiple matching crates for `{}`" , self . name ) ) ;
155
186
self . sess . note ( "candidates:" ) ;
156
- for lib in matches . iter ( ) {
187
+ for lib in libraries . iter ( ) {
157
188
match lib. dylib {
158
189
Some ( ref p) => {
159
190
self . sess . note ( format ! ( "path: {}" , p. display( ) ) ) ;
@@ -175,50 +206,90 @@ impl Context {
175
206
}
176
207
}
177
208
}
178
- self . sess . abort_if_errors ( ) ;
179
209
None
180
210
}
181
211
}
182
212
}
183
213
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
200
243
}
201
- return false ;
202
244
}
203
245
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
+ }
210
272
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
217
289
}
218
- Some ( ..) | None => { }
219
290
}
220
291
}
221
- return false ;
292
+ return Some ( lib ) ;
222
293
}
223
294
224
295
// Returns the corresponding (prefix, suffix) that files need to have for
@@ -239,16 +310,16 @@ pub fn note_crateid_attr(diag: @SpanHandler, crateid: &CrateId) {
239
310
}
240
311
241
312
fn 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 {
245
316
let attrs = decoder:: get_crate_attributes ( crate_data) ;
246
317
match attr:: find_crateid ( attrs) {
247
318
None => false ,
248
319
Some ( crateid) => {
249
320
if !hash. is_empty ( ) {
250
321
let chash = decoder:: get_crate_hash ( crate_data) ;
251
- if chash != hash { return false ; }
322
+ if chash. as_slice ( ) != hash { return false ; }
252
323
}
253
324
name == crateid. name &&
254
325
( version. is_empty ( ) ||
@@ -383,7 +454,9 @@ pub fn read_meta_section_name(os: Os) -> &'static str {
383
454
pub fn list_file_metadata ( os : Os , path : & Path ,
384
455
out : & mut io:: Writer ) -> io:: IoResult < ( ) > {
385
456
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
+ }
388
461
}
389
462
}
0 commit comments