]> git.proxmox.com Git - rustc.git/blame - src/librustc_metadata/locator.rs
New upstream version 1.19.0+dfsg1
[rustc.git] / src / librustc_metadata / locator.rs
CommitLineData
85aaf69f 1// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
223e47cc
LB
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
223e47cc 11//! Finds crate binaries and loads their metadata
1a4d82fc
JJ
12//!
13//! Might I be the first to welcome you to a world of platform differences,
14//! version requirements, dependency graphs, conflicting desires, and fun! This
15//! is the major guts (along with metadata::creader) of the compiler for loading
16//! crates and resolving dependencies. Let's take a tour!
17//!
18//! # The problem
19//!
20//! Each invocation of the compiler is immediately concerned with one primary
21//! problem, to connect a set of crates to resolved crates on the filesystem.
22//! Concretely speaking, the compiler follows roughly these steps to get here:
23//!
24//! 1. Discover a set of `extern crate` statements.
25//! 2. Transform these directives into crate names. If the directive does not
26//! have an explicit name, then the identifier is the name.
27//! 3. For each of these crate names, find a corresponding crate on the
28//! filesystem.
29//!
30//! Sounds easy, right? Let's walk into some of the nuances.
31//!
32//! ## Transitive Dependencies
33//!
34//! Let's say we've got three crates: A, B, and C. A depends on B, and B depends
35//! on C. When we're compiling A, we primarily need to find and locate B, but we
36//! also end up needing to find and locate C as well.
37//!
38//! The reason for this is that any of B's types could be composed of C's types,
39//! any function in B could return a type from C, etc. To be able to guarantee
40//! that we can always typecheck/translate any function, we have to have
41//! complete knowledge of the whole ecosystem, not just our immediate
42//! dependencies.
43//!
44//! So now as part of the "find a corresponding crate on the filesystem" step
45//! above, this involves also finding all crates for *all upstream
46//! dependencies*. This includes all dependencies transitively.
47//!
48//! ## Rlibs and Dylibs
49//!
50//! The compiler has two forms of intermediate dependencies. These are dubbed
51//! rlibs and dylibs for the static and dynamic variants, respectively. An rlib
52//! is a rustc-defined file format (currently just an ar archive) while a dylib
53//! is a platform-defined dynamic library. Each library has a metadata somewhere
54//! inside of it.
55//!
476ff2be
SL
56//! A third kind of dependency is an rmeta file. These are metadata files and do
57//! not contain any code, etc. To a first approximation, these are treated in the
58//! same way as rlibs. Where there is both an rlib and an rmeta file, the rlib
59//! gets priority (even if the rmeta file is newer). An rmeta file is only
60//! useful for checking a downstream crate, attempting to link one will cause an
61//! error.
62//!
1a4d82fc
JJ
63//! When translating a crate name to a crate on the filesystem, we all of a
64//! sudden need to take into account both rlibs and dylibs! Linkage later on may
65//! use either one of these files, as each has their pros/cons. The job of crate
66//! loading is to discover what's possible by finding all candidates.
67//!
68//! Most parts of this loading systems keep the dylib/rlib as just separate
69//! variables.
70//!
71//! ## Where to look?
72//!
73//! We can't exactly scan your whole hard drive when looking for dependencies,
74//! so we need to places to look. Currently the compiler will implicitly add the
75//! target lib search path ($prefix/lib/rustlib/$target/lib) to any compilation,
76//! and otherwise all -L flags are added to the search paths.
77//!
78//! ## What criterion to select on?
79//!
80//! This a pretty tricky area of loading crates. Given a file, how do we know
81//! whether it's the right crate? Currently, the rules look along these lines:
82//!
83//! 1. Does the filename match an rlib/dylib pattern? That is to say, does the
84//! filename have the right prefix/suffix?
85//! 2. Does the filename have the right prefix for the crate name being queried?
86//! This is filtering for files like `libfoo*.rlib` and such.
87//! 3. Is the file an actual rust library? This is done by loading the metadata
88//! from the library and making sure it's actually there.
89//! 4. Does the name in the metadata agree with the name of the library?
90//! 5. Does the target in the metadata agree with the current target?
91//! 6. Does the SVH match? (more on this later)
92//!
93//! If the file answers `yes` to all these questions, then the file is
94//! considered as being *candidate* for being accepted. It is illegal to have
95//! more than two candidates as the compiler has no method by which to resolve
96//! this conflict. Additionally, rlib/dylib candidates are considered
97//! separately.
98//!
99//! After all this has happened, we have 1 or two files as candidates. These
100//! represent the rlib/dylib file found for a library, and they're returned as
101//! being found.
102//!
103//! ### What about versions?
104//!
105//! A lot of effort has been put forth to remove versioning from the compiler.
106//! There have been forays in the past to have versioning baked in, but it was
107//! largely always deemed insufficient to the point that it was recognized that
108//! it's probably something the compiler shouldn't do anyway due to its
109//! complicated nature and the state of the half-baked solutions.
110//!
111//! With a departure from versioning, the primary criterion for loading crates
112//! is just the name of a crate. If we stopped here, it would imply that you
113//! could never link two crates of the same name from different sources
114//! together, which is clearly a bad state to be in.
115//!
116//! To resolve this problem, we come to the next section!
117//!
118//! # Expert Mode
119//!
120//! A number of flags have been added to the compiler to solve the "version
121//! problem" in the previous section, as well as generally enabling more
122//! powerful usage of the crate loading system of the compiler. The goal of
123//! these flags and options are to enable third-party tools to drive the
124//! compiler with prior knowledge about how the world should look.
125//!
126//! ## The `--extern` flag
127//!
128//! The compiler accepts a flag of this form a number of times:
129//!
130//! ```text
131//! --extern crate-name=path/to/the/crate.rlib
132//! ```
133//!
134//! This flag is basically the following letter to the compiler:
135//!
136//! > Dear rustc,
137//! >
138//! > When you are attempting to load the immediate dependency `crate-name`, I
9346a6ac 139//! > would like you to assume that the library is located at
1a4d82fc
JJ
140//! > `path/to/the/crate.rlib`, and look nowhere else. Also, please do not
141//! > assume that the path I specified has the name `crate-name`.
142//!
143//! This flag basically overrides most matching logic except for validating that
144//! the file is indeed a rust library. The same `crate-name` can be specified
145//! twice to specify the rlib/dylib pair.
146//!
147//! ## Enabling "multiple versions"
148//!
149//! This basically boils down to the ability to specify arbitrary packages to
150//! the compiler. For example, if crate A wanted to use Bv1 and Bv2, then it
151//! would look something like:
152//!
153//! ```ignore
154//! extern crate b1;
155//! extern crate b2;
156//!
157//! fn main() {}
158//! ```
159//!
160//! and the compiler would be invoked as:
161//!
162//! ```text
163//! rustc a.rs --extern b1=path/to/libb1.rlib --extern b2=path/to/libb2.rlib
164//! ```
165//!
166//! In this scenario there are two crates named `b` and the compiler must be
167//! manually driven to be informed where each crate is.
168//!
169//! ## Frobbing symbols
170//!
171//! One of the immediate problems with linking the same library together twice
172//! in the same problem is dealing with duplicate symbols. The primary way to
173//! deal with this in rustc is to add hashes to the end of each symbol.
174//!
175//! In order to force hashes to change between versions of a library, if
176//! desired, the compiler exposes an option `-C metadata=foo`, which is used to
177//! initially seed each symbol hash. The string `foo` is prepended to each
178//! string-to-hash to ensure that symbols change over time.
179//!
180//! ## Loading transitive dependencies
181//!
182//! Dealing with same-named-but-distinct crates is not just a local problem, but
183//! one that also needs to be dealt with for transitive dependencies. Note that
184//! in the letter above `--extern` flags only apply to the *local* set of
185//! dependencies, not the upstream transitive dependencies. Consider this
186//! dependency graph:
187//!
188//! ```text
189//! A.1 A.2
190//! | |
191//! | |
192//! B C
193//! \ /
194//! \ /
195//! D
196//! ```
197//!
198//! In this scenario, when we compile `D`, we need to be able to distinctly
199//! resolve `A.1` and `A.2`, but an `--extern` flag cannot apply to these
200//! transitive dependencies.
201//!
202//! Note that the key idea here is that `B` and `C` are both *already compiled*.
203//! That is, they have already resolved their dependencies. Due to unrelated
204//! technical reasons, when a library is compiled, it is only compatible with
205//! the *exact same* version of the upstream libraries it was compiled against.
206//! We use the "Strict Version Hash" to identify the exact copy of an upstream
207//! library.
208//!
209//! With this knowledge, we know that `B` and `C` will depend on `A` with
210//! different SVH values, so we crawl the normal `-L` paths looking for
211//! `liba*.rlib` and filter based on the contained SVH.
212//!
213//! In the end, this ends up not needing `--extern` to specify upstream
214//! transitive dependencies.
215//!
216//! # Wrapping up
217//!
218//! That's the general overview of loading crates in the compiler, but it's by
219//! no means all of the necessary details. Take a look at the rest of
c30ab7b3 220//! metadata::locator or metadata::creader for all the juicy details!
223e47cc 221
9e0c209e 222use cstore::MetadataBlob;
c30ab7b3
SL
223use creader::Library;
224use schema::{METADATA_HEADER, rustc_version};
92a42be0 225
54a0048b 226use rustc::hir::svh::Svh;
7cac9316 227use rustc::middle::cstore::MetadataLoader;
476ff2be 228use rustc::session::{config, Session};
92a42be0
SL
229use rustc::session::filesearch::{FileSearch, FileMatches, FileDoesntMatch};
230use rustc::session::search_paths::PathKind;
476ff2be 231use rustc::util::nodemap::FxHashMap;
92a42be0 232
3157f602 233use errors::DiagnosticBuilder;
476ff2be 234use syntax::symbol::Symbol;
3157f602 235use syntax_pos::Span;
85aaf69f 236use rustc_back::target::Target;
1a4d82fc 237
1a4d82fc 238use std::cmp;
a7813a04 239use std::fmt;
476ff2be
SL
240use std::fs::{self, File};
241use std::io::{self, Read};
c34b1796 242use std::path::{Path, PathBuf};
92a42be0 243use std::time::Instant;
1a4d82fc
JJ
244
245use flate;
7cac9316 246use owning_ref::{ErasedBoxRef, OwningRef};
1a4d82fc
JJ
247
248pub struct CrateMismatch {
c34b1796 249 path: PathBuf,
1a4d82fc 250 got: String,
223e47cc
LB
251}
252
1a4d82fc
JJ
253pub struct Context<'a> {
254 pub sess: &'a Session,
255 pub span: Span,
476ff2be
SL
256 pub ident: Symbol,
257 pub crate_name: Symbol,
1a4d82fc 258 pub hash: Option<&'a Svh>,
85aaf69f
SL
259 // points to either self.sess.target.target or self.sess.host, must match triple
260 pub target: &'a Target,
1a4d82fc
JJ
261 pub triple: &'a str,
262 pub filesearch: FileSearch<'a>,
263 pub root: &'a Option<CratePaths>,
264 pub rejected_via_hash: Vec<CrateMismatch>,
265 pub rejected_via_triple: Vec<CrateMismatch>,
85aaf69f 266 pub rejected_via_kind: Vec<CrateMismatch>,
a7813a04 267 pub rejected_via_version: Vec<CrateMismatch>,
476ff2be 268 pub rejected_via_filename: Vec<CrateMismatch>,
1a4d82fc 269 pub should_match_name: bool,
476ff2be 270 pub is_proc_macro: Option<bool>,
7cac9316 271 pub metadata_loader: &'a MetadataLoader,
223e47cc
LB
272}
273
1a4d82fc
JJ
274pub struct CratePaths {
275 pub ident: String,
c34b1796 276 pub dylib: Option<PathBuf>,
c30ab7b3 277 pub rlib: Option<PathBuf>,
476ff2be 278 pub rmeta: Option<PathBuf>,
223e47cc
LB
279}
280
a7813a04
XL
281#[derive(Copy, Clone, PartialEq)]
282enum CrateFlavor {
283 Rlib,
476ff2be 284 Rmeta,
c30ab7b3 285 Dylib,
a7813a04
XL
286}
287
288impl fmt::Display for CrateFlavor {
289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290 f.write_str(match *self {
291 CrateFlavor::Rlib => "rlib",
476ff2be 292 CrateFlavor::Rmeta => "rmeta",
c30ab7b3 293 CrateFlavor::Dylib => "dylib",
a7813a04
XL
294 })
295 }
296}
297
1a4d82fc 298impl CratePaths {
c34b1796 299 fn paths(&self) -> Vec<PathBuf> {
476ff2be 300 self.dylib.iter().chain(self.rlib.iter()).chain(self.rmeta.iter()).cloned().collect()
1a4d82fc
JJ
301 }
302}
303
304impl<'a> Context<'a> {
305 pub fn maybe_load_library_crate(&mut self) -> Option<Library> {
306 self.find_library_crate()
307 }
308
309 pub fn load_library_crate(&mut self) -> Library {
c30ab7b3 310 self.find_library_crate().unwrap_or_else(|| self.report_errs())
1a4d82fc
JJ
311 }
312
c30ab7b3 313 pub fn report_errs(&mut self) -> ! {
b039eaaf
SL
314 let add = match self.root {
315 &None => String::new(),
c30ab7b3 316 &Some(ref r) => format!(" which `{}` depends on", r.ident),
b039eaaf 317 };
9cc50fc6 318 let mut err = if !self.rejected_via_hash.is_empty() {
c30ab7b3
SL
319 struct_span_err!(self.sess,
320 self.span,
321 E0460,
9cc50fc6 322 "found possibly newer version of crate `{}`{}",
c30ab7b3
SL
323 self.ident,
324 add)
9346a6ac 325 } else if !self.rejected_via_triple.is_empty() {
c30ab7b3
SL
326 struct_span_err!(self.sess,
327 self.span,
328 E0461,
9cc50fc6 329 "couldn't find crate `{}` with expected target triple {}{}",
c30ab7b3
SL
330 self.ident,
331 self.triple,
332 add)
9346a6ac 333 } else if !self.rejected_via_kind.is_empty() {
c30ab7b3
SL
334 struct_span_err!(self.sess,
335 self.span,
336 E0462,
9cc50fc6 337 "found staticlib `{}` instead of rlib or dylib{}",
c30ab7b3
SL
338 self.ident,
339 add)
a7813a04 340 } else if !self.rejected_via_version.is_empty() {
c30ab7b3
SL
341 struct_span_err!(self.sess,
342 self.span,
343 E0514,
a7813a04 344 "found crate `{}` compiled by an incompatible version of rustc{}",
c30ab7b3
SL
345 self.ident,
346 add)
1a4d82fc 347 } else {
c30ab7b3
SL
348 let mut err = struct_span_err!(self.sess,
349 self.span,
350 E0463,
9e0c209e 351 "can't find crate for `{}`{}",
c30ab7b3
SL
352 self.ident,
353 add);
476ff2be
SL
354
355 if (self.ident == "std" || self.ident == "core")
356 && self.triple != config::host_triple() {
357 err.note(&format!("the `{}` target may not be installed", self.triple));
358 }
7cac9316 359 err.span_label(self.span, "can't find crate");
9e0c209e 360 err
9cc50fc6 361 };
1a4d82fc 362
9346a6ac 363 if !self.rejected_via_triple.is_empty() {
1a4d82fc 364 let mismatches = self.rejected_via_triple.iter();
c30ab7b3 365 for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() {
a7813a04 366 err.note(&format!("crate `{}`, path #{}, triple {}: {}",
c30ab7b3
SL
367 self.ident,
368 i + 1,
369 got,
370 path.display()));
1a4d82fc
JJ
371 }
372 }
9346a6ac 373 if !self.rejected_via_hash.is_empty() {
a7813a04 374 err.note("perhaps that crate needs to be recompiled?");
1a4d82fc 375 let mismatches = self.rejected_via_hash.iter();
c30ab7b3
SL
376 for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
377 err.note(&format!("crate `{}` path #{}: {}", self.ident, i + 1, path.display()));
1a4d82fc
JJ
378 }
379 match self.root {
380 &None => {}
381 &Some(ref r) => {
382 for (i, path) in r.paths().iter().enumerate() {
a7813a04 383 err.note(&format!("crate `{}` path #{}: {}",
c30ab7b3
SL
384 r.ident,
385 i + 1,
386 path.display()));
970d7e83 387 }
223e47cc 388 }
223e47cc 389 }
1a4d82fc 390 }
9346a6ac 391 if !self.rejected_via_kind.is_empty() {
a7813a04 392 err.help("please recompile that crate using --crate-type lib");
85aaf69f
SL
393 let mismatches = self.rejected_via_kind.iter();
394 for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
c30ab7b3 395 err.note(&format!("crate `{}` path #{}: {}", self.ident, i + 1, path.display()));
a7813a04
XL
396 }
397 }
398 if !self.rejected_via_version.is_empty() {
399 err.help(&format!("please recompile that crate using this compiler ({})",
c30ab7b3 400 rustc_version()));
a7813a04
XL
401 let mismatches = self.rejected_via_version.iter();
402 for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() {
403 err.note(&format!("crate `{}` path #{}: {} compiled by {:?}",
c30ab7b3
SL
404 self.ident,
405 i + 1,
406 path.display(),
407 got));
85aaf69f
SL
408 }
409 }
476ff2be
SL
410 if !self.rejected_via_filename.is_empty() {
411 let dylibname = self.dylibname();
412 let mismatches = self.rejected_via_filename.iter();
413 for &CrateMismatch { ref path, .. } in mismatches {
414 err.note(&format!("extern location for {} is of an unknown type: {}",
415 self.crate_name,
416 path.display()))
417 .help(&format!("file name should be lib*.rlib or {}*.{}",
418 dylibname.0,
419 dylibname.1));
420 }
421 }
9cc50fc6
SL
422
423 err.emit();
1a4d82fc 424 self.sess.abort_if_errors();
54a0048b 425 unreachable!();
1a4d82fc
JJ
426 }
427
428 fn find_library_crate(&mut self) -> Option<Library> {
429 // If an SVH is specified, then this is a transitive dependency that
430 // must be loaded via -L plus some filtering.
431 if self.hash.is_none() {
432 self.should_match_name = false;
476ff2be 433 if let Some(s) = self.sess.opts.externs.get(&self.crate_name.as_str()) {
5bcae85e 434 return self.find_commandline_library(s.iter());
1a4d82fc
JJ
435 }
436 self.should_match_name = true;
437 }
438
439 let dypair = self.dylibname();
7453a54e 440 let staticpair = self.staticlibname();
1a4d82fc
JJ
441
442 // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
443 let dylib_prefix = format!("{}{}", dypair.0, self.crate_name);
444 let rlib_prefix = format!("lib{}", self.crate_name);
7453a54e 445 let staticlib_prefix = format!("{}{}", staticpair.0, self.crate_name);
1a4d82fc 446
476ff2be 447 let mut candidates = FxHashMap();
c30ab7b3 448 let mut staticlibs = vec![];
1a4d82fc
JJ
449
450 // First, find all possible candidate rlibs and dylibs purely based on
451 // the name of the files themselves. We're trying to match against an
452 // exact crate name and a possibly an exact hash.
453 //
454 // During this step, we can filter all found libraries based on the
455 // name and id found in the crate id (we ignore the path portion for
456 // filename matching), as well as the exact hash (if specified). If we
457 // end up having many candidates, we must look at the metadata to
458 // perform exact matches against hashes/crate ids. Note that opening up
459 // the metadata is where we do an exact match against the full contents
460 // of the crate id (path/name/id).
461 //
462 // The goal of this step is to look at as little metadata as possible.
85aaf69f 463 self.filesearch.search(|path, kind| {
c34b1796 464 let file = match path.file_name().and_then(|s| s.to_str()) {
1a4d82fc
JJ
465 None => return FileDoesntMatch,
466 Some(file) => file,
467 };
476ff2be 468 let (hash, found_kind) =
cc61c64b 469 if file.starts_with(&rlib_prefix) && file.ends_with(".rlib") {
476ff2be 470 (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], CrateFlavor::Rlib)
cc61c64b 471 } else if file.starts_with(&rlib_prefix) && file.ends_with(".rmeta") {
476ff2be
SL
472 (&file[(rlib_prefix.len())..(file.len() - ".rmeta".len())], CrateFlavor::Rmeta)
473 } else if file.starts_with(&dylib_prefix) &&
474 file.ends_with(&dypair.1) {
475 (&file[(dylib_prefix.len())..(file.len() - dypair.1.len())], CrateFlavor::Dylib)
476 } else {
cc61c64b 477 if file.starts_with(&staticlib_prefix) && file.ends_with(&staticpair.1) {
476ff2be
SL
478 staticlibs.push(CrateMismatch {
479 path: path.to_path_buf(),
480 got: "static".to_string(),
481 });
482 }
483 return FileDoesntMatch;
484 };
1a4d82fc
JJ
485 info!("lib candidate: {}", path.display());
486
487 let hash_str = hash.to_string();
c34b1796 488 let slot = candidates.entry(hash_str)
476ff2be
SL
489 .or_insert_with(|| (FxHashMap(), FxHashMap(), FxHashMap()));
490 let (ref mut rlibs, ref mut rmetas, ref mut dylibs) = *slot;
c30ab7b3
SL
491 fs::canonicalize(path)
492 .map(|p| {
476ff2be
SL
493 match found_kind {
494 CrateFlavor::Rlib => { rlibs.insert(p, kind); }
495 CrateFlavor::Rmeta => { rmetas.insert(p, kind); }
496 CrateFlavor::Dylib => { dylibs.insert(p, kind); }
c30ab7b3
SL
497 }
498 FileMatches
499 })
500 .unwrap_or(FileDoesntMatch)
1a4d82fc 501 });
62682a34 502 self.rejected_via_kind.extend(staticlibs);
1a4d82fc
JJ
503
504 // We have now collected all known libraries into a set of candidates
505 // keyed of the filename hash listed. For each filename, we also have a
506 // list of rlibs/dylibs that apply. Here, we map each of these lists
507 // (per hash), to a Library candidate for returning.
508 //
509 // A Library candidate is created if the metadata for the set of
510 // libraries corresponds to the crate id and hash criteria that this
511 // search is being performed for.
476ff2be
SL
512 let mut libraries = FxHashMap();
513 for (_hash, (rlibs, rmetas, dylibs)) in candidates {
a7813a04
XL
514 let mut slot = None;
515 let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot);
476ff2be 516 let rmeta = self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot);
a7813a04
XL
517 let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot);
518 if let Some((h, m)) = slot {
c30ab7b3
SL
519 libraries.insert(h,
520 Library {
521 dylib: dylib,
522 rlib: rlib,
476ff2be 523 rmeta: rmeta,
c30ab7b3
SL
524 metadata: m,
525 });
1a4d82fc
JJ
526 }
527 }
528
529 // Having now translated all relevant found hashes into libraries, see
530 // what we've got and figure out if we found multiple candidates for
531 // libraries or not.
532 match libraries.len() {
533 0 => None,
a7813a04 534 1 => Some(libraries.into_iter().next().unwrap().1),
970d7e83 535 _ => {
c30ab7b3
SL
536 let mut err = struct_span_err!(self.sess,
537 self.span,
538 E0464,
9cc50fc6
SL
539 "multiple matching crates for `{}`",
540 self.crate_name);
541 err.note("candidates:");
a7813a04 542 for (_, lib) in libraries {
3157f602
XL
543 if let Some((ref p, _)) = lib.dylib {
544 err.note(&format!("path: {}", p.display()));
1a4d82fc 545 }
3157f602
XL
546 if let Some((ref p, _)) = lib.rlib {
547 err.note(&format!("path: {}", p.display()));
1a4d82fc 548 }
476ff2be 549 note_crate_name(&mut err, &lib.metadata.get_root().name.as_str());
1a4d82fc 550 }
9cc50fc6 551 err.emit();
970d7e83
LB
552 None
553 }
1a4d82fc
JJ
554 }
555 }
556
557 // Attempts to extract *one* library from the set `m`. If the set has no
558 // elements, `None` is returned. If the set has more than one element, then
559 // the errors and notes are emitted about the set of libraries.
560 //
561 // With only one library in the set, this function will extract it, and then
562 // read the metadata from it if `*slot` is `None`. If the metadata couldn't
563 // be read, it is assumed that the file isn't a valid rust library (no
564 // errors are emitted).
c30ab7b3 565 fn extract_one(&mut self,
476ff2be 566 m: FxHashMap<PathBuf, PathKind>,
c30ab7b3
SL
567 flavor: CrateFlavor,
568 slot: &mut Option<(Svh, MetadataBlob)>)
569 -> Option<(PathBuf, PathKind)> {
a7813a04 570 let mut ret: Option<(PathBuf, PathKind)> = None;
85aaf69f 571 let mut error = 0;
1a4d82fc
JJ
572
573 if slot.is_some() {
574 // FIXME(#10786): for an optimization, we only read one of the
a7813a04 575 // libraries' metadata sections. In theory we should
1a4d82fc
JJ
576 // read both, but reading dylib metadata is quite
577 // slow.
9346a6ac 578 if m.is_empty() {
c30ab7b3 579 return None;
1a4d82fc 580 } else if m.len() == 1 {
c30ab7b3 581 return Some(m.into_iter().next().unwrap());
1a4d82fc
JJ
582 }
583 }
584
9cc50fc6 585 let mut err: Option<DiagnosticBuilder> = None;
85aaf69f 586 for (lib, kind) in m {
1a4d82fc 587 info!("{} reading metadata from: {}", flavor, lib.display());
7cac9316
XL
588 let (hash, metadata) =
589 match get_metadata_section(self.target, flavor, &lib, self.metadata_loader) {
590 Ok(blob) => {
591 if let Some(h) = self.crate_matches(&blob, &lib) {
592 (h, blob)
593 } else {
594 info!("metadata mismatch");
595 continue;
596 }
597 }
598 Err(err) => {
599 info!("no metadata found: {}", err);
c30ab7b3 600 continue;
1a4d82fc 601 }
7cac9316 602 };
a7813a04
XL
603 // If we see multiple hashes, emit an error about duplicate candidates.
604 if slot.as_ref().map_or(false, |s| s.0 != hash) {
c30ab7b3
SL
605 let mut e = struct_span_err!(self.sess,
606 self.span,
607 E0465,
9cc50fc6 608 "multiple {} candidates for `{}` found",
c30ab7b3
SL
609 flavor,
610 self.crate_name);
9cc50fc6
SL
611 e.span_note(self.span,
612 &format!(r"candidate #1: {}",
c30ab7b3
SL
613 ret.as_ref()
614 .unwrap()
615 .0
616 .display()));
9cc50fc6
SL
617 if let Some(ref mut e) = err {
618 e.emit();
619 }
620 err = Some(e);
1a4d82fc 621 error = 1;
a7813a04 622 *slot = None;
1a4d82fc
JJ
623 }
624 if error > 0 {
625 error += 1;
9cc50fc6 626 err.as_mut().unwrap().span_note(self.span,
c30ab7b3
SL
627 &format!(r"candidate #{}: {}",
628 error,
9cc50fc6 629 lib.display()));
c30ab7b3 630 continue;
970d7e83 631 }
8bb4bdeb
XL
632
633 // Ok so at this point we've determined that `(lib, kind)` above is
634 // a candidate crate to load, and that `slot` is either none (this
635 // is the first crate of its kind) or if some the previous path has
636 // the exact same hash (e.g. it's the exact same crate).
637 //
638 // In principle these two candidate crates are exactly the same so
639 // we can choose either of them to link. As a stupidly gross hack,
640 // however, we favor crate in the sysroot.
641 //
642 // You can find more info in rust-lang/rust#39518 and various linked
643 // issues, but the general gist is that during testing libstd the
644 // compilers has two candidates to choose from: one in the sysroot
645 // and one in the deps folder. These two crates are the exact same
646 // crate but if the compiler chooses the one in the deps folder
647 // it'll cause spurious errors on Windows.
648 //
649 // As a result, we favor the sysroot crate here. Note that the
650 // candidates are all canonicalized, so we canonicalize the sysroot
651 // as well.
652 if let Some((ref prev, _)) = ret {
653 let sysroot = self.sess.sysroot();
654 let sysroot = sysroot.canonicalize()
655 .unwrap_or(sysroot.to_path_buf());
656 if prev.starts_with(&sysroot) {
657 continue
658 }
659 }
a7813a04 660 *slot = Some((hash, metadata));
85aaf69f 661 ret = Some((lib, kind));
1a4d82fc 662 }
9cc50fc6
SL
663
664 if error > 0 {
665 err.unwrap().emit();
666 None
667 } else {
668 ret
669 }
1a4d82fc
JJ
670 }
671
9e0c209e 672 fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> {
c30ab7b3 673 let rustc_version = rustc_version();
476ff2be
SL
674 let found_version = metadata.get_rustc_version();
675 if found_version != rustc_version {
9e0c209e 676 info!("Rejecting via version: expected {} got {}",
c30ab7b3 677 rustc_version,
476ff2be 678 found_version);
a7813a04
XL
679 self.rejected_via_version.push(CrateMismatch {
680 path: libpath.to_path_buf(),
476ff2be 681 got: found_version,
a7813a04
XL
682 });
683 return None;
684 }
685
476ff2be
SL
686 let root = metadata.get_root();
687 if let Some(is_proc_macro) = self.is_proc_macro {
688 if root.macro_derive_registrar.is_some() != is_proc_macro {
689 return None;
690 }
691 }
692
1a4d82fc 693 if self.should_match_name {
9e0c209e 694 if self.crate_name != root.name {
c30ab7b3
SL
695 info!("Rejecting via crate name");
696 return None;
1a4d82fc
JJ
697 }
698 }
1a4d82fc 699
9e0c209e
SL
700 if root.triple != self.triple {
701 info!("Rejecting via crate triple: expected {} got {}",
c30ab7b3
SL
702 self.triple,
703 root.triple);
1a4d82fc 704 self.rejected_via_triple.push(CrateMismatch {
c34b1796 705 path: libpath.to_path_buf(),
c30ab7b3 706 got: root.triple,
1a4d82fc 707 });
a7813a04 708 return None;
223e47cc 709 }
223e47cc 710
a7813a04 711 if let Some(myhash) = self.hash {
9e0c209e 712 if *myhash != root.hash {
c30ab7b3 713 info!("Rejecting via hash: expected {} got {}", *myhash, root.hash);
a7813a04
XL
714 self.rejected_via_hash.push(CrateMismatch {
715 path: libpath.to_path_buf(),
c30ab7b3 716 got: myhash.to_string(),
a7813a04
XL
717 });
718 return None;
1a4d82fc 719 }
223e47cc 720 }
a7813a04 721
9e0c209e 722 Some(root.hash)
223e47cc 723 }
223e47cc 724
1a4d82fc
JJ
725
726 // Returns the corresponding (prefix, suffix) that files need to have for
727 // dynamic libraries
728 fn dylibname(&self) -> (String, String) {
85aaf69f 729 let t = &self.target;
1a4d82fc 730 (t.options.dll_prefix.clone(), t.options.dll_suffix.clone())
223e47cc 731 }
223e47cc 732
7453a54e
SL
733 // Returns the corresponding (prefix, suffix) that files need to have for
734 // static libraries
735 fn staticlibname(&self) -> (String, String) {
736 let t = &self.target;
737 (t.options.staticlib_prefix.clone(), t.options.staticlib_suffix.clone())
738 }
739
c30ab7b3
SL
740 fn find_commandline_library<'b, LOCS>(&mut self, locs: LOCS) -> Option<Library>
741 where LOCS: Iterator<Item = &'b String>
5bcae85e 742 {
1a4d82fc
JJ
743 // First, filter out all libraries that look suspicious. We only accept
744 // files which actually exist that have the correct naming scheme for
745 // rlibs/dylibs.
746 let sess = self.sess;
747 let dylibname = self.dylibname();
476ff2be
SL
748 let mut rlibs = FxHashMap();
749 let mut rmetas = FxHashMap();
750 let mut dylibs = FxHashMap();
1a4d82fc 751 {
5bcae85e 752 let locs = locs.map(|l| PathBuf::from(l)).filter(|loc| {
1a4d82fc
JJ
753 if !loc.exists() {
754 sess.err(&format!("extern location for {} does not exist: {}",
c30ab7b3
SL
755 self.crate_name,
756 loc.display()));
1a4d82fc
JJ
757 return false;
758 }
c34b1796 759 let file = match loc.file_name().and_then(|s| s.to_str()) {
1a4d82fc
JJ
760 Some(file) => file,
761 None => {
762 sess.err(&format!("extern location for {} is not a file: {}",
c30ab7b3
SL
763 self.crate_name,
764 loc.display()));
1a4d82fc
JJ
765 return false;
766 }
767 };
476ff2be
SL
768 if file.starts_with("lib") &&
769 (file.ends_with(".rlib") || file.ends_with(".rmeta")) {
c30ab7b3 770 return true;
1a4d82fc
JJ
771 } else {
772 let (ref prefix, ref suffix) = dylibname;
c30ab7b3
SL
773 if file.starts_with(&prefix[..]) && file.ends_with(&suffix[..]) {
774 return true;
1a4d82fc
JJ
775 }
776 }
476ff2be
SL
777
778 self.rejected_via_filename.push(CrateMismatch {
779 path: loc.clone(),
780 got: String::new(),
781 });
782
1a4d82fc
JJ
783 false
784 });
785
85aaf69f
SL
786 // Now that we have an iterator of good candidates, make sure
787 // there's at most one rlib and at most one dylib.
1a4d82fc 788 for loc in locs {
c34b1796 789 if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") {
c30ab7b3 790 rlibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag);
476ff2be
SL
791 } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") {
792 rmetas.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag);
1a4d82fc 793 } else {
c30ab7b3 794 dylibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag);
1a4d82fc
JJ
795 }
796 }
797 };
798
799 // Extract the rlib/dylib pair.
a7813a04
XL
800 let mut slot = None;
801 let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot);
476ff2be 802 let rmeta = self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot);
a7813a04 803 let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot);
1a4d82fc 804
476ff2be 805 if rlib.is_none() && rmeta.is_none() && dylib.is_none() {
c30ab7b3
SL
806 return None;
807 }
a7813a04 808 match slot {
c30ab7b3
SL
809 Some((_, metadata)) => {
810 Some(Library {
811 dylib: dylib,
812 rlib: rlib,
476ff2be 813 rmeta: rmeta,
c30ab7b3
SL
814 metadata: metadata,
815 })
816 }
1a4d82fc
JJ
817 None => None,
818 }
223e47cc 819 }
223e47cc
LB
820}
821
9cc50fc6
SL
822pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) {
823 err.note(&format!("crate name: {}", name));
1a4d82fc 824}
223e47cc 825
1a4d82fc 826// Just a small wrapper to time how long reading metadata takes.
c30ab7b3
SL
827fn get_metadata_section(target: &Target,
828 flavor: CrateFlavor,
7cac9316
XL
829 filename: &Path,
830 loader: &MetadataLoader)
62682a34 831 -> Result<MetadataBlob, String> {
92a42be0 832 let start = Instant::now();
7cac9316 833 let ret = get_metadata_section_imp(target, flavor, filename, loader);
c30ab7b3
SL
834 info!("reading {:?} => {:?}",
835 filename.file_name().unwrap(),
92a42be0 836 start.elapsed());
c30ab7b3 837 return ret;
223e47cc
LB
838}
839
c30ab7b3
SL
840fn get_metadata_section_imp(target: &Target,
841 flavor: CrateFlavor,
7cac9316
XL
842 filename: &Path,
843 loader: &MetadataLoader)
62682a34 844 -> Result<MetadataBlob, String> {
1a4d82fc
JJ
845 if !filename.exists() {
846 return Err(format!("no such file: '{}'", filename.display()));
847 }
7cac9316
XL
848 let raw_bytes: ErasedBoxRef<[u8]> = match flavor {
849 CrateFlavor::Rlib => loader.get_rlib_metadata(target, filename)?,
850 CrateFlavor::Dylib => {
851 let buf = loader.get_dylib_metadata(target, filename)?;
852 // The header is uncompressed
853 let header_len = METADATA_HEADER.len();
854 debug!("checking {} bytes of metadata-version stamp", header_len);
855 let header = &buf[..cmp::min(header_len, buf.len())];
856 if header != METADATA_HEADER {
857 return Err(format!("incompatible metadata version found: '{}'",
858 filename.display()));
1a4d82fc 859 }
223e47cc 860
7cac9316
XL
861 // Header is okay -> inflate the actual metadata
862 let compressed_bytes = &buf[header_len..];
863 debug!("inflating {} bytes of compressed metadata", compressed_bytes.len());
864 match flate::inflate_bytes(compressed_bytes) {
865 Ok(inflated) => {
866 let buf = unsafe { OwningRef::new_assert_stable_address(inflated) };
867 buf.map_owner_box().erase_owner()
868 }
869 Err(_) => {
870 return Err(format!("failed to decompress metadata: {}", filename.display()));
223e47cc
LB
871 }
872 }
223e47cc 873 }
7cac9316
XL
874 CrateFlavor::Rmeta => {
875 let mut file = File::open(filename).map_err(|_|
876 format!("could not open file: '{}'", filename.display()))?;
877 let mut buf = vec![];
878 file.read_to_end(&mut buf).map_err(|_|
879 format!("failed to read rmeta metadata: '{}'", filename.display()))?;
880 OwningRef::new(buf).map_owner_box().erase_owner()
881 }
882 };
883 let blob = MetadataBlob(raw_bytes);
884 if blob.is_compatible() {
885 Ok(blob)
1a4d82fc 886 } else {
7cac9316 887 Err(format!("incompatible metadata version found: '{}'", filename.display()))
223e47cc
LB
888 }
889}
890
891// A diagnostic function for dumping crate metadata to an output stream
7cac9316
XL
892pub fn list_file_metadata(target: &Target,
893 path: &Path,
894 loader: &MetadataLoader,
895 out: &mut io::Write)
896 -> io::Result<()> {
a7813a04 897 let filename = path.file_name().unwrap().to_str().unwrap();
c30ab7b3
SL
898 let flavor = if filename.ends_with(".rlib") {
899 CrateFlavor::Rlib
476ff2be
SL
900 } else if filename.ends_with(".rmeta") {
901 CrateFlavor::Rmeta
c30ab7b3
SL
902 } else {
903 CrateFlavor::Dylib
904 };
7cac9316 905 match get_metadata_section(target, flavor, path, loader) {
9e0c209e 906 Ok(metadata) => metadata.list_crate_metadata(out),
c30ab7b3 907 Err(msg) => write!(out, "{}\n", msg),
223e47cc
LB
908 }
909}