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
17 use metadata::common::*;
18 use metadata::decoder;
19 use metadata::filesearch::FileSearch;
26 use syntax::codemap::{span, dummy_sp};
27 use syntax::diagnostic::span_handler;
28 use syntax::parse::token::ident_interner;
29 use syntax::print::pprust;
31 use syntax::{ast, ast_util};
32 use std::oldmap::HashMap;
34 // Traverses an AST, reading all the information about use'd crates and extern
35 // libraries necessary for later resolving, typechecking, linking, etc.
36 pub fn read_crates(diag: span_handler,
38 cstore: @mut cstore::CStore,
39 filesearch: FileSearch,
42 intr: @ident_interner) {
45 filesearch: filesearch,
49 crate_cache: @mut ~[],
54 visit::mk_simple_visitor(@visit::SimpleVisitor {
55 visit_view_item: |a| visit_view_item(e, a),
56 visit_item: |a| visit_item(e, a),
57 .. *visit::default_simple_visitor()});
58 visit::visit_crate(crate, (), v);
59 dump_crates(e.crate_cache);
60 warn_if_multiple_versions(e, diag, e.crate_cache);
67 metas: @~[@ast::meta_item]
70 fn dump_crates(+crate_cache: @mut ~[cache_entry]) {
71 debug!("resolved crates:");
72 for crate_cache.each |entry| {
73 debug!("cnum: %?", entry.cnum);
74 debug!("span: %?", entry.span);
75 debug!("hash: %?", entry.hash);
79 fn warn_if_multiple_versions(e: @mut Env,
81 crate_cache: @mut ~[cache_entry]) {
84 if crate_cache.len() != 0u {
85 let name = loader::crate_name_from_metas(
86 /*bad*/copy *crate_cache.last().metas);
87 let (matches, non_matches) =
88 partition(crate_cache.map_to_vec(|&entry| {
89 let othername = loader::crate_name_from_metas(
91 if name == othername {
98 assert !matches.is_empty();
100 if matches.len() != 1u {
102 fmt!("using multiple versions of crate `%s`", name));
103 for matches.each |match_| {
104 diag.span_note(match_.span, ~"used here");
106 attr::mk_attr(attr::mk_list_item(
107 ~"link", /*bad*/copy *match_.metas))
109 loader::note_linkage_attrs(e.intr, diag, attrs);
113 warn_if_multiple_versions(e, diag, @mut non_matches);
119 filesearch: FileSearch,
120 cstore: @mut cstore::CStore,
123 crate_cache: @mut ~[cache_entry],
124 next_crate_num: ast::crate_num,
125 intr: @ident_interner
128 fn visit_view_item(e: @mut Env, i: @ast::view_item) {
129 match /*bad*/copy i.node {
130 ast::view_item_extern_mod(ident, meta_items, id) => {
131 debug!("resolving extern mod stmt. ident: %?, meta: %?",
133 let cnum = resolve_crate(e, ident, meta_items, ~"", i.span);
134 cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
140 fn visit_item(e: @mut Env, i: @ast::item) {
141 match /*bad*/copy i.node {
142 ast::item_foreign_mod(fm) => {
143 match attr::foreign_abi(i.attrs) {
144 either::Right(abi) => {
145 if abi != ast::foreign_abi_cdecl &&
146 abi != ast::foreign_abi_stdcall { return; }
148 either::Left(ref msg) => e.diag.span_fatal(i.span, (*msg))
151 let cstore = e.cstore;
152 let mut already_added = false;
153 let link_args = attr::find_attrs_by_name(i.attrs, "link_args");
158 match attr::first_attr_value_str_by_name(i.attrs,
164 ~"empty #[link_name] not allowed; use #[nolink].");
168 None => /*bad*/copy *e.intr.get(i.ident)
170 if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() {
172 !cstore::add_used_library(cstore, copy foreign_name);
174 if !link_args.is_empty() && already_added {
175 e.diag.span_fatal(i.span, ~"library '" + foreign_name +
176 ~"' already added: can't specify link_args.");
179 ast::anonymous => { /* do nothing */ }
182 for link_args.each |a| {
183 match attr::get_meta_item_value_str(attr::attr_meta(*a)) {
184 Some(ref linkarg) => {
185 cstore::add_used_link_args(cstore, (/*bad*/copy *linkarg));
187 None => {/* fallthrough */ }
195 fn metas_with(+ident: ~str, +key: ~str, +metas: ~[@ast::meta_item])
196 -> ~[@ast::meta_item] {
197 let name_items = attr::find_meta_items_by_name(metas, key);
198 if name_items.is_empty() {
199 vec::append_one(metas, attr::mk_name_value_item_str(key, ident))
205 fn metas_with_ident(+ident: ~str, +metas: ~[@ast::meta_item])
206 -> ~[@ast::meta_item] {
207 metas_with(ident, ~"name", metas)
210 fn existing_match(e: @mut Env, metas: ~[@ast::meta_item], hash: ~str)
212 for e.crate_cache.each |c| {
213 if loader::metadata_matches(*c.metas, metas)
214 && (hash.is_empty() || c.hash == hash) {
221 fn resolve_crate(e: @mut Env,
223 +metas: ~[@ast::meta_item],
227 let metas = metas_with_ident(/*bad*/copy *e.intr.get(ident), metas);
229 match existing_match(e, metas, hash) {
231 let load_ctxt: loader::ctxt = {
233 filesearch: e.filesearch,
242 let cinfo = loader::load_library_crate(load_ctxt);
244 let cfilename = Path(cinfo.ident);
245 let cdata = cinfo.data;
247 let attrs = decoder::get_crate_attributes(cdata);
248 let linkage_metas = attr::find_linkage_metas(attrs);
249 let hash = decoder::get_crate_hash(cdata);
251 // Claim this crate number and cache it
252 let cnum = e.next_crate_num;
253 e.crate_cache.push({cnum: cnum, span: span,
254 hash: hash, metas: @linkage_metas});
255 e.next_crate_num += 1;
257 // Now resolve the crates referenced by this crate
258 let cnum_map = resolve_crate_deps(e, cdata);
261 match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
263 option::Some(ref v) => (/*bad*/copy *v),
264 option::None => /*bad*/copy *e.intr.get(ident)
266 let cmeta = @{name: cname, data: cdata,
267 cnum_map: cnum_map, cnum: cnum};
269 let cstore = e.cstore;
270 cstore::set_crate_data(cstore, cnum, cmeta);
271 cstore::add_used_crate_file(cstore, &cfilename);
280 // Go through the crate metadata and load any crates that it references
281 fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
282 debug!("resolving deps of external crate");
283 // The map from crate numbers in the crate we're resolving to local crate
285 let cnum_map = HashMap();
286 for decoder::get_crate_deps(e.intr, cdata).each |dep| {
287 let extrn_cnum = dep.cnum;
288 let cname = dep.name;
289 let cmetas = metas_with(/*bad*/copy dep.vers, ~"vers", ~[]);
290 debug!("resolving dep crate %s ver: %s hash: %s",
291 *e.intr.get(dep.name), dep.vers, dep.hash);
292 match existing_match(e, metas_with_ident(copy *e.intr.get(cname),
295 Some(local_cnum) => {
296 debug!("already have it");
297 // We've already seen this crate
298 cnum_map.insert(extrn_cnum, local_cnum);
301 debug!("need to load it");
302 // This is a new one so we've got to load it
303 // FIXME (#2404): Need better error reporting than just a bogus
305 let fake_span = dummy_sp();
306 let local_cnum = resolve_crate(e, cname, cmetas,
307 /*bad*/copy dep.hash, fake_span);
308 cnum_map.insert(extrn_cnum, local_cnum);
318 // indent-tabs-mode: nil
320 // buffer-file-coding-system: utf-8-unix