1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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.
11 #![allow(non_camel_case_types)]
13 //! Validates all used crates and extern libraries and loads their metadata
16 use session::{config, Session};
17 use session::search_paths::PathKind;
19 use metadata::cstore::{CStore, CrateSource};
20 use metadata::decoder;
22 use metadata::loader::CratePaths;
23 use plugin::load::PluginMetadata;
24 use util::nodemap::FnvHashMap;
27 use std::collections::hash_map::Entry::{Occupied, Vacant};
31 use syntax::attr::AttrMetaMethods;
32 use syntax::codemap::{Span};
33 use syntax::diagnostic::SpanHandler;
34 use syntax::parse::token::InternedString;
35 use syntax::parse::token;
41 next_crate_num: ast::CrateNum,
44 // Traverses an AST, reading all the information about use'd crates and extern
45 // libraries necessary for later resolving, typechecking, linking, etc.
46 pub fn read_crates(sess: &Session,
50 next_crate_num: sess.cstore.next_crate_num(),
52 visit_crate(&e, krate);
53 visit::walk_crate(&mut e, krate);
54 dump_crates(&sess.cstore);
55 warn_if_multiple_versions(sess.diagnostic(), &sess.cstore);
57 for &(ref name, kind) in sess.opts.libs.iter() {
58 register_native_lib(sess, None, name.clone(), kind);
62 impl<'a, 'v> visit::Visitor<'v> for Env<'a> {
63 fn visit_view_item(&mut self, a: &ast::ViewItem) {
64 visit_view_item(self, a);
65 visit::walk_view_item(self, a);
67 fn visit_item(&mut self, a: &ast::Item) {
69 visit::walk_item(self, a);
73 fn dump_crates(cstore: &CStore) {
74 debug!("resolved crates:");
75 cstore.iter_crate_data_origins(|_, data, opt_source| {
76 debug!(" name: {}", data.name());
77 debug!(" cnum: {}", data.cnum);
78 debug!(" hash: {}", data.hash());
80 let CrateSource { dylib, rlib, cnum: _ } = cs;
81 dylib.map(|dl| debug!(" dylib: {}", dl.display()));
82 rlib.map(|rl| debug!(" rlib: {}", rl.display()));
87 fn warn_if_multiple_versions(diag: &SpanHandler, cstore: &CStore) {
88 let mut map = FnvHashMap::new();
89 cstore.iter_crate_data(|cnum, data| {
90 match map.entry(&data.name()) {
91 Vacant(entry) => { entry.insert(vec![cnum]); },
92 Occupied(mut entry) => { entry.get_mut().push(cnum); },
96 for (name, dupes) in map.into_iter() {
97 if dupes.len() == 1 { continue }
99 format!("using multiple versions of crate `{}`", name)[]);
100 for dupe in dupes.into_iter() {
101 let data = cstore.get_crate_data(dupe);
102 diag.span_note(data.span, "used here");
103 loader::note_crate_name(diag, data.name()[]);
108 fn visit_crate(e: &Env, c: &ast::Crate) {
109 for a in c.attrs.iter().filter(|m| m.name() == "link_args") {
110 match a.value_str() {
111 Some(ref linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
112 None => { /* fallthrough */ }
117 fn should_link(i: &ast::ViewItem) -> bool {
118 i.attrs.iter().all(|attr| {
119 attr.name().get() != "phase" ||
120 attr.meta_item_list().map_or(false, |phases| {
121 attr::contains_name(phases[], "link")
126 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
131 match extract_crate_info(e, i) {
133 let (cnum, _, _) = resolve_crate(e,
140 e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
153 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
155 ast::ViewItemExternCrate(ident, ref path_opt, id) => {
156 let ident = token::get_ident(ident);
157 debug!("resolving extern crate stmt. ident: {} path_opt: {}",
159 let name = match *path_opt {
160 Some((ref path_str, _)) => {
161 let name = path_str.get().to_string();
162 validate_crate_name(Some(e.sess), name[],
166 None => ident.get().to_string(),
169 ident: ident.get().to_string(),
172 should_link: should_link(i),
179 pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
180 let err = |&: s: &str| {
182 (_, None) => panic!("{}", s),
183 (Some(sp), Some(sess)) => sess.span_err(sp, s),
184 (None, Some(sess)) => sess.err(s),
188 err("crate name must not be empty");
191 if c.is_alphanumeric() { continue }
192 if c == '_' || c == '-' { continue }
193 err(format!("invalid character `{}` in crate name: `{}`", c, s)[]);
196 Some(sess) => sess.abort_if_errors(),
201 fn visit_item(e: &Env, i: &ast::Item) {
203 ast::ItemForeignMod(ref fm) => {
204 if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
208 // First, add all of the custom link_args attributes
209 let link_args = i.attrs.iter()
210 .filter_map(|at| if at.name() == "link_args" {
215 .collect::<Vec<&ast::Attribute>>();
216 for m in link_args.iter() {
217 match m.value_str() {
218 Some(linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
219 None => { /* fallthrough */ }
223 // Next, process all of the #[link(..)]-style arguments
224 let link_args = i.attrs.iter()
225 .filter_map(|at| if at.name() == "link" {
230 .collect::<Vec<&ast::Attribute>>();
231 for m in link_args.iter() {
232 match m.meta_item_list() {
234 let kind = items.iter().find(|k| {
236 }).and_then(|a| a.value_str());
237 let kind = match kind {
241 } else if e.sess.target.target.options.is_like_osx
242 && k == "framework" {
243 cstore::NativeFramework
244 } else if k == "framework" {
245 cstore::NativeFramework
246 } else if k == "dylib" {
247 cstore::NativeUnknown
249 e.sess.span_err(m.span,
250 format!("unknown kind: `{}`",
252 cstore::NativeUnknown
255 None => cstore::NativeUnknown
257 let n = items.iter().find(|n| {
259 }).and_then(|a| a.value_str());
263 e.sess.span_err(m.span,
264 "#[link(...)] specified without \
266 InternedString::new("foo")
269 register_native_lib(e.sess, Some(m.span),
270 n.get().to_string(), kind);
280 fn register_native_lib(sess: &Session,
283 kind: cstore::NativeLibraryKind) {
287 sess.span_err(span, "#[link(name = \"\")] given with \
291 sess.err("empty library name given via `-l`");
296 let is_osx = sess.target.target.options.is_like_osx;
297 if kind == cstore::NativeFramework && !is_osx {
298 let msg = "native frameworks are only available on OSX targets";
300 Some(span) => sess.span_err(span, msg),
301 None => sess.err(msg),
304 sess.cstore.add_used_library(name, kind);
307 fn existing_match(e: &Env, name: &str,
308 hash: Option<&Svh>) -> Option<ast::CrateNum> {
310 e.sess.cstore.iter_crate_data(|cnum, data| {
311 if data.name != name { return }
314 Some(hash) if *hash == data.hash() => { ret = Some(cnum); return }
319 // When the hash is None we're dealing with a top-level dependency in
320 // which case we may have a specification on the command line for this
321 // library. Even though an upstream library may have loaded something of
322 // the same name, we have to make sure it was loaded from the exact same
325 // We're also sure to compare *paths*, not actual byte slices. The
326 // `source` stores paths which are normalized which may be different
327 // from the strings on the command line.
328 let source = e.sess.cstore.get_used_crate_source(cnum).unwrap();
329 match e.sess.opts.externs.get(name) {
331 let found = locs.iter().any(|l| {
332 let l = fs::realpath(&Path::new(l[])).ok();
333 l == source.dylib || l == source.rlib
339 None => ret = Some(cnum),
345 fn register_crate<'a>(e: &mut Env,
346 root: &Option<CratePaths>,
350 lib: loader::Library)
351 -> (ast::CrateNum, Rc<cstore::crate_metadata>,
352 cstore::CrateSource) {
353 // Claim this crate number and cache it
354 let cnum = e.next_crate_num;
355 e.next_crate_num += 1;
357 // Stash paths for top-most crate locally if necessary.
358 let crate_paths = if root.is_none() {
360 ident: ident.to_string(),
361 dylib: lib.dylib.clone(),
362 rlib: lib.rlib.clone(),
367 // Maintain a reference to the top most crate.
368 let root = if root.is_some() { root } else { &crate_paths };
370 let cnum_map = resolve_crate_deps(e, root, lib.metadata.as_slice(), span);
372 let loader::Library{ dylib, rlib, metadata } = lib;
374 let cmeta = Rc::new( cstore::crate_metadata {
375 name: name.to_string(),
382 let source = cstore::CrateSource {
388 e.sess.cstore.set_crate_data(cnum, cmeta.clone());
389 e.sess.cstore.add_used_crate_source(source.clone());
390 (cnum, cmeta, source)
393 fn resolve_crate(e: &mut Env,
394 root: &Option<CratePaths>,
400 -> (ast::CrateNum, Rc<cstore::crate_metadata>,
401 cstore::CrateSource) {
402 match existing_match(e, name, hash) {
404 let mut load_ctxt = loader::Context {
409 hash: hash.map(|a| &*a),
410 filesearch: e.sess.target_filesearch(kind),
411 triple: e.sess.opts.target_triple[],
413 rejected_via_hash: vec!(),
414 rejected_via_triple: vec!(),
415 should_match_name: true,
417 let library = load_ctxt.load_library_crate();
418 register_crate(e, root, ident, name, span, library)
421 e.sess.cstore.get_crate_data(cnum),
422 e.sess.cstore.get_used_crate_source(cnum).unwrap())
426 // Go through the crate metadata and load any crates that it references
427 fn resolve_crate_deps(e: &mut Env,
428 root: &Option<CratePaths>,
429 cdata: &[u8], span : Span)
430 -> cstore::cnum_map {
431 debug!("resolving deps of external crate");
432 // The map from crate numbers in the crate we're resolving to local crate
434 decoder::get_crate_deps(cdata).iter().map(|dep| {
435 debug!("resolving dep crate {} hash: `{}`", dep.name, dep.hash);
436 let (local_cnum, _, _) = resolve_crate(e, root,
441 PathKind::Dependency);
442 (dep.cnum, local_cnum)
446 pub struct PluginMetadataReader<'a> {
450 impl<'a> PluginMetadataReader<'a> {
451 pub fn new(sess: &'a Session) -> PluginMetadataReader<'a> {
452 PluginMetadataReader {
455 next_crate_num: sess.cstore.next_crate_num(),
460 pub fn read_plugin_metadata(&mut self,
461 krate: &ast::ViewItem) -> PluginMetadata {
462 let info = extract_crate_info(&self.env, krate).unwrap();
463 let target_triple = self.env.sess.opts.target_triple[];
464 let is_cross = target_triple != config::host_triple();
465 let mut should_link = info.should_link && !is_cross;
466 let mut load_ctxt = loader::Context {
470 crate_name: info.name[],
472 filesearch: self.env.sess.host_filesearch(PathKind::Crate),
473 triple: config::host_triple(),
475 rejected_via_hash: vec!(),
476 rejected_via_triple: vec!(),
477 should_match_name: true,
479 let library = match load_ctxt.maybe_load_library_crate() {
481 None if is_cross => {
482 // try loading from target crates (only valid if there are
483 // no syntax extensions)
484 load_ctxt.triple = target_triple;
485 load_ctxt.filesearch = self.env.sess.target_filesearch(PathKind::Crate);
486 let lib = load_ctxt.load_library_crate();
487 if decoder::get_plugin_registrar_fn(lib.metadata.as_slice()).is_some() {
488 let message = format!("crate `{}` contains a plugin_registrar fn but \
489 only a version for triple `{}` could be found (need {})",
490 info.ident, target_triple, config::host_triple());
491 self.env.sess.span_err(krate.span, message[]);
492 // need to abort now because the syntax expansion
493 // code will shortly attempt to load and execute
494 // code from the found library.
495 self.env.sess.abort_if_errors();
497 should_link = info.should_link;
500 None => { load_ctxt.report_load_errs(); unreachable!() },
502 let macros = decoder::get_exported_macros(library.metadata.as_slice());
503 let registrar = decoder::get_plugin_registrar_fn(library.metadata.as_slice()).map(|id| {
504 decoder::get_symbol(library.metadata.as_slice(), id)
506 if library.dylib.is_none() && registrar.is_some() {
507 let message = format!("plugin crate `{}` only found in rlib format, \
508 but must be available in dylib format",
510 self.env.sess.span_err(krate.span, message[]);
511 // No need to abort because the loading code will just ignore this
514 let pc = PluginMetadata {
515 lib: library.dylib.clone(),
517 registrar_symbol: registrar,
519 if should_link && existing_match(&self.env, info.name[],
521 // register crate now to avoid double-reading metadata
522 register_crate(&mut self.env, &None, info.ident[],
523 info.name[], krate.span, library);