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 driver::session::Session;
17 use driver::{driver, config};
19 use metadata::cstore::{CStore, CrateSource};
20 use metadata::decoder;
22 use metadata::loader::CratePaths;
23 use plugin::load::PluginMetadata;
26 use std::collections::HashMap;
30 use syntax::attr::AttrMetaMethods;
31 use syntax::codemap::{Span};
32 use syntax::diagnostic::SpanHandler;
33 use syntax::parse::token::InternedString;
34 use syntax::parse::token;
40 next_crate_num: ast::CrateNum,
43 // Traverses an AST, reading all the information about use'd crates and extern
44 // libraries necessary for later resolving, typechecking, linking, etc.
45 pub fn read_crates(sess: &Session,
49 next_crate_num: sess.cstore.next_crate_num(),
51 visit_crate(&e, krate);
52 visit::walk_crate(&mut e, krate);
53 dump_crates(&sess.cstore);
54 warn_if_multiple_versions(sess.diagnostic(), &sess.cstore)
57 impl<'a, 'v> visit::Visitor<'v> for Env<'a> {
58 fn visit_view_item(&mut self, a: &ast::ViewItem) {
59 visit_view_item(self, a);
60 visit::walk_view_item(self, a);
62 fn visit_item(&mut self, a: &ast::Item) {
64 visit::walk_item(self, a);
68 fn dump_crates(cstore: &CStore) {
69 debug!("resolved crates:");
70 cstore.iter_crate_data_origins(|_, data, opt_source| {
71 debug!(" name: {}", data.name());
72 debug!(" cnum: {}", data.cnum);
73 debug!(" hash: {}", data.hash());
75 let CrateSource { dylib, rlib, cnum: _ } = cs;
76 dylib.map(|dl| debug!(" dylib: {}", dl.display()));
77 rlib.map(|rl| debug!(" rlib: {}", rl.display()));
82 fn warn_if_multiple_versions(diag: &SpanHandler, cstore: &CStore) {
83 let mut map = HashMap::new();
84 cstore.iter_crate_data(|cnum, data| {
85 map.find_or_insert_with(data.name(), |_| Vec::new()).push(cnum);
88 for (name, dupes) in map.move_iter() {
89 if dupes.len() == 1 { continue }
91 format!("using multiple versions of crate `{}`", name).as_slice());
92 for dupe in dupes.move_iter() {
93 let data = cstore.get_crate_data(dupe);
94 diag.span_note(data.span, "used here");
95 loader::note_crate_name(diag, data.name().as_slice());
100 fn visit_crate(e: &Env, c: &ast::Crate) {
101 for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
102 match a.value_str() {
103 Some(ref linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
104 None => { /* fallthrough */ }
109 fn should_link(i: &ast::ViewItem) -> bool {
110 i.attrs.iter().all(|attr| {
111 attr.name().get() != "phase" ||
112 attr.meta_item_list().map_or(false, |phases| {
113 attr::contains_name(phases.as_slice(), "link")
118 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
123 match extract_crate_info(e, i) {
125 let (cnum, _, _) = resolve_crate(e,
127 info.ident.as_slice(),
128 info.name.as_slice(),
131 e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
144 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
146 ast::ViewItemExternCrate(ident, ref path_opt, id) => {
147 let ident = token::get_ident(ident);
148 debug!("resolving extern crate stmt. ident: {} path_opt: {}",
150 let name = match *path_opt {
151 Some((ref path_str, _)) => {
152 let name = path_str.get().to_string();
153 validate_crate_name(Some(e.sess), name.as_slice(),
157 None => ident.get().to_string(),
160 ident: ident.get().to_string(),
163 should_link: should_link(i),
170 pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
171 let err = |s: &str| {
173 (_, None) => fail!("{}", s),
174 (Some(sp), Some(sess)) => sess.span_err(sp, s),
175 (None, Some(sess)) => sess.err(s),
179 err("crate name must not be empty");
182 if c.is_alphanumeric() { continue }
183 if c == '_' || c == '-' { continue }
184 err(format!("invalid character `{}` in crate name: `{}`", c, s).as_slice());
187 Some(sess) => sess.abort_if_errors(),
192 fn visit_item(e: &Env, i: &ast::Item) {
194 ast::ItemForeignMod(ref fm) => {
195 if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
199 // First, add all of the custom link_args attributes
200 let link_args = i.attrs.iter()
201 .filter_map(|at| if at.name().equiv(&("link_args")) {
206 .collect::<Vec<&ast::Attribute>>();
207 for m in link_args.iter() {
208 match m.value_str() {
209 Some(linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
210 None => { /* fallthrough */ }
214 // Next, process all of the #[link(..)]-style arguments
215 let link_args = i.attrs.iter()
216 .filter_map(|at| if at.name().equiv(&("link")) {
221 .collect::<Vec<&ast::Attribute>>();
222 for m in link_args.iter() {
223 match m.meta_item_list() {
225 let kind = items.iter().find(|k| {
226 k.name().equiv(&("kind"))
227 }).and_then(|a| a.value_str());
228 let kind = match kind {
230 if k.equiv(&("static")) {
232 } else if (e.sess.targ_cfg.os == abi::OsMacos ||
233 e.sess.targ_cfg.os == abi::OsiOS) &&
234 k.equiv(&("framework")) {
235 cstore::NativeFramework
236 } else if k.equiv(&("framework")) {
237 e.sess.span_err(m.span,
238 "native frameworks are only available \
240 cstore::NativeUnknown
242 e.sess.span_err(m.span,
243 format!("unknown kind: `{}`",
245 cstore::NativeUnknown
248 None => cstore::NativeUnknown
250 let n = items.iter().find(|n| {
251 n.name().equiv(&("name"))
252 }).and_then(|a| a.value_str());
256 e.sess.span_err(m.span,
257 "#[link(...)] specified without \
259 InternedString::new("foo")
262 if n.get().is_empty() {
263 e.sess.span_err(m.span,
264 "#[link(name = \"\")] given with \
269 .add_used_library(n.get().to_string(), kind);
280 fn existing_match(e: &Env, name: &str,
281 hash: Option<&Svh>) -> Option<ast::CrateNum> {
283 e.sess.cstore.iter_crate_data(|cnum, data| {
284 if data.name.as_slice() != name { return }
287 Some(hash) if *hash == data.hash() => { ret = Some(cnum); return }
292 // When the hash is None we're dealing with a top-level dependency in
293 // which case we may have a specification on the command line for this
294 // library. Even though an upstream library may have loaded something of
295 // the same name, we have to make sure it was loaded from the exact same
298 // We're also sure to compare *paths*, not actual byte slices. The
299 // `source` stores paths which are normalized which may be different
300 // from the strings on the command line.
301 let source = e.sess.cstore.get_used_crate_source(cnum).unwrap();
302 match e.sess.opts.externs.find_equiv(&name) {
304 let found = locs.iter().any(|l| {
305 let l = fs::realpath(&Path::new(l.as_slice())).ok();
306 l == source.dylib || l == source.rlib
312 None => ret = Some(cnum),
318 fn register_crate<'a>(e: &mut Env,
319 root: &Option<CratePaths>,
323 lib: loader::Library)
324 -> (ast::CrateNum, Rc<cstore::crate_metadata>,
325 cstore::CrateSource) {
326 // Claim this crate number and cache it
327 let cnum = e.next_crate_num;
328 e.next_crate_num += 1;
330 // Stash paths for top-most crate locally if necessary.
331 let crate_paths = if root.is_none() {
333 ident: ident.to_string(),
334 dylib: lib.dylib.clone(),
335 rlib: lib.rlib.clone(),
340 // Maintain a reference to the top most crate.
341 let root = if root.is_some() { root } else { &crate_paths };
343 let cnum_map = resolve_crate_deps(e, root, lib.metadata.as_slice(), span);
345 let loader::Library{ dylib, rlib, metadata } = lib;
347 let cmeta = Rc::new( cstore::crate_metadata {
348 name: name.to_string(),
355 let source = cstore::CrateSource {
361 e.sess.cstore.set_crate_data(cnum, cmeta.clone());
362 e.sess.cstore.add_used_crate_source(source.clone());
363 (cnum, cmeta, source)
366 fn resolve_crate<'a>(e: &mut Env,
367 root: &Option<CratePaths>,
372 -> (ast::CrateNum, Rc<cstore::crate_metadata>,
373 cstore::CrateSource) {
374 match existing_match(e, name, hash) {
376 let mut load_ctxt = loader::Context {
381 hash: hash.map(|a| &*a),
382 filesearch: e.sess.target_filesearch(),
383 os: e.sess.targ_cfg.os,
384 triple: e.sess.targ_cfg.target_strs.target_triple.as_slice(),
386 rejected_via_hash: vec!(),
387 rejected_via_triple: vec!(),
388 should_match_name: true,
390 let library = load_ctxt.load_library_crate();
391 register_crate(e, root, ident, name, span, library)
394 e.sess.cstore.get_crate_data(cnum),
395 e.sess.cstore.get_used_crate_source(cnum).unwrap())
399 // Go through the crate metadata and load any crates that it references
400 fn resolve_crate_deps(e: &mut Env,
401 root: &Option<CratePaths>,
402 cdata: &[u8], span : Span)
403 -> cstore::cnum_map {
404 debug!("resolving deps of external crate");
405 // The map from crate numbers in the crate we're resolving to local crate
407 decoder::get_crate_deps(cdata).iter().map(|dep| {
408 debug!("resolving dep crate {} hash: `{}`", dep.name, dep.hash);
409 let (local_cnum, _, _) = resolve_crate(e, root,
414 (dep.cnum, local_cnum)
418 pub struct PluginMetadataReader<'a> {
422 impl<'a> PluginMetadataReader<'a> {
423 pub fn new(sess: &'a Session) -> PluginMetadataReader<'a> {
424 PluginMetadataReader {
427 next_crate_num: sess.cstore.next_crate_num(),
432 pub fn read_plugin_metadata(&mut self, krate: &ast::ViewItem) -> PluginMetadata {
433 let info = extract_crate_info(&self.env, krate).unwrap();
434 let target_triple = self.env.sess.targ_cfg.target_strs.target_triple.as_slice();
435 let is_cross = target_triple != driver::host_triple();
436 let mut should_link = info.should_link && !is_cross;
437 let os = config::get_os(driver::host_triple()).unwrap();
438 let mut load_ctxt = loader::Context {
441 ident: info.ident.as_slice(),
442 crate_name: info.name.as_slice(),
444 filesearch: self.env.sess.host_filesearch(),
445 triple: driver::host_triple(),
448 rejected_via_hash: vec!(),
449 rejected_via_triple: vec!(),
450 should_match_name: true,
452 let library = match load_ctxt.maybe_load_library_crate() {
454 None if is_cross => {
455 // try loading from target crates (only valid if there are
456 // no syntax extensions)
457 load_ctxt.triple = target_triple;
458 load_ctxt.os = self.env.sess.targ_cfg.os;
459 load_ctxt.filesearch = self.env.sess.target_filesearch();
460 let lib = load_ctxt.load_library_crate();
461 if decoder::get_plugin_registrar_fn(lib.metadata.as_slice()).is_some() {
462 let message = format!("crate `{}` contains a plugin_registrar fn but \
463 only a version for triple `{}` could be found (need {})",
464 info.ident, target_triple, driver::host_triple());
465 self.env.sess.span_err(krate.span, message.as_slice());
466 // need to abort now because the syntax expansion
467 // code will shortly attempt to load and execute
468 // code from the found library.
469 self.env.sess.abort_if_errors();
471 should_link = info.should_link;
474 None => { load_ctxt.report_load_errs(); unreachable!() },
476 let macros = decoder::get_exported_macros(library.metadata.as_slice());
477 let registrar = decoder::get_plugin_registrar_fn(library.metadata.as_slice()).map(|id| {
478 decoder::get_symbol(library.metadata.as_slice(), id)
480 if library.dylib.is_none() && registrar.is_some() {
481 let message = format!("plugin crate `{}` only found in rlib format, \
482 but must be available in dylib format",
484 self.env.sess.span_err(krate.span, message.as_slice());
485 // No need to abort because the loading code will just ignore this
488 let pc = PluginMetadata {
489 lib: library.dylib.clone(),
491 registrar_symbol: registrar,
493 if should_link && existing_match(&self.env, info.name.as_slice(),
495 // register crate now to avoid double-reading metadata
496 register_crate(&mut self.env, &None, info.ident.as_slice(),
497 info.name.as_slice(), krate.span, library);