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::session::Session;
15 use metadata::decoder;
18 use std::hashmap::HashMap;
22 use syntax::attr::AttrMetaMethods;
23 use syntax::codemap::{Span, dummy_sp};
24 use syntax::diagnostic::span_handler;
25 use syntax::parse::token;
26 use syntax::parse::token::ident_interner;
29 // Traverses an AST, reading all the information about use'd crates and extern
30 // libraries necessary for later resolving, typechecking, linking, etc.
31 pub fn read_crates(sess: Session,
34 intr: @ident_interner) {
38 crate_cache: @mut ~[],
42 let mut v = ReadCrateVisitor{ e:e };
43 visit_crate(e, crate);
44 visit::walk_crate(&mut v, crate, ());
45 dump_crates(*e.crate_cache);
46 warn_if_multiple_versions(e, sess.diagnostic(), *e.crate_cache);
49 struct ReadCrateVisitor { e:@mut Env }
50 impl visit::Visitor<()> for ReadCrateVisitor {
51 fn visit_view_item(&mut self, a:&ast::view_item, _:()) {
52 visit_view_item(self.e, a);
53 visit::walk_view_item(self, a, ());
55 fn visit_item(&mut self, a:@ast::item, _:()) {
56 visit_item(self.e, a);
57 visit::walk_item(self, a, ());
66 metas: @~[@ast::MetaItem]
69 fn dump_crates(crate_cache: &[cache_entry]) {
70 debug!("resolved crates:");
71 for entry in crate_cache.iter() {
72 debug!("cnum: {:?}", entry.cnum);
73 debug!("span: {:?}", entry.span);
74 debug!("hash: {:?}", entry.hash);
78 fn warn_if_multiple_versions(e: @mut Env,
79 diag: @mut span_handler,
80 crate_cache: &[cache_entry]) {
81 if crate_cache.len() != 0u {
82 let name = loader::crate_name_from_metas(
83 *crate_cache[crate_cache.len() - 1].metas
86 let (matches, non_matches) = crate_cache.partitioned(|entry|
87 name == loader::crate_name_from_metas(*entry.metas));
89 assert!(!matches.is_empty());
91 if matches.len() != 1u {
93 format!("using multiple versions of crate `{}`", name));
94 for match_ in matches.iter() {
95 diag.span_note(match_.span, "used here");
97 attr::mk_attr(attr::mk_list_item(@"link",
98 (*match_.metas).clone()))
100 loader::note_linkage_attrs(e.intr, diag, attrs);
104 warn_if_multiple_versions(e, diag, non_matches);
111 crate_cache: @mut ~[cache_entry],
112 next_crate_num: ast::CrateNum,
113 intr: @ident_interner
116 fn visit_crate(e: &Env, c: &ast::Crate) {
117 let cstore = e.sess.cstore;
119 for a in c.attrs.iter().filter(|m| "link_args" == m.name()) {
120 match a.value_str() {
121 Some(ref linkarg) => {
122 cstore::add_used_link_args(cstore, *linkarg);
124 None => {/* fallthrough */ }
129 fn visit_view_item(e: @mut Env, i: &ast::view_item) {
131 ast::view_item_extern_mod(ident, path_opt, ref meta_items, id) => {
132 let ident = token::ident_to_str(&ident);
133 let meta_items = match path_opt {
134 None => meta_items.clone(),
135 Some((p, _path_str_style)) => {
136 let p_path = Path::init(p);
137 match p_path.filestem_str() {
139 e.sess.span_bug(i.span, "Bad package path in `extern mod` item"),
142 ~[attr::mk_name_value_item_str(@"package_id", p),
143 attr::mk_name_value_item_str(@"name", s.to_managed())],
148 debug!("resolving extern mod stmt. ident: {:?}, meta: {:?}",
150 let cnum = resolve_crate(e,
155 cstore::add_extern_mod_stmt_cnum(e.sess.cstore, id, cnum);
161 fn visit_item(e: &Env, i: @ast::item) {
163 ast::item_foreign_mod(ref fm) => {
164 if fm.abis.is_rust() || fm.abis.is_intrinsic() {
168 // First, add all of the custom link_args attributes
169 let cstore = e.sess.cstore;
170 let link_args = i.attrs.iter()
171 .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
173 for m in link_args.iter() {
174 match m.value_str() {
176 cstore::add_used_link_args(cstore, linkarg);
178 None => { /* fallthrough */ }
182 // Next, process all of the #[link(..)]-style arguments
183 let cstore = e.sess.cstore;
184 let link_args = i.attrs.iter()
185 .filter_map(|at| if "link" == at.name() {Some(at)} else {None})
187 for m in link_args.iter() {
188 match m.meta_item_list() {
190 let kind = do items.iter().find |k| {
192 }.and_then(|a| a.value_str());
193 let kind = match kind {
194 Some(k) if "static" == k => cstore::NativeStatic,
196 e.sess.span_fatal(i.span,
197 format!("unknown kind: `{}`", k));
199 None => cstore::NativeUnknown
201 let n = do items.iter().find |n| {
203 }.and_then(|a| a.value_str());
207 e.sess.span_fatal(i.span,
208 "#[link(...)] specified without \
212 cstore::add_used_library(cstore, n.to_owned(), kind);
222 fn metas_with(ident: @str, key: @str, mut metas: ~[@ast::MetaItem])
223 -> ~[@ast::MetaItem] {
224 // Check if key isn't there yet.
225 if !attr::contains_name(metas, key) {
226 metas.push(attr::mk_name_value_item_str(key, ident));
231 fn metas_with_ident(ident: @str, metas: ~[@ast::MetaItem])
232 -> ~[@ast::MetaItem] {
233 metas_with(ident, @"name", metas)
236 fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str)
237 -> Option<ast::CrateNum> {
238 for c in e.crate_cache.iter() {
239 if loader::metadata_matches(*c.metas, metas)
240 && (hash.is_empty() || c.hash.as_slice() == hash) {
247 fn resolve_crate(e: @mut Env,
249 metas: ~[@ast::MetaItem],
253 let metas = metas_with_ident(ident, metas);
255 match existing_match(e, metas, hash) {
257 let load_ctxt = loader::Context {
266 let loader::Library {
267 dylib, rlib, metadata
268 } = load_ctxt.load_library_crate();
270 let attrs = decoder::get_crate_attributes(metadata);
271 let linkage_metas = attr::find_linkage_metas(attrs);
272 let hash = decoder::get_crate_hash(metadata);
274 // Claim this crate number and cache it
275 let cnum = e.next_crate_num;
276 e.crate_cache.push(cache_entry {
280 metas: @linkage_metas
282 e.next_crate_num += 1;
284 // Now resolve the crates referenced by this crate
285 let cnum_map = resolve_crate_deps(e, metadata);
288 match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
293 let cmeta = @cstore::crate_metadata {
300 let cstore = e.sess.cstore;
301 cstore::set_crate_data(cstore, cnum, cmeta);
302 cstore::add_used_crate_source(cstore, cstore::CrateSource {
315 // Go through the crate metadata and load any crates that it references
316 fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
317 debug!("resolving deps of external crate");
318 // The map from crate numbers in the crate we're resolving to local crate
320 let mut cnum_map = HashMap::new();
321 let r = decoder::get_crate_deps(cdata);
322 for dep in r.iter() {
323 let extrn_cnum = dep.cnum;
324 let cname_str = token::ident_to_str(&dep.name);
325 let cmetas = metas_with(dep.vers, @"vers", ~[]);
326 debug!("resolving dep crate {} ver: {} hash: {}",
327 cname_str, dep.vers, dep.hash);
328 match existing_match(e,
329 metas_with_ident(cname_str, cmetas.clone()),
331 Some(local_cnum) => {
332 debug!("already have it");
333 // We've already seen this crate
334 cnum_map.insert(extrn_cnum, local_cnum);
337 debug!("need to load it");
338 // This is a new one so we've got to load it
339 // FIXME (#2404): Need better error reporting than just a bogus
341 let fake_span = dummy_sp();
342 let local_cnum = resolve_crate(e, cname_str, cmetas, dep.hash,
344 cnum_map.insert(extrn_cnum, local_cnum);
348 return @mut cnum_map;