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
17 use driver::{driver, session};
18 use driver::session::Session;
19 use metadata::csearch;
21 use metadata::decoder;
23 use metadata::loader::Os;
25 use std::cell::RefCell;
26 use collections::HashMap;
30 use syntax::attr::AttrMetaMethods;
31 use syntax::codemap::{Span};
32 use syntax::diagnostic::SpanHandler;
33 use syntax::ext::base::{CrateLoader, MacroCrate};
34 use syntax::parse::token::{IdentInterner, InternedString};
35 use syntax::parse::token;
36 use syntax::crateid::CrateId;
39 // Traverses an AST, reading all the information about use'd crates and extern
40 // libraries necessary for later resolving, typechecking, linking, etc.
41 pub fn read_crates(sess: &Session,
44 intr: @IdentInterner) {
48 crate_cache: @RefCell::new(Vec::new()),
52 visit_crate(&e, krate);
53 visit::walk_crate(&mut e, krate, ());
54 dump_crates(e.crate_cache.borrow().as_slice());
55 warn_if_multiple_versions(&mut e,
57 e.crate_cache.borrow().as_slice());
60 impl<'a> visit::Visitor<()> for Env<'a> {
61 fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
62 visit_view_item(self, a);
63 visit::walk_view_item(self, a, ());
65 fn visit_item(&mut self, a: &ast::Item, _: ()) {
67 visit::walk_item(self, a, ());
79 fn dump_crates(crate_cache: &[cache_entry]) {
80 debug!("resolved crates:");
81 for entry in crate_cache.iter() {
82 debug!("cnum: {:?}", entry.cnum);
83 debug!("span: {:?}", entry.span);
84 debug!("hash: {:?}", entry.hash);
88 fn warn_if_multiple_versions(e: &mut Env,
90 crate_cache: &[cache_entry]) {
91 if crate_cache.len() != 0u {
92 let name = crate_cache[crate_cache.len() - 1].crate_id.name.clone();
94 let (matches, non_matches) = crate_cache.partitioned(|entry|
95 name == entry.crate_id.name);
97 assert!(!matches.is_empty());
99 if matches.len() != 1u {
101 format!("using multiple versions of crate `{}`", name));
102 for match_ in matches.iter() {
103 diag.span_note(match_.span, "used here");
104 loader::note_crateid_attr(diag, &match_.crate_id);
108 warn_if_multiple_versions(e, diag, non_matches);
115 crate_cache: @RefCell<Vec<cache_entry>>,
116 next_crate_num: ast::CrateNum,
120 fn visit_crate(e: &Env, c: &ast::Crate) {
121 for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
122 match a.value_str() {
123 Some(ref linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
124 None => { /* fallthrough */ }
129 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
130 let should_load = i.attrs.iter().all(|attr| {
131 attr.name().get() != "phase" ||
132 attr.meta_item_list().map_or(false, |phases| {
133 attr::contains_name(phases.as_slice(), "link")
141 match extract_crate_info(e, i) {
143 let cnum = resolve_crate(e, None, info.ident, &info.crate_id, None,
145 e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
157 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
159 ast::ViewItemExternCrate(ident, ref path_opt, id) => {
160 let ident = token::get_ident(ident);
161 debug!("resolving extern crate stmt. ident: {:?} path_opt: {:?}",
163 let crate_id = match *path_opt {
164 Some((ref path_str, _)) => {
165 let crateid: Option<CrateId> = from_str(path_str.get());
168 e.sess.span_err(i.span, "malformed crate id");
174 None => from_str(ident.get().to_str()).unwrap()
177 ident: ident.get().to_str(),
186 fn visit_item(e: &Env, i: &ast::Item) {
188 ast::ItemForeignMod(ref fm) => {
189 if fm.abis.is_rust() || fm.abis.is_intrinsic() {
193 // First, add all of the custom link_args attributes
194 let link_args = i.attrs.iter()
195 .filter_map(|at| if at.name().equiv(&("link_args")) {
200 .collect::<~[&ast::Attribute]>();
201 for m in link_args.iter() {
202 match m.value_str() {
203 Some(linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
204 None => { /* fallthrough */ }
208 // Next, process all of the #[link(..)]-style arguments
209 let link_args = i.attrs.iter()
210 .filter_map(|at| if at.name().equiv(&("link")) {
215 .collect::<~[&ast::Attribute]>();
216 for m in link_args.iter() {
217 match m.meta_item_list() {
219 let kind = items.iter().find(|k| {
220 k.name().equiv(&("kind"))
221 }).and_then(|a| a.value_str());
222 let kind = match kind {
224 if k.equiv(&("static")) {
226 } else if e.sess.targ_cfg.os == abi::OsMacos &&
227 k.equiv(&("framework")) {
228 cstore::NativeFramework
229 } else if k.equiv(&("framework")) {
230 e.sess.span_err(m.span,
231 "native frameworks are only available \
233 cstore::NativeUnknown
235 e.sess.span_err(m.span,
236 format!("unknown kind: `{}`", k));
237 cstore::NativeUnknown
240 None => cstore::NativeUnknown
242 let n = items.iter().find(|n| {
243 n.name().equiv(&("name"))
244 }).and_then(|a| a.value_str());
248 e.sess.span_err(m.span,
249 "#[link(...)] specified without \
251 InternedString::new("foo")
254 if n.get().is_empty() {
255 e.sess.span_err(m.span, "#[link(name = \"\")] given with empty name");
257 e.sess.cstore.add_used_library(n.get().to_owned(), kind);
268 fn existing_match(e: &Env, crate_id: &CrateId,
269 hash: Option<&Svh>) -> Option<ast::CrateNum> {
270 for c in e.crate_cache.borrow().iter() {
271 if !crate_id.matches(&c.crate_id) { continue }
273 Some(hash) if *hash != c.hash => {}
274 Some(..) | None => return Some(c.cnum)
280 fn resolve_crate(e: &mut Env,
281 root_ident: Option<&str>,
287 match existing_match(e, crate_id, hash) {
289 let id_hash = link::crate_id_hash(crate_id);
290 let mut load_ctxt = loader::Context {
296 hash: hash.map(|a| &*a),
299 rejected_via_hash: false,
301 let loader::Library {
302 dylib, rlib, metadata
303 } = load_ctxt.load_library_crate(root_ident);
305 let crate_id = decoder::get_crate_id(metadata.as_slice());
306 let hash = decoder::get_crate_hash(metadata.as_slice());
308 // Claim this crate number and cache it
309 let cnum = e.next_crate_num;
310 e.crate_cache.borrow_mut().push(cache_entry {
316 e.next_crate_num += 1;
318 // Maintain a reference to the top most crate.
319 let root_crate = match root_ident {
321 None => load_ctxt.ident.clone()
324 // Now resolve the crates referenced by this crate
325 let cnum_map = resolve_crate_deps(e,
330 let cmeta = @cstore::crate_metadata {
331 name: load_ctxt.crate_id.name.to_owned(),
337 e.sess.cstore.set_crate_data(cnum, cmeta);
338 e.sess.cstore.add_used_crate_source(cstore::CrateSource {
349 // Go through the crate metadata and load any crates that it references
350 fn resolve_crate_deps(e: &mut Env,
351 root_ident: Option<&str>,
352 cdata: &[u8], span : Span)
353 -> cstore::cnum_map {
354 debug!("resolving deps of external crate");
355 // The map from crate numbers in the crate we're resolving to local crate
357 let mut cnum_map = HashMap::new();
358 let r = decoder::get_crate_deps(cdata);
359 for dep in r.iter() {
360 let extrn_cnum = dep.cnum;
361 debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
362 let local_cnum = resolve_crate(e, root_ident,
363 dep.crate_id.name.as_slice(),
367 cnum_map.insert(extrn_cnum, local_cnum);
369 return @RefCell::new(cnum_map);
372 pub struct Loader<'a> {
376 impl<'a> Loader<'a> {
377 pub fn new(sess: &'a Session) -> Loader<'a> {
378 let os = driver::get_os(driver::host_triple()).unwrap();
379 let os = session::sess_os_to_meta_os(os);
384 crate_cache: @RefCell::new(Vec::new()),
386 intr: token::get_ident_interner(),
392 impl<'a> CrateLoader for Loader<'a> {
393 fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
394 let info = extract_crate_info(&self.env, krate).unwrap();
395 let cnum = resolve_crate(&mut self.env, None, info.ident,
396 &info.crate_id, None, krate.span);
397 let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap();
404 fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> Vec<~str> {
405 csearch::get_exported_macros(&self.env.sess.cstore, cnum).move_iter()
409 fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> {
410 let cstore = &self.env.sess.cstore;
411 csearch::get_macro_registrar_fn(cstore, cnum)
412 .map(|did| csearch::get_symbol(cstore, did))