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.
12 //! Validates all used crates and extern libraries and loads their metadata
15 use metadata::decoder;
16 use metadata::filesearch::FileSearch;
19 use core::hashmap::HashMap;
21 use syntax::codemap::{span, dummy_sp};
22 use syntax::diagnostic::span_handler;
23 use syntax::parse::token::ident_interner;
27 // Traverses an AST, reading all the information about use'd crates and extern
28 // libraries necessary for later resolving, typechecking, linking, etc.
29 pub fn read_crates(diag: @span_handler,
31 cstore: @mut cstore::CStore,
32 filesearch: @FileSearch,
35 intr: @ident_interner) {
38 filesearch: filesearch,
42 crate_cache: @mut ~[],
47 visit::mk_simple_visitor(@visit::SimpleVisitor {
48 visit_view_item: |a| visit_view_item(e, a),
49 visit_item: |a| visit_item(e, a),
50 .. *visit::default_simple_visitor()});
51 visit_crate(e, crate);
52 visit::visit_crate(crate, (), v);
53 dump_crates(e.crate_cache);
54 warn_if_multiple_versions(e, diag, e.crate_cache);
61 metas: @~[@ast::meta_item]
64 fn dump_crates(crate_cache: @mut ~[cache_entry]) {
65 debug!("resolved crates:");
66 for crate_cache.each |entry| {
67 debug!("cnum: %?", entry.cnum);
68 debug!("span: %?", entry.span);
69 debug!("hash: %?", entry.hash);
73 fn warn_if_multiple_versions(e: @mut Env,
75 crate_cache: @mut ~[cache_entry]) {
78 let crate_cache = &mut *crate_cache;
80 if crate_cache.len() != 0u {
81 let name = loader::crate_name_from_metas(
82 *crate_cache[crate_cache.len() - 1].metas
85 let (matches, non_matches) =
86 partition(crate_cache.map_to_vec(|&entry| {
87 let othername = loader::crate_name_from_metas(
89 if name == othername {
96 assert!(!matches.is_empty());
98 if matches.len() != 1u {
100 fmt!("using multiple versions of crate `%s`", *name));
101 for matches.each |match_| {
102 diag.span_note(match_.span, ~"used here");
104 attr::mk_attr(attr::mk_list_item(
105 @~"link", /*bad*/copy *match_.metas))
107 loader::note_linkage_attrs(e.intr, diag, attrs);
111 warn_if_multiple_versions(e, diag, @mut non_matches);
117 filesearch: @FileSearch,
118 cstore: @mut cstore::CStore,
121 crate_cache: @mut ~[cache_entry],
122 next_crate_num: ast::crate_num,
123 intr: @ident_interner
126 fn visit_crate(e: @mut Env, c: &ast::crate) {
127 let cstore = e.cstore;
128 let link_args = attr::find_attrs_by_name(c.node.attrs, "link_args");
130 for link_args.each |a| {
131 match attr::get_meta_item_value_str(attr::attr_meta(*a)) {
132 Some(ref linkarg) => {
133 cstore::add_used_link_args(cstore, **linkarg);
135 None => {/* fallthrough */ }
140 fn visit_view_item(e: @mut Env, i: @ast::view_item) {
142 ast::view_item_extern_mod(ident, /*bad*/copy meta_items, id) => {
143 debug!("resolving extern mod stmt. ident: %?, meta: %?",
145 let cnum = resolve_crate(e, ident, meta_items, @~"", i.span);
146 cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
152 fn visit_item(e: @mut Env, i: @ast::item) {
154 ast::item_foreign_mod(ref fm) => {
155 if fm.abis.is_rust() || fm.abis.is_intrinsic() {
159 let cstore = e.cstore;
160 let mut already_added = false;
161 let link_args = attr::find_attrs_by_name(i.attrs, "link_args");
166 match attr::first_attr_value_str_by_name(i.attrs,
172 ~"empty #[link_name] not allowed; use " +
177 None => e.intr.get(i.ident)
179 if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() {
181 !cstore::add_used_library(cstore, foreign_name);
183 if !link_args.is_empty() && already_added {
184 e.diag.span_fatal(i.span, ~"library '" + *foreign_name +
185 ~"' already added: can't specify link_args.");
188 ast::anonymous => { /* do nothing */ }
191 for link_args.each |a| {
192 match attr::get_meta_item_value_str(attr::attr_meta(*a)) {
194 cstore::add_used_link_args(cstore, *linkarg);
196 None => { /* fallthrough */ }
204 fn metas_with(ident: @~str, key: @~str, metas: ~[@ast::meta_item])
205 -> ~[@ast::meta_item] {
206 let name_items = attr::find_meta_items_by_name(metas, *key);
207 if name_items.is_empty() {
208 vec::append_one(metas, attr::mk_name_value_item_str(key, ident))
214 fn metas_with_ident(ident: @~str, metas: ~[@ast::meta_item])
215 -> ~[@ast::meta_item] {
216 metas_with(ident, @~"name", metas)
219 fn existing_match(e: @mut Env, metas: &[@ast::meta_item], hash: @~str)
221 for e.crate_cache.each |c| {
222 if loader::metadata_matches(*c.metas, metas)
223 && (hash.is_empty() || c.hash == hash) {
230 fn resolve_crate(e: @mut Env,
232 metas: ~[@ast::meta_item],
236 let metas = metas_with_ident(@/*bad*/copy *e.intr.get(ident), metas);
238 match existing_match(e, metas, hash) {
240 let load_ctxt = loader::Context {
242 filesearch: e.filesearch,
251 let (lident, ldata) = loader::load_library_crate(&load_ctxt);
253 let cfilename = Path(lident);
256 let attrs = decoder::get_crate_attributes(cdata);
257 let linkage_metas = attr::find_linkage_metas(attrs);
258 let hash = decoder::get_crate_hash(cdata);
260 // Claim this crate number and cache it
261 let cnum = e.next_crate_num;
262 e.crate_cache.push(cache_entry {
266 metas: @linkage_metas
268 e.next_crate_num += 1;
270 // Now resolve the crates referenced by this crate
271 let cnum_map = resolve_crate_deps(e, cdata);
274 match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
277 None => e.intr.get(ident),
279 let cmeta = @cstore::crate_metadata {
286 let cstore = e.cstore;
287 cstore::set_crate_data(cstore, cnum, cmeta);
288 cstore::add_used_crate_file(cstore, &cfilename);
297 // Go through the crate metadata and load any crates that it references
298 fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
299 debug!("resolving deps of external crate");
300 // The map from crate numbers in the crate we're resolving to local crate
302 let mut cnum_map = HashMap::new();
303 for decoder::get_crate_deps(e.intr, cdata).each |dep| {
304 let extrn_cnum = dep.cnum;
305 let cname = dep.name;
306 let cmetas = metas_with(dep.vers, @~"vers", ~[]);
307 debug!("resolving dep crate %s ver: %s hash: %s",
308 *e.intr.get(dep.name), *dep.vers, *dep.hash);
309 match existing_match(e, metas_with_ident(e.intr.get(cname),
312 Some(local_cnum) => {
313 debug!("already have it");
314 // We've already seen this crate
315 cnum_map.insert(extrn_cnum, local_cnum);
318 debug!("need to load it");
319 // This is a new one so we've got to load it
320 // FIXME (#2404): Need better error reporting than just a bogus
322 let fake_span = dummy_sp();
323 let local_cnum = resolve_crate(e, cname, cmetas, dep.hash,
325 cnum_map.insert(extrn_cnum, local_cnum);
329 return @mut cnum_map;
335 // indent-tabs-mode: nil
337 // buffer-file-coding-system: utf-8-unix