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(~[]),
52 visit_crate(&e, krate);
54 let mut v = ReadCrateVisitor {
57 visit::walk_crate(&mut v, krate, ());
59 let crate_cache = e.crate_cache.borrow();
60 dump_crates(*crate_cache.get());
61 warn_if_multiple_versions(&mut e, sess.diagnostic(), *crate_cache.get());
64 struct ReadCrateVisitor<'a> {
68 impl<'a> visit::Visitor<()> for ReadCrateVisitor<'a> {
69 fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
70 visit_view_item(self.e, a);
71 visit::walk_view_item(self, a, ());
73 fn visit_item(&mut self, a: &ast::Item, _: ()) {
74 visit_item(self.e, a);
75 visit::walk_item(self, a, ());
87 fn dump_crates(crate_cache: &[cache_entry]) {
88 debug!("resolved crates:");
89 for entry in crate_cache.iter() {
90 debug!("cnum: {:?}", entry.cnum);
91 debug!("span: {:?}", entry.span);
92 debug!("hash: {:?}", entry.hash);
96 fn warn_if_multiple_versions(e: &mut Env,
98 crate_cache: &[cache_entry]) {
99 if crate_cache.len() != 0u {
100 let name = crate_cache[crate_cache.len() - 1].crate_id.name.clone();
102 let (matches, non_matches) = crate_cache.partitioned(|entry|
103 name == entry.crate_id.name);
105 assert!(!matches.is_empty());
107 if matches.len() != 1u {
109 format!("using multiple versions of crate `{}`", name));
110 for match_ in matches.iter() {
111 diag.span_note(match_.span, "used here");
112 loader::note_crateid_attr(diag, &match_.crate_id);
116 warn_if_multiple_versions(e, diag, non_matches);
123 crate_cache: @RefCell<~[cache_entry]>,
124 next_crate_num: ast::CrateNum,
128 fn visit_crate(e: &Env, c: &ast::Crate) {
129 let cstore = e.sess.cstore;
131 for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
132 match a.value_str() {
133 Some(ref linkarg) => cstore.add_used_link_args(linkarg.get()),
134 None => { /* fallthrough */ }
139 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
140 let should_load = i.attrs.iter().all(|attr| {
141 attr.name().get() != "phase" ||
142 attr.meta_item_list().map_or(false, |phases| {
143 attr::contains_name(phases, "link")
151 match extract_crate_info(e, i) {
153 let cnum = resolve_crate(e, None, info.ident, &info.crate_id, None,
155 e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
167 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
169 ast::ViewItemExternMod(ident, ref path_opt, id) => {
170 let ident = token::get_ident(ident);
171 debug!("resolving extern crate stmt. ident: {:?} path_opt: {:?}",
173 let crate_id = match *path_opt {
174 Some((ref path_str, _)) => {
175 let crateid: Option<CrateId> = from_str(path_str.get());
178 e.sess.span_err(i.span, "malformed crate id");
184 None => from_str(ident.get().to_str()).unwrap()
187 ident: ident.get().to_str(),
196 fn visit_item(e: &Env, i: &ast::Item) {
198 ast::ItemForeignMod(ref fm) => {
199 if fm.abis.is_rust() || fm.abis.is_intrinsic() {
203 // First, add all of the custom link_args attributes
204 let cstore = e.sess.cstore;
205 let link_args = i.attrs.iter()
206 .filter_map(|at| if at.name().equiv(&("link_args")) {
212 for m in link_args.iter() {
213 match m.value_str() {
214 Some(linkarg) => cstore.add_used_link_args(linkarg.get()),
215 None => { /* fallthrough */ }
219 // Next, process all of the #[link(..)]-style arguments
220 let cstore = e.sess.cstore;
221 let link_args = i.attrs.iter()
222 .filter_map(|at| if at.name().equiv(&("link")) {
228 for m in link_args.iter() {
229 match m.meta_item_list() {
231 let kind = items.iter().find(|k| {
232 k.name().equiv(&("kind"))
233 }).and_then(|a| a.value_str());
234 let kind = match kind {
236 if k.equiv(&("static")) {
238 } else if e.sess.targ_cfg.os == abi::OsMacos &&
239 k.equiv(&("framework")) {
240 cstore::NativeFramework
241 } else if k.equiv(&("framework")) {
242 e.sess.span_err(m.span,
243 "native frameworks are only available \
245 cstore::NativeUnknown
247 e.sess.span_err(m.span,
248 format!("unknown kind: `{}`", k));
249 cstore::NativeUnknown
252 None => cstore::NativeUnknown
254 let n = items.iter().find(|n| {
255 n.name().equiv(&("name"))
256 }).and_then(|a| a.value_str());
260 e.sess.span_err(m.span,
261 "#[link(...)] specified without \
263 InternedString::new("foo")
266 if n.get().is_empty() {
267 e.sess.span_err(m.span, "#[link(name = \"\")] given with empty name");
269 cstore.add_used_library(n.get().to_owned(), kind);
280 fn existing_match(e: &Env, crate_id: &CrateId,
281 hash: Option<&Svh>) -> Option<ast::CrateNum> {
282 let crate_cache = e.crate_cache.borrow();
283 for c in crate_cache.get().iter() {
284 if !crate_id.matches(&c.crate_id) { continue }
286 Some(hash) if *hash != c.hash => {}
287 Some(..) | None => return Some(c.cnum)
293 fn resolve_crate(e: &mut Env,
294 root_ident: Option<&str>,
300 match existing_match(e, crate_id, hash) {
302 let id_hash = link::crate_id_hash(crate_id);
303 let mut load_ctxt = loader::Context {
309 hash: hash.map(|a| &*a),
312 rejected_via_hash: false,
314 let loader::Library {
315 dylib, rlib, metadata
316 } = load_ctxt.load_library_crate(root_ident);
318 let crate_id = decoder::get_crate_id(metadata.as_slice());
319 let hash = decoder::get_crate_hash(metadata.as_slice());
321 // Claim this crate number and cache it
322 let cnum = e.next_crate_num;
324 let mut crate_cache = e.crate_cache.borrow_mut();
325 crate_cache.get().push(cache_entry {
332 e.next_crate_num += 1;
334 // Maintain a reference to the top most crate.
335 let root_crate = match root_ident {
337 None => load_ctxt.ident.clone()
340 // Now resolve the crates referenced by this crate
341 let cnum_map = resolve_crate_deps(e,
346 let cmeta = @cstore::crate_metadata {
347 name: load_ctxt.crate_id.name.to_owned(),
353 let cstore = e.sess.cstore;
354 cstore.set_crate_data(cnum, cmeta);
355 cstore.add_used_crate_source(cstore::CrateSource {
368 // Go through the crate metadata and load any crates that it references
369 fn resolve_crate_deps(e: &mut Env,
370 root_ident: Option<&str>,
371 cdata: &[u8], span : Span)
372 -> cstore::cnum_map {
373 debug!("resolving deps of external crate");
374 // The map from crate numbers in the crate we're resolving to local crate
376 let mut cnum_map = HashMap::new();
377 let r = decoder::get_crate_deps(cdata);
378 for dep in r.iter() {
379 let extrn_cnum = dep.cnum;
380 debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
381 let local_cnum = resolve_crate(e, root_ident,
382 dep.crate_id.name.as_slice(),
386 cnum_map.insert(extrn_cnum, local_cnum);
388 return @RefCell::new(cnum_map);
396 pub fn new(sess: Session) -> Loader {
397 let os = driver::get_os(driver::host_triple()).unwrap();
398 let os = session::sess_os_to_meta_os(os);
403 crate_cache: @RefCell::new(~[]),
405 intr: token::get_ident_interner(),
411 impl CrateLoader for Loader {
412 fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
413 let info = extract_crate_info(&self.env, krate).unwrap();
414 let cnum = resolve_crate(&mut self.env, None, info.ident,
415 &info.crate_id, None, krate.span);
416 let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap();
423 fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> ~[~str] {
424 csearch::get_exported_macros(self.env.sess.cstore, cnum)
427 fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> {
428 let cstore = self.env.sess.cstore;
429 csearch::get_macro_registrar_fn(cstore, cnum)
430 .map(|did| csearch::get_symbol(cstore, did))