1 // Copyright 2012 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 //! Validates all used crates and extern libraries and loads their metadata
13 use driver::{driver, session};
14 use driver::session::Session;
15 use metadata::csearch;
17 use metadata::decoder;
19 use metadata::loader::Os;
21 use std::cell::RefCell;
22 use std::hashmap::HashMap;
26 use syntax::attr::AttrMetaMethods;
27 use syntax::codemap::{Span, DUMMY_SP};
28 use syntax::diagnostic::SpanHandler;
29 use syntax::ext::base::{CrateLoader, MacroCrate};
30 use syntax::parse::token::{IdentInterner, InternedString};
31 use syntax::parse::token;
32 use syntax::crateid::CrateId;
35 // Traverses an AST, reading all the information about use'd crates and extern
36 // libraries necessary for later resolving, typechecking, linking, etc.
37 pub fn read_crates(sess: Session,
40 intr: @IdentInterner) {
44 crate_cache: @RefCell::new(~[]),
48 visit_crate(&e, crate);
50 let mut v = ReadCrateVisitor {
53 visit::walk_crate(&mut v, crate, ());
55 let crate_cache = e.crate_cache.borrow();
56 dump_crates(*crate_cache.get());
57 warn_if_multiple_versions(&mut e, sess.diagnostic(), *crate_cache.get());
60 struct ReadCrateVisitor<'a> {
64 impl<'a> visit::Visitor<()> for ReadCrateVisitor<'a> {
65 fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
66 visit_view_item(self.e, a);
67 visit::walk_view_item(self, a, ());
69 fn visit_item(&mut self, a: &ast::Item, _: ()) {
70 visit_item(self.e, a);
71 visit::walk_item(self, a, ());
83 fn dump_crates(crate_cache: &[cache_entry]) {
84 debug!("resolved crates:");
85 for entry in crate_cache.iter() {
86 debug!("cnum: {:?}", entry.cnum);
87 debug!("span: {:?}", entry.span);
88 debug!("hash: {:?}", entry.hash);
92 fn warn_if_multiple_versions(e: &mut Env,
94 crate_cache: &[cache_entry]) {
95 if crate_cache.len() != 0u {
96 let name = crate_cache[crate_cache.len() - 1].crateid.name.clone();
98 let (matches, non_matches) = crate_cache.partitioned(|entry|
99 name == entry.crateid.name);
101 assert!(!matches.is_empty());
103 if matches.len() != 1u {
105 format!("using multiple versions of crate `{}`", name));
106 for match_ in matches.iter() {
107 diag.span_note(match_.span, "used here");
108 loader::note_crateid_attr(diag, &match_.crateid);
112 warn_if_multiple_versions(e, diag, non_matches);
119 crate_cache: @RefCell<~[cache_entry]>,
120 next_crate_num: ast::CrateNum,
124 fn visit_crate(e: &Env, c: &ast::Crate) {
125 let cstore = e.sess.cstore;
127 for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
128 match a.value_str() {
129 Some(ref linkarg) => cstore.add_used_link_args(linkarg.get()),
130 None => { /* fallthrough */ }
135 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
136 let should_load = i.attrs.iter().all(|attr| {
137 attr.name().get() != "phase" ||
138 attr.meta_item_list().map_or(false, |phases| {
139 attr::contains_name(phases, "link")
147 match extract_crate_info(i) {
149 let cnum = resolve_crate(e, info.ident, info.name, info.version,
151 e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
164 fn extract_crate_info(i: &ast::ViewItem) -> Option<CrateInfo> {
166 ast::ViewItemExternMod(ref ident, ref path_opt, id) => {
167 let ident = token::ident_to_str(ident);
168 debug!("resolving extern mod stmt. ident: {:?} path_opt: {:?}",
170 let (name, version) = match *path_opt {
171 Some((ref path_str, _)) => {
172 let crateid: Option<CrateId> = from_str(path_str.get());
176 let version = match crateid.version {
178 Some(ref ver) => ver.to_managed(),
180 (crateid.name.to_managed(), version)
184 None => (ident, @""),
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, name: @str, version: @str, hash: &str) -> Option<ast::CrateNum> {
282 let crate_cache = e.crate_cache.borrow();
283 for c in crate_cache.get().iter() {
284 let crateid_version = match c.crateid.version {
286 Some(ref ver) => ver.to_managed(),
288 if (name.is_empty() || c.crateid.name.to_managed() == name) &&
289 (version.is_empty() || crateid_version == version) &&
290 (hash.is_empty() || c.hash.as_slice() == hash) {
297 fn resolve_crate(e: &mut Env,
304 match existing_match(e, name, version, hash) {
306 let load_ctxt = loader::Context {
316 let loader::Library {
317 dylib, rlib, metadata
318 } = load_ctxt.load_library_crate();
320 let attrs = decoder::get_crate_attributes(metadata.as_slice());
321 let crateid = attr::find_crateid(attrs).unwrap();
322 let hash = decoder::get_crate_hash(metadata.as_slice());
324 // Claim this crate number and cache it
325 let cnum = e.next_crate_num;
327 let mut crate_cache = e.crate_cache.borrow_mut();
328 crate_cache.get().push(cache_entry {
335 e.next_crate_num += 1;
337 // Now resolve the crates referenced by this crate
338 let cnum_map = resolve_crate_deps(e, metadata.as_slice());
340 let cmeta = @cstore::crate_metadata {
347 let cstore = e.sess.cstore;
348 cstore.set_crate_data(cnum, cmeta);
349 cstore.add_used_crate_source(cstore::CrateSource {
362 // Go through the crate metadata and load any crates that it references
363 fn resolve_crate_deps(e: &mut Env, cdata: &[u8]) -> cstore::cnum_map {
364 debug!("resolving deps of external crate");
365 // The map from crate numbers in the crate we're resolving to local crate
367 let mut cnum_map = HashMap::new();
368 let r = decoder::get_crate_deps(cdata);
369 for dep in r.iter() {
370 let extrn_cnum = dep.cnum;
371 let cname_str = token::ident_to_str(&dep.name);
372 debug!("resolving dep crate {} ver: {} hash: {}",
373 cname_str, dep.vers, dep.hash);
374 match existing_match(e, cname_str, dep.vers, dep.hash) {
375 Some(local_cnum) => {
376 debug!("already have it");
377 // We've already seen this crate
378 cnum_map.insert(extrn_cnum, local_cnum);
381 debug!("need to load it");
382 // This is a new one so we've got to load it
383 // FIXME (#2404): Need better error reporting than just a bogus
385 let fake_span = DUMMY_SP;
386 let local_cnum = resolve_crate(e, cname_str, cname_str, dep.vers,
387 dep.hash, fake_span);
388 cnum_map.insert(extrn_cnum, local_cnum);
392 return @RefCell::new(cnum_map);
400 pub fn new(sess: Session) -> Loader {
401 let os = driver::get_os(driver::host_triple()).unwrap();
402 let os = session::sess_os_to_meta_os(os);
407 crate_cache: @RefCell::new(~[]),
409 intr: token::get_ident_interner(),
415 impl CrateLoader for Loader {
416 fn load_crate(&mut self, crate: &ast::ViewItem) -> MacroCrate {
417 let info = extract_crate_info(crate).unwrap();
418 let cnum = resolve_crate(&mut self.env, info.ident, info.name,
419 info.version, @"", crate.span);
420 let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap();
427 fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> ~[~str] {
428 csearch::get_exported_macros(self.env.sess.cstore, cnum)
431 fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> {
432 let cstore = self.env.sess.cstore;
433 csearch::get_macro_registrar_fn(cstore, cnum)
434 .map(|did| csearch::get_symbol(cstore, did))