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;
27 use collections::HashMap;
31 use syntax::attr::AttrMetaMethods;
32 use syntax::codemap::{Span};
33 use syntax::diagnostic::SpanHandler;
34 use syntax::ext::base::{CrateLoader, MacroCrate};
35 use syntax::parse::token::{IdentInterner, InternedString};
36 use syntax::parse::token;
37 use syntax::crateid::CrateId;
40 // Traverses an AST, reading all the information about use'd crates and extern
41 // libraries necessary for later resolving, typechecking, linking, etc.
42 pub fn read_crates(sess: Session,
45 intr: @IdentInterner) {
49 crate_cache: @RefCell::new(Vec::new()),
53 visit_crate(&e, krate);
55 let mut v = ReadCrateVisitor {
58 visit::walk_crate(&mut v, krate, ());
60 let crate_cache = e.crate_cache.borrow();
61 dump_crates(*crate_cache.get());
62 warn_if_multiple_versions(&mut e, sess.diagnostic(), *crate_cache.get());
65 struct ReadCrateVisitor<'a> {
69 impl<'a> visit::Visitor<()> for ReadCrateVisitor<'a> {
70 fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
71 visit_view_item(self.e, a);
72 visit::walk_view_item(self, a, ());
74 fn visit_item(&mut self, a: &ast::Item, _: ()) {
75 visit_item(self.e, a);
76 visit::walk_item(self, a, ());
88 fn dump_crates(crate_cache: &[cache_entry]) {
89 debug!("resolved crates:");
90 for entry in crate_cache.iter() {
91 debug!("cnum: {:?}", entry.cnum);
92 debug!("span: {:?}", entry.span);
93 debug!("hash: {:?}", entry.hash);
97 fn warn_if_multiple_versions(e: &mut Env,
99 crate_cache: &[cache_entry]) {
100 if crate_cache.len() != 0u {
101 let name = crate_cache[crate_cache.len() - 1].crate_id.name.clone();
103 let (matches, non_matches) = crate_cache.partitioned(|entry|
104 name == entry.crate_id.name);
106 assert!(!matches.is_empty());
108 if matches.len() != 1u {
110 format!("using multiple versions of crate `{}`", name));
111 for match_ in matches.iter() {
112 diag.span_note(match_.span, "used here");
113 loader::note_crateid_attr(diag, &match_.crate_id);
117 warn_if_multiple_versions(e, diag, non_matches);
124 crate_cache: @RefCell<vec!(cache_entry)>,
125 next_crate_num: ast::CrateNum,
129 fn visit_crate(e: &Env, c: &ast::Crate) {
130 let cstore = e.sess.cstore;
132 for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
133 match a.value_str() {
134 Some(ref linkarg) => cstore.add_used_link_args(linkarg.get()),
135 None => { /* fallthrough */ }
140 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
141 let should_load = i.attrs.iter().all(|attr| {
142 attr.name().get() != "phase" ||
143 attr.meta_item_list().map_or(false, |phases| {
144 attr::contains_name(phases.as_slice(), "link")
152 match extract_crate_info(e, i) {
154 let cnum = resolve_crate(e, None, info.ident, &info.crate_id, None,
156 e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
168 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
170 ast::ViewItemExternCrate(ident, ref path_opt, id) => {
171 let ident = token::get_ident(ident);
172 debug!("resolving extern crate stmt. ident: {:?} path_opt: {:?}",
174 let crate_id = match *path_opt {
175 Some((ref path_str, _)) => {
176 let crateid: Option<CrateId> = from_str(path_str.get());
179 e.sess.span_err(i.span, "malformed crate id");
185 None => from_str(ident.get().to_str()).unwrap()
188 ident: ident.get().to_str(),
197 fn visit_item(e: &Env, i: &ast::Item) {
199 ast::ItemForeignMod(ref fm) => {
200 if fm.abis.is_rust() || fm.abis.is_intrinsic() {
204 // First, add all of the custom link_args attributes
205 let cstore = e.sess.cstore;
206 let link_args = i.attrs.iter()
207 .filter_map(|at| if at.name().equiv(&("link_args")) {
213 for m in link_args.iter() {
214 match m.value_str() {
215 Some(linkarg) => cstore.add_used_link_args(linkarg.get()),
216 None => { /* fallthrough */ }
220 // Next, process all of the #[link(..)]-style arguments
221 let cstore = e.sess.cstore;
222 let link_args = i.attrs.iter()
223 .filter_map(|at| if at.name().equiv(&("link")) {
229 for m in link_args.iter() {
230 match m.meta_item_list() {
232 let kind = items.iter().find(|k| {
233 k.name().equiv(&("kind"))
234 }).and_then(|a| a.value_str());
235 let kind = match kind {
237 if k.equiv(&("static")) {
239 } else if e.sess.targ_cfg.os == abi::OsMacos &&
240 k.equiv(&("framework")) {
241 cstore::NativeFramework
242 } else if k.equiv(&("framework")) {
243 e.sess.span_err(m.span,
244 "native frameworks are only available \
246 cstore::NativeUnknown
248 e.sess.span_err(m.span,
249 format!("unknown kind: `{}`", k));
250 cstore::NativeUnknown
253 None => cstore::NativeUnknown
255 let n = items.iter().find(|n| {
256 n.name().equiv(&("name"))
257 }).and_then(|a| a.value_str());
261 e.sess.span_err(m.span,
262 "#[link(...)] specified without \
264 InternedString::new("foo")
267 if n.get().is_empty() {
268 e.sess.span_err(m.span, "#[link(name = \"\")] given with empty name");
270 cstore.add_used_library(n.get().to_owned(), kind);
281 fn existing_match(e: &Env, crate_id: &CrateId,
282 hash: Option<&Svh>) -> Option<ast::CrateNum> {
283 let crate_cache = e.crate_cache.borrow();
284 for c in crate_cache.get().iter() {
285 if !crate_id.matches(&c.crate_id) { continue }
287 Some(hash) if *hash != c.hash => {}
288 Some(..) | None => return Some(c.cnum)
294 fn resolve_crate(e: &mut Env,
295 root_ident: Option<&str>,
301 match existing_match(e, crate_id, hash) {
303 let id_hash = link::crate_id_hash(crate_id);
304 let mut load_ctxt = loader::Context {
310 hash: hash.map(|a| &*a),
313 rejected_via_hash: false,
315 let loader::Library {
316 dylib, rlib, metadata
317 } = load_ctxt.load_library_crate(root_ident);
319 let crate_id = decoder::get_crate_id(metadata.as_slice());
320 let hash = decoder::get_crate_hash(metadata.as_slice());
322 // Claim this crate number and cache it
323 let cnum = e.next_crate_num;
325 let mut crate_cache = e.crate_cache.borrow_mut();
326 crate_cache.get().push(cache_entry {
333 e.next_crate_num += 1;
335 // Maintain a reference to the top most crate.
336 let root_crate = match root_ident {
338 None => load_ctxt.ident.clone()
341 // Now resolve the crates referenced by this crate
342 let cnum_map = resolve_crate_deps(e,
347 let cmeta = @cstore::crate_metadata {
348 name: load_ctxt.crate_id.name.to_owned(),
354 let cstore = e.sess.cstore;
355 cstore.set_crate_data(cnum, cmeta);
356 cstore.add_used_crate_source(cstore::CrateSource {
369 // Go through the crate metadata and load any crates that it references
370 fn resolve_crate_deps(e: &mut Env,
371 root_ident: Option<&str>,
372 cdata: &[u8], span : Span)
373 -> cstore::cnum_map {
374 debug!("resolving deps of external crate");
375 // The map from crate numbers in the crate we're resolving to local crate
377 let mut cnum_map = HashMap::new();
378 let r = decoder::get_crate_deps(cdata);
379 for dep in r.iter() {
380 let extrn_cnum = dep.cnum;
381 debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
382 let local_cnum = resolve_crate(e, root_ident,
383 dep.crate_id.name.as_slice(),
387 cnum_map.insert(extrn_cnum, local_cnum);
389 return @RefCell::new(cnum_map);
397 pub fn new(sess: Session) -> Loader {
398 let os = driver::get_os(driver::host_triple()).unwrap();
399 let os = session::sess_os_to_meta_os(os);
404 crate_cache: @RefCell::new(Vec::new()),
406 intr: token::get_ident_interner(),
412 impl CrateLoader for Loader {
413 fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
414 let info = extract_crate_info(&self.env, krate).unwrap();
415 let cnum = resolve_crate(&mut self.env, None, info.ident,
416 &info.crate_id, None, krate.span);
417 let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap();
424 fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> Vec<~str> {
425 csearch::get_exported_macros(self.env.sess.cstore, cnum).move_iter()
429 fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> {
430 let cstore = self.env.sess.cstore;
431 csearch::get_macro_registrar_fn(cstore, cnum)
432 .map(|did| csearch::get_symbol(cstore, did))